fire_http/routes/
path_params.rs

1use std::{
2	collections::{HashMap, HashSet},
3	str::FromStr,
4};
5
6use byte_parser::{ParseIterator, StrParser};
7use matchit::Params;
8
9#[derive(Debug, Clone)]
10pub struct PathParams {
11	inner: HashMap<String, String>,
12}
13
14impl PathParams {
15	pub(crate) fn new(params: Params) -> Self {
16		let mut inner = HashMap::new();
17
18		for (key, value) in params.iter() {
19			inner.insert(key.to_string(), value.to_string());
20		}
21
22		Self { inner }
23	}
24
25	pub fn exists(&self, key: impl AsRef<str>) -> bool {
26		self.inner.get(key.as_ref()).is_some()
27	}
28
29	pub fn parse<T>(&self, key: impl AsRef<str>) -> Result<T, T::Err>
30	where
31		T: FromStr,
32	{
33		self.inner.get(key.as_ref()).unwrap().parse()
34	}
35
36	pub fn get(&self, key: impl AsRef<str>) -> Option<&str> {
37		self.inner.get(key.as_ref()).map(|s| s.as_str())
38	}
39}
40
41/* pub struct Parser {
42	pub path: &'a str
43} */
44
45// we need to parse {}
46// and allow it to be escaped with {{ or }}
47
48#[derive(Debug, Clone)]
49pub struct ParamsNames<'a> {
50	list: HashSet<&'a str>,
51}
52
53impl<'a> ParamsNames<'a> {
54	pub fn parse(s: &'a str) -> Self {
55		let mut parser = StrParser::new(s);
56
57		let mut list = HashSet::new();
58
59		'template_loop: loop {
60			parser.consume_while_byte_fn(|&b| b != b'{');
61			// either we're at the end or we found a {
62			// we need to check for escapes
63			let Some(b) = parser.next() else {
64				return Self { list };
65			};
66			debug_assert_eq!(b, b'{');
67
68			// handle escapes
69			if parser.next_if(|&b| b == b'{').is_some() {
70				continue 'template_loop;
71			}
72
73			let mut parser = parser.record();
74
75			loop {
76				parser.consume_while_byte_fn(|&b| b != b'}' && b != b'{');
77				match parser.peek() {
78					Some(b) if b == b'{' => {
79						panic!("unexpected {{");
80					}
81					Some(b) if b == b'}' => {
82						assert!(
83							!matches!(parser.peek_at(2), Some(b) if b == b'}'),
84							"escapping does not work in template string"
85						);
86
87						let s = parser.to_str();
88						let s = s.trim_start_matches('*');
89						list.insert(s);
90
91						parser.next().unwrap();
92
93						continue 'template_loop;
94					}
95					Some(b) => unreachable!("reached byte {b}"),
96					None => {
97						panic!("unexpected end of string");
98					}
99				}
100			}
101		}
102	}
103
104	pub fn exists(&self, key: impl AsRef<str>) -> bool {
105		self.list.contains(key.as_ref())
106	}
107
108	pub fn is_empty(&self) -> bool {
109		self.list.is_empty()
110	}
111}