File: parse.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 (159 lines) | stat: -rw-r--r-- 4,038 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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
//! An example to parse IRI from the CLI argument.

use iri_string::types::{IriStr, RiReferenceStr, RiStr};

const USAGE: &str = "\
USAGE:
    parse [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)

ARGS:
    <IRI>           IRI or URI
";

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,
}

impl CliOpt {
    fn parse() -> Self {
        let mut args = std::env::args();
        // Skip `argv[0]`.
        args.next();

        let mut iri = None;
        let mut spec = None;

        for arg in args.by_ref() {
            match arg.as_str() {
                "--iri" | "-i" => spec = Some(Spec::Iri),
                "--uri" | "-u" => spec = Some(Spec::Uri),
                "--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 }
    }
}

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

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

fn parse_iri(opt: &CliOpt) {
    let iri = parse::<iri_string::spec::IriSpec>(opt);
    let uri = iri.encode_to_uri();
    println!("ASCII:      {:?}", uri);
}

fn parse_uri(opt: &CliOpt) {
    let iri = parse::<iri_string::spec::UriSpec>(opt);
    println!("ASCII:      {:?}", iri);
}

fn parse<S: iri_string::spec::Spec>(opt: &CliOpt) -> &RiReferenceStr<S>
where
    RiStr<S>: AsRef<RiStr<iri_string::spec::IriSpec>>,
{
    let raw = &opt.iri.as_str();
    let iri = match RiReferenceStr::<S>::new(raw) {
        Ok(v) => v,
        Err(e) => die(format_args!("Failed to parse {:?}: {}", raw, e)),
    };
    println!("Successfully parsed: {:?}", iri);

    let absolute = iri.to_iri().ok();
    match absolute {
        Some(_) => println!("IRI is ablolute."),
        None => println!("IRI is relative."),
    }

    print_components(iri);
    if let Some(absolute) = absolute {
        print_normalized(absolute.as_ref());
    }

    iri
}

fn print_components<S: iri_string::spec::Spec>(iri: &RiReferenceStr<S>) {
    println!("scheme:     {:?}", iri.scheme_str());
    println!("authority:  {:?}", iri.authority_str());
    if let Some(components) = iri.authority_components() {
        println!("    userinfo: {:?}", components.userinfo());
        println!("    host:     {:?}", components.host());
        println!("    port:     {:?}", components.port());
    }
    println!("path:       {:?}", iri.path_str());
    println!("query:      {:?}", iri.query_str());
    println!("fragment:   {:?}", iri.fragment());
}

pub fn print_normalized(iri: &IriStr) {
    println!("is_normalized_rfc3986: {}", iri.is_normalized_rfc3986());
    println!(
        "is_normalized_but_authorityless_relative_path_preserved: {}",
        iri.is_normalized_but_authorityless_relative_path_preserved()
    );
    println!("normalized: {}", iri.normalize());
}