iri_string/template/
expand.rs

1//! Expansion.
2
3use core::fmt::{self, Write as _};
4use core::marker::PhantomData;
5use core::mem;
6use core::ops::ControlFlow;
7
8#[cfg(feature = "alloc")]
9use alloc::string::{String, ToString};
10
11use crate::parser::str::{find_split, find_split_hole};
12use crate::parser::str::{process_percent_encoded_best_effort, PctEncodedFragments};
13use crate::percent_encode::PercentEncoded;
14use crate::spec::Spec;
15use crate::template::components::{ExprBody, Modifier, Operator, VarName, VarSpec};
16use crate::template::context::{
17    private::Sealed as VisitorSealed, AssocVisitor, Context, DynamicContext, ListVisitor,
18    VisitPurpose, Visitor,
19};
20use crate::template::error::{Error, ErrorKind};
21use crate::template::{UriTemplateStr, ValueType};
22#[cfg(feature = "alloc")]
23use crate::types;
24
25/// A chunk in a template string.
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub(super) enum Chunk<'a> {
28    /// Literal.
29    Literal(&'a str),
30    /// Expression excluding the wrapping braces.
31    Expr(ExprBody<'a>),
32}
33
34/// Iterator of template chunks.
35#[derive(Debug, Clone)]
36pub(super) struct Chunks<'a> {
37    /// Template.
38    template: &'a str,
39}
40
41impl<'a> Chunks<'a> {
42    /// Creates a new iterator.
43    #[inline]
44    #[must_use]
45    pub(super) fn new(template: &'a UriTemplateStr) -> Self {
46        Self {
47            template: template.as_str(),
48        }
49    }
50}
51
52impl<'a> Iterator for Chunks<'a> {
53    type Item = Chunk<'a>;
54
55    fn next(&mut self) -> Option<Self::Item> {
56        if self.template.is_empty() {
57            return None;
58        }
59        match find_split(self.template, b'{') {
60            Some(("", _)) => {
61                let (expr_body, rest) = find_split_hole(&self.template[1..], b'}')
62                    .expect("[validity] expression inside a template must be closed");
63                self.template = rest;
64                Some(Chunk::Expr(ExprBody::new(expr_body)))
65            }
66            Some((lit, rest)) => {
67                self.template = rest;
68                Some(Chunk::Literal(lit))
69            }
70            None => Some(Chunk::Literal(mem::take(&mut self.template))),
71        }
72    }
73}
74
75/// Template expansion result.
76#[derive(Debug, Clone, Copy)]
77pub struct Expanded<'a, S, C> {
78    /// Compiled template.
79    template: &'a UriTemplateStr,
80    /// Context.
81    context: &'a C,
82    /// Spec.
83    _spec: PhantomData<fn() -> S>,
84}
85
86impl<'a, S: Spec, C: Context> Expanded<'a, S, C> {
87    /// Creates a new `Expanded` object.
88    #[inline]
89    pub(super) fn new(template: &'a UriTemplateStr, context: &'a C) -> Result<Self, Error> {
90        Self::typecheck_context(template, context)?;
91        Ok(Self {
92            template,
93            context,
94            _spec: PhantomData,
95        })
96    }
97
98    /// Checks if the types of variables are allowed for the corresponding expressions in the template.
99    fn typecheck_context(template: &UriTemplateStr, context: &C) -> Result<(), Error> {
100        let mut pos = 0;
101        for chunk in Chunks::new(template) {
102            let (expr_len, (op, varlist)) = match chunk {
103                Chunk::Expr(expr_body) => (expr_body.as_str().len(), expr_body.decompose()),
104                Chunk::Literal(lit) => {
105                    pos += lit.len();
106                    continue;
107                }
108            };
109            // +2: wrapping braces (`{` and `}`).
110            let chunk_end_pos = pos + expr_len + 2;
111            // +1: opening brace `{`.
112            pos += op.len() + 1;
113            for (varspec_len, varspec) in varlist {
114                let ty = context.visit(TypeVisitor::new(varspec.name()));
115                let modifier = varspec.modifier();
116
117                if matches!(modifier, Modifier::MaxLen(_))
118                    && matches!(ty, ValueType::List | ValueType::Assoc)
119                {
120                    // > Prefix modifiers are not applicable to variables that
121                    // > have composite values.
122                    //
123                    // --- [RFC 6570 Section 2.4.1. Prefix](https://www.rfc-editor.org/rfc/rfc6570.html#section-2.4.1)
124                    return Err(Error::new(ErrorKind::UnexpectedValueType, pos));
125                }
126
127                // +1: A trailing comman (`,`) or a closing brace (`}`).
128                pos += varspec_len + 1;
129            }
130            assert_eq!(pos, chunk_end_pos);
131        }
132        Ok(())
133    }
134}
135
136impl<S: Spec, C: Context> fmt::Display for Expanded<'_, S, C> {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        for chunk in Chunks::new(self.template) {
139            let expr = match chunk {
140                Chunk::Literal(lit) => {
141                    f.write_str(lit)?;
142                    continue;
143                }
144                Chunk::Expr(body) => body,
145            };
146            expand::<S, _>(f, expr, self.context)?;
147        }
148
149        Ok(())
150    }
151}
152
153/// Implement `TryFrom<Expanded<...>> for SomeUriStringType`.
154macro_rules! impl_try_from_expanded {
155    ($ty_outer:ident) => {
156        #[cfg(feature = "alloc")]
157        impl<S: Spec, C: Context> TryFrom<Expanded<'_, S, C>> for types::$ty_outer<S> {
158            type Error = types::CreationError<String>;
159
160            #[inline]
161            fn try_from(v: Expanded<'_, S, C>) -> Result<Self, Self::Error> {
162                Self::try_from(v.to_string())
163            }
164        }
165    };
166}
167
168// Not implementing `TryFrom<Expand<...>>` for query and fragment strings
169// since they cannot behave as a query or a fragment only by themselves.
170// Query strings in practical starts with `?` prefix but `RiQueryStr{,ing}`
171// strips that, and so do fragment strings (but `#` instead of `?`).
172// Because of this, query and fragment string types won't be used to represent
173// a relative IRIs without combining the prefix.
174//
175// In contrast, RFC 6570 URI Template expects that the users are constructing a
176// "working" IRIs, including the necessary prefixes for syntax components.
177// For example, fragment expansion `{#var}`, where `var` is "hello", expands to
178// `#hello`, including the prefix `#`. This means that a URI template will be
179// used to generate neither `RiQueryStr{,ing}` nor `RiFragmentStr{,ing}` strings.
180impl_try_from_expanded!(RiAbsoluteString);
181impl_try_from_expanded!(RiReferenceString);
182impl_try_from_expanded!(RiRelativeString);
183impl_try_from_expanded!(RiString);
184
185/// Expands the whole template with the dynamic context.
186pub(super) fn expand_whole_dynamic<S: Spec, W: fmt::Write, C: DynamicContext>(
187    template: &UriTemplateStr,
188    writer: &mut W,
189    context: &mut C,
190) -> Result<(), Error> {
191    context.on_expansion_start();
192    let result = expand_whole_dynamic_impl::<S, W, C>(template, writer, context);
193    context.on_expansion_end();
194    result
195}
196
197/// Expands the whole template with the dynamic context.
198///
199/// Note that the caller is responsible to set up or finalize the `context`.
200fn expand_whole_dynamic_impl<S: Spec, W: fmt::Write, C: DynamicContext>(
201    template: &UriTemplateStr,
202    writer: &mut W,
203    context: &mut C,
204) -> Result<(), Error> {
205    let mut pos = 0;
206    for chunk in Chunks::new(template) {
207        let expr = match chunk {
208            Chunk::Literal(lit) => {
209                writer
210                    .write_str(lit)
211                    .map_err(|_| Error::new(ErrorKind::WriteFailed, pos))?;
212                pos += lit.len();
213                continue;
214            }
215            Chunk::Expr(body) => body,
216        };
217        expand_expr_mut::<S, _, _>(writer, &mut pos, expr, context)?;
218    }
219
220    Ok(())
221}
222
223/// Expands the expression using the given operator and the dynamic context.
224fn expand_expr_mut<S: Spec, W: fmt::Write, C: DynamicContext>(
225    writer: &mut W,
226    pos: &mut usize,
227    expr: ExprBody<'_>,
228    context: &mut C,
229) -> Result<(), Error> {
230    let (op, varlist) = expr.decompose();
231
232    let mut is_first_varspec = true;
233    // +2: wrapping braces (`{` and `}`).
234    let chunk_end_pos = *pos + expr.as_str().len() + 2;
235    // +1: opening brace `{`.
236    *pos += op.len() + 1;
237    for (varspec_len, varspec) in varlist {
238        // Check the type before the actual expansion.
239        let ty = context.visit_dynamic(TypeVisitor::new(varspec.name()));
240        let modifier = varspec.modifier();
241
242        if matches!(modifier, Modifier::MaxLen(_))
243            && matches!(ty, ValueType::List | ValueType::Assoc)
244        {
245            // > Prefix modifiers are not applicable to variables that
246            // > have composite values.
247            //
248            // --- [RFC 6570 Section 2.4.1. Prefix](https://www.rfc-editor.org/rfc/rfc6570.html#section-2.4.1)
249            return Err(Error::new(ErrorKind::UnexpectedValueType, *pos));
250        }
251
252        // Typecheck passed. Expand.
253        let visitor = ValueVisitor::<S, _>::new(writer, varspec, op, &mut is_first_varspec);
254        let token = context
255            .visit_dynamic(visitor)
256            .map_err(|_| Error::new(ErrorKind::WriteFailed, *pos))?;
257        let writer_ptr = token.writer_ptr();
258        if !core::ptr::eq(writer_ptr, writer) {
259            // Invalid `VisitDoneToken` was returned. This cannot usually happen
260            // without intentional unnatural usage.
261            panic!("invalid `VisitDoneToken` was returned");
262        }
263
264        // +1: A trailing comman (`,`) or a closing brace (`}`).
265        *pos += varspec_len + 1;
266    }
267    assert_eq!(*pos, chunk_end_pos);
268
269    Ok(())
270}
271
272/// Properties of an operator.
273///
274/// See [RFC 6570 Appendix A](https://www.rfc-editor.org/rfc/rfc6570#appendix-A).
275#[derive(Debug, Clone, Copy)]
276struct OpProps {
277    /// Prefix for the first element.
278    first: &'static str,
279    /// Separator.
280    sep: &'static str,
281    /// Whether or not the expansion includes the variable or key name.
282    named: bool,
283    /// Result string if the variable is empty.
284    ifemp: &'static str,
285    /// Whether or not the reserved values can be written without being encoded.
286    allow_reserved: bool,
287}
288
289impl OpProps {
290    /// Properties for all known operators.
291    const PROPS: [Self; 8] = [
292        // String
293        Self {
294            first: "",
295            sep: ",",
296            named: false,
297            ifemp: "",
298            allow_reserved: false,
299        },
300        // Reserved
301        Self {
302            first: "",
303            sep: ",",
304            named: false,
305            ifemp: "",
306            allow_reserved: true,
307        },
308        // Fragment
309        Self {
310            first: "#",
311            sep: ",",
312            named: false,
313            ifemp: "",
314            allow_reserved: true,
315        },
316        // Label
317        Self {
318            first: ".",
319            sep: ".",
320            named: false,
321            ifemp: "",
322            allow_reserved: false,
323        },
324        // PathSegments
325        Self {
326            first: "/",
327            sep: "/",
328            named: false,
329            ifemp: "",
330            allow_reserved: false,
331        },
332        // PathParams
333        Self {
334            first: ";",
335            sep: ";",
336            named: true,
337            ifemp: "",
338            allow_reserved: false,
339        },
340        // FormQuery
341        Self {
342            first: "?",
343            sep: "&",
344            named: true,
345            ifemp: "=",
346            allow_reserved: false,
347        },
348        // FormQueryCont
349        Self {
350            first: "&",
351            sep: "&",
352            named: true,
353            ifemp: "=",
354            allow_reserved: false,
355        },
356    ];
357
358    /// Returns the properties for the operator.
359    #[must_use]
360    #[inline]
361    pub(super) fn from_op(op: Operator) -> &'static Self {
362        let index = match op {
363            Operator::String => 0,
364            Operator::Reserved => 1,
365            Operator::Fragment => 2,
366            Operator::Label => 3,
367            Operator::PathSegments => 4,
368            Operator::PathParams => 5,
369            Operator::FormQuery => 6,
370            Operator::FormQueryCont => 7,
371        };
372        &Self::PROPS[index]
373    }
374}
375
376/// Expands the expression using the given operator.
377fn expand<S: Spec, C: Context>(
378    f: &mut fmt::Formatter<'_>,
379    expr: ExprBody<'_>,
380    context: &C,
381) -> fmt::Result {
382    let (op, varlist) = expr.decompose();
383
384    let mut is_first_varspec = true;
385    for (_varspec_len, varspec) in varlist {
386        let visitor = ValueVisitor::<S, _>::new(f, varspec, op, &mut is_first_varspec);
387        let token = context.visit(visitor)?;
388        let writer_ptr = token.writer_ptr();
389        if !core::ptr::eq(writer_ptr, f) {
390            // Invalid `VisitDoneToken` was returned. This cannot usually happen
391            // without intentional unnatural usage.
392            panic!("invalid `VisitDoneToken` was returned");
393        }
394    }
395
396    Ok(())
397}
398
399/// Escapes the given value and writes it.
400#[inline]
401fn escape_write<S: Spec, T: fmt::Display, W: fmt::Write>(
402    f: &mut W,
403    v: T,
404    allow_reserved: bool,
405) -> fmt::Result {
406    if allow_reserved {
407        let result = process_percent_encoded_best_effort(v, |frag| {
408            let result = match frag {
409                PctEncodedFragments::Char(s, _) => f.write_str(s),
410                PctEncodedFragments::NoPctStr(s) => {
411                    write!(f, "{}", PercentEncoded::<_, S>::characters(s))
412                }
413                PctEncodedFragments::StrayPercent => f.write_str("%25"),
414                PctEncodedFragments::InvalidUtf8PctTriplets(s) => f.write_str(s),
415            };
416            if result.is_err() {
417                return ControlFlow::Break(result);
418            }
419            ControlFlow::Continue(())
420        });
421        match result {
422            Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()),
423            Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e),
424        }
425    } else {
426        /// Writer that escapes the unreserved characters and writes them.
427        struct UnreservePercentEncodeWriter<'a, S, W> {
428            /// Inner writer.
429            writer: &'a mut W,
430            /// Spec.
431            _spec: PhantomData<fn() -> S>,
432        }
433        impl<S: Spec, W: fmt::Write> fmt::Write for UnreservePercentEncodeWriter<'_, S, W> {
434            #[inline]
435            fn write_str(&mut self, s: &str) -> fmt::Result {
436                write!(self.writer, "{}", PercentEncoded::<_, S>::unreserve(s))
437            }
438        }
439        let mut writer = UnreservePercentEncodeWriter::<S, W> {
440            writer: f,
441            _spec: PhantomData,
442        };
443        write!(writer, "{v}")
444    }
445}
446
447/// Truncates the given value as a string, escapes the value, and writes it.
448fn escape_write_with_maxlen<S: Spec, T: fmt::Display, W: fmt::Write>(
449    writer: &mut PrefixOnceWriter<'_, W>,
450    v: T,
451    allow_reserved: bool,
452    max_len: Option<u16>,
453) -> fmt::Result {
454    if allow_reserved {
455        let mut max_len = max_len.map_or(usize::MAX, usize::from);
456        let result = process_percent_encoded_best_effort(v, |frag| {
457            if max_len == 0 {
458                return ControlFlow::Break(Ok(()));
459            }
460            let result =
461                match frag {
462                    PctEncodedFragments::Char(s, _) => {
463                        max_len -= 1;
464                        writer.write_str(s)
465                    }
466                    PctEncodedFragments::NoPctStr(s) => {
467                        let mut chars = s.char_indices();
468                        let count =
469                            chars.by_ref().take(max_len).last().map(|(i, _)| i).expect(
470                                "[consistency] decomposed string fragment must not be empty",
471                            );
472                        let sub_len = s.len() - chars.as_str().len();
473                        max_len -= count;
474                        write!(
475                            writer,
476                            "{}",
477                            PercentEncoded::<_, S>::characters(&s[..sub_len])
478                        )
479                    }
480                    PctEncodedFragments::StrayPercent => {
481                        max_len -= 1;
482                        writer.write_str("%25")
483                    }
484                    PctEncodedFragments::InvalidUtf8PctTriplets(s) => {
485                        let count = max_len.min(s.len() / 3);
486                        let sub_len = count * 3;
487                        max_len -= count;
488                        writer.write_str(&s[..sub_len])
489                    }
490                };
491            if result.is_err() {
492                return ControlFlow::Break(result);
493            }
494            ControlFlow::Continue(())
495        });
496        match result {
497            Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()),
498            Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e),
499        }
500    } else {
501        match max_len {
502            Some(max_len) => {
503                let mut writer = TruncatePercentEncodeWriter::<S, _> {
504                    inner: writer,
505                    rest_num_chars: usize::from(max_len),
506                    _spec: PhantomData,
507                };
508                write!(writer, "{v}")
509            }
510            None => write!(writer, "{}", PercentEncoded::<_, S>::unreserve(v)),
511        }
512    }
513}
514
515/// A writer that truncates the input to the given length and writes to the backend.
516struct TruncatePercentEncodeWriter<'a, S, W> {
517    /// Inner writer.
518    inner: &'a mut W,
519    /// Maximum number of characters to be written.
520    rest_num_chars: usize,
521    /// Spec.
522    _spec: PhantomData<fn() -> S>,
523}
524
525impl<S: Spec, W: fmt::Write> fmt::Write for TruncatePercentEncodeWriter<'_, S, W> {
526    fn write_str(&mut self, s: &str) -> fmt::Result {
527        if self.rest_num_chars == 0 {
528            return Ok(());
529        }
530        let mut chars = s.char_indices();
531        let skip_count = chars
532            .by_ref()
533            .take(self.rest_num_chars)
534            .last()
535            .map_or(0, |(i, _)| i + 1);
536        let len = s.len() - chars.as_str().len();
537        let truncated = &s[..len];
538        write!(
539            self.inner,
540            "{}",
541            PercentEncoded::<_, S>::unreserve(truncated)
542        )?;
543        self.rest_num_chars -= skip_count;
544        Ok(())
545    }
546}
547
548/// A writer that writes a prefix only once if and only if some value is written.
549struct PrefixOnceWriter<'a, W> {
550    /// Inner writer.
551    inner: &'a mut W,
552    /// Prefix to write.
553    prefix: Option<&'a str>,
554}
555
556impl<'a, W: fmt::Write> PrefixOnceWriter<'a, W> {
557    /// Creates a new writer with no prefix.
558    #[inline]
559    #[must_use]
560    fn new(inner: &'a mut W) -> Self {
561        Self {
562            inner,
563            prefix: None,
564        }
565    }
566
567    /// Creates a new writer with a prefix.
568    #[inline]
569    #[must_use]
570    fn with_prefix(inner: &'a mut W, prefix: &'a str) -> Self {
571        Self {
572            inner,
573            prefix: Some(prefix),
574        }
575    }
576
577    /// Returns true if the writer have not yet written the prefix.
578    #[inline]
579    #[must_use]
580    fn has_unwritten_prefix(&self) -> bool {
581        self.prefix.is_some()
582    }
583}
584
585impl<W: fmt::Write> fmt::Write for PrefixOnceWriter<'_, W> {
586    #[inline]
587    fn write_str(&mut self, s: &str) -> fmt::Result {
588        if let Some(prefix) = self.prefix.take() {
589            self.inner.write_str(prefix)?;
590        }
591        self.inner.write_str(s)
592    }
593}
594
595/// An opaque token value that proves some variable is visited.
596// This should not be able to be created by any means other than `VarVisitor::visit_foo()`.
597// Do not derive any traits that allows the value to be generated or cloned.
598struct VisitDoneToken<'a, S, W>(ValueVisitor<'a, S, W>);
599
600impl<'a, S: Spec, W: fmt::Write> VisitDoneToken<'a, S, W> {
601    /// Creates a new token.
602    #[inline]
603    #[must_use]
604    fn new(visitor: ValueVisitor<'a, S, W>) -> Self {
605        Self(visitor)
606    }
607
608    /// Returns the raw pointer to the backend formatter.
609    #[inline]
610    #[must_use]
611    fn writer_ptr(&self) -> *const W {
612        self.0.writer_ptr()
613    }
614}
615
616impl<S: Spec, W: fmt::Write> fmt::Debug for VisitDoneToken<'_, S, W> {
617    #[inline]
618    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
619        f.write_str("VisitDoneToken")
620    }
621}
622
623/// Visitor to retrieve a variable value.
624// Single `ValueVisitor` should be used for single expansion.
625// Do not derive any traits that allows the value to be generated or cloned.
626struct ValueVisitor<'a, S, W> {
627    /// Formatter.
628    writer: &'a mut W,
629    /// Varspec.
630    varspec: VarSpec<'a>,
631    /// Operator.
632    op: Operator,
633    /// Whether the variable to visit is the first one in an expression.
634    is_first_varspec: &'a mut bool,
635    /// Spec.
636    _spec: PhantomData<fn() -> S>,
637}
638
639impl<'a, S: Spec, W: fmt::Write> ValueVisitor<'a, S, W> {
640    /// Creates a visitor.
641    #[inline]
642    #[must_use]
643    fn new(
644        f: &'a mut W,
645        varspec: VarSpec<'a>,
646        op: Operator,
647        is_first_varspec: &'a mut bool,
648    ) -> Self {
649        Self {
650            writer: f,
651            varspec,
652            op,
653            is_first_varspec,
654            _spec: PhantomData,
655        }
656    }
657
658    /// Returns the raw pointer to the backend formatter.
659    #[inline]
660    #[must_use]
661    fn writer_ptr(&self) -> *const W {
662        self.writer as &_ as *const _
663    }
664}
665
666impl<S: Spec, W: fmt::Write> VisitorSealed for ValueVisitor<'_, S, W> {}
667
668impl<'a, S: Spec, W: fmt::Write> Visitor for ValueVisitor<'a, S, W> {
669    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
670    type ListVisitor = ListValueVisitor<'a, S, W>;
671    type AssocVisitor = AssocValueVisitor<'a, S, W>;
672
673    /// Returns the name of the variable to visit.
674    #[inline]
675    fn var_name(&self) -> VarName<'a> {
676        self.varspec.name()
677    }
678
679    #[inline]
680    fn purpose(&self) -> VisitPurpose {
681        VisitPurpose::Expand
682    }
683
684    /// Visits an undefined variable, i.e. indicates that the requested variable is unavailable.
685    #[inline]
686    fn visit_undefined(self) -> Self::Result {
687        Ok(VisitDoneToken::new(self))
688    }
689
690    /// Visits a string variable.
691    #[inline]
692    fn visit_string<T: fmt::Display>(self, v: T) -> Self::Result {
693        let oppr = OpProps::from_op(self.op);
694
695        if mem::replace(self.is_first_varspec, false) {
696            self.writer.write_str(oppr.first)?;
697        } else {
698            self.writer.write_str(oppr.sep)?;
699        }
700        let mut writer = if oppr.named {
701            self.writer.write_str(self.varspec.name().as_str())?;
702            PrefixOnceWriter::with_prefix(self.writer, "=")
703        } else {
704            PrefixOnceWriter::new(self.writer)
705        };
706
707        let max_len = match self.varspec.modifier() {
708            Modifier::None | Modifier::Explode => None,
709            Modifier::MaxLen(max_len) => Some(max_len),
710        };
711        escape_write_with_maxlen::<S, T, W>(&mut writer, v, oppr.allow_reserved, max_len)?;
712        if writer.has_unwritten_prefix() {
713            self.writer.write_str(oppr.ifemp)?;
714        }
715        Ok(VisitDoneToken::new(self))
716    }
717
718    /// Visits a list variable.
719    #[inline]
720    fn visit_list(self) -> Self::ListVisitor {
721        let oppr = OpProps::from_op(self.op);
722        ListValueVisitor {
723            visitor: self,
724            num_elems: 0,
725            oppr,
726        }
727    }
728
729    /// Visits an associative array variable.
730    #[inline]
731    fn visit_assoc(self) -> Self::AssocVisitor {
732        let oppr = OpProps::from_op(self.op);
733        AssocValueVisitor {
734            visitor: self,
735            num_elems: 0,
736            oppr,
737        }
738    }
739}
740
741/// Visitor to retrieve value of a list variable.
742// RFC 6570 section 2.3:
743//
744// > A variable defined as a list value is considered undefined if the
745// > list contains zero members.  A variable defined as an associative
746// > array of (name, value) pairs is considered undefined if the array
747// > contains zero members or if all member names in the array are
748// > associated with undefined values.
749//
750// Single variable visitor should be used for single expansion.
751// Do not derive any traits that allows the value to be generated or cloned.
752struct ListValueVisitor<'a, S, W> {
753    /// Visitor.
754    visitor: ValueVisitor<'a, S, W>,
755    /// Number of already emitted elements.
756    num_elems: usize,
757    /// Operator props.
758    oppr: &'static OpProps,
759}
760
761impl<S: Spec, W: fmt::Write> ListValueVisitor<'_, S, W> {
762    /// Visits an item.
763    fn visit_item_impl<T: fmt::Display>(&mut self, item: T) -> fmt::Result {
764        let modifier = self.visitor.varspec.modifier();
765        let is_explode = match modifier {
766            Modifier::MaxLen(_) => panic!(
767                "value type changed since `UriTemplateStr::expand()`: \
768                 prefix modifier is not applicable to a list"
769            ),
770            Modifier::None => false,
771            Modifier::Explode => true,
772        };
773
774        // Write prefix for each variable.
775        if self.num_elems == 0 {
776            if mem::replace(self.visitor.is_first_varspec, false) {
777                self.visitor.writer.write_str(self.oppr.first)?;
778            } else {
779                self.visitor.writer.write_str(self.oppr.sep)?;
780            }
781            if self.oppr.named {
782                self.visitor
783                    .writer
784                    .write_str(self.visitor.varspec.name().as_str())?;
785                self.visitor.writer.write_char('=')?;
786            }
787        } else {
788            // Write prefix for the non-first item.
789            match (self.oppr.named, is_explode) {
790                (_, false) => self.visitor.writer.write_char(',')?,
791                (false, true) => self.visitor.writer.write_str(self.oppr.sep)?,
792                (true, true) => {
793                    self.visitor.writer.write_str(self.oppr.sep)?;
794                    escape_write::<S, _, _>(
795                        self.visitor.writer,
796                        self.visitor.varspec.name().as_str(),
797                        self.oppr.allow_reserved,
798                    )?;
799                    self.visitor.writer.write_char('=')?;
800                }
801            }
802        }
803
804        escape_write::<S, _, _>(self.visitor.writer, item, self.oppr.allow_reserved)?;
805
806        self.num_elems += 1;
807        Ok(())
808    }
809}
810
811impl<S: Spec, W: fmt::Write> VisitorSealed for ListValueVisitor<'_, S, W> {}
812
813impl<'a, S: Spec, W: fmt::Write> ListVisitor for ListValueVisitor<'a, S, W> {
814    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
815
816    /// Visits an item.
817    #[inline]
818    fn visit_item<T: fmt::Display>(&mut self, item: T) -> ControlFlow<Self::Result> {
819        match self.visit_item_impl(item) {
820            Ok(_) => ControlFlow::Continue(()),
821            Err(e) => ControlFlow::Break(Err(e)),
822        }
823    }
824
825    /// Finishes visiting the list.
826    #[inline]
827    fn finish(self) -> Self::Result {
828        Ok(VisitDoneToken::new(self.visitor))
829    }
830}
831
832/// Visitor to retrieve entries of an associative array variable.
833// RFC 6570 section 2.3:
834//
835// > A variable defined as a list value is considered undefined if the
836// > list contains zero members.  A variable defined as an associative
837// > array of (name, value) pairs is considered undefined if the array
838// > contains zero members or if all member names in the array are
839// > associated with undefined values.
840//
841// Single variable visitor should be used for single expansion.
842// Do not derive any traits that allows the value to be generated or cloned.
843struct AssocValueVisitor<'a, S, W> {
844    /// Visitor.
845    visitor: ValueVisitor<'a, S, W>,
846    /// Number of already emitted elements.
847    num_elems: usize,
848    /// Operator props.
849    oppr: &'static OpProps,
850}
851
852impl<S: Spec, W: fmt::Write> AssocValueVisitor<'_, S, W> {
853    /// Visits an entry.
854    fn visit_entry_impl<K: fmt::Display, V: fmt::Display>(
855        &mut self,
856        key: K,
857        value: V,
858    ) -> fmt::Result {
859        let modifier = self.visitor.varspec.modifier();
860        let is_explode = match modifier {
861            Modifier::MaxLen(_) => panic!(
862                "value type changed since `UriTemplateStr::expand()`: \
863                 prefix modifier is not applicable to an associative array"
864            ),
865            Modifier::None => false,
866            Modifier::Explode => true,
867        };
868
869        // Write prefix for each variable.
870        if self.num_elems == 0 {
871            if mem::replace(self.visitor.is_first_varspec, false) {
872                self.visitor.writer.write_str(self.oppr.first)?;
873            } else {
874                self.visitor.writer.write_str(self.oppr.sep)?;
875            }
876            if is_explode {
877                escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
878                self.visitor.writer.write_char('=')?;
879            } else {
880                if self.oppr.named {
881                    escape_write::<S, _, _>(
882                        self.visitor.writer,
883                        self.visitor.varspec.name().as_str(),
884                        self.oppr.allow_reserved,
885                    )?;
886                    self.visitor.writer.write_char('=')?;
887                }
888                escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
889                self.visitor.writer.write_char(',')?;
890            }
891        } else {
892            // Write prefix for the non-first item.
893            match (self.oppr.named, is_explode) {
894                (_, false) => {
895                    self.visitor.writer.write_char(',')?;
896                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
897                    self.visitor.writer.write_char(',')?;
898                }
899                (false, true) => {
900                    self.visitor.writer.write_str(self.oppr.sep)?;
901                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
902                    self.visitor.writer.write_char('=')?;
903                }
904                (true, true) => {
905                    self.visitor.writer.write_str(self.oppr.sep)?;
906                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
907                    self.visitor.writer.write_char('=')?;
908                }
909            }
910        }
911
912        escape_write::<S, _, _>(self.visitor.writer, value, self.oppr.allow_reserved)?;
913
914        self.num_elems += 1;
915        Ok(())
916    }
917}
918
919impl<S: Spec, W: fmt::Write> VisitorSealed for AssocValueVisitor<'_, S, W> {}
920
921impl<'a, S: Spec, W: fmt::Write> AssocVisitor for AssocValueVisitor<'a, S, W> {
922    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
923
924    /// Visits an entry.
925    #[inline]
926    fn visit_entry<K: fmt::Display, V: fmt::Display>(
927        &mut self,
928        key: K,
929        value: V,
930    ) -> ControlFlow<Self::Result> {
931        match self.visit_entry_impl(key, value) {
932            Ok(_) => ControlFlow::Continue(()),
933            Err(e) => ControlFlow::Break(Err(e)),
934        }
935    }
936
937    /// Finishes visiting the associative array.
938    #[inline]
939    fn finish(self) -> Self::Result {
940        Ok(VisitDoneToken::new(self.visitor))
941    }
942}
943
944/// Visitor to retrieve effective type of a variable.
945struct TypeVisitor<'a> {
946    /// Variable name.
947    var_name: VarName<'a>,
948}
949
950impl<'a> TypeVisitor<'a> {
951    /// Creates a new type visitor.
952    #[inline]
953    #[must_use]
954    fn new(var_name: VarName<'a>) -> Self {
955        Self { var_name }
956    }
957}
958
959impl VisitorSealed for TypeVisitor<'_> {}
960
961impl<'a> Visitor for TypeVisitor<'a> {
962    type Result = ValueType;
963    type ListVisitor = ListTypeVisitor;
964    type AssocVisitor = AssocTypeVisitor;
965
966    #[inline]
967    fn var_name(&self) -> VarName<'a> {
968        self.var_name
969    }
970    #[inline]
971    fn purpose(&self) -> VisitPurpose {
972        VisitPurpose::Typecheck
973    }
974    #[inline]
975    fn visit_undefined(self) -> Self::Result {
976        ValueType::undefined()
977    }
978    #[inline]
979    fn visit_string<T: fmt::Display>(self, _: T) -> Self::Result {
980        ValueType::string()
981    }
982    #[inline]
983    fn visit_list(self) -> Self::ListVisitor {
984        ListTypeVisitor
985    }
986    #[inline]
987    fn visit_assoc(self) -> Self::AssocVisitor {
988        AssocTypeVisitor
989    }
990}
991
992/// Visitor to retrieve effective type of a list variable.
993struct ListTypeVisitor;
994
995impl VisitorSealed for ListTypeVisitor {}
996
997impl ListVisitor for ListTypeVisitor {
998    type Result = ValueType;
999
1000    /// Visits an item.
1001    #[inline]
1002    fn visit_item<T: fmt::Display>(&mut self, _item: T) -> ControlFlow<Self::Result> {
1003        ControlFlow::Break(ValueType::nonempty_list())
1004    }
1005
1006    /// Finishes visiting the list.
1007    #[inline]
1008    fn finish(self) -> Self::Result {
1009        ValueType::empty_list()
1010    }
1011}
1012
1013/// Visitor to retrieve effective type of an associative array variable.
1014struct AssocTypeVisitor;
1015
1016impl VisitorSealed for AssocTypeVisitor {}
1017
1018impl AssocVisitor for AssocTypeVisitor {
1019    type Result = ValueType;
1020
1021    /// Visits an item.
1022    #[inline]
1023    fn visit_entry<K: fmt::Display, V: fmt::Display>(
1024        &mut self,
1025        _key: K,
1026        _value: V,
1027    ) -> ControlFlow<Self::Result> {
1028        ControlFlow::Break(ValueType::nonempty_assoc())
1029    }
1030
1031    /// Finishes visiting the list.
1032    #[inline]
1033    fn finish(self) -> Self::Result {
1034        ValueType::empty_assoc()
1035    }
1036}