1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub(super) enum Chunk<'a> {
28 Literal(&'a str),
30 Expr(ExprBody<'a>),
32}
33
34#[derive(Debug, Clone)]
36pub(super) struct Chunks<'a> {
37 template: &'a str,
39}
40
41impl<'a> Chunks<'a> {
42 #[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#[derive(Debug, Clone, Copy)]
77pub struct Expanded<'a, S, C> {
78 template: &'a UriTemplateStr,
80 context: &'a C,
82 _spec: PhantomData<fn() -> S>,
84}
85
86impl<'a, S: Spec, C: Context> Expanded<'a, S, C> {
87 #[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 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 let chunk_end_pos = pos + expr_len + 2;
111 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 return Err(Error::new(ErrorKind::UnexpectedValueType, pos));
125 }
126
127 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
153macro_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
168impl_try_from_expanded!(RiAbsoluteString);
181impl_try_from_expanded!(RiReferenceString);
182impl_try_from_expanded!(RiRelativeString);
183impl_try_from_expanded!(RiString);
184
185pub(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
197fn 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
223fn 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 let chunk_end_pos = *pos + expr.as_str().len() + 2;
235 *pos += op.len() + 1;
237 for (varspec_len, varspec) in varlist {
238 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 return Err(Error::new(ErrorKind::UnexpectedValueType, *pos));
250 }
251
252 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 panic!("invalid `VisitDoneToken` was returned");
262 }
263
264 *pos += varspec_len + 1;
266 }
267 assert_eq!(*pos, chunk_end_pos);
268
269 Ok(())
270}
271
272#[derive(Debug, Clone, Copy)]
276struct OpProps {
277 first: &'static str,
279 sep: &'static str,
281 named: bool,
283 ifemp: &'static str,
285 allow_reserved: bool,
287}
288
289impl OpProps {
290 const PROPS: [Self; 8] = [
292 Self {
294 first: "",
295 sep: ",",
296 named: false,
297 ifemp: "",
298 allow_reserved: false,
299 },
300 Self {
302 first: "",
303 sep: ",",
304 named: false,
305 ifemp: "",
306 allow_reserved: true,
307 },
308 Self {
310 first: "#",
311 sep: ",",
312 named: false,
313 ifemp: "",
314 allow_reserved: true,
315 },
316 Self {
318 first: ".",
319 sep: ".",
320 named: false,
321 ifemp: "",
322 allow_reserved: false,
323 },
324 Self {
326 first: "/",
327 sep: "/",
328 named: false,
329 ifemp: "",
330 allow_reserved: false,
331 },
332 Self {
334 first: ";",
335 sep: ";",
336 named: true,
337 ifemp: "",
338 allow_reserved: false,
339 },
340 Self {
342 first: "?",
343 sep: "&",
344 named: true,
345 ifemp: "=",
346 allow_reserved: false,
347 },
348 Self {
350 first: "&",
351 sep: "&",
352 named: true,
353 ifemp: "=",
354 allow_reserved: false,
355 },
356 ];
357
358 #[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
376fn 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 panic!("invalid `VisitDoneToken` was returned");
393 }
394 }
395
396 Ok(())
397}
398
399#[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 struct UnreservePercentEncodeWriter<'a, S, W> {
428 writer: &'a mut W,
430 _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
447fn 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
515struct TruncatePercentEncodeWriter<'a, S, W> {
517 inner: &'a mut W,
519 rest_num_chars: usize,
521 _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
548struct PrefixOnceWriter<'a, W> {
550 inner: &'a mut W,
552 prefix: Option<&'a str>,
554}
555
556impl<'a, W: fmt::Write> PrefixOnceWriter<'a, W> {
557 #[inline]
559 #[must_use]
560 fn new(inner: &'a mut W) -> Self {
561 Self {
562 inner,
563 prefix: None,
564 }
565 }
566
567 #[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 #[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
595struct VisitDoneToken<'a, S, W>(ValueVisitor<'a, S, W>);
599
600impl<'a, S: Spec, W: fmt::Write> VisitDoneToken<'a, S, W> {
601 #[inline]
603 #[must_use]
604 fn new(visitor: ValueVisitor<'a, S, W>) -> Self {
605 Self(visitor)
606 }
607
608 #[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
623struct ValueVisitor<'a, S, W> {
627 writer: &'a mut W,
629 varspec: VarSpec<'a>,
631 op: Operator,
633 is_first_varspec: &'a mut bool,
635 _spec: PhantomData<fn() -> S>,
637}
638
639impl<'a, S: Spec, W: fmt::Write> ValueVisitor<'a, S, W> {
640 #[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 #[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 #[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 #[inline]
686 fn visit_undefined(self) -> Self::Result {
687 Ok(VisitDoneToken::new(self))
688 }
689
690 #[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 #[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 #[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
741struct ListValueVisitor<'a, S, W> {
753 visitor: ValueVisitor<'a, S, W>,
755 num_elems: usize,
757 oppr: &'static OpProps,
759}
760
761impl<S: Spec, W: fmt::Write> ListValueVisitor<'_, S, W> {
762 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 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 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 #[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 #[inline]
827 fn finish(self) -> Self::Result {
828 Ok(VisitDoneToken::new(self.visitor))
829 }
830}
831
832struct AssocValueVisitor<'a, S, W> {
844 visitor: ValueVisitor<'a, S, W>,
846 num_elems: usize,
848 oppr: &'static OpProps,
850}
851
852impl<S: Spec, W: fmt::Write> AssocValueVisitor<'_, S, W> {
853 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 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 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 #[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 #[inline]
939 fn finish(self) -> Self::Result {
940 Ok(VisitDoneToken::new(self.visitor))
941 }
942}
943
944struct TypeVisitor<'a> {
946 var_name: VarName<'a>,
948}
949
950impl<'a> TypeVisitor<'a> {
951 #[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
992struct ListTypeVisitor;
994
995impl VisitorSealed for ListTypeVisitor {}
996
997impl ListVisitor for ListTypeVisitor {
998 type Result = ValueType;
999
1000 #[inline]
1002 fn visit_item<T: fmt::Display>(&mut self, _item: T) -> ControlFlow<Self::Result> {
1003 ControlFlow::Break(ValueType::nonempty_list())
1004 }
1005
1006 #[inline]
1008 fn finish(self) -> Self::Result {
1009 ValueType::empty_list()
1010 }
1011}
1012
1013struct AssocTypeVisitor;
1015
1016impl VisitorSealed for AssocTypeVisitor {}
1017
1018impl AssocVisitor for AssocTypeVisitor {
1019 type Result = ValueType;
1020
1021 #[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 #[inline]
1033 fn finish(self) -> Self::Result {
1034 ValueType::empty_assoc()
1035 }
1036}