1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154
|
//! 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> Base IRI or URI to resolve REFERENCE against
<REFERENCE> 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::<iri_string::spec::IriSpec>(&opt),
Spec::Uri => parse::<iri_string::spec::UriSpec>(&opt),
}
}
fn parse<S: iri_string::spec::Spec>(opt: &CliOpt) {
let base_raw = &opt.base.as_str();
let reference_raw = &opt.reference.as_str();
let base = match RiAbsoluteStr::<S>::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::<S>::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);
}
|