use ansi_term::Style;
use cjval::CJValidator;
#[macro_use]
extern crate clap;
use std::path::Path;
use url::Url;
use clap::{App, AppSettings, Arg};
use anyhow::{anyhow, Result};
fn summary_and_bye(finalresult: i32) {
println!("\n");
println!("============= SUMMARY =============");
if finalresult == -1 {
println!("❌ File is invalid");
} else if finalresult == 0 {
println!("🟡 File is valid but has warnings");
} else {
println!("✅ File is valid");
}
println!("===================================");
std::process::exit(0);
}
fn main() {
let sversions: Vec<String> = cjval::get_cityjson_schema_all_versions();
let desc = format!(
"{}\nSupports CityJSON versions: 2.0 + 1.1 + 1.0 (schemas: v{} + v{} + v{} are used)",
crate_description!(),
sversions[2],
sversions[1],
sversions[0]
);
#[cfg(windows)]
let _ = ansi_term::enable_ansi_support();
let app = App::new(crate_name!())
.setting(AppSettings::ColorAuto)
.setting(AppSettings::ColoredHelp)
.setting(AppSettings::DeriveDisplayOrder)
.max_term_width(90)
.version(crate_version!())
.about(&*desc)
.arg(
Arg::with_name("INPUT")
.required(true)
.help("CityJSON file to validate."),
)
.arg(
Arg::with_name("PATH")
.short("e")
.long("extensionfile")
.multiple(true)
.takes_value(true)
.help(
"Read the CityJSON Extensions files locally. More than one can \
be given. By default the Extension schemas are automatically \
downloaded, this overwrites this behaviour",
),
);
let matches = app.get_matches();
let p1 = Path::new(matches.value_of("INPUT").unwrap())
.canonicalize()
.unwrap();
let s1 = std::fs::read_to_string(&matches.value_of("INPUT").unwrap())
.expect("Couldn't read CityJSON file");
println!(
"{}",
Style::new().bold().paint("=== Input CityJSON file ===")
);
println!("{:?}", p1);
let mut val = CJValidator::from_str(&s1);
println!("{}", Style::new().bold().paint("=== CityJSON schemas ==="));
if val.get_input_cityjson_version() == 0 {
println!("none");
} else {
println!("v{} (builtin)", val.get_cityjson_schema_version());
}
println!("{}", Style::new().bold().paint("=== Extensions ==="));
if val.get_input_cityjson_version() >= 11 {
if let Some(efiles) = matches.values_of("PATH") {
let l: Vec<&str> = efiles.collect();
let is_valid = true;
for s in l {
let s2 = std::fs::read_to_string(s).expect("Couldn't read Extension file");
let scanon = Path::new(s).canonicalize().unwrap();
let re = val.add_one_extension_from_str(&s2);
match re {
Ok(()) => println!("- {}.. ok", scanon.to_str().unwrap()),
Err(e) => {
println!("- {}.. ERROR", scanon.to_str().unwrap());
println!("({})", e);
summary_and_bye(-1);
}
}
}
if is_valid == false {
summary_and_bye(-1);
}
} else {
let re = val.get_extensions_urls();
if re.is_some() {
let lexts = re.unwrap();
for ext in lexts {
let o = download_extension(&ext);
match o {
Ok(l) => {
let re = val.add_one_extension_from_str(&l);
match re {
Ok(()) => println!("- {}.. ok", ext),
Err(e) => {
println!("- {}.. ERROR", ext);
println!("({})", e);
summary_and_bye(-1);
}
}
}
Err(e) => {
println!("- {}.. ERROR \n\t{}", ext, e);
summary_and_bye(-1);
}
}
}
} else {
println!("none");
}
}
}
if val.get_input_cityjson_version() == 10 {
println!("(validation of Extensions is not supported in CityJSON v1.0, upgrade to v1.1)");
}
let valsumm = val.validate();
let mut has_errors = false;
let mut has_warnings = false;
for (criterion, summ) in valsumm.iter() {
println!("=== {} ===", criterion);
println!("{}", summ);
if summ.has_errors() == true {
if summ.is_warning() == true {
has_warnings = true;
} else {
has_errors = true;
}
}
}
if has_errors == false && has_warnings == false {
summary_and_bye(1);
} else if has_errors == false && has_warnings == true {
summary_and_bye(0);
} else {
summary_and_bye(-1);
}
}
#[tokio::main]
async fn download_extension(theurl: &str) -> Result<String> {
let u = Url::parse(theurl).unwrap();
let res = reqwest::get(u).await?;
if res.status().is_success() {
Ok(res.text().await?)
} else {
return Err(anyhow!("Cannot download extension schema: {}", theurl));
}
}