| Crates.io | vac_downloader |
| lib.rs | vac_downloader |
| version | 0.5.0 |
| created_at | 2025-12-11 18:12:47.560267+00 |
| updated_at | 2025-12-15 11:14:13.16412+00 |
| description | Rust crate/tool to download and keep up-to-date versions of the French Visual Approach Charts |
| homepage | |
| repository | https://github.com/jcorbier/VAC-Downloader |
| max_upload_size | |
| id | 1980240 |
| size | 111,153 |
A Rust module that fetches French VAC (Visual Approach Charts) data from the SOFIA API, filters for airport (AD) entries, caches versions in SQLite, and downloads PDFs only when newer versions are available.
src/
├── cli/
│ ├── main.rs # CLI executable entry point
│ └── config.rs # Configuration file handling
└── lib/
├── lib.rs # Library module exports
├── models.rs # Data structures (OACIS response, VAC entries)
├── auth.rs # Authentication (SHA-512 + Basic Auth)
├── database.rs # SQLite caching and version management
└── downloader.rs # Main sync logic with API client
use vac_downloader::VacDownloader;
fn main() -> anyhow::Result<()> {
let downloader = VacDownloader::new("vac_cache.db", "./downloads")?;
// Download all entries
let stats = downloader.sync(None)?;
// Or download specific OACI codes
let oaci_codes = vec!["LFPG".to_string(), "LFPO".to_string()];
let stats = downloader.sync(Some(&oaci_codes))?;
println!("Downloaded: {}", stats.downloaded);
println!("Up to date: {}", stats.up_to_date);
Ok(())
}
# Build
cargo build --release
# Run with default settings (db: vac_cache.db, downloads: ./downloads)
cargo run --release
# Specify custom database path
cargo run --release -- --db-path /path/to/custom.db
# Specify custom download directory
cargo run --release -- --download-dir /path/to/downloads
# Specify both
cargo run --release -- -d /path/to/custom.db -o /path/to/downloads
# Download specific airports by OACI code
cargo run --release -- --oaci LFPG
cargo run --release -- --oaci LFPG,LFPO,LFPB
# Combine with custom paths
cargo run --release -- -d custom.db -o ./pdfs --oaci LFPG
# View help
cargo run --release -- --help
# Or use the binary directly
./target/release/vac_downloader --db-path custom.db --download-dir ./pdfs
| Option | Short | Default | Description |
|---|---|---|---|
--db-path |
-d |
vac_cache.db |
Path to the SQLite database file |
--download-dir |
-o |
./downloads |
Directory where PDFs will be downloaded |
--oaci |
-c |
- | OACI codes to download (can specify multiple, separated by commas) |
--help |
-h |
- | Print help information |
--version |
-V |
- | Print version information |
You can create a configuration file to set default values for the database path and download directory. Command-line arguments will override these settings.
The configuration file is located at:
~/.config/vac-downloader/config.toml~/Library/Application Support/vac-downloader/config.toml%APPDATA%\vac-downloader\config.tomlIt has the following format:
# Path to the SQLite database file
db_path = "/var/lib/vac/cache.db"
# Directory where PDFs will be downloaded
download_dir = "/var/lib/vac/pdfs"
See config.toml.example for a complete example with documentation.
🛩️ VAC Downloader - Airport (AD) PDF Sync Tool
📦 First run detected - database is empty
Will download ALL AD entries
🌐 Fetching OACIS data from API...
Fetching page 1 from OACIS API...
Found 156 total AD entries so far
Fetching page 2 from OACIS API...
Found 312 total AD entries so far
Total AD entries fetched: 312
🔍 Checking for updates...
Downloading LFPG (LFPG_AD.pdf)...
✓ Saved to "./downloads/LFPG_AD.pdf" (1048576 bytes)
Downloading LFPO (LFPO_AD.pdf)...
✓ Saved to "./downloads/LFPO_AD.pdf" (987654 bytes)
...
✅ Sync complete!
Total entries: 312
Up to date: 0
Downloaded: 312
Failed: 0
reqwest - HTTP client with blocking APIserde / serde_json - JSON serializationrusqlite - SQLite databasesha2 - SHA-512 hashingbase64 - Base64 encodinganyhow - Error handlingtokio - Async runtime (for reqwest)clap - Command-line argument parsingtoml - TOML configuration file parsingdirs - Cross-platform config directory detection| Endpoint | Purpose |
|---|---|
GET /api/v1/oacis |
Fetch VAC metadata (paginated) |
GET /api/v1/custom/file-path/{oaci}/{type} |
Download PDF file |
// Generates: {"tokenUri": "<sha512_hash>"}
let auth = AuthGenerator::generate_auth_header("/api/v1/oacis", None);
// Generates: "Basic YXBpOkw0YjZQIWQ5K1l1aUc4LU0="
let basic = AuthGenerator::generate_basic_auth();
CREATE TABLE vac_cache (
oaci TEXT NOT NULL,
vac_type TEXT NOT NULL,
version TEXT NOT NULL,
file_name TEXT NOT NULL,
file_size INTEGER NOT NULL,
city TEXT NOT NULL,
last_updated DATETIME DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (oaci, vac_type)
);
The module uses anyhow::Result for comprehensive error handling:
# Run all tests
cargo test
# Run with output
cargo test -- --nocapture
# Run specific test
cargo test test_auth_generation
MIT