rocket_http_community/uri/fmt/formatter.rs
1use std::borrow::Cow;
2use std::fmt::{self, Write};
3use std::marker::PhantomData;
4
5use crate::uri::fmt::{Kind, Part, Path, Query, UriDisplay};
6use crate::uri::{Absolute, Origin, Reference};
7
8/// A struct used to format strings for [`UriDisplay`].
9///
10/// # Marker Generic: `Formatter<Path>` vs. `Formatter<Query>`
11///
12/// Like [`UriDisplay`], the [`Part`] parameter `P` in `Formatter<P>` must be
13/// either [`Path`] or [`Query`] resulting in either `Formatter<Path>` or
14/// `Formatter<Query>`. The `Path` version is used when formatting parameters
15/// in the path part of the URI while the `Query` version is used when
16/// formatting parameters in the query part of the URI. The
17/// [`write_named_value()`] method is only available to `UriDisplay<Query>`.
18///
19/// # Overview
20///
21/// A mutable version of this struct is passed to [`UriDisplay::fmt()`]. This
22/// struct properly formats series of values for use in URIs. In particular,
23/// this struct applies the following transformations:
24///
25/// * When **multiple values** are written, they are separated by `/` for
26/// `Path` types and `&` for `Query` types.
27///
28/// Additionally, for `Formatter<Query>`:
29///
30/// * When a **named value** is written with [`write_named_value()`], the name
31/// is written out, followed by a `=`, followed by the value.
32///
33/// * When **nested named values** are written, typically by passing a value
34/// to [`write_named_value()`] whose implementation of `UriDisplay` also
35/// calls `write_named_value()`, the nested names are joined by a `.`,
36/// written out followed by a `=`, followed by the value.
37///
38/// # Usage
39///
40/// Usage is fairly straightforward:
41///
42/// * For every _named value_ you wish to emit, call [`write_named_value()`].
43/// * For every _unnamed value_ you wish to emit, call [`write_value()`].
44/// * To write a string directly, call [`write_raw()`].
45///
46/// The `write_named_value` method automatically prefixes the `name` to the
47/// written value and, along with `write_value` and `write_raw`, handles nested
48/// calls to `write_named_value` automatically, prefixing names when necessary.
49/// Unlike the other methods, `write_raw` does _not_ prefix any nested names
50/// every time it is called. Instead, it only prefixes the _first_ time it is
51/// called, after a call to `write_named_value` or `write_value`, or after a
52/// call to [`refresh()`].
53///
54/// # Example
55///
56/// The following example uses all of the `write` methods in a varied order to
57/// display the semantics of `Formatter<Query>`. Note that `UriDisplay` should
58/// rarely be implemented manually, preferring to use the derive, and that this
59/// implementation is purely demonstrative.
60///
61/// ```rust
62/// # extern crate rocket;
63/// use std::fmt;
64///
65/// use rocket::http::uri::fmt::{Formatter, UriDisplay, Query};
66///
67/// struct Outer {
68/// value: Inner,
69/// another: usize,
70/// extra: usize
71/// }
72///
73/// struct Inner {
74/// value: usize,
75/// extra: usize
76/// }
77///
78/// impl UriDisplay<Query> for Outer {
79/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
80/// f.write_named_value("outer_field", &self.value)?;
81/// f.write_named_value("another", &self.another)?;
82/// f.write_raw("out")?;
83/// f.write_raw("side")?;
84/// f.write_value(&self.extra)
85/// }
86/// }
87///
88/// impl UriDisplay<Query> for Inner {
89/// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
90/// f.write_named_value("inner_field", &self.value)?;
91/// f.write_value(&self.extra)?;
92/// f.write_raw("inside")
93/// }
94/// }
95///
96/// let inner = Inner { value: 0, extra: 1 };
97/// let outer = Outer { value: inner, another: 2, extra: 3 };
98/// let uri_string = format!("{}", &outer as &dyn UriDisplay<Query>);
99/// assert_eq!(uri_string, "outer_field.inner_field=0&\
100/// outer_field=1&\
101/// outer_field=inside&\
102/// another=2&\
103/// outside&\
104/// 3");
105/// ```
106///
107/// Note that you can also use the `write!` macro to write directly to the
108/// formatter as long as the [`std::fmt::Write`] trait is in scope. Internally,
109/// the `write!` macro calls [`write_raw()`], so care must be taken to ensure
110/// that the written string is URI-safe.
111///
112/// ```rust
113/// # #[macro_use] extern crate rocket;
114/// use std::fmt::{self, Write};
115///
116/// use rocket::http::uri::fmt::{UriDisplay, Formatter, Part, Path, Query};
117///
118/// pub struct Complex(u8, u8);
119///
120/// impl<P: Part> UriDisplay<P> for Complex {
121/// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
122/// write!(f, "{}+{}", self.0, self.1)
123/// }
124/// }
125///
126/// let uri_string = format!("{}", &Complex(42, 231) as &dyn UriDisplay<Path>);
127/// assert_eq!(uri_string, "42+231");
128///
129/// #[derive(UriDisplayQuery)]
130/// struct Message {
131/// number: Complex,
132/// }
133///
134/// let message = Message { number: Complex(42, 47) };
135/// let uri_string = format!("{}", &message as &dyn UriDisplay<Query>);
136/// assert_eq!(uri_string, "number=42+47");
137/// ```
138///
139/// [`write_named_value()`]: Formatter::write_value()
140/// [`write_value()`]: Formatter::write_value()
141/// [`write_raw()`]: Formatter::write_raw()
142/// [`refresh()`]: Formatter::refresh()
143pub struct Formatter<'i, P: Part> {
144 prefixes: tinyvec::TinyVec<[&'static str; 3]>,
145 inner: &'i mut (dyn Write + 'i),
146 previous: bool,
147 fresh: bool,
148 _marker: PhantomData<P>,
149}
150
151impl<'i, P: Part> Formatter<'i, P> {
152 #[inline(always)]
153 pub(crate) fn new(inner: &'i mut (dyn Write + 'i)) -> Self {
154 Formatter {
155 inner,
156 prefixes: Default::default(),
157 previous: false,
158 fresh: true,
159 _marker: PhantomData,
160 }
161 }
162
163 #[inline(always)]
164 fn refreshed<F: FnOnce(&mut Self) -> fmt::Result>(&mut self, f: F) -> fmt::Result {
165 self.refresh();
166 let result = f(self);
167 self.refresh();
168 result
169 }
170
171 /// Writes `string` to `self`.
172 ///
173 /// If `self` is _fresh_ (after a call to other `write_` methods or
174 /// [`refresh()`]), prefixes any names and adds separators as necessary.
175 ///
176 /// This method is called by the `write!` macro.
177 ///
178 /// [`refresh()`]: Formatter::refresh()
179 ///
180 /// # Example
181 ///
182 /// ```rust
183 /// # extern crate rocket;
184 /// use std::fmt;
185 ///
186 /// use rocket::http::uri::fmt::{Formatter, UriDisplay, Part, Path};
187 ///
188 /// struct Foo;
189 ///
190 /// impl<P: Part> UriDisplay<P> for Foo {
191 /// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
192 /// f.write_raw("f")?;
193 /// f.write_raw("o")?;
194 /// f.write_raw("o")
195 /// }
196 /// }
197 ///
198 /// let foo = Foo;
199 /// let uri_string = format!("{}", &foo as &dyn UriDisplay<Path>);
200 /// assert_eq!(uri_string, "foo");
201 /// ```
202 pub fn write_raw<S: AsRef<str>>(&mut self, string: S) -> fmt::Result {
203 // This implementation is a bit of a lie to the type system. Instead of
204 // implementing this twice, one for <Path> and again for <Query>, we do
205 // this once here. This is okay since we know that this handles the
206 // cases for both Path and Query, and doing it this way allows us to
207 // keep the uri part generic _generic_ in other implementations that use
208 // `write_raw`.
209 if self.fresh {
210 if self.previous {
211 self.inner.write_char(P::DELIMITER)?;
212 }
213
214 if P::KIND == Kind::Query && !self.prefixes.is_empty() {
215 for (i, prefix) in self.prefixes.iter().enumerate() {
216 if i != 0 {
217 self.inner.write_char('.')?
218 }
219 self.inner.write_str(prefix)?;
220 }
221
222 self.inner.write_str("=")?;
223 }
224 }
225
226 self.fresh = false;
227 self.previous = true;
228 self.inner.write_str(string.as_ref())
229 }
230
231 /// Writes the unnamed value `value`. Any nested names are prefixed as
232 /// necessary.
233 ///
234 /// Refreshes `self` before and after the value is written.
235 ///
236 /// # Example
237 ///
238 /// ```rust
239 /// # extern crate rocket;
240 /// use std::fmt;
241 ///
242 /// use rocket::http::uri::fmt::{Formatter, UriDisplay, Part, Path, Query};
243 ///
244 /// struct Foo(usize);
245 ///
246 /// impl<P: Part> UriDisplay<P> for Foo {
247 /// fn fmt(&self, f: &mut Formatter<P>) -> fmt::Result {
248 /// f.write_value(&self.0)
249 /// }
250 /// }
251 ///
252 /// let foo = Foo(123);
253 ///
254 /// let uri_string = format!("{}", &foo as &dyn UriDisplay<Path>);
255 /// assert_eq!(uri_string, "123");
256 ///
257 /// let uri_string = format!("{}", &foo as &dyn UriDisplay<Query>);
258 /// assert_eq!(uri_string, "123");
259 /// ```
260 #[inline]
261 pub fn write_value<T: UriDisplay<P>>(&mut self, value: T) -> fmt::Result {
262 self.refreshed(|f| UriDisplay::fmt(&value, f))
263 }
264
265 /// Refreshes the formatter.
266 ///
267 /// After refreshing, [`write_raw()`] will prefix any nested names as well
268 /// as insert a separator.
269 ///
270 /// [`write_raw()`]: Formatter::write_raw()
271 ///
272 /// # Example
273 ///
274 /// ```rust
275 /// # #[macro_use] extern crate rocket;
276 /// use std::fmt;
277 ///
278 /// use rocket::http::uri::fmt::{Formatter, UriDisplay, Query, Path};
279 ///
280 /// struct Foo;
281 ///
282 /// impl UriDisplay<Query> for Foo {
283 /// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
284 /// f.write_raw("a")?;
285 /// f.write_raw("raw")?;
286 /// f.refresh();
287 /// f.write_raw("format")
288 /// }
289 /// }
290 ///
291 /// let uri_string = format!("{}", &Foo as &dyn UriDisplay<Query>);
292 /// assert_eq!(uri_string, "araw&format");
293 ///
294 /// impl UriDisplay<Path> for Foo {
295 /// fn fmt(&self, f: &mut Formatter<Path>) -> fmt::Result {
296 /// f.write_raw("a")?;
297 /// f.write_raw("raw")?;
298 /// f.refresh();
299 /// f.write_raw("format")
300 /// }
301 /// }
302 ///
303 /// let uri_string = format!("{}", &Foo as &dyn UriDisplay<Path>);
304 /// assert_eq!(uri_string, "araw/format");
305 ///
306 /// #[derive(UriDisplayQuery)]
307 /// struct Message {
308 /// inner: Foo,
309 /// }
310 ///
311 /// let msg = Message { inner: Foo };
312 /// let uri_string = format!("{}", &msg as &dyn UriDisplay<Query>);
313 /// assert_eq!(uri_string, "inner=araw&inner=format");
314 /// ```
315 #[inline(always)]
316 pub fn refresh(&mut self) {
317 self.fresh = true;
318 }
319}
320
321impl Formatter<'_, Query> {
322 fn with_prefix<F>(&mut self, prefix: &str, f: F) -> fmt::Result
323 where
324 F: FnOnce(&mut Self) -> fmt::Result,
325 {
326 struct PrefixGuard<'f, 'i>(&'f mut Formatter<'i, Query>);
327
328 impl<'f, 'i> PrefixGuard<'f, 'i> {
329 fn new(prefix: &str, f: &'f mut Formatter<'i, Query>) -> Self {
330 // SAFETY: The `prefix` string is pushed in a `StackVec` for use
331 // by recursive (nested) calls to `write_raw`. The string is
332 // pushed in `PrefixGuard` here and then popped in `Drop`.
333 // `prefixes` is modified nowhere else, and no concrete-lifetime
334 // strings leak from the the vector. As a result, it is
335 // impossible for a `prefix` to be accessed incorrectly as:
336 //
337 // * Rust _guarantees_ `prefix` is valid for this method
338 // * `prefix` is only reachable while this method's stack is
339 // active because it is unconditionally popped before this
340 // method returns via `PrefixGuard::drop()`.
341 // * should a panic occur in `f()`, `PrefixGuard::drop()` is
342 // still called (or the program aborts), ensuring `prefix`
343 // is no longer in `prefixes` and thus inaccessible.
344 // * thus, at any point `prefix` is reachable, it is valid
345 //
346 // Said succinctly: `prefixes` shadows a subset of the
347 // `with_prefix` stack, making it reachable to other code.
348 let prefix = unsafe { std::mem::transmute::<&str, &str>(prefix) };
349 f.prefixes.push(prefix);
350 PrefixGuard(f)
351 }
352 }
353
354 impl Drop for PrefixGuard<'_, '_> {
355 fn drop(&mut self) {
356 self.0.prefixes.pop();
357 }
358 }
359
360 f(PrefixGuard::new(prefix, self).0)
361 }
362
363 /// Writes the named value `value` by prefixing `name` followed by `=` to
364 /// the value. Any nested names are also prefixed as necessary.
365 ///
366 /// Refreshes `self` before the name is written and after the value is
367 /// written.
368 ///
369 /// # Example
370 ///
371 /// ```rust
372 /// # extern crate rocket;
373 /// use std::fmt;
374 ///
375 /// use rocket::http::uri::fmt::{Formatter, UriDisplay, Query};
376 ///
377 /// struct Foo {
378 /// name: usize
379 /// }
380 ///
381 /// // Note: This is identical to what #[derive(UriDisplayQuery)] would
382 /// // generate! In practice, _always_ use the derive.
383 /// impl UriDisplay<Query> for Foo {
384 /// fn fmt(&self, f: &mut Formatter<Query>) -> fmt::Result {
385 /// f.write_named_value("name", &self.name)
386 /// }
387 /// }
388 ///
389 /// let foo = Foo { name: 123 };
390 /// let uri_string = format!("{}", &foo as &dyn UriDisplay<Query>);
391 /// assert_eq!(uri_string, "name=123");
392 /// ```
393 #[inline]
394 pub fn write_named_value<T: UriDisplay<Query>>(&mut self, name: &str, value: T) -> fmt::Result {
395 self.refreshed(|f| f.with_prefix(name, |f| f.write_value(value)))
396 }
397}
398
399impl<P: Part> fmt::Write for Formatter<'_, P> {
400 fn write_str(&mut self, s: &str) -> fmt::Result {
401 self.write_raw(s)
402 }
403}
404
405// Used by code generation.
406#[doc(hidden)]
407pub enum UriArgumentsKind<A> {
408 Static(&'static str),
409 Dynamic(A),
410}
411
412// Used by code generation.
413#[doc(hidden)]
414pub enum UriQueryArgument<'a> {
415 Raw(&'a str),
416 NameValue(&'a str, &'a dyn UriDisplay<Query>),
417 Value(&'a dyn UriDisplay<Query>),
418}
419
420/// No prefix at all.
421#[doc(hidden)]
422pub struct Void;
423
424// Used by code generation.
425#[doc(hidden)]
426pub trait ValidRoutePrefix {
427 type Output;
428
429 fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output;
430}
431
432impl<'a> ValidRoutePrefix for Origin<'a> {
433 type Output = Self;
434
435 fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output {
436 // No-op if `self` is already normalized.
437 let mut prefix = self.into_normalized();
438 prefix.clear_query();
439
440 // Avoid a double `//` to start.
441 if prefix.path() == "/" {
442 return Origin::new(path, query);
443 }
444
445 // Avoid allocating if the `path` would result in just the prefix.
446 if path == "/" {
447 prefix.set_query(query);
448 return prefix;
449 }
450
451 // Avoid a `//` resulting from joining.
452 if prefix.has_trailing_slash() && path.starts_with('/') {
453 return Origin::new(format!("{}{}", prefix.path(), &path[1..]), query);
454 }
455
456 // Join normally.
457 Origin::new(format!("{}{}", prefix.path(), path), query)
458 }
459}
460
461impl<'a> ValidRoutePrefix for Absolute<'a> {
462 type Output = Self;
463
464 fn append(self, path: Cow<'static, str>, query: Option<Cow<'static, str>>) -> Self::Output {
465 // No-op if `self` is already normalized.
466 let mut prefix = self.into_normalized();
467 prefix.clear_query();
468
469 // Distinguish for routes `/` with bases of `/foo/` and `/foo`. The
470 // latter base, without a trailing slash, should combine as `/foo`.
471 if path == "/" {
472 prefix.set_query(query);
473 return prefix;
474 }
475
476 // In these cases, appending `path` would be a no-op or worse.
477 if prefix.path().is_empty() || prefix.path() == "/" {
478 prefix.set_path(path);
479 prefix.set_query(query);
480 return prefix;
481 }
482
483 // Create the combined URI.
484 prefix.set_path(format!("{}{}", prefix.path(), path));
485 prefix.set_query(query);
486 prefix
487 }
488}
489
490// `Self` is a valid suffix for `T`.
491#[doc(hidden)]
492pub trait ValidRouteSuffix<T> {
493 type Output;
494
495 fn prepend(self, prefix: T) -> Self::Output;
496}
497
498impl<'a> ValidRouteSuffix<Origin<'a>> for Reference<'a> {
499 type Output = Self;
500
501 fn prepend(self, prefix: Origin<'a>) -> Self::Output {
502 Reference::from(prefix).with_query_fragment_of(self)
503 }
504}
505
506impl<'a> ValidRouteSuffix<Absolute<'a>> for Reference<'a> {
507 type Output = Self;
508
509 fn prepend(self, prefix: Absolute<'a>) -> Self::Output {
510 Reference::from(prefix).with_query_fragment_of(self)
511 }
512}
513
514impl<'a> ValidRouteSuffix<Origin<'a>> for Absolute<'a> {
515 type Output = Origin<'a>;
516
517 fn prepend(self, mut prefix: Origin<'a>) -> Self::Output {
518 if let Some(query) = self.query {
519 if prefix.query().is_none() {
520 prefix.set_query(query.value.into_concrete(&self.source));
521 }
522 }
523
524 prefix
525 }
526}
527
528impl<'a> ValidRouteSuffix<Absolute<'a>> for Absolute<'a> {
529 type Output = Self;
530
531 fn prepend(self, mut prefix: Absolute<'a>) -> Self::Output {
532 if let Some(query) = self.query {
533 if prefix.query().is_none() {
534 prefix.set_query(query.value.into_concrete(&self.source));
535 }
536 }
537
538 prefix
539 }
540}
541
542// Used by code generation.
543#[doc(hidden)]
544pub struct RouteUriBuilder {
545 pub path: Cow<'static, str>,
546 pub query: Option<Cow<'static, str>>,
547}
548
549// Used by code generation.
550#[doc(hidden)]
551pub struct PrefixedRouteUri<T>(T);
552
553// Used by code generation.
554#[doc(hidden)]
555pub struct SuffixedRouteUri<T>(T);
556
557// Used by code generation.
558#[doc(hidden)]
559impl RouteUriBuilder {
560 /// Create a new `RouteUriBuilder` with the given path/query args.
561 pub fn new(
562 path_args: UriArgumentsKind<&[&dyn UriDisplay<Path>]>,
563 query_args: Option<UriArgumentsKind<&[UriQueryArgument<'_>]>>,
564 ) -> Self {
565 use self::{UriArgumentsKind::*, UriQueryArgument::*};
566
567 let path: Cow<'static, str> = match path_args {
568 Static(path) => path.into(),
569 Dynamic(args) => {
570 let mut string = String::from("/");
571 let mut formatter = Formatter::<Path>::new(&mut string);
572 for value in args {
573 let _ = formatter.write_value(value);
574 }
575
576 string.into()
577 }
578 };
579
580 let query: Option<Cow<'_, str>> = match query_args {
581 None => None,
582 Some(Static(query)) => Some(query.into()),
583 Some(Dynamic(args)) => {
584 let mut string = String::new();
585 let mut f = Formatter::<Query>::new(&mut string);
586 for arg in args {
587 let _ = match arg {
588 Raw(v) => f.write_raw(v),
589 NameValue(n, v) => f.write_named_value(n, v),
590 Value(v) => f.write_value(v),
591 };
592 }
593
594 (!string.is_empty()).then(|| string.into())
595 }
596 };
597
598 RouteUriBuilder { path, query }
599 }
600
601 pub fn with_prefix<P: ValidRoutePrefix>(self, p: P) -> PrefixedRouteUri<P::Output> {
602 PrefixedRouteUri(p.append(self.path, self.query))
603 }
604
605 pub fn with_suffix<S>(self, suffix: S) -> SuffixedRouteUri<S::Output>
606 where
607 S: ValidRouteSuffix<Origin<'static>>,
608 {
609 SuffixedRouteUri(suffix.prepend(self.render()))
610 }
611
612 pub fn render(self) -> Origin<'static> {
613 Origin::new(self.path, self.query)
614 }
615}
616
617#[doc(hidden)]
618impl<T> PrefixedRouteUri<T> {
619 pub fn with_suffix<S: ValidRouteSuffix<T>>(self, suffix: S) -> SuffixedRouteUri<S::Output> {
620 SuffixedRouteUri(suffix.prepend(self.0))
621 }
622
623 pub fn render(self) -> T {
624 self.0
625 }
626}
627
628#[doc(hidden)]
629impl<T> SuffixedRouteUri<T> {
630 pub fn render(self) -> T {
631 self.0
632 }
633}
634
635// See https://github.com/rwf2/Rocket/issues/1534.
636#[cfg(test)]
637mod prefix_soundness_test {
638 use crate::uri::fmt::{Formatter, Query, UriDisplay};
639
640 struct MyValue;
641
642 impl UriDisplay<Query> for MyValue {
643 fn fmt(&self, _f: &mut Formatter<'_, Query>) -> std::fmt::Result {
644 panic!()
645 }
646 }
647
648 struct MyDisplay;
649
650 impl UriDisplay<Query> for MyDisplay {
651 fn fmt(&self, formatter: &mut Formatter<'_, Query>) -> std::fmt::Result {
652 struct Wrapper<'a, 'b>(&'a mut Formatter<'b, Query>);
653
654 impl<'a, 'b> Drop for Wrapper<'a, 'b> {
655 fn drop(&mut self) {
656 let _overlap = String::from("12345");
657 self.0.write_raw("world").ok();
658 assert!(self.0.prefixes.is_empty());
659 }
660 }
661
662 let wrapper = Wrapper(formatter);
663 let temporary_string = String::from("hello");
664
665 // `write_named_value` will push `temp_string` into a buffer and
666 // call the formatter for `MyValue`, which panics. At the panic
667 // point, `formatter` contains an (illegal) static reference to
668 // `temp_string` in its `prefixes` stack. When unwinding occurs,
669 // `Wrapper` will be dropped. `Wrapper` holds a reference to
670 // `Formatter`, thus `Formatter` must be consistent at this point.
671 let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
672 wrapper.0.write_named_value(&temporary_string, MyValue)
673 }));
674
675 Ok(())
676 }
677 }
678
679 #[test]
680 fn check_consistency() {
681 let string = format!("{}", &MyDisplay as &dyn UriDisplay<Query>);
682 assert_eq!(string, "world");
683 }
684}