uri_parsing_rs/parser/parsers/
uri_parsers.rs

1use nom::bytes::complete::tag;
2use nom::character::complete;
3use nom::combinator::{eof, map, opt};
4use nom::error::context;
5use nom::sequence::{preceded, terminated, tuple};
6
7use crate::ast::uri::Uri;
8use crate::parser::parsers::{
9  Elms, fragment_parsers, hier_part_parsers, query_parsers, scheme_parsers, UResult,
10};
11
12// absolute-URI  = scheme ":" hier-part [ "?" query ]
13#[inline]
14pub fn absolute_uri(i: Elms) -> UResult<Elms, Uri> {
15  context(
16    "absolute_uri",
17    map(
18      tuple((
19        scheme_parsers::scheme,
20        terminated(
21          preceded(
22            tag(":"),
23            tuple((
24              hier_part_parsers::hier_part,
25              opt(preceded(complete::char('?'), query_parsers::query)),
26            )),
27          ),
28          eof,
29        ),
30      )),
31      |(s, ((a, p), q))| Uri::new(s, a, p, q, None),
32    ),
33  )(i)
34}
35
36// URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
37#[inline]
38pub fn uri(i: Elms) -> UResult<Elms, Uri> {
39  context(
40    "uri",
41    map(
42      tuple((
43        scheme_parsers::scheme,
44        terminated(
45          preceded(
46            tag(":"),
47            tuple((
48              hier_part_parsers::hier_part,
49              opt(preceded(complete::char('?'), query_parsers::query)),
50              opt(preceded(complete::char('#'), fragment_parsers::fragment)),
51            )),
52          ),
53          eof,
54        ),
55      )),
56      |(s, ((a, p), q, f))| Uri::new(s, a, p, q, f),
57    ),
58  )(i)
59}
60
61#[cfg(test)]
62pub mod gens {
63  use prop_check_rs::gen::Gen;
64
65  use crate::parser::parsers::fragment_parsers::gens::fragment_str_gen;
66  use crate::parser::parsers::hier_part_parsers::gens::hier_part_gen;
67  use crate::parser::parsers::query_parsers::gens::query_gen;
68  use crate::parser::parsers::scheme_parsers::gens::scheme_gen;
69  use crate::parser::parsers::path_parsers::gens::Pair;
70
71  pub fn uri_gen() -> Gen<String> {
72    scheme_gen().bind(|scheme| {
73      let base_gen = hier_part_gen()
74        .fmap(move |Pair(hier_part, is_empty)| (format!("{}:{}", scheme, hier_part), is_empty));
75      let query_gen = base_gen.bind(|(s, is_empty_opt)| {
76        if is_empty_opt.unwrap_or(false) {
77          Gen::<(String, Option<bool>)>::unit(|| (s.clone(), is_empty_opt))
78        } else {
79          query_gen().fmap(move |q| (format!("{}?{}", s, q), is_empty_opt))
80        }
81      });
82      let fragment_gen = query_gen.bind(|(s, is_empty_opt)| {
83        if is_empty_opt.unwrap_or(false) {
84          Gen::<(String, Option<bool>)>::unit(|| s.clone())
85        } else {
86          fragment_str_gen().fmap(move |f| format!("{}#{}", s, f))
87        }
88      });
89      fragment_gen
90    })
91  }
92}
93
94#[cfg(test)]
95mod tests {
96  use std::env;
97
98  use anyhow::Result;
99  use prop_check_rs::prop;
100  use prop_check_rs::prop::TestCases;
101  use prop_check_rs::rng::RNG;
102
103  use super::*;
104  use super::gens::*;
105
106  const TEST_COUNT: TestCases = 100;
107
108  fn init() {
109    env::set_var("RUST_LOG", "debug");
110    let _ = env_logger::builder().is_test(true).try_init();
111  }
112
113  #[test]
114  fn test_uri() -> Result<()> {
115    init();
116    let mut counter = 0;
117    let prop = prop::for_all(
118      || uri_gen(),
119      move |s| {
120        counter += 1;
121        log::debug!("{:>03} uri = {}", counter, s);
122        let (_, uri) = uri(Elms::new(s.as_bytes())).ok().unwrap();
123        assert_eq!(uri.to_string(), s);
124        true
125      },
126    );
127    prop::test_with_prop(prop, 5, TEST_COUNT, RNG::new())
128  }
129}