use clap::Parser;
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
use log::info;
use std::{path::PathBuf, process};
use skyway::{ConversionBuilder, FileFormatOptions, OsmFormat, SkywayError, sort::SortStrategy};
#[cfg(feature = "filter")]
use skyway::filter::filter_from_path;
fn start_progress(message: &str) -> ProgressBar {
let multi = MultiProgress::new();
let spinner_style = ProgressStyle::with_template("{prefix:.bold.dim} {spinner} {wide_msg}")
.unwrap()
.tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏ ");
let progress = multi.add(ProgressBar::new_spinner());
progress.set_style(spinner_style.clone());
progress.set_message(message.to_owned());
let progress_clone = progress.clone();
std::thread::spawn(move || {
loop {
std::thread::sleep(std::time::Duration::from_millis(100));
progress_clone.tick();
if progress_clone.is_finished() {
break;
}
}
});
progress
}
#[derive(Parser)]
#[command(name = "skyway")]
#[command(author = "Jacob Hall <email@jacobhall.net>")]
#[command(version = env!("CARGO_PKG_VERSION"))]
#[command(about = "Converts OpenStreetMap data between various file formats")]
struct Cli {
#[arg(long)]
from: Option<OsmFormat>,
#[arg(long)]
to: Option<OsmFormat>,
#[arg(long)]
#[arg(value_parser = clap::value_parser!(PathBuf))]
input: Option<PathBuf>,
#[cfg(feature = "filter")]
#[arg(long)]
filter: Option<Vec<PathBuf>>,
#[arg(long)]
#[arg(value_parser = clap::value_parser!(PathBuf))]
output: Option<PathBuf>,
#[arg(long)]
#[arg(value_parser = clap::value_parser!(SortStrategy))]
sort_strategy: Option<SortStrategy>,
#[arg(long)]
omit_references: bool,
#[arg(long)]
no_overwrite: bool,
#[arg(long)]
preserve_generator: bool,
#[arg(long)]
chunk_size: Option<usize>,
}
fn run() -> Result<(), SkywayError> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn"))
.target(env_logger::Target::Stderr)
.init();
let cli = Cli::parse();
let from = OsmFormat::parse(cli.from, &cli.input)?;
info!("Input format determined: {:?}", from);
let to = OsmFormat::parse(cli.to, &cli.output)?;
info!("Output format determined: {:?}", to);
let src = match cli.input {
Some(path) => match cli.no_overwrite && path.exists() {
true => return Err(SkywayError::OutputFileExists),
false => Some(path),
},
None => None,
};
let mut conversion_builder = ConversionBuilder::new(from, to)
.with_source(src)
.with_dest(cli.output)
.with_omit_references(cli.omit_references)
.with_preserve_generator(cli.preserve_generator);
if let Some(chunk_size) = cli.chunk_size {
conversion_builder = conversion_builder.with_chunk_size(chunk_size)
}
#[cfg(feature = "filter")]
if let Some(filters) = cli.filter {
for filter in filters {
let element_filter = filter_from_path(&filter)?;
conversion_builder = conversion_builder.add_filter(element_filter)
}
}
let progress = start_progress("Running conversion...");
conversion_builder.run_conversion()?;
progress.finish_with_message("Running conversion...done");
Ok(())
}
fn main() {
if let Err(err) = run() {
eprintln!("Error: {err}");
process::exit(1);
}
}