uri_template_system_core/template.rs
1pub mod component;
2
3use std::fmt::{
4 Error,
5 Write,
6};
7
8use thiserror::Error;
9
10use crate::{
11 template::component::Component,
12 value::Values,
13};
14
15// =============================================================================
16// Template
17// =============================================================================
18
19// Traits
20
21trait Expand {
22 fn expand(&self, values: &Values, write: &mut impl Write) -> Result<(), ExpandError>;
23}
24
25trait Parse<'t>
26where
27 Self: Sized,
28{
29 fn parse(raw: &'t str, global: usize) -> (usize, Self);
30}
31
32trait TryParse<'t>
33where
34 Self: Sized,
35{
36 fn try_parse(raw: &'t str, base: usize) -> Result<(usize, Self), ParseError>;
37}
38
39// -----------------------------------------------------------------------------
40
41// Errors
42
43/// An [`Error`](std::error::Error) compatible type which may be the result of a
44/// failure of [`Template::expand`] (given a valid [`Template`] and provided
45/// [`Values`]).
46#[derive(Debug, Error)]
47pub enum ExpandError {
48 /// Formatting for this expansion failed due to an internal error in
49 /// [`std::fmt::Write`], which is not recoverable.
50 #[error("formatting failed")]
51 Format(#[from] Error),
52}
53
54/// An [`Error`](std::error::Error) compatible type which may be the result of a
55/// failure of [`Template::parse`], likely due to an invalid URI Template format
56/// (as defined by the grammar given in [RFC6570](https://datatracker.ietf.org/doc/html/rfc6570)).
57#[derive(Debug, Error)]
58pub enum ParseError {
59 /// The input given contained an unexpected value according to the URI
60 /// Template value grammar, causing parsing to fail. See the grammar at
61 /// [RFC6570](https://datatracker.ietf.org/doc/html/rfc6570) for the definition of a
62 /// valid URI Template.
63 #[error("{message} at position: {position}. expected: {expected}.")]
64 UnexpectedInput {
65 /// The position (in bytes) of the input at which the unexpected input
66 /// occurs.
67 position: usize,
68 /// A message giving more detail about which grammatical element failed
69 /// to parse the given input.
70 message: String,
71 /// An indication of what (valid) input was expected by the parser.
72 expected: String,
73 },
74}
75
76// -----------------------------------------------------------------------------
77
78// Types
79
80/// The [`Template`] type is the basis for most simple tasks. Parsing and
81/// expansion are both template functions.
82#[derive(Debug, Eq, PartialEq)]
83pub struct Template<'t> {
84 components: Vec<Component<'t>>,
85}
86
87impl<'t> Template<'t> {
88 /// Expands the template using the given [`Values`], returning a [`String`]
89 /// if expansion was successful.
90 ///
91 /// # Errors
92 ///
93 /// This function may fail due to internal formatting errors
94 /// ([`std::fmt::Write`] is an abstraction which allows for underlying
95 /// failures) though this is very unlikely given [`String`] output.
96 ///
97 /// ```
98 /// # use uri_template_system_core::{ Template, Values, Value };
99 /// #
100 /// let template = Template::parse("hello/{name}!").unwrap();
101 /// let values = Values::default().add("name", Value::item("world"));
102 ///
103 /// assert_eq!("hello/world!", template.expand(&values).unwrap());
104 pub fn expand(&self, values: &Values) -> Result<String, ExpandError> {
105 let mut expanded = String::default();
106
107 Expand::expand(self, values, &mut expanded)?;
108
109 Ok(expanded)
110 }
111
112 /// Parses a [`&str`] representing a potential template, and returns a new
113 /// [`Template`] instance if valid. See [RFC6570](https://datatracker.ietf.org/doc/html/rfc6570)
114 /// for the grammar of a valid URI Template. `uri-template-system` supports
115 /// all operators and modifiers up-to and including Level 4.
116 ///
117 /// # Errors
118 ///
119 /// This function may fail when the given input is not a valid URI Template
120 /// according the RFC-defined grammar. The resultant [`ParseError`]
121 /// should give useful information about where the parser failed.
122 ///
123 /// ```
124 /// # use uri_template_system_core::Template;
125 /// #
126 /// let template = Template::parse("my/valid/{template}");
127 ///
128 /// assert!(template.is_ok());
129 /// ```
130 pub fn parse(raw: &'t str) -> Result<Self, ParseError> {
131 Self::try_parse(raw, 0).map(|(_, template)| template)
132 }
133
134 const fn new(components: Vec<Component<'t>>) -> Self {
135 Self { components }
136 }
137}
138
139// -----------------------------------------------------------------------------
140
141// Parse
142
143impl<'t> TryParse<'t> for Template<'t> {
144 fn try_parse(raw: &'t str, global: usize) -> Result<(usize, Self), ParseError> {
145 Vec::<Component<'t>>::try_parse(raw, global)
146 .map(|(position, components)| (position, Self::new(components)))
147 }
148}
149
150// -----------------------------------------------------------------------------
151
152// Expand
153
154impl<'t> Expand for Template<'t> {
155 fn expand(&self, values: &Values, write: &mut impl Write) -> Result<(), ExpandError> {
156 self.components
157 .iter()
158 .try_for_each(|component| component.expand(values, write))
159 }
160}