uri_parsing_rs/parser/parsers/
uri_parsers.rs1use 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#[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#[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}