use std::path::{Path, PathBuf};
use clap::Parser;
#[derive(Parser)]
#[command(author, version, about)]
pub struct Cli {
#[arg(short, long, value_parser = existing_canonical_path)]
pub metadata: PathBuf,
#[arg(short, long, value_parser = existing_canonical_path)]
pub features: PathBuf,
#[arg(short, long)]
pub output: PathBuf,
#[arg(long, value_enum)]
pub object_type: Option<Vec<crate::parser::CityObjectType>>,
#[arg(long)]
pub object_attribute: Option<Vec<String>>,
#[arg(long = "3dtiles-metadata-class")]
pub cesium3dtiles_metadata_class: Option<String>,
#[arg(long = "3dtiles-implicit")]
pub cesium3dtiles_implicit: bool,
#[arg(long, short = 'e', default_value = "12")]
pub geometric_error_above_leaf: Option<f64>,
#[arg(long, default_value = "250")]
pub grid_cellsize: Option<u16>,
#[arg(long)]
pub grid_minz: Option<i32>,
#[arg(long)]
pub grid_maxz: Option<i32>,
#[arg(long)]
pub grid_export: bool,
#[arg(long, default_value = "42000")]
pub qtree_capacity: Option<usize>,
#[arg(long, value_parser = existing_path)]
pub exe_geof: Option<PathBuf>,
#[arg(long, default_value = "0.05")]
pub reduce_vertices: Option<f64>,
#[arg(long)]
pub lod_building: Option<String>,
#[arg(long)]
pub lod_building_part: Option<String>,
#[arg(long)]
pub lod_building_installation: Option<String>,
#[arg(long)]
pub lod_tin_relief: Option<String>,
#[arg(long)]
pub lod_road: Option<String>,
#[arg(long)]
pub lod_railway: Option<String>,
#[arg(long)]
pub lod_transport_square: Option<String>,
#[arg(long)]
pub lod_water_body: Option<String>,
#[arg(long)]
pub lod_plant_cover: Option<String>,
#[arg(long)]
pub lod_solitary_vegetation_object: Option<String>,
#[arg(long)]
pub lod_land_use: Option<String>,
#[arg(long)]
pub lod_city_furniture: Option<String>,
#[arg(long)]
pub lod_bridge: Option<String>,
#[arg(long)]
pub lod_bridge_part: Option<String>,
#[arg(long)]
pub lod_bridge_installation: Option<String>,
#[arg(long)]
pub lod_bridge_construction_element: Option<String>,
#[arg(long)]
pub lod_tunnel: Option<String>,
#[arg(long)]
pub lod_tunnel_part: Option<String>,
#[arg(long)]
pub lod_tunnel_installation: Option<String>,
#[arg(long)]
pub lod_generic_city_object: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_building: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_building_part: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_building_installation: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_tin_relief: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_road: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_railway: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_transport_square: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_water_body: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_plant_cover: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_solitary_vegetation_object: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_land_use: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_city_furniture: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_bridge: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_bridge_part: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_bridge_installation: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_bridge_construction_element: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_tunnel: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_tunnel_part: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_tunnel_installation: Option<String>,
#[arg(long, value_parser = hex_color)]
pub color_generic_city_object: Option<String>,
}
fn existing_canonical_path(s: &str) -> Result<PathBuf, String> {
if let Ok(c) = Path::new(s).canonicalize() {
if c.exists() {
Ok(c)
} else {
Err(format!("path {:?} does not exist", &c))
}
} else {
Err(format!("could not resolve the path {:?}", s))
}
}
fn existing_path(s: &str) -> Result<PathBuf, String> {
let p = Path::new(s).to_path_buf();
if p.exists() {
Ok(p)
} else {
Err(format!("path {:?} does not exist", &p))
}
}
fn hex_color(s: &str) -> Result<String, String> {
if s.len() != 7 || !s.starts_with('#') {
return Err(String::from(
"Input must be a 6-digit hexadecimal value preceded by a '#'",
));
}
let hex_digits = &s[1..];
if !hex_digits.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(String::from(
"Input must be a 6-digit hexadecimal value preceded by a '#'",
));
}
Ok(String::from(s))
}
#[cfg(test)]
mod tests {
use super::Cli;
use clap::{CommandFactory, Parser};
fn required_args() -> Vec<&'static str> {
vec![
"tyler",
"-m",
"metadata.city.json",
"-f",
env!("CARGO_MANIFEST_DIR"),
"-o",
env!("CARGO_MANIFEST_DIR"),
"--format",
"3dtiles",
]
}
#[test]
fn verify_cli() {
Cli::command().debug_assert()
}
#[test]
fn verify_object_types() {
let mut types: Vec<&'static str> =
vec!["--object-type", "Building", "--object-type", "PlantCover"];
let mut args = required_args();
args.append(&mut types);
let cli = Cli::try_parse_from(args).unwrap();
let otypes = &cli.object_type.unwrap();
assert!(otypes.contains(&crate::parser::CityObjectType::Building));
assert!(otypes.contains(&crate::parser::CityObjectType::PlantCover));
}
}