1#[cfg(feature = "chumsky")]
4use chumsky::{
5 IterParser as _, Parser,
6 prelude::{any, just, one_of},
7 text::digits,
8};
9
10#[cfg(feature = "chumsky")]
16#[must_use]
17pub fn url_text_component_parser<'src>()
18-> impl Parser<'src, &'src str, String, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
19 any()
20 .filter(|c: &char| {
21 c.is_alphabetic() || c.is_numeric() || *c == '%' || *c == '-' || *c == '~' || *c == '.'
22 })
23 .repeated()
24 .at_least(1)
25 .collect::<String>()
26 .try_map(|s, span| {
27 percent_encoding::percent_decode(s.as_bytes())
28 .decode_utf8()
29 .map(|s| s.into_owned())
30 .map_err(|e| chumsky::error::Rich::custom(span, format!("{e:?}")))
31 })
32}
33
34#[cfg(feature = "chumsky")]
40#[must_use]
41pub fn usize_parser<'src>()
42-> impl Parser<'src, &'src str, usize, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
43 digits(10).collect::<String>().try_map(|c: String, span| {
44 c.parse().map_err(|err| {
45 chumsky::error::Rich::custom(span, format!("failed to parse {c} as usize: {err:?}"))
46 })
47 })
48}
49
50#[cfg(feature = "chumsky")]
56#[must_use]
57pub fn isize_parser<'src>()
58-> impl Parser<'src, &'src str, isize, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
59 one_of("+-")
60 .or_not()
61 .then(digits(10).collect::<String>())
62 .try_map(|(sign, c): (Option<char>, String), span| {
63 let c = if let Some(sign) = sign {
64 format!("{sign}{c}")
65 } else {
66 c
67 };
68 c.parse().map_err(|err| {
69 chumsky::error::Rich::custom(span, format!("failed to parse {c} as isize: {err:?}"))
70 })
71 })
72}
73
74#[cfg(feature = "chumsky")]
80#[must_use]
81pub fn u8_parser<'src>()
82-> impl Parser<'src, &'src str, u8, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
83 digits(10).collect::<String>().try_map(|c: String, span| {
84 c.parse().map_err(|err| {
85 chumsky::error::Rich::custom(span, format!("failed to parse {c} as u8: {err:?}"))
86 })
87 })
88}
89
90#[cfg(feature = "chumsky")]
96#[must_use]
97pub fn u16_parser<'src>()
98-> impl Parser<'src, &'src str, u16, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
99 digits(10).collect::<String>().try_map(|c: String, span| {
100 c.parse().map_err(|err| {
101 chumsky::error::Rich::custom(span, format!("failed to parse {c} as u16: {err:?}"))
102 })
103 })
104}
105
106#[cfg(feature = "chumsky")]
112#[must_use]
113pub fn u32_parser<'src>()
114-> impl Parser<'src, &'src str, u32, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
115 digits(10).collect::<String>().try_map(|c: String, span| {
116 c.parse().map_err(|err| {
117 chumsky::error::Rich::custom(span, format!("failed to parse {c} as u32: {err:?}"))
118 })
119 })
120}
121
122#[cfg(feature = "chumsky")]
128#[must_use]
129pub fn u64_parser<'src>()
130-> impl Parser<'src, &'src str, u64, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
131 digits(10).collect::<String>().try_map(|c: String, span| {
132 c.parse().map_err(|err| {
133 chumsky::error::Rich::custom(span, format!("failed to parse {c} as u64: {err:?}"))
134 })
135 })
136}
137
138#[cfg(feature = "chumsky")]
144#[must_use]
145pub fn i8_parser<'src>()
146-> impl Parser<'src, &'src str, i8, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
147 one_of("+-")
148 .or_not()
149 .then(digits(10).collect::<String>())
150 .try_map(|(sign, c): (Option<char>, String), span| {
151 let c = if let Some(sign) = sign {
152 format!("{sign}{c}")
153 } else {
154 c
155 };
156 c.parse().map_err(|err| {
157 chumsky::error::Rich::custom(span, format!("failed to parse {c} as i8: {err:?}"))
158 })
159 })
160}
161
162#[cfg(feature = "chumsky")]
168#[must_use]
169pub fn i16_parser<'src>()
170-> impl Parser<'src, &'src str, i16, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
171 one_of("+-")
172 .or_not()
173 .then(digits(10).collect::<String>())
174 .try_map(|(sign, c): (Option<char>, String), span| {
175 let c = if let Some(sign) = sign {
176 format!("{sign}{c}")
177 } else {
178 c
179 };
180 c.parse().map_err(|err| {
181 chumsky::error::Rich::custom(span, format!("failed to parse {c} as i16: {err:?}"))
182 })
183 })
184}
185
186#[cfg(feature = "chumsky")]
192#[must_use]
193pub fn i32_parser<'src>()
194-> impl Parser<'src, &'src str, i32, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
195 one_of("+-")
196 .or_not()
197 .then(digits(10).collect::<String>())
198 .try_map(|(sign, c): (Option<char>, String), span| {
199 let c = if let Some(sign) = sign {
200 format!("{sign}{c}")
201 } else {
202 c
203 };
204 c.parse().map_err(|err| {
205 chumsky::error::Rich::custom(span, format!("failed to parse {c} as i32: {err:?}"))
206 })
207 })
208}
209
210#[cfg(feature = "chumsky")]
216#[must_use]
217pub fn i64_parser<'src>()
218-> impl Parser<'src, &'src str, i64, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
219 one_of("+-")
220 .or_not()
221 .then(digits(10).collect::<String>())
222 .try_map(|(sign, c): (Option<char>, String), span| {
223 let c = if let Some(sign) = sign {
224 format!("{sign}{c}")
225 } else {
226 c
227 };
228 c.parse().map_err(|err| {
229 chumsky::error::Rich::custom(span, format!("failed to parse {c} as i64: {err:?}"))
230 })
231 })
232}
233
234#[cfg(feature = "chumsky")]
240#[must_use]
241pub fn unsigned_f32_parser<'src>()
242-> impl Parser<'src, &'src str, f32, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
243 digits(10)
244 .collect::<String>()
245 .then(
246 just('.')
247 .ignore_then(digits(10).collect::<String>())
248 .or_not(),
249 )
250 .try_map(|(before_point, after_point), span| {
251 let raw_float = format!(
252 "{}.{}",
253 before_point,
254 after_point.unwrap_or_else(|| "0".to_string())
255 );
256 raw_float.parse().map_err(|err| {
257 chumsky::error::Rich::custom(
258 span,
259 format!("Could not parse {raw_float} as f32: {err:?}"),
260 )
261 })
262 })
263}
264
265#[cfg(feature = "chumsky")]
271#[must_use]
272pub fn unsigned_f64_parser<'src>()
273-> impl Parser<'src, &'src str, f64, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
274 digits(10)
275 .collect::<String>()
276 .then(
277 just('.')
278 .ignore_then(digits(10).collect::<String>())
279 .or_not(),
280 )
281 .try_map(|(before_point, after_point), span| {
282 let raw_float = format!(
283 "{}.{}",
284 before_point,
285 after_point.unwrap_or_else(|| "0".to_string())
286 );
287 raw_float.parse().map_err(|err| {
288 chumsky::error::Rich::custom(
289 span,
290 format!("Could not parse {raw_float} as f64: {err:?}"),
291 )
292 })
293 })
294}
295
296#[cfg(feature = "chumsky")]
302#[must_use]
303pub fn f32_parser<'src>()
304-> impl Parser<'src, &'src str, f32, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
305 one_of("+-")
306 .or_not()
307 .then(unsigned_f32_parser())
308 .map(
309 |(sign, value)| {
310 if sign == Some('-') { -value } else { value }
311 },
312 )
313}
314
315#[cfg(feature = "chumsky")]
321#[must_use]
322pub fn f64_parser<'src>()
323-> impl Parser<'src, &'src str, f64, chumsky::extra::Err<chumsky::error::Rich<'src, char>>> {
324 one_of("+-")
325 .or_not()
326 .then(unsigned_f64_parser())
327 .map(
328 |(sign, value)| {
329 if sign == Some('-') { -value } else { value }
330 },
331 )
332}