Skip to main content

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("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 = match frag {
461                PctEncodedFragments::Char(s, _) => {
462                    max_len -= 1;
463                    writer.write_str(s)
464                }
465                PctEncodedFragments::NoPctStr(s) => {
466                    let mut chars = s.char_indices();
467                    let count = chars
468                        .by_ref()
469                        .take(max_len)
470                        .last()
471                        .map(|(i, _)| i)
472                        .expect("decomposed string fragment must not be empty");
473                    let sub_len = s.len() - chars.as_str().len();
474                    max_len -= count;
475                    write!(
476                        writer,
477                        "{}",
478                        PercentEncoded::<_, S>::characters(&s[..sub_len])
479                    )
480                }
481                PctEncodedFragments::StrayPercent => {
482                    max_len -= 1;
483                    writer.write_str("%25")
484                }
485                PctEncodedFragments::InvalidUtf8PctTriplets(s) => {
486                    let count = max_len.min(s.len() / 3);
487                    let sub_len = count * 3;
488                    max_len -= count;
489                    writer.write_str(&s[..sub_len])
490                }
491            };
492            if result.is_err() {
493                return ControlFlow::Break(result);
494            }
495            ControlFlow::Continue(())
496        });
497        match result {
498            Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()),
499            Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e),
500        }
501    } else {
502        match max_len {
503            Some(max_len) => {
504                let mut writer = TruncatePercentEncodeWriter::<S, _> {
505                    inner: writer,
506                    rest_num_chars: usize::from(max_len),
507                    _spec: PhantomData,
508                };
509                write!(writer, "{v}")
510            }
511            None => write!(writer, "{}", PercentEncoded::<_, S>::unreserve(v)),
512        }
513    }
514}
515
516/// A writer that truncates the input to the given length and writes to the backend.
517struct TruncatePercentEncodeWriter<'a, S, W> {
518    /// Inner writer.
519    inner: &'a mut W,
520    /// Maximum number of characters to be written.
521    rest_num_chars: usize,
522    /// Spec.
523    _spec: PhantomData<fn() -> S>,
524}
525
526impl<S: Spec, W: fmt::Write> fmt::Write for TruncatePercentEncodeWriter<'_, S, W> {
527    fn write_str(&mut self, s: &str) -> fmt::Result {
528        if self.rest_num_chars == 0 {
529            return Ok(());
530        }
531        let mut chars = s.char_indices();
532        let skip_count = chars
533            .by_ref()
534            .take(self.rest_num_chars)
535            .last()
536            .map_or(0, |(i, _)| i + 1);
537        let len = s.len() - chars.as_str().len();
538        let truncated = &s[..len];
539        write!(
540            self.inner,
541            "{}",
542            PercentEncoded::<_, S>::unreserve(truncated)
543        )?;
544        self.rest_num_chars -= skip_count;
545        Ok(())
546    }
547}
548
549/// A writer that writes a prefix only once if and only if some value is written.
550struct PrefixOnceWriter<'a, W> {
551    /// Inner writer.
552    inner: &'a mut W,
553    /// Prefix to write.
554    prefix: Option<&'a str>,
555}
556
557impl<'a, W: fmt::Write> PrefixOnceWriter<'a, W> {
558    /// Creates a new writer with no prefix.
559    #[inline]
560    #[must_use]
561    fn new(inner: &'a mut W) -> Self {
562        Self {
563            inner,
564            prefix: None,
565        }
566    }
567
568    /// Creates a new writer with a prefix.
569    #[inline]
570    #[must_use]
571    fn with_prefix(inner: &'a mut W, prefix: &'a str) -> Self {
572        Self {
573            inner,
574            prefix: Some(prefix),
575        }
576    }
577
578    /// Returns true if the writer have not yet written the prefix.
579    #[inline]
580    #[must_use]
581    fn has_unwritten_prefix(&self) -> bool {
582        self.prefix.is_some()
583    }
584}
585
586impl<W: fmt::Write> fmt::Write for PrefixOnceWriter<'_, W> {
587    #[inline]
588    fn write_str(&mut self, s: &str) -> fmt::Result {
589        if let Some(prefix) = self.prefix.take() {
590            self.inner.write_str(prefix)?;
591        }
592        self.inner.write_str(s)
593    }
594}
595
596/// An opaque token value that proves some variable is visited.
597// This should not be able to be created by any means other than `VarVisitor::visit_foo()`.
598// Do not derive any traits that allows the value to be generated or cloned.
599struct VisitDoneToken<'a, S, W>(ValueVisitor<'a, S, W>);
600
601impl<'a, S: Spec, W: fmt::Write> VisitDoneToken<'a, S, W> {
602    /// Creates a new token.
603    #[inline]
604    #[must_use]
605    fn new(visitor: ValueVisitor<'a, S, W>) -> Self {
606        Self(visitor)
607    }
608
609    /// Returns the raw pointer to the backend formatter.
610    #[inline]
611    #[must_use]
612    fn writer_ptr(&self) -> *const W {
613        self.0.writer_ptr()
614    }
615}
616
617impl<S: Spec, W: fmt::Write> fmt::Debug for VisitDoneToken<'_, S, W> {
618    #[inline]
619    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
620        f.write_str("VisitDoneToken")
621    }
622}
623
624/// Visitor to retrieve a variable value.
625// Single `ValueVisitor` should be used for single expansion.
626// Do not derive any traits that allows the value to be generated or cloned.
627struct ValueVisitor<'a, S, W> {
628    /// Formatter.
629    writer: &'a mut W,
630    /// Varspec.
631    varspec: VarSpec<'a>,
632    /// Operator.
633    op: Operator,
634    /// Whether the variable to visit is the first one in an expression.
635    is_first_varspec: &'a mut bool,
636    /// Spec.
637    _spec: PhantomData<fn() -> S>,
638}
639
640impl<'a, S: Spec, W: fmt::Write> ValueVisitor<'a, S, W> {
641    /// Creates a visitor.
642    #[inline]
643    #[must_use]
644    fn new(
645        f: &'a mut W,
646        varspec: VarSpec<'a>,
647        op: Operator,
648        is_first_varspec: &'a mut bool,
649    ) -> Self {
650        Self {
651            writer: f,
652            varspec,
653            op,
654            is_first_varspec,
655            _spec: PhantomData,
656        }
657    }
658
659    /// Returns the raw pointer to the backend formatter.
660    #[inline]
661    #[must_use]
662    fn writer_ptr(&self) -> *const W {
663        self.writer as &_ as *const _
664    }
665}
666
667impl<S: Spec, W: fmt::Write> VisitorSealed for ValueVisitor<'_, S, W> {}
668
669impl<'a, S: Spec, W: fmt::Write> Visitor for ValueVisitor<'a, S, W> {
670    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
671    type ListVisitor = ListValueVisitor<'a, S, W>;
672    type AssocVisitor = AssocValueVisitor<'a, S, W>;
673
674    /// Returns the name of the variable to visit.
675    #[inline]
676    fn var_name(&self) -> VarName<'a> {
677        self.varspec.name()
678    }
679
680    #[inline]
681    fn purpose(&self) -> VisitPurpose {
682        VisitPurpose::Expand
683    }
684
685    /// Visits an undefined variable, i.e. indicates that the requested variable is unavailable.
686    #[inline]
687    fn visit_undefined(self) -> Self::Result {
688        Ok(VisitDoneToken::new(self))
689    }
690
691    /// Visits a string variable.
692    #[inline]
693    fn visit_string<T: fmt::Display>(self, v: T) -> Self::Result {
694        let oppr = OpProps::from_op(self.op);
695
696        if mem::replace(self.is_first_varspec, false) {
697            self.writer.write_str(oppr.first)?;
698        } else {
699            self.writer.write_str(oppr.sep)?;
700        }
701        let mut writer = if oppr.named {
702            self.writer.write_str(self.varspec.name().as_str())?;
703            PrefixOnceWriter::with_prefix(self.writer, "=")
704        } else {
705            PrefixOnceWriter::new(self.writer)
706        };
707
708        let max_len = match self.varspec.modifier() {
709            Modifier::None | Modifier::Explode => None,
710            Modifier::MaxLen(max_len) => Some(max_len),
711        };
712        escape_write_with_maxlen::<S, T, W>(&mut writer, v, oppr.allow_reserved, max_len)?;
713        if writer.has_unwritten_prefix() {
714            self.writer.write_str(oppr.ifemp)?;
715        }
716        Ok(VisitDoneToken::new(self))
717    }
718
719    /// Visits a list variable.
720    #[inline]
721    fn visit_list(self) -> Self::ListVisitor {
722        let oppr = OpProps::from_op(self.op);
723        ListValueVisitor {
724            visitor: self,
725            num_elems: 0,
726            oppr,
727        }
728    }
729
730    /// Visits an associative array variable.
731    #[inline]
732    fn visit_assoc(self) -> Self::AssocVisitor {
733        let oppr = OpProps::from_op(self.op);
734        AssocValueVisitor {
735            visitor: self,
736            num_elems: 0,
737            oppr,
738        }
739    }
740}
741
742/// Visitor to retrieve value of a list variable.
743// RFC 6570 section 2.3:
744//
745// > A variable defined as a list value is considered undefined if the
746// > list contains zero members.  A variable defined as an associative
747// > array of (name, value) pairs is considered undefined if the array
748// > contains zero members or if all member names in the array are
749// > associated with undefined values.
750//
751// Single variable visitor should be used for single expansion.
752// Do not derive any traits that allows the value to be generated or cloned.
753struct ListValueVisitor<'a, S, W> {
754    /// Visitor.
755    visitor: ValueVisitor<'a, S, W>,
756    /// Number of already emitted elements.
757    num_elems: usize,
758    /// Operator props.
759    oppr: &'static OpProps,
760}
761
762impl<S: Spec, W: fmt::Write> ListValueVisitor<'_, S, W> {
763    /// Visits an item.
764    fn visit_item_impl<T: fmt::Display>(&mut self, item: T) -> fmt::Result {
765        let modifier = self.visitor.varspec.modifier();
766        let is_explode = match modifier {
767            Modifier::MaxLen(_) => panic!(
768                "value type changed since `UriTemplateStr::expand()`: \
769                 prefix modifier is not applicable to a list"
770            ),
771            Modifier::None => false,
772            Modifier::Explode => true,
773        };
774
775        // Write prefix for each variable.
776        if self.num_elems == 0 {
777            if mem::replace(self.visitor.is_first_varspec, false) {
778                self.visitor.writer.write_str(self.oppr.first)?;
779            } else {
780                self.visitor.writer.write_str(self.oppr.sep)?;
781            }
782            if self.oppr.named {
783                self.visitor
784                    .writer
785                    .write_str(self.visitor.varspec.name().as_str())?;
786                self.visitor.writer.write_char('=')?;
787            }
788        } else {
789            // Write prefix for the non-first item.
790            match (self.oppr.named, is_explode) {
791                (_, false) => self.visitor.writer.write_char(',')?,
792                (false, true) => self.visitor.writer.write_str(self.oppr.sep)?,
793                (true, true) => {
794                    self.visitor.writer.write_str(self.oppr.sep)?;
795                    escape_write::<S, _, _>(
796                        self.visitor.writer,
797                        self.visitor.varspec.name().as_str(),
798                        self.oppr.allow_reserved,
799                    )?;
800                    self.visitor.writer.write_char('=')?;
801                }
802            }
803        }
804
805        escape_write::<S, _, _>(self.visitor.writer, item, self.oppr.allow_reserved)?;
806
807        self.num_elems += 1;
808        Ok(())
809    }
810}
811
812impl<S: Spec, W: fmt::Write> VisitorSealed for ListValueVisitor<'_, S, W> {}
813
814impl<'a, S: Spec, W: fmt::Write> ListVisitor for ListValueVisitor<'a, S, W> {
815    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
816
817    /// Visits an item.
818    #[inline]
819    fn visit_item<T: fmt::Display>(&mut self, item: T) -> ControlFlow<Self::Result> {
820        match self.visit_item_impl(item) {
821            Ok(_) => ControlFlow::Continue(()),
822            Err(e) => ControlFlow::Break(Err(e)),
823        }
824    }
825
826    /// Finishes visiting the list.
827    #[inline]
828    fn finish(self) -> Self::Result {
829        Ok(VisitDoneToken::new(self.visitor))
830    }
831}
832
833/// Visitor to retrieve entries of an associative array variable.
834// RFC 6570 section 2.3:
835//
836// > A variable defined as a list value is considered undefined if the
837// > list contains zero members.  A variable defined as an associative
838// > array of (name, value) pairs is considered undefined if the array
839// > contains zero members or if all member names in the array are
840// > associated with undefined values.
841//
842// Single variable visitor should be used for single expansion.
843// Do not derive any traits that allows the value to be generated or cloned.
844struct AssocValueVisitor<'a, S, W> {
845    /// Visitor.
846    visitor: ValueVisitor<'a, S, W>,
847    /// Number of already emitted elements.
848    num_elems: usize,
849    /// Operator props.
850    oppr: &'static OpProps,
851}
852
853impl<S: Spec, W: fmt::Write> AssocValueVisitor<'_, S, W> {
854    /// Visits an entry.
855    fn visit_entry_impl<K: fmt::Display, V: fmt::Display>(
856        &mut self,
857        key: K,
858        value: V,
859    ) -> fmt::Result {
860        let modifier = self.visitor.varspec.modifier();
861        let is_explode = match modifier {
862            Modifier::MaxLen(_) => panic!(
863                "value type changed since `UriTemplateStr::expand()`: \
864                 prefix modifier is not applicable to an associative array"
865            ),
866            Modifier::None => false,
867            Modifier::Explode => true,
868        };
869
870        // Write prefix for each variable.
871        if self.num_elems == 0 {
872            if mem::replace(self.visitor.is_first_varspec, false) {
873                self.visitor.writer.write_str(self.oppr.first)?;
874            } else {
875                self.visitor.writer.write_str(self.oppr.sep)?;
876            }
877            if is_explode {
878                escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
879                self.visitor.writer.write_char('=')?;
880            } else {
881                if self.oppr.named {
882                    escape_write::<S, _, _>(
883                        self.visitor.writer,
884                        self.visitor.varspec.name().as_str(),
885                        self.oppr.allow_reserved,
886                    )?;
887                    self.visitor.writer.write_char('=')?;
888                }
889                escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
890                self.visitor.writer.write_char(',')?;
891            }
892        } else {
893            // Write prefix for the non-first item.
894            match (self.oppr.named, is_explode) {
895                (_, false) => {
896                    self.visitor.writer.write_char(',')?;
897                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
898                    self.visitor.writer.write_char(',')?;
899                }
900                (false, true) => {
901                    self.visitor.writer.write_str(self.oppr.sep)?;
902                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
903                    self.visitor.writer.write_char('=')?;
904                }
905                (true, true) => {
906                    self.visitor.writer.write_str(self.oppr.sep)?;
907                    escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
908                    self.visitor.writer.write_char('=')?;
909                }
910            }
911        }
912
913        escape_write::<S, _, _>(self.visitor.writer, value, self.oppr.allow_reserved)?;
914
915        self.num_elems += 1;
916        Ok(())
917    }
918}
919
920impl<S: Spec, W: fmt::Write> VisitorSealed for AssocValueVisitor<'_, S, W> {}
921
922impl<'a, S: Spec, W: fmt::Write> AssocVisitor for AssocValueVisitor<'a, S, W> {
923    type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
924
925    /// Visits an entry.
926    #[inline]
927    fn visit_entry<K: fmt::Display, V: fmt::Display>(
928        &mut self,
929        key: K,
930        value: V,
931    ) -> ControlFlow<Self::Result> {
932        match self.visit_entry_impl(key, value) {
933            Ok(_) => ControlFlow::Continue(()),
934            Err(e) => ControlFlow::Break(Err(e)),
935        }
936    }
937
938    /// Finishes visiting the associative array.
939    #[inline]
940    fn finish(self) -> Self::Result {
941        Ok(VisitDoneToken::new(self.visitor))
942    }
943}
944
945/// Visitor to retrieve effective type of a variable.
946struct TypeVisitor<'a> {
947    /// Variable name.
948    var_name: VarName<'a>,
949}
950
951impl<'a> TypeVisitor<'a> {
952    /// Creates a new type visitor.
953    #[inline]
954    #[must_use]
955    fn new(var_name: VarName<'a>) -> Self {
956        Self { var_name }
957    }
958}
959
960impl VisitorSealed for TypeVisitor<'_> {}
961
962impl<'a> Visitor for TypeVisitor<'a> {
963    type Result = ValueType;
964    type ListVisitor = ListTypeVisitor;
965    type AssocVisitor = AssocTypeVisitor;
966
967    #[inline]
968    fn var_name(&self) -> VarName<'a> {
969        self.var_name
970    }
971    #[inline]
972    fn purpose(&self) -> VisitPurpose {
973        VisitPurpose::Typecheck
974    }
975    #[inline]
976    fn visit_undefined(self) -> Self::Result {
977        ValueType::undefined()
978    }
979    #[inline]
980    fn visit_string<T: fmt::Display>(self, _: T) -> Self::Result {
981        ValueType::string()
982    }
983    #[inline]
984    fn visit_list(self) -> Self::ListVisitor {
985        ListTypeVisitor
986    }
987    #[inline]
988    fn visit_assoc(self) -> Self::AssocVisitor {
989        AssocTypeVisitor
990    }
991}
992
993/// Visitor to retrieve effective type of a list variable.
994struct ListTypeVisitor;
995
996impl VisitorSealed for ListTypeVisitor {}
997
998impl ListVisitor for ListTypeVisitor {
999    type Result = ValueType;
1000
1001    /// Visits an item.
1002    #[inline]
1003    fn visit_item<T: fmt::Display>(&mut self, _item: T) -> ControlFlow<Self::Result> {
1004        ControlFlow::Break(ValueType::nonempty_list())
1005    }
1006
1007    /// Finishes visiting the list.
1008    #[inline]
1009    fn finish(self) -> Self::Result {
1010        ValueType::empty_list()
1011    }
1012}
1013
1014/// Visitor to retrieve effective type of an associative array variable.
1015struct AssocTypeVisitor;
1016
1017impl VisitorSealed for AssocTypeVisitor {}
1018
1019impl AssocVisitor for AssocTypeVisitor {
1020    type Result = ValueType;
1021
1022    /// Visits an item.
1023    #[inline]
1024    fn visit_entry<K: fmt::Display, V: fmt::Display>(
1025        &mut self,
1026        _key: K,
1027        _value: V,
1028    ) -> ControlFlow<Self::Result> {
1029        ControlFlow::Break(ValueType::nonempty_assoc())
1030    }
1031
1032    /// Finishes visiting the list.
1033    #[inline]
1034    fn finish(self) -> Self::Result {
1035        ValueType::empty_assoc()
1036    }
1037}