uri_parsing_rs/parser/parsers/
host_parsers.rs1use nom::{AsChar, InputTakeAtPosition, IResult};
2use nom::branch::alt;
3use nom::character::complete;
4use nom::combinator::map;
5use nom::error::{context, ErrorKind, ParseError};
6use nom::multi::many0;
7use nom::sequence::{delimited, preceded, terminated, tuple};
8
9use crate::ast::host_name::HostName;
10use crate::parser::parsers::{Elms, ipv4_address_parsers, ipv6_address_parsers, UResult};
11use crate::parser::parsers::basic_parsers::*;
12
13#[inline]
14fn hd_code_point<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
15where
16 T: InputTakeAtPosition,
17 <T as InputTakeAtPosition>::Item: AsChar,
18{
19 input.split_at_position1_complete(
20 |item| {
21 let c = item.as_char();
22 !is_hex_digit(c)
23 },
24 ErrorKind::HexDigit,
25 )
26}
27
28#[inline]
29fn p_code_point<T, E: ParseError<T>>(input: T) -> IResult<T, T, E>
30where
31 T: InputTakeAtPosition,
32 <T as InputTakeAtPosition>::Item: AsChar,
33{
34 input.split_at_position1_complete(
35 |item| {
36 let c = item.as_char();
37 !(is_unreserved(c) || is_sub_delims(c) || c == ':')
38 },
39 ErrorKind::Char,
40 )
41}
42
43#[inline]
45pub(crate) fn ipv_future(i: Elms) -> UResult<Elms, String> {
46 map(
47 tuple((
48 preceded(
49 complete::char('v'),
50 terminated(hd_code_point, complete::char('.')),
51 ),
52 p_code_point,
53 )),
54 |(k, m): (Elms, Elms)| {
55 let ks = k.as_str().unwrap();
56 let ms = m.as_str().unwrap();
57 format!("v{}.{}", ks, ms)
58 },
59 )(i)
60}
61
62#[inline]
63pub(crate) fn reg_name(i: Elms) -> UResult<Elms, String> {
64 context(
65 "reg_name",
66 map(
67 many0(alt((
68 map(unreserved, |c| c.into()),
69 pct_encoded,
70 map(sub_delims, |c| c.into()),
71 ))),
72 |sl| sl.into_iter().collect(),
73 ),
74 )(i)
75}
76
77#[inline]
78pub(crate) fn ip_literal(i: Elms) -> UResult<Elms, String> {
79 context(
80 "ip_literal",
81 map(
82 delimited(
83 complete::char('['),
84 alt((ipv_future, ipv6_address_parsers::ipv6_address)),
85 complete::char(']'),
86 ),
87 |s| format!("[{}]", s),
88 ),
89 )(i)
90}
91
92#[inline]
93pub fn host_name(i: Elms) -> UResult<Elms, HostName> {
94 map(
95 context(
96 "host",
97 alt((ip_literal, ipv4_address_parsers::ipv4_address, reg_name)),
98 ),
99 |s| HostName::new(s),
100 )(i)
101}
102
103#[cfg(test)]
104pub mod gens {
105 use prop_check_rs::gen::{Gen, Gens};
106
107 use crate::parser::parsers::basic_parsers::gens::*;
108 use crate::parser::parsers::ipv4_address_parsers::gens::*;
109 use crate::parser::parsers::ipv6_address_parsers::gens::*;
110
111 pub fn reg_name_str_gen() -> Gen<String> {
112 rep_str_gen(1, 10, || {
113 Gens::choose_u8(1, 3).bind(|n| match n {
114 1 => unreserved_char_gen().fmap(|c| c.into()),
115 2 => sub_delims_char_gen().fmap(|c| c.into()),
116 3 => pct_encoded_str_gen(),
117 x => panic!("x = {}", x),
118 })
119 })
120 }
121
122 pub fn ipv_future_str_gen() -> Gen<String> {
123 let a = || rep_char_gen(5, || hex_digit_char_gen());
124 let b = || {
125 rep_char_gen(5, || {
126 Gens::choose_u8(1, 3).bind(|n| match n {
127 1 => unreserved_char_gen(),
128 2 => sub_delims_char_gen(),
129 3 => Gen::<char>::unit(|| ':'),
130 x => panic!("x = {}", x),
131 })
132 })
133 };
134 a().bind(move |s1| b().fmap(move |s2| format!("v{}.{}", s1, s2)))
135 }
136
137 pub fn ip_literal_str_gen() -> Gen<String> {
138 Gens::choose_u8(1, 2)
139 .bind(|n| match n {
140 1 => ipv6_address_str_gen(),
141 2 => ipv_future_str_gen(),
142 x => panic!("x = {}", x),
143 })
144 .fmap(|s| format!("[{}]", s))
145 }
146
147 pub fn host_gen() -> Gen<String> {
148 Gens::choose_u8(1, 3).bind(|n| match n {
149 1 => ip_literal_str_gen(),
150 2 => ipv4_address_str_gen(),
151 3 => reg_name_str_gen(),
152 x => panic!("x = {}", x),
153 })
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use std::env;
160
161 use anyhow::Result;
162 use prop_check_rs::prop;
163 use prop_check_rs::prop::TestCases;
164 use prop_check_rs::rng::RNG;
165
166 use super::*;
167 use super::gens::*;
168
169 const TEST_COUNT: TestCases = 100;
170
171 fn init() {
172 env::set_var("RUST_LOG", "debug");
173 let _ = env_logger::builder().is_test(true).try_init();
174 }
175
176 #[test]
177 fn test_ip_literal() -> Result<()> {
178 init();
179 let mut counter = 0;
180 let prop = prop::for_all(
181 || ip_literal_str_gen(),
182 move |s| {
183 counter += 1;
184 log::debug!("{}, ip_literal = {}", counter, s);
185 let (_, r) = ip_literal(Elms::new(s.as_bytes())).ok().unwrap();
186 assert_eq!(r, s);
187 true
188 },
189 );
190 prop::test_with_prop(prop, 5, TEST_COUNT, RNG::new())
191 }
192
193 #[test]
194 fn test_ipv_future() -> Result<()> {
195 init();
196 let mut counter = 0;
197 let prop = prop::for_all(
198 || ipv_future_str_gen(),
199 move |s| {
200 counter += 1;
201 log::debug!("{}, ipv_future = {}", counter, s);
202 let (_, r) = ipv_future(Elms::new(s.as_bytes())).ok().unwrap();
203 assert_eq!(r, s);
204 true
205 },
206 );
207 prop::test_with_prop(prop, 5, TEST_COUNT, RNG::new())
208 }
209
210 #[test]
211 fn test_reg_name() -> Result<()> {
212 init();
213 let mut counter = 0;
214 let prop = prop::for_all(
215 || reg_name_str_gen(),
216 move |s| {
217 counter += 1;
218 log::debug!("{}, reg_name = {}", counter, s);
219 let (_, reg_name) = reg_name(Elms::new(s.as_bytes())).ok().unwrap();
220 assert_eq!(reg_name.to_string(), s);
221 true
222 },
223 );
224 prop::test_with_prop(prop, 5, TEST_COUNT, RNG::new())
225 }
226
227 #[test]
228 fn test_host() -> Result<()> {
229 init();
230 let mut counter = 0;
231 let prop = prop::for_all(
232 || host_gen(),
233 move |s| {
234 counter += 1;
235 log::debug!("{}, host_name = {}", counter, s);
236 let (_, host_name) = host_name(Elms::new(s.as_bytes())).ok().unwrap();
237 assert_eq!(host_name.to_string(), s);
238 true
239 },
240 );
241 prop::test_with_prop(prop, 5, TEST_COUNT, RNG::new())
242 }
243}