//! An example to parse IRI from the CLI argument. use iri_string::types::{RiAbsoluteStr, RiReferenceStr}; const USAGE: &str = "\ USAGE: resolve [FLAGS] [--] BASE REFERENCE FLAGS: -h, --help Prints this help -i, --iri Handle the input as an IRI (RFC 3987) -u, --uri Handle the input as an URI (RFC 3986) -w, --whatwg Serialize normalization result according to WHATWG URL Standard. ARGS: Base IRI or URI to resolve REFERENCE against IRI or URI to resolve "; fn print_help() { eprintln!("{}", USAGE); } fn help_and_exit() -> ! { print_help(); std::process::exit(1); } fn die(msg: impl std::fmt::Display) -> ! { eprintln!("ERROR: {}", msg); eprintln!(); print_help(); std::process::exit(1); } /// Syntax specification. #[derive(Debug, Clone, Copy)] enum Spec { /// RFC 3986 URI. Uri, /// RFC 3987 IRI. Iri, } impl Default for Spec { #[inline] fn default() -> Self { Self::Iri } } /// CLI options. #[derive(Default, Debug, Clone)] struct CliOpt { /// Base IRI. base: String, /// Reference IRI. reference: String, /// Syntax spec. spec: Spec, /// Whether to serialize in WHATWG URL Standard way. whatwg_serialization: bool, } impl CliOpt { fn parse() -> Self { let mut args = std::env::args(); // Skip `argv[0]`. args.next(); let mut base = None; let mut reference = None; let mut spec = None; let mut whatwg_serialization = false; for arg in args.by_ref() { match arg.as_str() { "--iri" | "-i" => spec = Some(Spec::Iri), "--uri" | "-u" => spec = Some(Spec::Uri), "--whatwg" | "-w" => whatwg_serialization = true, "--help" | "-h" => help_and_exit(), opt if opt.starts_with('-') => die(format_args!("Unknown option: {}", opt)), _ => { if base.is_none() { base = Some(arg); } else if reference.is_none() { reference = Some(arg); } else { die("IRI can be specified at most twice"); } } } } for arg in args { if base.is_none() { base = Some(arg); } else if reference.is_none() { reference = Some(arg); } else { die("IRI can be specified at most twice"); } } let base = base.unwrap_or_else(|| die("Base IRI should be specified")); let reference = reference.unwrap_or_else(|| die("Reference IRI should be specified")); let spec = spec.unwrap_or_default(); Self { base, reference, spec, whatwg_serialization, } } } fn main() { let opt = CliOpt::parse(); match opt.spec { Spec::Iri => parse::(&opt), Spec::Uri => parse::(&opt), } } fn parse(opt: &CliOpt) { let base_raw = &opt.base.as_str(); let reference_raw = &opt.reference.as_str(); let base = match RiAbsoluteStr::::new(base_raw) { Ok(v) => v, Err(e) => die(format_args!( "Failed to parse {:?} as an IRI (without fragment): {}", reference_raw, e )), }; let reference = match RiReferenceStr::::new(reference_raw) { Ok(v) => v, Err(e) => die(format_args!( "Failed to parse {:?} as an IRI reference: {}", reference_raw, e )), }; let resolved = reference.resolve_against(base); if !opt.whatwg_serialization { if let Err(e) = resolved.ensure_rfc3986_normalizable() { die(format_args!( "Failed to resolve {:?} against {:?}: {}", reference_raw, base_raw, e )); } } println!("{}", resolved); }