iri_string/template/
components.rs

1//! Syntax components of URI templates.
2
3use core::mem;
4
5use crate::parser::str::find_split_hole;
6use crate::template::error::Error;
7use crate::template::parser::validate as validate_parser;
8
9/// Expression body.
10///
11/// This does not contain the wrapping braces (`{` and `}`).
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub(super) struct ExprBody<'a>(&'a str);
14
15impl<'a> ExprBody<'a> {
16    /// Creates a new expression body.
17    ///
18    /// # Precondition
19    ///
20    /// The given string should be a valid expression body.
21    #[inline]
22    #[must_use]
23    pub(super) fn new(s: &'a str) -> Self {
24        debug_assert!(
25            !s.is_empty(),
26            "[precondition] valid expression body is not empty"
27        );
28
29        Self(s)
30    }
31
32    /// Decomposes the expression into an `operator` and `variable-list`.
33    ///
34    /// # Panics
35    ///
36    /// May panic if the input is invalid.
37    #[must_use]
38    pub(super) fn decompose(&self) -> (Operator, VarListStr<'a>) {
39        debug_assert!(
40            !self.0.is_empty(),
41            "[precondition] valid expression body is not empty"
42        );
43        let first = self.0.as_bytes()[0];
44        if first.is_ascii_alphanumeric() || (first == b'_') || (first == b'%') {
45            // The first byte is a part of the variable list.
46            (Operator::String, VarListStr::new(self.0))
47        } else {
48            let op = Operator::from_byte(first).unwrap_or_else(|| {
49                unreachable!(
50                    "[precondition] valid expression has (optional) \
51                     valid operator, but got a byte {first:#02x?}"
52                )
53            });
54            (op, VarListStr::new(&self.0[1..]))
55        }
56    }
57
58    /// Returns the raw expression in a string slice.
59    #[inline]
60    #[must_use]
61    pub(super) fn as_str(&self) -> &'a str {
62        self.0
63    }
64}
65
66/// Variable name.
67// QUESTION: Should hexdigits in percent-encoded triplets be compared case sensitively?
68#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
69pub struct VarName<'a>(&'a str);
70
71impl<'a> VarName<'a> {
72    /// Creates a `VarName` from the trusted string.
73    ///
74    /// # Precondition
75    ///
76    /// The given string should be a valid variable name.
77    #[inline]
78    #[must_use]
79    pub(super) fn from_trusted(s: &'a str) -> Self {
80        Self(s)
81    }
82
83    /// Creates a `VarName` from the string.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// # use iri_string::template::Error;
89    /// use iri_string::template::context::VarName;
90    ///
91    /// let name = VarName::new("hello")?;
92    /// assert_eq!(name.as_str(), "hello");
93    ///
94    /// assert!(VarName::new("0+non-variable-name").is_err());
95    ///
96    /// # Ok::<_, Error>(())
97    /// ```
98    #[inline]
99    pub fn new(s: &'a str) -> Result<Self, Error> {
100        match validate_parser::validate_varname(s, 0) {
101            Ok(_) => Ok(Self::from_trusted(s)),
102            Err(e) => Err(e),
103        }
104    }
105
106    /// Returns the varibale name.
107    #[inline]
108    #[must_use]
109    pub fn as_str(&self) -> &'a str {
110        self.0
111    }
112}
113
114/// Variable specifier.
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub struct VarSpec<'a> {
117    /// Variable name.
118    name: VarName<'a>,
119    /// Variable modifier.
120    modifier: Modifier,
121}
122
123impl<'a> VarSpec<'a> {
124    /// Returns the varibale name.
125    #[inline]
126    #[must_use]
127    pub(super) fn name(&self) -> VarName<'a> {
128        self.name
129    }
130
131    /// Returns the modifier.
132    #[inline]
133    #[must_use]
134    pub(super) fn modifier(&self) -> Modifier {
135        self.modifier
136    }
137
138    /// Parses the trusted varspec string.
139    ///
140    /// # Panics
141    ///
142    /// May panic if the input is invalid.
143    #[must_use]
144    pub(super) fn parse_trusted(s: &'a str) -> Self {
145        if let Some(varname) = s.strip_suffix('*') {
146            // `varname "*"`.
147            return Self {
148                name: VarName::from_trusted(varname),
149                modifier: Modifier::Explode,
150            };
151        }
152        // `varname ":" max-length` or `varname`.
153        match find_split_hole(s, b':') {
154            Some((varname, max_len)) => {
155                let max_len: u16 = max_len
156                    .parse()
157                    .expect("[precondition] the input should be valid `varspec`");
158                Self {
159                    name: VarName::from_trusted(varname),
160                    modifier: Modifier::MaxLen(max_len),
161                }
162            }
163            None => Self {
164                name: VarName(s),
165                modifier: Modifier::None,
166            },
167        }
168    }
169}
170
171/// Variable list.
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub(super) struct VarListStr<'a>(&'a str);
174
175impl<'a> VarListStr<'a> {
176    /// Creates a new variable list.
177    ///
178    /// # Precondition
179    ///
180    /// The given string should be a valid variable list.
181    #[inline]
182    #[must_use]
183    pub(super) fn new(s: &'a str) -> Self {
184        Self(s)
185    }
186}
187
188impl<'a> IntoIterator for VarListStr<'a> {
189    type IntoIter = VarListIter<'a>;
190    type Item = (usize, VarSpec<'a>);
191
192    #[inline]
193    fn into_iter(self) -> Self::IntoIter {
194        VarListIter { rest: self.0 }
195    }
196}
197
198/// Iterator of variable specs.
199#[derive(Debug, Clone)]
200pub(super) struct VarListIter<'a> {
201    /// Remaining input.
202    rest: &'a str,
203}
204
205impl<'a> Iterator for VarListIter<'a> {
206    /// A pair of the length of the varspec and the varspec itself.
207    type Item = (usize, VarSpec<'a>);
208
209    fn next(&mut self) -> Option<Self::Item> {
210        match find_split_hole(self.rest, b',') {
211            Some((prefix, new_rest)) => {
212                self.rest = new_rest;
213                Some((prefix.len(), VarSpec::parse_trusted(prefix)))
214            }
215            None => {
216                if self.rest.is_empty() {
217                    None
218                } else {
219                    Some((
220                        self.rest.len(),
221                        VarSpec::parse_trusted(mem::take(&mut self.rest)),
222                    ))
223                }
224            }
225        }
226    }
227}
228
229/// Variable modifier.
230#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
231pub(super) enum Modifier {
232    /// No modifiers.
233    None,
234    /// Max length, greater than 0 and less than 10000.
235    MaxLen(u16),
236    /// Explode the variable, e.g. the var spec has `*`.
237    Explode,
238}
239
240/// Operator that is possibly reserved for future extension.
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242pub(super) enum MaybeOperator {
243    /// Working operator.
244    Operator(Operator),
245    /// Reserved for future extensions.
246    Reserved(OperatorReservedForFuture),
247}
248
249impl MaybeOperator {
250    /// Returns the operator for the given character.
251    pub(super) fn from_byte(b: u8) -> Option<Self> {
252        match b {
253            b'+' => Some(Self::Operator(Operator::Reserved)),
254            b'#' => Some(Self::Operator(Operator::Fragment)),
255            b'.' => Some(Self::Operator(Operator::Label)),
256            b'/' => Some(Self::Operator(Operator::PathSegments)),
257            b';' => Some(Self::Operator(Operator::PathParams)),
258            b'?' => Some(Self::Operator(Operator::FormQuery)),
259            b'&' => Some(Self::Operator(Operator::FormQueryCont)),
260            b'=' => Some(Self::Reserved(OperatorReservedForFuture::Equals)),
261            b',' => Some(Self::Reserved(OperatorReservedForFuture::Comma)),
262            b'!' => Some(Self::Reserved(OperatorReservedForFuture::Exclamation)),
263            b'@' => Some(Self::Reserved(OperatorReservedForFuture::AtSign)),
264            b'|' => Some(Self::Reserved(OperatorReservedForFuture::Pipe)),
265            _ => None,
266        }
267    }
268}
269
270/// Working operator.
271#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
272pub(super) enum Operator {
273    /// No operator. String expansion.
274    String,
275    /// Reserved expansion by `+`.
276    Reserved,
277    /// Fragment expansion by `#`.
278    Fragment,
279    /// Label expansion by `.`.
280    Label,
281    /// Path segments by `/`.
282    PathSegments,
283    /// Path-style parameters by `;`.
284    PathParams,
285    /// Form-style query by `?`.
286    FormQuery,
287    /// Form-style query continuation by `&`.
288    FormQueryCont,
289}
290
291impl Operator {
292    /// Returns the operator for the given character.
293    #[must_use]
294    pub(super) fn from_byte(b: u8) -> Option<Self> {
295        match b {
296            b'+' => Some(Self::Reserved),
297            b'#' => Some(Self::Fragment),
298            b'.' => Some(Self::Label),
299            b'/' => Some(Self::PathSegments),
300            b';' => Some(Self::PathParams),
301            b'?' => Some(Self::FormQuery),
302            b'&' => Some(Self::FormQueryCont),
303            _ => None,
304        }
305    }
306
307    /// Returns the string length of the operator.
308    #[inline]
309    #[must_use]
310    pub(super) const fn len(self) -> usize {
311        if matches!(self, Self::String) {
312            0
313        } else {
314            1
315        }
316    }
317}
318
319/// Operator reserved for future extension.
320#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
321pub(super) enum OperatorReservedForFuture {
322    /// Reserved `=` operator.
323    Equals,
324    /// Reserved `,` operator.
325    Comma,
326    /// Reserved `!` operator.
327    Exclamation,
328    /// Reserved `@` operator.
329    AtSign,
330    /// Reserved `|` operator.
331    Pipe,
332}