/rust/registry/src/index.crates.io-1949cf8c6b5b557f/iri-string-0.7.9/src/template/expand.rs
Line | Count | Source |
1 | | //! Expansion. |
2 | | |
3 | | use core::fmt::{self, Write as _}; |
4 | | use core::marker::PhantomData; |
5 | | use core::mem; |
6 | | use core::ops::ControlFlow; |
7 | | |
8 | | #[cfg(feature = "alloc")] |
9 | | use alloc::string::{String, ToString}; |
10 | | |
11 | | use crate::parser::str::{find_split, find_split_hole}; |
12 | | use crate::parser::str::{process_percent_encoded_best_effort, PctEncodedFragments}; |
13 | | use crate::percent_encode::PercentEncoded; |
14 | | use crate::spec::Spec; |
15 | | use crate::template::components::{ExprBody, Modifier, Operator, VarName, VarSpec}; |
16 | | use crate::template::context::{ |
17 | | private::Sealed as VisitorSealed, AssocVisitor, Context, DynamicContext, ListVisitor, |
18 | | VisitPurpose, Visitor, |
19 | | }; |
20 | | use crate::template::error::{Error, ErrorKind}; |
21 | | use crate::template::{UriTemplateStr, ValueType}; |
22 | | #[cfg(feature = "alloc")] |
23 | | use crate::types; |
24 | | |
25 | | /// A chunk in a template string. |
26 | | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
27 | | pub(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)] |
36 | | pub(super) struct Chunks<'a> { |
37 | | /// Template. |
38 | | template: &'a str, |
39 | | } |
40 | | |
41 | | impl<'a> Chunks<'a> { |
42 | | /// Creates a new iterator. |
43 | | #[inline] |
44 | | #[must_use] |
45 | 0 | pub(super) fn new(template: &'a UriTemplateStr) -> Self { |
46 | 0 | Self { |
47 | 0 | template: template.as_str(), |
48 | 0 | } |
49 | 0 | } |
50 | | } |
51 | | |
52 | | impl<'a> Iterator for Chunks<'a> { |
53 | | type Item = Chunk<'a>; |
54 | | |
55 | 0 | fn next(&mut self) -> Option<Self::Item> { |
56 | 0 | if self.template.is_empty() { |
57 | 0 | return None; |
58 | 0 | } |
59 | 0 | match find_split(self.template, b'{') { |
60 | 0 | Some(("", _)) => { |
61 | 0 | let (expr_body, rest) = find_split_hole(&self.template[1..], b'}') |
62 | 0 | .expect("[validity] expression inside a template must be closed"); |
63 | 0 | self.template = rest; |
64 | 0 | Some(Chunk::Expr(ExprBody::new(expr_body))) |
65 | | } |
66 | 0 | Some((lit, rest)) => { |
67 | 0 | self.template = rest; |
68 | 0 | Some(Chunk::Literal(lit)) |
69 | | } |
70 | 0 | None => Some(Chunk::Literal(mem::take(&mut self.template))), |
71 | | } |
72 | 0 | } |
73 | | } |
74 | | |
75 | | /// Template expansion result. |
76 | | #[derive(Debug, Clone, Copy)] |
77 | | pub 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 | | |
86 | | impl<'a, S: Spec, C: Context> Expanded<'a, S, C> { |
87 | | /// Creates a new `Expanded` object. |
88 | | #[inline] |
89 | 0 | pub(super) fn new(template: &'a UriTemplateStr, context: &'a C) -> Result<Self, Error> { |
90 | 0 | Self::typecheck_context(template, context)?; |
91 | 0 | Ok(Self { |
92 | 0 | template, |
93 | 0 | context, |
94 | 0 | _spec: PhantomData, |
95 | 0 | }) |
96 | 0 | } |
97 | | |
98 | | /// Checks if the types of variables are allowed for the corresponding expressions in the template. |
99 | 0 | fn typecheck_context(template: &UriTemplateStr, context: &C) -> Result<(), Error> { |
100 | 0 | let mut pos = 0; |
101 | 0 | for chunk in Chunks::new(template) { |
102 | 0 | let (expr_len, (op, varlist)) = match chunk { |
103 | 0 | Chunk::Expr(expr_body) => (expr_body.as_str().len(), expr_body.decompose()), |
104 | 0 | Chunk::Literal(lit) => { |
105 | 0 | pos += lit.len(); |
106 | 0 | continue; |
107 | | } |
108 | | }; |
109 | | // +2: wrapping braces (`{` and `}`). |
110 | 0 | let chunk_end_pos = pos + expr_len + 2; |
111 | | // +1: opening brace `{`. |
112 | 0 | pos += op.len() + 1; |
113 | 0 | for (varspec_len, varspec) in varlist { |
114 | 0 | let ty = context.visit(TypeVisitor::new(varspec.name())); |
115 | 0 | let modifier = varspec.modifier(); |
116 | | |
117 | 0 | if matches!(modifier, Modifier::MaxLen(_)) |
118 | 0 | && 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 | 0 | return Err(Error::new(ErrorKind::UnexpectedValueType, pos)); |
125 | 0 | } |
126 | | |
127 | | // +1: A trailing comman (`,`) or a closing brace (`}`). |
128 | 0 | pos += varspec_len + 1; |
129 | | } |
130 | 0 | assert_eq!(pos, chunk_end_pos); |
131 | | } |
132 | 0 | Ok(()) |
133 | 0 | } |
134 | | } |
135 | | |
136 | | impl<S: Spec, C: Context> fmt::Display for Expanded<'_, S, C> { |
137 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
138 | 0 | for chunk in Chunks::new(self.template) { |
139 | 0 | let expr = match chunk { |
140 | 0 | Chunk::Literal(lit) => { |
141 | 0 | f.write_str(lit)?; |
142 | 0 | continue; |
143 | | } |
144 | 0 | Chunk::Expr(body) => body, |
145 | | }; |
146 | 0 | expand::<S, _>(f, expr, self.context)?; |
147 | | } |
148 | | |
149 | 0 | Ok(()) |
150 | 0 | } |
151 | | } |
152 | | |
153 | | /// Implement `TryFrom<Expanded<...>> for SomeUriStringType`. |
154 | | macro_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 | 0 | fn try_from(v: Expanded<'_, S, C>) -> Result<Self, Self::Error> { |
162 | 0 | Self::try_from(v.to_string()) |
163 | 0 | } Unexecuted instantiation: <iri_string::types::generic::reference::RiReferenceString<_> as core::convert::TryFrom<iri_string::template::expand::Expanded<_, _>>>::try_from Unexecuted instantiation: <iri_string::types::generic::relative::RiRelativeString<_> as core::convert::TryFrom<iri_string::template::expand::Expanded<_, _>>>::try_from Unexecuted instantiation: <iri_string::types::generic::normal::RiString<_> as core::convert::TryFrom<iri_string::template::expand::Expanded<_, _>>>::try_from Unexecuted instantiation: <iri_string::types::generic::absolute::RiAbsoluteString<_> as core::convert::TryFrom<iri_string::template::expand::Expanded<_, _>>>::try_from |
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. |
180 | | impl_try_from_expanded!(RiAbsoluteString); |
181 | | impl_try_from_expanded!(RiReferenceString); |
182 | | impl_try_from_expanded!(RiRelativeString); |
183 | | impl_try_from_expanded!(RiString); |
184 | | |
185 | | /// Expands the whole template with the dynamic context. |
186 | 0 | pub(super) fn expand_whole_dynamic<S: Spec, W: fmt::Write, C: DynamicContext>( |
187 | 0 | template: &UriTemplateStr, |
188 | 0 | writer: &mut W, |
189 | 0 | context: &mut C, |
190 | 0 | ) -> Result<(), Error> { |
191 | 0 | context.on_expansion_start(); |
192 | 0 | let result = expand_whole_dynamic_impl::<S, W, C>(template, writer, context); |
193 | 0 | context.on_expansion_end(); |
194 | 0 | result |
195 | 0 | } |
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`. |
200 | 0 | fn expand_whole_dynamic_impl<S: Spec, W: fmt::Write, C: DynamicContext>( |
201 | 0 | template: &UriTemplateStr, |
202 | 0 | writer: &mut W, |
203 | 0 | context: &mut C, |
204 | 0 | ) -> Result<(), Error> { |
205 | 0 | let mut pos = 0; |
206 | 0 | for chunk in Chunks::new(template) { |
207 | 0 | let expr = match chunk { |
208 | 0 | Chunk::Literal(lit) => { |
209 | 0 | writer |
210 | 0 | .write_str(lit) |
211 | 0 | .map_err(|_| Error::new(ErrorKind::WriteFailed, pos))?; |
212 | 0 | pos += lit.len(); |
213 | 0 | continue; |
214 | | } |
215 | 0 | Chunk::Expr(body) => body, |
216 | | }; |
217 | 0 | expand_expr_mut::<S, _, _>(writer, &mut pos, expr, context)?; |
218 | | } |
219 | | |
220 | 0 | Ok(()) |
221 | 0 | } |
222 | | |
223 | | /// Expands the expression using the given operator and the dynamic context. |
224 | 0 | fn expand_expr_mut<S: Spec, W: fmt::Write, C: DynamicContext>( |
225 | 0 | writer: &mut W, |
226 | 0 | pos: &mut usize, |
227 | 0 | expr: ExprBody<'_>, |
228 | 0 | context: &mut C, |
229 | 0 | ) -> Result<(), Error> { |
230 | 0 | let (op, varlist) = expr.decompose(); |
231 | | |
232 | 0 | let mut is_first_varspec = true; |
233 | | // +2: wrapping braces (`{` and `}`). |
234 | 0 | let chunk_end_pos = *pos + expr.as_str().len() + 2; |
235 | | // +1: opening brace `{`. |
236 | 0 | *pos += op.len() + 1; |
237 | 0 | for (varspec_len, varspec) in varlist { |
238 | | // Check the type before the actual expansion. |
239 | 0 | let ty = context.visit_dynamic(TypeVisitor::new(varspec.name())); |
240 | 0 | let modifier = varspec.modifier(); |
241 | | |
242 | 0 | if matches!(modifier, Modifier::MaxLen(_)) |
243 | 0 | && 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 | 0 | return Err(Error::new(ErrorKind::UnexpectedValueType, *pos)); |
250 | 0 | } |
251 | | |
252 | | // Typecheck passed. Expand. |
253 | 0 | let visitor = ValueVisitor::<S, _>::new(writer, varspec, op, &mut is_first_varspec); |
254 | 0 | let token = context |
255 | 0 | .visit_dynamic(visitor) |
256 | 0 | .map_err(|_| Error::new(ErrorKind::WriteFailed, *pos))?; |
257 | 0 | let writer_ptr = token.writer_ptr(); |
258 | 0 | if !core::ptr::eq(writer_ptr, writer) { |
259 | | // Invalid `VisitDoneToken` was returned. This cannot usually happen |
260 | | // without intentional unnatural usage. |
261 | 0 | panic!("invalid `VisitDoneToken` was returned"); |
262 | 0 | } |
263 | | |
264 | | // +1: A trailing comman (`,`) or a closing brace (`}`). |
265 | 0 | *pos += varspec_len + 1; |
266 | | } |
267 | 0 | assert_eq!(*pos, chunk_end_pos); |
268 | | |
269 | 0 | Ok(()) |
270 | 0 | } |
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)] |
276 | | struct 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 | | |
289 | | impl 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 | 0 | pub(super) fn from_op(op: Operator) -> &'static Self { |
362 | 0 | let index = match op { |
363 | 0 | Operator::String => 0, |
364 | 0 | Operator::Reserved => 1, |
365 | 0 | Operator::Fragment => 2, |
366 | 0 | Operator::Label => 3, |
367 | 0 | Operator::PathSegments => 4, |
368 | 0 | Operator::PathParams => 5, |
369 | 0 | Operator::FormQuery => 6, |
370 | 0 | Operator::FormQueryCont => 7, |
371 | | }; |
372 | 0 | &Self::PROPS[index] |
373 | 0 | } |
374 | | } |
375 | | |
376 | | /// Expands the expression using the given operator. |
377 | 0 | fn expand<S: Spec, C: Context>( |
378 | 0 | f: &mut fmt::Formatter<'_>, |
379 | 0 | expr: ExprBody<'_>, |
380 | 0 | context: &C, |
381 | 0 | ) -> fmt::Result { |
382 | 0 | let (op, varlist) = expr.decompose(); |
383 | | |
384 | 0 | let mut is_first_varspec = true; |
385 | 0 | for (_varspec_len, varspec) in varlist { |
386 | 0 | let visitor = ValueVisitor::<S, _>::new(f, varspec, op, &mut is_first_varspec); |
387 | 0 | let token = context.visit(visitor)?; |
388 | 0 | let writer_ptr = token.writer_ptr(); |
389 | 0 | if !core::ptr::eq(writer_ptr, f) { |
390 | | // Invalid `VisitDoneToken` was returned. This cannot usually happen |
391 | | // without intentional unnatural usage. |
392 | 0 | panic!("invalid `VisitDoneToken` was returned"); |
393 | 0 | } |
394 | | } |
395 | | |
396 | 0 | Ok(()) |
397 | 0 | } |
398 | | |
399 | | /// Escapes the given value and writes it. |
400 | | #[inline] |
401 | 0 | fn escape_write<S: Spec, T: fmt::Display, W: fmt::Write>( |
402 | 0 | f: &mut W, |
403 | 0 | v: T, |
404 | 0 | allow_reserved: bool, |
405 | 0 | ) -> fmt::Result { |
406 | 0 | if allow_reserved { |
407 | 0 | let result = process_percent_encoded_best_effort(v, |frag| { |
408 | 0 | let result = match frag { |
409 | 0 | PctEncodedFragments::Char(s, _) => f.write_str(s), |
410 | 0 | PctEncodedFragments::NoPctStr(s) => { |
411 | 0 | write!(f, "{}", PercentEncoded::<_, S>::characters(s)) |
412 | | } |
413 | 0 | PctEncodedFragments::StrayPercent => f.write_str("%25"), |
414 | 0 | PctEncodedFragments::InvalidUtf8PctTriplets(s) => f.write_str(s), |
415 | | }; |
416 | 0 | if result.is_err() { |
417 | 0 | return ControlFlow::Break(result); |
418 | 0 | } |
419 | 0 | ControlFlow::Continue(()) |
420 | 0 | }); |
421 | 0 | match result { |
422 | 0 | Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()), |
423 | 0 | 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 | 0 | fn write_str(&mut self, s: &str) -> fmt::Result { |
436 | 0 | write!(self.writer, "{}", PercentEncoded::<_, S>::unreserve(s)) |
437 | 0 | } |
438 | | } |
439 | 0 | let mut writer = UnreservePercentEncodeWriter::<S, W> { |
440 | 0 | writer: f, |
441 | 0 | _spec: PhantomData, |
442 | 0 | }; |
443 | 0 | write!(writer, "{v}") |
444 | | } |
445 | 0 | } |
446 | | |
447 | | /// Truncates the given value as a string, escapes the value, and writes it. |
448 | 0 | fn escape_write_with_maxlen<S: Spec, T: fmt::Display, W: fmt::Write>( |
449 | 0 | writer: &mut PrefixOnceWriter<'_, W>, |
450 | 0 | v: T, |
451 | 0 | allow_reserved: bool, |
452 | 0 | max_len: Option<u16>, |
453 | 0 | ) -> fmt::Result { |
454 | 0 | if allow_reserved { |
455 | 0 | let mut max_len = max_len.map_or(usize::MAX, usize::from); |
456 | 0 | let result = process_percent_encoded_best_effort(v, |frag| { |
457 | 0 | if max_len == 0 { |
458 | 0 | return ControlFlow::Break(Ok(())); |
459 | 0 | } |
460 | 0 | let result = |
461 | 0 | match frag { |
462 | 0 | PctEncodedFragments::Char(s, _) => { |
463 | 0 | max_len -= 1; |
464 | 0 | writer.write_str(s) |
465 | | } |
466 | 0 | PctEncodedFragments::NoPctStr(s) => { |
467 | 0 | let mut chars = s.char_indices(); |
468 | 0 | let count = |
469 | 0 | chars.by_ref().take(max_len).last().map(|(i, _)| i).expect( |
470 | 0 | "[consistency] decomposed string fragment must not be empty", |
471 | | ); |
472 | 0 | let sub_len = s.len() - chars.as_str().len(); |
473 | 0 | max_len -= count; |
474 | 0 | write!( |
475 | 0 | writer, |
476 | 0 | "{}", |
477 | 0 | PercentEncoded::<_, S>::characters(&s[..sub_len]) |
478 | | ) |
479 | | } |
480 | | PctEncodedFragments::StrayPercent => { |
481 | 0 | max_len -= 1; |
482 | 0 | writer.write_str("%25") |
483 | | } |
484 | 0 | PctEncodedFragments::InvalidUtf8PctTriplets(s) => { |
485 | 0 | let count = max_len.min(s.len() / 3); |
486 | 0 | let sub_len = count * 3; |
487 | 0 | max_len -= count; |
488 | 0 | writer.write_str(&s[..sub_len]) |
489 | | } |
490 | | }; |
491 | 0 | if result.is_err() { |
492 | 0 | return ControlFlow::Break(result); |
493 | 0 | } |
494 | 0 | ControlFlow::Continue(()) |
495 | 0 | }); |
496 | 0 | match result { |
497 | 0 | Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()), |
498 | 0 | Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e), |
499 | | } |
500 | | } else { |
501 | 0 | match max_len { |
502 | 0 | Some(max_len) => { |
503 | 0 | let mut writer = TruncatePercentEncodeWriter::<S, _> { |
504 | 0 | inner: writer, |
505 | 0 | rest_num_chars: usize::from(max_len), |
506 | 0 | _spec: PhantomData, |
507 | 0 | }; |
508 | 0 | write!(writer, "{v}") |
509 | | } |
510 | 0 | None => write!(writer, "{}", PercentEncoded::<_, S>::unreserve(v)), |
511 | | } |
512 | | } |
513 | 0 | } |
514 | | |
515 | | /// A writer that truncates the input to the given length and writes to the backend. |
516 | | struct 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 | | |
525 | | impl<S: Spec, W: fmt::Write> fmt::Write for TruncatePercentEncodeWriter<'_, S, W> { |
526 | 0 | fn write_str(&mut self, s: &str) -> fmt::Result { |
527 | 0 | if self.rest_num_chars == 0 { |
528 | 0 | return Ok(()); |
529 | 0 | } |
530 | 0 | let mut chars = s.char_indices(); |
531 | 0 | let skip_count = chars |
532 | 0 | .by_ref() |
533 | 0 | .take(self.rest_num_chars) |
534 | 0 | .last() |
535 | 0 | .map_or(0, |(i, _)| i + 1); |
536 | 0 | let len = s.len() - chars.as_str().len(); |
537 | 0 | let truncated = &s[..len]; |
538 | 0 | write!( |
539 | 0 | self.inner, |
540 | 0 | "{}", |
541 | 0 | PercentEncoded::<_, S>::unreserve(truncated) |
542 | 0 | )?; |
543 | 0 | self.rest_num_chars -= skip_count; |
544 | 0 | Ok(()) |
545 | 0 | } |
546 | | } |
547 | | |
548 | | /// A writer that writes a prefix only once if and only if some value is written. |
549 | | struct PrefixOnceWriter<'a, W> { |
550 | | /// Inner writer. |
551 | | inner: &'a mut W, |
552 | | /// Prefix to write. |
553 | | prefix: Option<&'a str>, |
554 | | } |
555 | | |
556 | | impl<'a, W: fmt::Write> PrefixOnceWriter<'a, W> { |
557 | | /// Creates a new writer with no prefix. |
558 | | #[inline] |
559 | | #[must_use] |
560 | 0 | fn new(inner: &'a mut W) -> Self { |
561 | 0 | Self { |
562 | 0 | inner, |
563 | 0 | prefix: None, |
564 | 0 | } |
565 | 0 | } |
566 | | |
567 | | /// Creates a new writer with a prefix. |
568 | | #[inline] |
569 | | #[must_use] |
570 | 0 | fn with_prefix(inner: &'a mut W, prefix: &'a str) -> Self { |
571 | 0 | Self { |
572 | 0 | inner, |
573 | 0 | prefix: Some(prefix), |
574 | 0 | } |
575 | 0 | } |
576 | | |
577 | | /// Returns true if the writer have not yet written the prefix. |
578 | | #[inline] |
579 | | #[must_use] |
580 | 0 | fn has_unwritten_prefix(&self) -> bool { |
581 | 0 | self.prefix.is_some() |
582 | 0 | } |
583 | | } |
584 | | |
585 | | impl<W: fmt::Write> fmt::Write for PrefixOnceWriter<'_, W> { |
586 | | #[inline] |
587 | 0 | fn write_str(&mut self, s: &str) -> fmt::Result { |
588 | 0 | if let Some(prefix) = self.prefix.take() { |
589 | 0 | self.inner.write_str(prefix)?; |
590 | 0 | } |
591 | 0 | self.inner.write_str(s) |
592 | 0 | } |
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. |
598 | | struct VisitDoneToken<'a, S, W>(ValueVisitor<'a, S, W>); |
599 | | |
600 | | impl<'a, S: Spec, W: fmt::Write> VisitDoneToken<'a, S, W> { |
601 | | /// Creates a new token. |
602 | | #[inline] |
603 | | #[must_use] |
604 | 0 | fn new(visitor: ValueVisitor<'a, S, W>) -> Self { |
605 | 0 | Self(visitor) |
606 | 0 | } |
607 | | |
608 | | /// Returns the raw pointer to the backend formatter. |
609 | | #[inline] |
610 | | #[must_use] |
611 | 0 | fn writer_ptr(&self) -> *const W { |
612 | 0 | self.0.writer_ptr() |
613 | 0 | } |
614 | | } |
615 | | |
616 | | impl<S: Spec, W: fmt::Write> fmt::Debug for VisitDoneToken<'_, S, W> { |
617 | | #[inline] |
618 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
619 | 0 | f.write_str("VisitDoneToken") |
620 | 0 | } |
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. |
626 | | struct 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 | | |
639 | | impl<'a, S: Spec, W: fmt::Write> ValueVisitor<'a, S, W> { |
640 | | /// Creates a visitor. |
641 | | #[inline] |
642 | | #[must_use] |
643 | 0 | fn new( |
644 | 0 | f: &'a mut W, |
645 | 0 | varspec: VarSpec<'a>, |
646 | 0 | op: Operator, |
647 | 0 | is_first_varspec: &'a mut bool, |
648 | 0 | ) -> Self { |
649 | 0 | Self { |
650 | 0 | writer: f, |
651 | 0 | varspec, |
652 | 0 | op, |
653 | 0 | is_first_varspec, |
654 | 0 | _spec: PhantomData, |
655 | 0 | } |
656 | 0 | } |
657 | | |
658 | | /// Returns the raw pointer to the backend formatter. |
659 | | #[inline] |
660 | | #[must_use] |
661 | 0 | fn writer_ptr(&self) -> *const W { |
662 | 0 | self.writer as &_ as *const _ |
663 | 0 | } |
664 | | } |
665 | | |
666 | | impl<S: Spec, W: fmt::Write> VisitorSealed for ValueVisitor<'_, S, W> {} |
667 | | |
668 | | impl<'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 | 0 | fn var_name(&self) -> VarName<'a> { |
676 | 0 | self.varspec.name() |
677 | 0 | } |
678 | | |
679 | | #[inline] |
680 | 0 | fn purpose(&self) -> VisitPurpose { |
681 | 0 | VisitPurpose::Expand |
682 | 0 | } |
683 | | |
684 | | /// Visits an undefined variable, i.e. indicates that the requested variable is unavailable. |
685 | | #[inline] |
686 | 0 | fn visit_undefined(self) -> Self::Result { |
687 | 0 | Ok(VisitDoneToken::new(self)) |
688 | 0 | } |
689 | | |
690 | | /// Visits a string variable. |
691 | | #[inline] |
692 | 0 | fn visit_string<T: fmt::Display>(self, v: T) -> Self::Result { |
693 | 0 | let oppr = OpProps::from_op(self.op); |
694 | | |
695 | 0 | if mem::replace(self.is_first_varspec, false) { |
696 | 0 | self.writer.write_str(oppr.first)?; |
697 | | } else { |
698 | 0 | self.writer.write_str(oppr.sep)?; |
699 | | } |
700 | 0 | let mut writer = if oppr.named { |
701 | 0 | self.writer.write_str(self.varspec.name().as_str())?; |
702 | 0 | PrefixOnceWriter::with_prefix(self.writer, "=") |
703 | | } else { |
704 | 0 | PrefixOnceWriter::new(self.writer) |
705 | | }; |
706 | | |
707 | 0 | let max_len = match self.varspec.modifier() { |
708 | 0 | Modifier::None | Modifier::Explode => None, |
709 | 0 | Modifier::MaxLen(max_len) => Some(max_len), |
710 | | }; |
711 | 0 | escape_write_with_maxlen::<S, T, W>(&mut writer, v, oppr.allow_reserved, max_len)?; |
712 | 0 | if writer.has_unwritten_prefix() { |
713 | 0 | self.writer.write_str(oppr.ifemp)?; |
714 | 0 | } |
715 | 0 | Ok(VisitDoneToken::new(self)) |
716 | 0 | } |
717 | | |
718 | | /// Visits a list variable. |
719 | | #[inline] |
720 | 0 | fn visit_list(self) -> Self::ListVisitor { |
721 | 0 | let oppr = OpProps::from_op(self.op); |
722 | 0 | ListValueVisitor { |
723 | 0 | visitor: self, |
724 | 0 | num_elems: 0, |
725 | 0 | oppr, |
726 | 0 | } |
727 | 0 | } |
728 | | |
729 | | /// Visits an associative array variable. |
730 | | #[inline] |
731 | 0 | fn visit_assoc(self) -> Self::AssocVisitor { |
732 | 0 | let oppr = OpProps::from_op(self.op); |
733 | 0 | AssocValueVisitor { |
734 | 0 | visitor: self, |
735 | 0 | num_elems: 0, |
736 | 0 | oppr, |
737 | 0 | } |
738 | 0 | } |
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. |
752 | | struct 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 | | |
761 | | impl<S: Spec, W: fmt::Write> ListValueVisitor<'_, S, W> { |
762 | | /// Visits an item. |
763 | 0 | fn visit_item_impl<T: fmt::Display>(&mut self, item: T) -> fmt::Result { |
764 | 0 | let modifier = self.visitor.varspec.modifier(); |
765 | 0 | let is_explode = match modifier { |
766 | 0 | Modifier::MaxLen(_) => panic!( |
767 | 0 | "value type changed since `UriTemplateStr::expand()`: \ |
768 | 0 | prefix modifier is not applicable to a list" |
769 | | ), |
770 | 0 | Modifier::None => false, |
771 | 0 | Modifier::Explode => true, |
772 | | }; |
773 | | |
774 | | // Write prefix for each variable. |
775 | 0 | if self.num_elems == 0 { |
776 | 0 | if mem::replace(self.visitor.is_first_varspec, false) { |
777 | 0 | self.visitor.writer.write_str(self.oppr.first)?; |
778 | | } else { |
779 | 0 | self.visitor.writer.write_str(self.oppr.sep)?; |
780 | | } |
781 | 0 | if self.oppr.named { |
782 | 0 | self.visitor |
783 | 0 | .writer |
784 | 0 | .write_str(self.visitor.varspec.name().as_str())?; |
785 | 0 | self.visitor.writer.write_char('=')?; |
786 | 0 | } |
787 | | } else { |
788 | | // Write prefix for the non-first item. |
789 | 0 | match (self.oppr.named, is_explode) { |
790 | 0 | (_, false) => self.visitor.writer.write_char(',')?, |
791 | 0 | (false, true) => self.visitor.writer.write_str(self.oppr.sep)?, |
792 | | (true, true) => { |
793 | 0 | self.visitor.writer.write_str(self.oppr.sep)?; |
794 | 0 | escape_write::<S, _, _>( |
795 | 0 | self.visitor.writer, |
796 | 0 | self.visitor.varspec.name().as_str(), |
797 | 0 | self.oppr.allow_reserved, |
798 | 0 | )?; |
799 | 0 | self.visitor.writer.write_char('=')?; |
800 | | } |
801 | | } |
802 | | } |
803 | | |
804 | 0 | escape_write::<S, _, _>(self.visitor.writer, item, self.oppr.allow_reserved)?; |
805 | | |
806 | 0 | self.num_elems += 1; |
807 | 0 | Ok(()) |
808 | 0 | } |
809 | | } |
810 | | |
811 | | impl<S: Spec, W: fmt::Write> VisitorSealed for ListValueVisitor<'_, S, W> {} |
812 | | |
813 | | impl<'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 | 0 | fn visit_item<T: fmt::Display>(&mut self, item: T) -> ControlFlow<Self::Result> { |
819 | 0 | match self.visit_item_impl(item) { |
820 | 0 | Ok(_) => ControlFlow::Continue(()), |
821 | 0 | Err(e) => ControlFlow::Break(Err(e)), |
822 | | } |
823 | 0 | } |
824 | | |
825 | | /// Finishes visiting the list. |
826 | | #[inline] |
827 | 0 | fn finish(self) -> Self::Result { |
828 | 0 | Ok(VisitDoneToken::new(self.visitor)) |
829 | 0 | } |
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. |
843 | | struct 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 | | |
852 | | impl<S: Spec, W: fmt::Write> AssocValueVisitor<'_, S, W> { |
853 | | /// Visits an entry. |
854 | 0 | fn visit_entry_impl<K: fmt::Display, V: fmt::Display>( |
855 | 0 | &mut self, |
856 | 0 | key: K, |
857 | 0 | value: V, |
858 | 0 | ) -> fmt::Result { |
859 | 0 | let modifier = self.visitor.varspec.modifier(); |
860 | 0 | let is_explode = match modifier { |
861 | 0 | Modifier::MaxLen(_) => panic!( |
862 | 0 | "value type changed since `UriTemplateStr::expand()`: \ |
863 | 0 | prefix modifier is not applicable to an associative array" |
864 | | ), |
865 | 0 | Modifier::None => false, |
866 | 0 | Modifier::Explode => true, |
867 | | }; |
868 | | |
869 | | // Write prefix for each variable. |
870 | 0 | if self.num_elems == 0 { |
871 | 0 | if mem::replace(self.visitor.is_first_varspec, false) { |
872 | 0 | self.visitor.writer.write_str(self.oppr.first)?; |
873 | | } else { |
874 | 0 | self.visitor.writer.write_str(self.oppr.sep)?; |
875 | | } |
876 | 0 | if is_explode { |
877 | 0 | escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?; |
878 | 0 | self.visitor.writer.write_char('=')?; |
879 | | } else { |
880 | 0 | if self.oppr.named { |
881 | 0 | escape_write::<S, _, _>( |
882 | 0 | self.visitor.writer, |
883 | 0 | self.visitor.varspec.name().as_str(), |
884 | 0 | self.oppr.allow_reserved, |
885 | 0 | )?; |
886 | 0 | self.visitor.writer.write_char('=')?; |
887 | 0 | } |
888 | 0 | escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?; |
889 | 0 | self.visitor.writer.write_char(',')?; |
890 | | } |
891 | | } else { |
892 | | // Write prefix for the non-first item. |
893 | 0 | match (self.oppr.named, is_explode) { |
894 | | (_, false) => { |
895 | 0 | self.visitor.writer.write_char(',')?; |
896 | 0 | escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?; |
897 | 0 | self.visitor.writer.write_char(',')?; |
898 | | } |
899 | | (false, true) => { |
900 | 0 | self.visitor.writer.write_str(self.oppr.sep)?; |
901 | 0 | escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?; |
902 | 0 | self.visitor.writer.write_char('=')?; |
903 | | } |
904 | | (true, true) => { |
905 | 0 | self.visitor.writer.write_str(self.oppr.sep)?; |
906 | 0 | escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?; |
907 | 0 | self.visitor.writer.write_char('=')?; |
908 | | } |
909 | | } |
910 | | } |
911 | | |
912 | 0 | escape_write::<S, _, _>(self.visitor.writer, value, self.oppr.allow_reserved)?; |
913 | | |
914 | 0 | self.num_elems += 1; |
915 | 0 | Ok(()) |
916 | 0 | } |
917 | | } |
918 | | |
919 | | impl<S: Spec, W: fmt::Write> VisitorSealed for AssocValueVisitor<'_, S, W> {} |
920 | | |
921 | | impl<'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 | 0 | fn visit_entry<K: fmt::Display, V: fmt::Display>( |
927 | 0 | &mut self, |
928 | 0 | key: K, |
929 | 0 | value: V, |
930 | 0 | ) -> ControlFlow<Self::Result> { |
931 | 0 | match self.visit_entry_impl(key, value) { |
932 | 0 | Ok(_) => ControlFlow::Continue(()), |
933 | 0 | Err(e) => ControlFlow::Break(Err(e)), |
934 | | } |
935 | 0 | } |
936 | | |
937 | | /// Finishes visiting the associative array. |
938 | | #[inline] |
939 | 0 | fn finish(self) -> Self::Result { |
940 | 0 | Ok(VisitDoneToken::new(self.visitor)) |
941 | 0 | } |
942 | | } |
943 | | |
944 | | /// Visitor to retrieve effective type of a variable. |
945 | | struct TypeVisitor<'a> { |
946 | | /// Variable name. |
947 | | var_name: VarName<'a>, |
948 | | } |
949 | | |
950 | | impl<'a> TypeVisitor<'a> { |
951 | | /// Creates a new type visitor. |
952 | | #[inline] |
953 | | #[must_use] |
954 | 0 | fn new(var_name: VarName<'a>) -> Self { |
955 | 0 | Self { var_name } |
956 | 0 | } |
957 | | } |
958 | | |
959 | | impl VisitorSealed for TypeVisitor<'_> {} |
960 | | |
961 | | impl<'a> Visitor for TypeVisitor<'a> { |
962 | | type Result = ValueType; |
963 | | type ListVisitor = ListTypeVisitor; |
964 | | type AssocVisitor = AssocTypeVisitor; |
965 | | |
966 | | #[inline] |
967 | 0 | fn var_name(&self) -> VarName<'a> { |
968 | 0 | self.var_name |
969 | 0 | } |
970 | | #[inline] |
971 | 0 | fn purpose(&self) -> VisitPurpose { |
972 | 0 | VisitPurpose::Typecheck |
973 | 0 | } |
974 | | #[inline] |
975 | 0 | fn visit_undefined(self) -> Self::Result { |
976 | 0 | ValueType::undefined() |
977 | 0 | } |
978 | | #[inline] |
979 | 0 | fn visit_string<T: fmt::Display>(self, _: T) -> Self::Result { |
980 | 0 | ValueType::string() |
981 | 0 | } |
982 | | #[inline] |
983 | 0 | fn visit_list(self) -> Self::ListVisitor { |
984 | 0 | ListTypeVisitor |
985 | 0 | } |
986 | | #[inline] |
987 | 0 | fn visit_assoc(self) -> Self::AssocVisitor { |
988 | 0 | AssocTypeVisitor |
989 | 0 | } |
990 | | } |
991 | | |
992 | | /// Visitor to retrieve effective type of a list variable. |
993 | | struct ListTypeVisitor; |
994 | | |
995 | | impl VisitorSealed for ListTypeVisitor {} |
996 | | |
997 | | impl ListVisitor for ListTypeVisitor { |
998 | | type Result = ValueType; |
999 | | |
1000 | | /// Visits an item. |
1001 | | #[inline] |
1002 | 0 | fn visit_item<T: fmt::Display>(&mut self, _item: T) -> ControlFlow<Self::Result> { |
1003 | 0 | ControlFlow::Break(ValueType::nonempty_list()) |
1004 | 0 | } |
1005 | | |
1006 | | /// Finishes visiting the list. |
1007 | | #[inline] |
1008 | 0 | fn finish(self) -> Self::Result { |
1009 | 0 | ValueType::empty_list() |
1010 | 0 | } |
1011 | | } |
1012 | | |
1013 | | /// Visitor to retrieve effective type of an associative array variable. |
1014 | | struct AssocTypeVisitor; |
1015 | | |
1016 | | impl VisitorSealed for AssocTypeVisitor {} |
1017 | | |
1018 | | impl AssocVisitor for AssocTypeVisitor { |
1019 | | type Result = ValueType; |
1020 | | |
1021 | | /// Visits an item. |
1022 | | #[inline] |
1023 | 0 | fn visit_entry<K: fmt::Display, V: fmt::Display>( |
1024 | 0 | &mut self, |
1025 | 0 | _key: K, |
1026 | 0 | _value: V, |
1027 | 0 | ) -> ControlFlow<Self::Result> { |
1028 | 0 | ControlFlow::Break(ValueType::nonempty_assoc()) |
1029 | 0 | } |
1030 | | |
1031 | | /// Finishes visiting the list. |
1032 | | #[inline] |
1033 | 0 | fn finish(self) -> Self::Result { |
1034 | 0 | ValueType::empty_assoc() |
1035 | 0 | } |
1036 | | } |