File: normalize.rs

package info (click to toggle)
rust-iri-string 0.7.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 976 kB
  • sloc: makefile: 2
file content (145 lines) | stat: -rw-r--r-- 3,671 bytes parent folder | download
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
//! An example to normalize an IRI from the CLI argument.

use iri_string::format::ToDedicatedString;
use iri_string::types::{RiStr, RiString};

const USAGE: &str = "\
USAGE:
    normalize [FLAGS] [--] IRI

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)
    -a, --ascii     Converts the output to an URI (RFC 3986)
    -w, --whatwg    Serialize normalization result according to WHATWG URL Standard.

ARGS:
    <IRI>           IRI
";

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 {
    /// IRI.
    iri: String,
    /// Syntax spec.
    spec: Spec,
    /// Whether to convert output to ASCII URI or not.
    output_ascii: bool,
    /// 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 iri = None;
        let mut spec = None;
        let mut output_ascii = false;
        let mut whatwg_serialization = false;

        for arg in args.by_ref() {
            match arg.as_str() {
                "--ascii" | "-a" => output_ascii = true,
                "--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 iri.replace(arg).is_some() {
                        die("IRI can be specified at most once");
                    }
                }
            }
        }

        for arg in args {
            if iri.replace(arg).is_some() {
                eprintln!("ERROR: IRI can be specified at most once");
            }
        }

        let iri = iri.unwrap_or_else(|| die("IRI should be specified"));
        let spec = spec.unwrap_or_default();
        Self {
            iri,
            spec,
            output_ascii,
            whatwg_serialization,
        }
    }
}

fn main() {
    let opt = CliOpt::parse();

    match opt.spec {
        Spec::Iri => process_iri(&opt),
        Spec::Uri => process_uri(&opt),
    }
}

fn process_iri(opt: &CliOpt) {
    let mut normalized = normalize::<iri_string::spec::IriSpec>(opt);
    if opt.output_ascii {
        normalized.encode_to_uri_inline();
    }
    println!("{normalized}");
}

fn process_uri(opt: &CliOpt) {
    let normalized = normalize::<iri_string::spec::UriSpec>(opt);
    println!("{normalized}");
}

fn normalize<S: iri_string::spec::Spec>(opt: &CliOpt) -> RiString<S> {
    let raw = &opt.iri.as_str();
    let iri = match RiStr::<S>::new(raw) {
        Ok(v) => v,
        Err(e) => die(format_args!("Failed to parse {raw:?}: {e:?}")),
    };
    let normalized = iri.normalize();
    if !opt.whatwg_serialization {
        if let Err(e) = normalized.ensure_rfc3986_normalizable() {
            die(format_args!("Failed to normalize: {e:?}"));
        }
    }
    normalized.to_dedicated_string()
}