summaryrefslogtreecommitdiff
path: root/vendor/iri-string/src/template
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/iri-string/src/template')
-rw-r--r--vendor/iri-string/src/template/components.rs332
-rw-r--r--vendor/iri-string/src/template/context.rs339
-rw-r--r--vendor/iri-string/src/template/error.rs154
-rw-r--r--vendor/iri-string/src/template/expand.rs1039
-rw-r--r--vendor/iri-string/src/template/parser.rs6
-rw-r--r--vendor/iri-string/src/template/parser/char.rs190
-rw-r--r--vendor/iri-string/src/template/parser/validate.rs161
-rw-r--r--vendor/iri-string/src/template/simple_context.rs218
-rw-r--r--vendor/iri-string/src/template/string.rs647
-rw-r--r--vendor/iri-string/src/template/string/owned.rs296
10 files changed, 3382 insertions, 0 deletions
diff --git a/vendor/iri-string/src/template/components.rs b/vendor/iri-string/src/template/components.rs
new file mode 100644
index 00000000..7eb83a58
--- /dev/null
+++ b/vendor/iri-string/src/template/components.rs
@@ -0,0 +1,332 @@
+//! Syntax components of URI templates.
+
+use core::mem;
+
+use crate::parser::str::find_split_hole;
+use crate::template::error::Error;
+use crate::template::parser::validate as validate_parser;
+
+/// Expression body.
+///
+/// This does not contain the wrapping braces (`{` and `}`).
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) struct ExprBody<'a>(&'a str);
+
+impl<'a> ExprBody<'a> {
+ /// Creates a new expression body.
+ ///
+ /// # Precondition
+ ///
+ /// The given string should be a valid expression body.
+ #[inline]
+ #[must_use]
+ pub(super) fn new(s: &'a str) -> Self {
+ debug_assert!(
+ !s.is_empty(),
+ "[precondition] valid expression body is not empty"
+ );
+
+ Self(s)
+ }
+
+ /// Decomposes the expression into an `operator` and `variable-list`.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the input is invalid.
+ #[must_use]
+ pub(super) fn decompose(&self) -> (Operator, VarListStr<'a>) {
+ debug_assert!(
+ !self.0.is_empty(),
+ "[precondition] valid expression body is not empty"
+ );
+ let first = self.0.as_bytes()[0];
+ if first.is_ascii_alphanumeric() || (first == b'_') || (first == b'%') {
+ // The first byte is a part of the variable list.
+ (Operator::String, VarListStr::new(self.0))
+ } else {
+ let op = Operator::from_byte(first).unwrap_or_else(|| {
+ unreachable!(
+ "[precondition] valid expression has (optional) \
+ valid operator, but got a byte {first:#02x?}"
+ )
+ });
+ (op, VarListStr::new(&self.0[1..]))
+ }
+ }
+
+ /// Returns the raw expression in a string slice.
+ #[inline]
+ #[must_use]
+ pub(super) fn as_str(&self) -> &'a str {
+ self.0
+ }
+}
+
+/// Variable name.
+// QUESTION: Should hexdigits in percent-encoded triplets be compared case sensitively?
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct VarName<'a>(&'a str);
+
+impl<'a> VarName<'a> {
+ /// Creates a `VarName` from the trusted string.
+ ///
+ /// # Precondition
+ ///
+ /// The given string should be a valid variable name.
+ #[inline]
+ #[must_use]
+ pub(super) fn from_trusted(s: &'a str) -> Self {
+ Self(s)
+ }
+
+ /// Creates a `VarName` from the string.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::context::VarName;
+ ///
+ /// let name = VarName::new("hello")?;
+ /// assert_eq!(name.as_str(), "hello");
+ ///
+ /// assert!(VarName::new("0+non-variable-name").is_err());
+ ///
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn new(s: &'a str) -> Result<Self, Error> {
+ match validate_parser::validate_varname(s, 0) {
+ Ok(_) => Ok(Self::from_trusted(s)),
+ Err(e) => Err(e),
+ }
+ }
+
+ /// Returns the varibale name.
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &'a str {
+ self.0
+ }
+}
+
+/// Variable specifier.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct VarSpec<'a> {
+ /// Variable name.
+ name: VarName<'a>,
+ /// Variable modifier.
+ modifier: Modifier,
+}
+
+impl<'a> VarSpec<'a> {
+ /// Returns the varibale name.
+ #[inline]
+ #[must_use]
+ pub(super) fn name(&self) -> VarName<'a> {
+ self.name
+ }
+
+ /// Returns the modifier.
+ #[inline]
+ #[must_use]
+ pub(super) fn modifier(&self) -> Modifier {
+ self.modifier
+ }
+
+ /// Parses the trusted varspec string.
+ ///
+ /// # Panics
+ ///
+ /// May panic if the input is invalid.
+ #[must_use]
+ pub(super) fn parse_trusted(s: &'a str) -> Self {
+ if let Some(varname) = s.strip_suffix('*') {
+ // `varname "*"`.
+ return Self {
+ name: VarName::from_trusted(varname),
+ modifier: Modifier::Explode,
+ };
+ }
+ // `varname ":" max-length` or `varname`.
+ match find_split_hole(s, b':') {
+ Some((varname, max_len)) => {
+ let max_len: u16 = max_len
+ .parse()
+ .expect("[precondition] the input should be valid `varspec`");
+ Self {
+ name: VarName::from_trusted(varname),
+ modifier: Modifier::MaxLen(max_len),
+ }
+ }
+ None => Self {
+ name: VarName(s),
+ modifier: Modifier::None,
+ },
+ }
+ }
+}
+
+/// Variable list.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) struct VarListStr<'a>(&'a str);
+
+impl<'a> VarListStr<'a> {
+ /// Creates a new variable list.
+ ///
+ /// # Precondition
+ ///
+ /// The given string should be a valid variable list.
+ #[inline]
+ #[must_use]
+ pub(super) fn new(s: &'a str) -> Self {
+ Self(s)
+ }
+}
+
+impl<'a> IntoIterator for VarListStr<'a> {
+ type IntoIter = VarListIter<'a>;
+ type Item = (usize, VarSpec<'a>);
+
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ VarListIter { rest: self.0 }
+ }
+}
+
+/// Iterator of variable specs.
+#[derive(Debug, Clone)]
+pub(super) struct VarListIter<'a> {
+ /// Remaining input.
+ rest: &'a str,
+}
+
+impl<'a> Iterator for VarListIter<'a> {
+ /// A pair of the length of the varspec and the varspec itself.
+ type Item = (usize, VarSpec<'a>);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match find_split_hole(self.rest, b',') {
+ Some((prefix, new_rest)) => {
+ self.rest = new_rest;
+ Some((prefix.len(), VarSpec::parse_trusted(prefix)))
+ }
+ None => {
+ if self.rest.is_empty() {
+ None
+ } else {
+ Some((
+ self.rest.len(),
+ VarSpec::parse_trusted(mem::take(&mut self.rest)),
+ ))
+ }
+ }
+ }
+ }
+}
+
+/// Variable modifier.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(super) enum Modifier {
+ /// No modifiers.
+ None,
+ /// Max length, greater than 0 and less than 10000.
+ MaxLen(u16),
+ /// Explode the variable, e.g. the var spec has `*`.
+ Explode,
+}
+
+/// Operator that is possibly reserved for future extension.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(super) enum MaybeOperator {
+ /// Working operator.
+ Operator(Operator),
+ /// Reserved for future extensions.
+ Reserved(OperatorReservedForFuture),
+}
+
+impl MaybeOperator {
+ /// Returns the operator for the given character.
+ pub(super) fn from_byte(b: u8) -> Option<Self> {
+ match b {
+ b'+' => Some(Self::Operator(Operator::Reserved)),
+ b'#' => Some(Self::Operator(Operator::Fragment)),
+ b'.' => Some(Self::Operator(Operator::Label)),
+ b'/' => Some(Self::Operator(Operator::PathSegments)),
+ b';' => Some(Self::Operator(Operator::PathParams)),
+ b'?' => Some(Self::Operator(Operator::FormQuery)),
+ b'&' => Some(Self::Operator(Operator::FormQueryCont)),
+ b'=' => Some(Self::Reserved(OperatorReservedForFuture::Equals)),
+ b',' => Some(Self::Reserved(OperatorReservedForFuture::Comma)),
+ b'!' => Some(Self::Reserved(OperatorReservedForFuture::Exclamation)),
+ b'@' => Some(Self::Reserved(OperatorReservedForFuture::AtSign)),
+ b'|' => Some(Self::Reserved(OperatorReservedForFuture::Pipe)),
+ _ => None,
+ }
+ }
+}
+
+/// Working operator.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(super) enum Operator {
+ /// No operator. String expansion.
+ String,
+ /// Reserved expansion by `+`.
+ Reserved,
+ /// Fragment expansion by `#`.
+ Fragment,
+ /// Label expansion by `.`.
+ Label,
+ /// Path segments by `/`.
+ PathSegments,
+ /// Path-style parameters by `;`.
+ PathParams,
+ /// Form-style query by `?`.
+ FormQuery,
+ /// Form-style query continuation by `&`.
+ FormQueryCont,
+}
+
+impl Operator {
+ /// Returns the operator for the given character.
+ #[must_use]
+ pub(super) fn from_byte(b: u8) -> Option<Self> {
+ match b {
+ b'+' => Some(Self::Reserved),
+ b'#' => Some(Self::Fragment),
+ b'.' => Some(Self::Label),
+ b'/' => Some(Self::PathSegments),
+ b';' => Some(Self::PathParams),
+ b'?' => Some(Self::FormQuery),
+ b'&' => Some(Self::FormQueryCont),
+ _ => None,
+ }
+ }
+
+ /// Returns the string length of the operator.
+ #[inline]
+ #[must_use]
+ pub(super) const fn len(self) -> usize {
+ if matches!(self, Self::String) {
+ 0
+ } else {
+ 1
+ }
+ }
+}
+
+/// Operator reserved for future extension.
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub(super) enum OperatorReservedForFuture {
+ /// Reserved `=` operator.
+ Equals,
+ /// Reserved `,` operator.
+ Comma,
+ /// Reserved `!` operator.
+ Exclamation,
+ /// Reserved `@` operator.
+ AtSign,
+ /// Reserved `|` operator.
+ Pipe,
+}
diff --git a/vendor/iri-string/src/template/context.rs b/vendor/iri-string/src/template/context.rs
new file mode 100644
index 00000000..ea3f14bb
--- /dev/null
+++ b/vendor/iri-string/src/template/context.rs
@@ -0,0 +1,339 @@
+//! Template expansion context.
+//!
+//! # Examples
+//!
+//! 1. Define your context type.
+//! 2. Implement [`Context`] trait (and [`Context::visit`] method) for the type.
+//! 1. Get variable name by [`Visitor::var_name`] method.
+//! 2. Feed the corresponding value(s) by one of `Visitor::visit_*` methods.
+//!
+//! Note that contexts should return consistent result across multiple visits for
+//! the same variable. In other words, `Context::visit` should return the same
+//! result for the same `Visitor::var_name()` during the context is borrowed.
+//! If this condition is violated, the URI template processor can return
+//! invalid result or panic at worst.
+//!
+//! ```
+//! use iri_string::template::context::{Context, Visitor, ListVisitor, AssocVisitor};
+//!
+//! struct MyContext {
+//! name: &'static str,
+//! id: u64,
+//! tags: &'static [&'static str],
+//! children: &'static [(&'static str, usize)],
+//! }
+//!
+//! impl Context for MyContext {
+//! fn visit<V: Visitor>(&self, visitor: V) -> V::Result {
+//! let name = visitor.var_name().as_str();
+//! match name {
+//! "name" => visitor.visit_string(self.name),
+//! "id" => visitor.visit_string(self.id),
+//! "tags" => visitor.visit_list().visit_items_and_finish(self.tags),
+//! "children" => visitor
+//! .visit_assoc()
+//! .visit_entries_and_finish(self.children.iter().copied()),
+//! _ => visitor.visit_undefined(),
+//! }
+//! }
+//! }
+//! ```
+//
+// # Developers note
+//
+// Visitor types **should not** be cloneable in order to enforce just one
+// visitor is used to visit a variable. If visitors are cloneable, it can make
+// the wrong usage to be available, i.e. storing cloned visitors somewhere and
+// using the wrong one.
+//
+// However, if visitors are made cloneable by any chance, it does not indicate
+// the whole implementation will be broken. Users can only use the visitors
+// through visitor traits (and their API do not allow cloning), so the logic
+// would work as expected if the internal usage of the visitors are correct.
+// Making visitors noncloneable is an optional safety guard (with no overhead).
+
+use core::fmt;
+use core::ops::ControlFlow;
+
+pub use crate::template::components::VarName;
+
+/// A trait for types that can behave as a static URI template expansion context.
+///
+/// This type is for use with [`UriTemplateStr::expand`] method.
+///
+/// See [the module documentation][`crate::template`] for usage.
+///
+/// [`UriTemplateStr::expand`]: `crate::template::UriTemplateStr::expand`
+pub trait Context: Sized {
+ /// Visits a variable.
+ ///
+ /// To get variable name, use [`Visitor::var_name()`].
+ #[must_use]
+ fn visit<V: Visitor>(&self, visitor: V) -> V::Result;
+}
+
+/// A trait for types that can behave as a dynamic (mutable) URI template expansion context.
+///
+/// This type is for use with [`UriTemplateStr::expand_dynamic`] method and its
+/// family.
+///
+/// Note that "dynamic" here does not mean that the value of variables can
+/// change during a template expansion. The value should be fixed and consistent
+/// during each expansion, but the context is allowed to mutate itself if it
+/// does not break this rule.
+///
+/// # Exmaples
+///
+/// ```
+/// # #[cfg(feature = "alloc")]
+/// # extern crate alloc;
+/// # use iri_string::template::Error;
+/// # #[cfg(feature = "alloc")] {
+/// # use alloc::string::String;
+/// use iri_string::template::UriTemplateStr;
+/// use iri_string::template::context::{DynamicContext, Visitor, VisitPurpose};
+/// use iri_string::spec::UriSpec;
+///
+/// struct MyContext<'a> {
+/// /// Target path.
+/// target: &'a str,
+/// /// Username.
+/// username: Option<&'a str>,
+/// /// A flag to remember whether the URI template
+/// /// attempted to use `username` variable.
+/// username_visited: bool,
+/// }
+///
+/// impl DynamicContext for MyContext<'_> {
+/// fn on_expansion_start(&mut self) {
+/// // Reset the state.
+/// self.username_visited = false;
+/// }
+/// fn visit_dynamic<V: Visitor>(&mut self, visitor: V) -> V::Result {
+/// match visitor.var_name().as_str() {
+/// "target" => visitor.visit_string(self.target),
+/// "username" => {
+/// if visitor.purpose() == VisitPurpose::Expand {
+/// // The variable `username` is being used
+/// // on the template expansion.
+/// // Don't care whether `username` is defined or not.
+/// self.username_visited = true;
+/// }
+/// if let Some(username) = &self.username {
+/// visitor.visit_string(username)
+/// } else {
+/// visitor.visit_undefined()
+/// }
+/// }
+/// _ => visitor.visit_undefined(),
+/// }
+/// }
+/// }
+///
+/// let mut context = MyContext {
+/// target: "/posts/1",
+/// username: Some("the_admin"),
+/// username_visited: false,
+/// };
+/// let mut buf = String::new();
+///
+/// // No access to the variable `username`.
+/// let template1 = UriTemplateStr::new("{+target}")?;
+/// template1.expand_dynamic::<UriSpec, _, _>(&mut buf, &mut context)?;
+/// assert_eq!(buf, "/posts/1");
+/// assert!(!context.username_visited);
+///
+/// buf.clear();
+/// // Will access to the variable `username`.
+/// let template2 = UriTemplateStr::new("{+target}{?username}")?;
+/// template2.expand_dynamic::<UriSpec, _, _>(&mut buf, &mut context)?;
+/// assert_eq!(buf, "/posts/1?username=the_admin");
+/// assert!(context.username_visited);
+///
+/// buf.clear();
+/// context.username = None;
+/// // Will access to the variable `username` but it is undefined.
+/// template2.expand_dynamic::<UriSpec, _, _>(&mut buf, &mut context)?;
+/// assert_eq!(buf, "/posts/1");
+/// assert!(
+/// context.username_visited,
+/// "`MyContext` can know and remember whether `visit_dynamic()` is called
+/// for `username`, even if its value is undefined"
+/// );
+/// # }
+/// # Ok::<_, Error>(())
+/// ```
+///
+/// [`UriTemplateStr::expand_dynamic`]: `crate::template::UriTemplateStr::expand_dynamic`
+pub trait DynamicContext: Sized {
+ /// Visits a variable.
+ ///
+ /// To get variable name, use [`Visitor::var_name()`].
+ ///
+ /// # Restriction
+ ///
+ /// The visit results should be consistent and unchanged between the last
+ /// time [`on_expansion_start`][`Self::on_expansion_start`] was called and
+ /// the next time [`on_expansion_end`][`Self::on_expansion_end`] will be
+ /// called. If this condition is violated, template expansion will produce
+ /// wrong result or may panic at worst.
+ #[must_use]
+ fn visit_dynamic<V: Visitor>(&mut self, visitor: V) -> V::Result;
+
+ /// A callback that is called before the expansion of a URI template.
+ #[inline]
+ fn on_expansion_start(&mut self) {}
+
+ /// A callback that is called after the expansion of a URI template.
+ #[inline]
+ fn on_expansion_end(&mut self) {}
+}
+
+impl<C: Context> DynamicContext for C {
+ #[inline]
+ fn visit_dynamic<V: Visitor>(&mut self, visitor: V) -> V::Result {
+ self.visit(visitor)
+ }
+}
+
+/// A purpose of a visit.
+///
+/// This enum is nonexhaustive since this partially exposes the internal
+/// implementation of the template expansion, and thus this is subject to
+/// change.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum VisitPurpose {
+ /// A visit for type checking.
+ Typecheck,
+ /// A visit for template expansion to retrieve the value.
+ Expand,
+}
+
+/// Variable visitor.
+///
+/// See [the module documentation][self] for usage.
+// NOTE (internal): Visitor types **should not** be cloneable.
+pub trait Visitor: Sized + private::Sealed {
+ /// Result of the visit.
+ type Result;
+ /// List visitor.
+ type ListVisitor: ListVisitor<Result = Self::Result>;
+ /// Associative array visitor.
+ type AssocVisitor: AssocVisitor<Result = Self::Result>;
+
+ /// Returns the name of the variable to visit.
+ #[must_use]
+ fn var_name(&self) -> VarName<'_>;
+ /// Returns the purpose of the visit.
+ ///
+ /// The template expansion algorithm checks the types for some variables
+ /// depending on its usage. To get the usage count correctly, you should
+ /// only count visits with [`VisitPurpose::Expand`].
+ ///
+ /// If you need to know whether the variable is accessed and does not
+ /// need dynamic context generation or access counts, consider using
+ /// [`UriTemplateStr::variables`] method to iterate the variables in the
+ /// URI template.
+ ///
+ /// [`UriTemplateStr::variables`]: `crate::template::UriTemplateStr::variables`
+ #[must_use]
+ fn purpose(&self) -> VisitPurpose;
+ /// Visits an undefined variable, i.e. indicates that the requested variable is unavailable.
+ #[must_use]
+ fn visit_undefined(self) -> Self::Result;
+ /// Visits a string variable.
+ #[must_use]
+ fn visit_string<T: fmt::Display>(self, v: T) -> Self::Result;
+ /// Visits a list variable.
+ #[must_use]
+ fn visit_list(self) -> Self::ListVisitor;
+ /// Visits an associative array variable.
+ #[must_use]
+ fn visit_assoc(self) -> Self::AssocVisitor;
+}
+
+/// List visitor.
+///
+/// See [the module documentation][self] for usage.
+// NOTE (internal): Visitor types **should not** be cloneable.
+pub trait ListVisitor: Sized + private::Sealed {
+ /// Result of the visit.
+ type Result;
+
+ /// Visits an item.
+ ///
+ /// If this returned `ControlFlow::Break(v)`, [`Context::visit`] should also
+ /// return this `v`.
+ ///
+ /// To feed multiple items at once, do
+ /// `items.into_iter().try_for_each(|item| self.visit_item(item))` for example.
+ #[must_use]
+ fn visit_item<T: fmt::Display>(&mut self, item: T) -> ControlFlow<Self::Result>;
+ /// Finishes visiting the list.
+ #[must_use]
+ fn finish(self) -> Self::Result;
+
+ /// Visits items and finish.
+ #[must_use]
+ fn visit_items_and_finish<T, I>(mut self, items: I) -> Self::Result
+ where
+ T: fmt::Display,
+ I: IntoIterator<Item = T>,
+ {
+ match items.into_iter().try_for_each(|item| self.visit_item(item)) {
+ ControlFlow::Break(v) => v,
+ ControlFlow::Continue(()) => self.finish(),
+ }
+ }
+}
+
+/// Associative array visitor.
+///
+/// See [the module documentation][self] for usage.
+// NOTE (internal): Visitor types **should not** be cloneable.
+pub trait AssocVisitor: Sized + private::Sealed {
+ /// Result of the visit.
+ type Result;
+
+ /// Visits an entry.
+ ///
+ /// If this returned `ControlFlow::Break(v)`, [`Context::visit`] should also
+ /// return this `v`.
+ ///
+ /// To feed multiple items at once, do
+ /// `entries.into_iter().try_for_each(|(key, value)| self.visit_entry(key, value))`
+ /// for example.
+ #[must_use]
+ fn visit_entry<K: fmt::Display, V: fmt::Display>(
+ &mut self,
+ key: K,
+ value: V,
+ ) -> ControlFlow<Self::Result>;
+ /// Finishes visiting the associative array.
+ #[must_use]
+ fn finish(self) -> Self::Result;
+
+ /// Visits entries and finish.
+ #[must_use]
+ fn visit_entries_and_finish<K, V, I>(mut self, entries: I) -> Self::Result
+ where
+ K: fmt::Display,
+ V: fmt::Display,
+ I: IntoIterator<Item = (K, V)>,
+ {
+ match entries
+ .into_iter()
+ .try_for_each(|(key, value)| self.visit_entry(key, value))
+ {
+ ControlFlow::Break(v) => v,
+ ControlFlow::Continue(()) => self.finish(),
+ }
+ }
+}
+
+/// Private module to put the trait to seal.
+pub(super) mod private {
+ /// A trait for visitor types of variables in a context.
+ pub trait Sealed {}
+}
diff --git a/vendor/iri-string/src/template/error.rs b/vendor/iri-string/src/template/error.rs
new file mode 100644
index 00000000..f5206a4b
--- /dev/null
+++ b/vendor/iri-string/src/template/error.rs
@@ -0,0 +1,154 @@
+//! Errors related to URI templates.
+
+use core::fmt;
+
+#[cfg(feature = "std")]
+use std::error;
+
+/// Template construction and expansion error kind.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum ErrorKind {
+ /// Cannot write to the backend.
+ WriteFailed,
+ /// Expression is not closed.
+ ExpressionNotClosed,
+ /// Invalid character.
+ InvalidCharacter,
+ /// Invalid expression.
+ InvalidExpression,
+ /// Invalid percent-encoded triplets.
+ InvalidPercentEncoding,
+ /// Invalid UTF-8 bytes.
+ InvalidUtf8,
+ /// Unexpected value type for the variable.
+ UnexpectedValueType,
+ /// Unsupported operator, including operators reserved for future.
+ UnsupportedOperator,
+}
+
+impl ErrorKind {
+ /// Returns the error message.
+ #[must_use]
+ fn as_str(self) -> &'static str {
+ match self {
+ Self::WriteFailed => "failed to write to the backend writer",
+ Self::ExpressionNotClosed => "expression not closed",
+ Self::InvalidCharacter => "invalid character",
+ Self::InvalidExpression => "invalid expression",
+ Self::InvalidPercentEncoding => "invalid percent-encoded triplets",
+ Self::InvalidUtf8 => "invalid utf-8 byte sequence",
+ Self::UnexpectedValueType => "unexpected value type for the variable",
+ Self::UnsupportedOperator => "unsupported operator",
+ }
+ }
+}
+
+/// Template construction and expansion error.
+///
+// Note that this type should implement `Copy` trait.
+// To return additional non-`Copy` data as an error, use wrapper type
+// (as `std::string::FromUtf8Error` contains `std::str::Utf8Error`).
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Error {
+ /// Error kind.
+ kind: ErrorKind,
+ /// Location (byte position of the error).
+ location: usize,
+}
+
+impl Error {
+ /// Creates a new `Error`.
+ ///
+ /// For internal use.
+ #[inline]
+ #[must_use]
+ pub(super) fn new(kind: ErrorKind, location: usize) -> Self {
+ Self { kind, location }
+ }
+
+ /// Returns the byte position the error is detected.
+ ///
+ /// NOTE: This is not a part of the public API since the value to be
+ /// returned (i.e., the definition of the "position" of an error) is not
+ /// guaranteed to be stable.
+ #[cfg(test)]
+ pub(super) fn location(&self) -> usize {
+ self.location
+ }
+}
+
+impl fmt::Display for Error {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "invalid URI template: {} (at {}-th byte)",
+ self.kind.as_str(),
+ self.location
+ )
+ }
+}
+
+#[cfg(feature = "std")]
+impl error::Error for Error {}
+
+/// Error on conversion into a URI template type.
+// TODO: Unifiable to `types::CreationError`?
+#[cfg(feature = "alloc")]
+pub struct CreationError<T> {
+ /// Soruce data.
+ source: T,
+ /// Validation error.
+ error: Error,
+}
+
+#[cfg(feature = "alloc")]
+impl<T> CreationError<T> {
+ /// Returns the source data.
+ #[must_use]
+ pub fn into_source(self) -> T {
+ self.source
+ }
+
+ /// Returns the validation error.
+ #[must_use]
+ pub fn validation_error(&self) -> Error {
+ self.error
+ }
+
+ /// Creates a new `CreationError`.
+ #[must_use]
+ pub(crate) fn new(error: Error, source: T) -> Self {
+ Self { source, error }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T: fmt::Debug> fmt::Debug for CreationError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("CreationError")
+ .field("source", &self.source)
+ .field("error", &self.error)
+ .finish()
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T: Clone> Clone for CreationError<T> {
+ fn clone(&self) -> Self {
+ Self {
+ source: self.source.clone(),
+ error: self.error,
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<T> fmt::Display for CreationError<T> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ self.error.fmt(f)
+ }
+}
+
+#[cfg(feature = "std")]
+impl<T: fmt::Debug> error::Error for CreationError<T> {}
diff --git a/vendor/iri-string/src/template/expand.rs b/vendor/iri-string/src/template/expand.rs
new file mode 100644
index 00000000..605043ab
--- /dev/null
+++ b/vendor/iri-string/src/template/expand.rs
@@ -0,0 +1,1039 @@
+//! Expansion.
+
+use core::fmt::{self, Write as _};
+use core::marker::PhantomData;
+use core::mem;
+use core::ops::ControlFlow;
+
+#[cfg(feature = "alloc")]
+use alloc::string::{String, ToString};
+
+use crate::parser::str::{find_split, find_split_hole};
+use crate::parser::str::{process_percent_encoded_best_effort, PctEncodedFragments};
+use crate::percent_encode::PercentEncoded;
+use crate::spec::Spec;
+use crate::template::components::{ExprBody, Modifier, Operator, VarName, VarSpec};
+use crate::template::context::{
+ private::Sealed as VisitorSealed, AssocVisitor, Context, DynamicContext, ListVisitor,
+ VisitPurpose, Visitor,
+};
+use crate::template::error::{Error, ErrorKind};
+use crate::template::{UriTemplateStr, ValueType};
+#[cfg(feature = "alloc")]
+use crate::types;
+
+/// A chunk in a template string.
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub(super) enum Chunk<'a> {
+ /// Literal.
+ Literal(&'a str),
+ /// Expression excluding the wrapping braces.
+ Expr(ExprBody<'a>),
+}
+
+/// Iterator of template chunks.
+#[derive(Debug, Clone)]
+pub(super) struct Chunks<'a> {
+ /// Template.
+ template: &'a str,
+}
+
+impl<'a> Chunks<'a> {
+ /// Creates a new iterator.
+ #[inline]
+ #[must_use]
+ pub(super) fn new(template: &'a UriTemplateStr) -> Self {
+ Self {
+ template: template.as_str(),
+ }
+ }
+}
+
+impl<'a> Iterator for Chunks<'a> {
+ type Item = Chunk<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.template.is_empty() {
+ return None;
+ }
+ match find_split(self.template, b'{') {
+ Some(("", _)) => {
+ let (expr_body, rest) = find_split_hole(&self.template[1..], b'}')
+ .expect("[validity] expression inside a template must be closed");
+ self.template = rest;
+ Some(Chunk::Expr(ExprBody::new(expr_body)))
+ }
+ Some((lit, rest)) => {
+ self.template = rest;
+ Some(Chunk::Literal(lit))
+ }
+ None => Some(Chunk::Literal(mem::take(&mut self.template))),
+ }
+ }
+}
+
+/// Template expansion result.
+#[derive(Debug, Clone, Copy)]
+pub struct Expanded<'a, S, C> {
+ /// Compiled template.
+ template: &'a UriTemplateStr,
+ /// Context.
+ context: &'a C,
+ /// Spec.
+ _spec: PhantomData<fn() -> S>,
+}
+
+impl<'a, S: Spec, C: Context> Expanded<'a, S, C> {
+ /// Creates a new `Expanded` object.
+ #[inline]
+ pub(super) fn new(template: &'a UriTemplateStr, context: &'a C) -> Result<Self, Error> {
+ Self::typecheck_context(template, context)?;
+ Ok(Self {
+ template,
+ context,
+ _spec: PhantomData,
+ })
+ }
+
+ /// Checks if the types of variables are allowed for the corresponding expressions in the template.
+ fn typecheck_context(template: &UriTemplateStr, context: &C) -> Result<(), Error> {
+ let mut pos = 0;
+ for chunk in Chunks::new(template) {
+ let (expr_len, (op, varlist)) = match chunk {
+ Chunk::Expr(expr_body) => (expr_body.as_str().len(), expr_body.decompose()),
+ Chunk::Literal(lit) => {
+ pos += lit.len();
+ continue;
+ }
+ };
+ // +2: wrapping braces (`{` and `}`).
+ let chunk_end_pos = pos + expr_len + 2;
+ // +1: opening brace `{`.
+ pos += op.len() + 1;
+ for (varspec_len, varspec) in varlist {
+ let ty = context.visit(TypeVisitor::new(varspec.name()));
+ let modifier = varspec.modifier();
+
+ if matches!(modifier, Modifier::MaxLen(_))
+ && matches!(ty, ValueType::List | ValueType::Assoc)
+ {
+ // > Prefix modifiers are not applicable to variables that
+ // > have composite values.
+ //
+ // --- [RFC 6570 Section 2.4.1. Prefix](https://www.rfc-editor.org/rfc/rfc6570.html#section-2.4.1)
+ return Err(Error::new(ErrorKind::UnexpectedValueType, pos));
+ }
+
+ // +1: A trailing comman (`,`) or a closing brace (`}`).
+ pos += varspec_len + 1;
+ }
+ assert_eq!(pos, chunk_end_pos);
+ }
+ Ok(())
+ }
+}
+
+impl<S: Spec, C: Context> fmt::Display for Expanded<'_, S, C> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ for chunk in Chunks::new(self.template) {
+ let expr = match chunk {
+ Chunk::Literal(lit) => {
+ f.write_str(lit)?;
+ continue;
+ }
+ Chunk::Expr(body) => body,
+ };
+ expand::<S, _>(f, expr, self.context)?;
+ }
+
+ Ok(())
+ }
+}
+
+/// Implement `TryFrom<Expanded<...>> for SomeUriStringType`.
+macro_rules! impl_try_from_expanded {
+ ($ty_outer:ident) => {
+ #[cfg(feature = "alloc")]
+ impl<S: Spec, C: Context> TryFrom<Expanded<'_, S, C>> for types::$ty_outer<S> {
+ type Error = types::CreationError<String>;
+
+ #[inline]
+ fn try_from(v: Expanded<'_, S, C>) -> Result<Self, Self::Error> {
+ Self::try_from(v.to_string())
+ }
+ }
+ };
+}
+
+// Not implementing `TryFrom<Expand<...>>` for query and fragment strings
+// since they cannot behave as a query or a fragment only by themselves.
+// Query strings in practical starts with `?` prefix but `RiQueryStr{,ing}`
+// strips that, and so do fragment strings (but `#` instead of `?`).
+// Because of this, query and fragment string types won't be used to represent
+// a relative IRIs without combining the prefix.
+//
+// In contrast, RFC 6570 URI Template expects that the users are constructing a
+// "working" IRIs, including the necessary prefixes for syntax components.
+// For example, fragment expansion `{#var}`, where `var` is "hello", expands to
+// `#hello`, including the prefix `#`. This means that a URI template will be
+// used to generate neither `RiQueryStr{,ing}` nor `RiFragmentStr{,ing}` strings.
+impl_try_from_expanded!(RiAbsoluteString);
+impl_try_from_expanded!(RiReferenceString);
+impl_try_from_expanded!(RiRelativeString);
+impl_try_from_expanded!(RiString);
+
+/// Expands the whole template with the dynamic context.
+pub(super) fn expand_whole_dynamic<S: Spec, W: fmt::Write, C: DynamicContext>(
+ template: &UriTemplateStr,
+ writer: &mut W,
+ context: &mut C,
+) -> Result<(), Error> {
+ context.on_expansion_start();
+ let result = expand_whole_dynamic_impl::<S, W, C>(template, writer, context);
+ context.on_expansion_end();
+ result
+}
+
+/// Expands the whole template with the dynamic context.
+///
+/// Note that the caller is responsible to set up or finalize the `context`.
+fn expand_whole_dynamic_impl<S: Spec, W: fmt::Write, C: DynamicContext>(
+ template: &UriTemplateStr,
+ writer: &mut W,
+ context: &mut C,
+) -> Result<(), Error> {
+ let mut pos = 0;
+ for chunk in Chunks::new(template) {
+ let expr = match chunk {
+ Chunk::Literal(lit) => {
+ writer
+ .write_str(lit)
+ .map_err(|_| Error::new(ErrorKind::WriteFailed, pos))?;
+ pos += lit.len();
+ continue;
+ }
+ Chunk::Expr(body) => body,
+ };
+ expand_expr_mut::<S, _, _>(writer, &mut pos, expr, context)?;
+ }
+
+ Ok(())
+}
+
+/// Expands the expression using the given operator and the dynamic context.
+fn expand_expr_mut<S: Spec, W: fmt::Write, C: DynamicContext>(
+ writer: &mut W,
+ pos: &mut usize,
+ expr: ExprBody<'_>,
+ context: &mut C,
+) -> Result<(), Error> {
+ let (op, varlist) = expr.decompose();
+
+ let mut is_first_varspec = true;
+ // +2: wrapping braces (`{` and `}`).
+ let chunk_end_pos = *pos + expr.as_str().len() + 2;
+ // +1: opening brace `{`.
+ *pos += op.len() + 1;
+ for (varspec_len, varspec) in varlist {
+ // Check the type before the actual expansion.
+ let ty = context.visit_dynamic(TypeVisitor::new(varspec.name()));
+ let modifier = varspec.modifier();
+
+ if matches!(modifier, Modifier::MaxLen(_))
+ && matches!(ty, ValueType::List | ValueType::Assoc)
+ {
+ // > Prefix modifiers are not applicable to variables that
+ // > have composite values.
+ //
+ // --- [RFC 6570 Section 2.4.1. Prefix](https://www.rfc-editor.org/rfc/rfc6570.html#section-2.4.1)
+ return Err(Error::new(ErrorKind::UnexpectedValueType, *pos));
+ }
+
+ // Typecheck passed. Expand.
+ let visitor = ValueVisitor::<S, _>::new(writer, varspec, op, &mut is_first_varspec);
+ let token = context
+ .visit_dynamic(visitor)
+ .map_err(|_| Error::new(ErrorKind::WriteFailed, *pos))?;
+ let writer_ptr = token.writer_ptr();
+ if writer_ptr != writer as *mut _ {
+ // Invalid `VisitDoneToken` was returned. This cannot usually happen
+ // without intentional unnatural usage.
+ panic!("invalid `VisitDoneToken` was returned");
+ }
+
+ // +1: A trailing comman (`,`) or a closing brace (`}`).
+ *pos += varspec_len + 1;
+ }
+ assert_eq!(*pos, chunk_end_pos);
+
+ Ok(())
+}
+
+/// Properties of an operator.
+///
+/// See [RFC 6570 Appendix A](https://www.rfc-editor.org/rfc/rfc6570#appendix-A).
+#[derive(Debug, Clone, Copy)]
+struct OpProps {
+ /// Prefix for the first element.
+ first: &'static str,
+ /// Separator.
+ sep: &'static str,
+ /// Whether or not the expansion includes the variable or key name.
+ named: bool,
+ /// Result string if the variable is empty.
+ ifemp: &'static str,
+ /// Whether or not the reserved values can be written without being encoded.
+ allow_reserved: bool,
+}
+
+impl OpProps {
+ /// Properties for all known operators.
+ const PROPS: [Self; 8] = [
+ // String
+ Self {
+ first: "",
+ sep: ",",
+ named: false,
+ ifemp: "",
+ allow_reserved: false,
+ },
+ // Reserved
+ Self {
+ first: "",
+ sep: ",",
+ named: false,
+ ifemp: "",
+ allow_reserved: true,
+ },
+ // Fragment
+ Self {
+ first: "#",
+ sep: ",",
+ named: false,
+ ifemp: "",
+ allow_reserved: true,
+ },
+ // Label
+ Self {
+ first: ".",
+ sep: ".",
+ named: false,
+ ifemp: "",
+ allow_reserved: false,
+ },
+ // PathSegments
+ Self {
+ first: "/",
+ sep: "/",
+ named: false,
+ ifemp: "",
+ allow_reserved: false,
+ },
+ // PathParams
+ Self {
+ first: ";",
+ sep: ";",
+ named: true,
+ ifemp: "",
+ allow_reserved: false,
+ },
+ // FormQuery
+ Self {
+ first: "?",
+ sep: "&",
+ named: true,
+ ifemp: "=",
+ allow_reserved: false,
+ },
+ // FormQueryCont
+ Self {
+ first: "&",
+ sep: "&",
+ named: true,
+ ifemp: "=",
+ allow_reserved: false,
+ },
+ ];
+
+ /// Returns the properties for the operator.
+ #[must_use]
+ #[inline]
+ pub(super) fn from_op(op: Operator) -> &'static Self {
+ let index = match op {
+ Operator::String => 0,
+ Operator::Reserved => 1,
+ Operator::Fragment => 2,
+ Operator::Label => 3,
+ Operator::PathSegments => 4,
+ Operator::PathParams => 5,
+ Operator::FormQuery => 6,
+ Operator::FormQueryCont => 7,
+ };
+ &Self::PROPS[index]
+ }
+}
+
+/// Expands the expression using the given operator.
+fn expand<S: Spec, C: Context>(
+ f: &mut fmt::Formatter<'_>,
+ expr: ExprBody<'_>,
+ context: &C,
+) -> fmt::Result {
+ let (op, varlist) = expr.decompose();
+
+ let mut is_first_varspec = true;
+ for (_varspec_len, varspec) in varlist {
+ let visitor = ValueVisitor::<S, _>::new(f, varspec, op, &mut is_first_varspec);
+ let token = context.visit(visitor)?;
+ let writer_ptr = token.writer_ptr();
+ if writer_ptr != f as *mut _ {
+ // Invalid `VisitDoneToken` was returned. This cannot usually happen
+ // without intentional unnatural usage.
+ panic!("invalid `VisitDoneToken` was returned");
+ }
+ }
+
+ Ok(())
+}
+
+/// Escapes the given value and writes it.
+#[inline]
+fn escape_write<S: Spec, T: fmt::Display, W: fmt::Write>(
+ f: &mut W,
+ v: T,
+ allow_reserved: bool,
+) -> fmt::Result {
+ if allow_reserved {
+ let result = process_percent_encoded_best_effort(v, |frag| {
+ let result = match frag {
+ PctEncodedFragments::Char(s, _) => f.write_str(s),
+ PctEncodedFragments::NoPctStr(s) => {
+ write!(f, "{}", PercentEncoded::<_, S>::characters(s))
+ }
+ PctEncodedFragments::StrayPercent => f.write_str("%25"),
+ PctEncodedFragments::InvalidUtf8PctTriplets(s) => f.write_str(s),
+ };
+ if result.is_err() {
+ return ControlFlow::Break(result);
+ }
+ ControlFlow::Continue(())
+ });
+ match result {
+ Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()),
+ Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e),
+ }
+ } else {
+ /// Writer that escapes the unreserved characters and writes them.
+ struct UnreservePercentEncodeWriter<'a, S, W> {
+ /// Inner writer.
+ writer: &'a mut W,
+ /// Spec.
+ _spec: PhantomData<fn() -> S>,
+ }
+ impl<S: Spec, W: fmt::Write> fmt::Write for UnreservePercentEncodeWriter<'_, S, W> {
+ #[inline]
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ write!(self.writer, "{}", PercentEncoded::<_, S>::unreserve(s))
+ }
+ }
+ let mut writer = UnreservePercentEncodeWriter::<S, W> {
+ writer: f,
+ _spec: PhantomData,
+ };
+ write!(writer, "{v}")
+ }
+}
+
+/// Truncates the given value as a string, escapes the value, and writes it.
+fn escape_write_with_maxlen<S: Spec, T: fmt::Display, W: fmt::Write>(
+ writer: &mut PrefixOnceWriter<'_, W>,
+ v: T,
+ allow_reserved: bool,
+ max_len: Option<u16>,
+) -> fmt::Result {
+ if allow_reserved {
+ let mut max_len = max_len.map_or(usize::MAX, usize::from);
+ let result = process_percent_encoded_best_effort(v, |frag| {
+ if max_len == 0 {
+ return ControlFlow::Break(Ok(()));
+ }
+ let result =
+ match frag {
+ PctEncodedFragments::Char(s, _) => {
+ max_len -= 1;
+ writer.write_str(s)
+ }
+ PctEncodedFragments::NoPctStr(s) => {
+ let mut chars = s.char_indices();
+ let count =
+ chars.by_ref().take(max_len).last().map(|(i, _)| i).expect(
+ "[consistency] decomposed string fragment must not be empty",
+ );
+ let sub_len = s.len() - chars.as_str().len();
+ max_len -= count;
+ write!(
+ writer,
+ "{}",
+ PercentEncoded::<_, S>::characters(&s[..sub_len])
+ )
+ }
+ PctEncodedFragments::StrayPercent => {
+ max_len -= 1;
+ writer.write_str("%25")
+ }
+ PctEncodedFragments::InvalidUtf8PctTriplets(s) => {
+ let count = max_len.min(s.len() / 3);
+ let sub_len = count * 3;
+ max_len -= count;
+ writer.write_str(&s[..sub_len])
+ }
+ };
+ if result.is_err() {
+ return ControlFlow::Break(result);
+ }
+ ControlFlow::Continue(())
+ });
+ match result {
+ Ok(ControlFlow::Break(Ok(_)) | ControlFlow::Continue(_)) => Ok(()),
+ Ok(ControlFlow::Break(Err(e))) | Err(e) => Err(e),
+ }
+ } else {
+ match max_len {
+ Some(max_len) => {
+ let mut writer = TruncatePercentEncodeWriter::<S, _> {
+ inner: writer,
+ rest_num_chars: usize::from(max_len),
+ _spec: PhantomData,
+ };
+ write!(writer, "{v}")
+ }
+ None => write!(writer, "{}", PercentEncoded::<_, S>::unreserve(v)),
+ }
+ }
+}
+
+/// A writer that truncates the input to the given length and writes to the backend.
+struct TruncatePercentEncodeWriter<'a, S, W> {
+ /// Inner writer.
+ inner: &'a mut W,
+ /// Maximum number of characters to be written.
+ rest_num_chars: usize,
+ /// Spec.
+ _spec: PhantomData<fn() -> S>,
+}
+
+impl<S: Spec, W: fmt::Write> fmt::Write for TruncatePercentEncodeWriter<'_, S, W> {
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ if self.rest_num_chars == 0 {
+ return Ok(());
+ }
+ let mut chars = s.char_indices();
+ let skip_count = chars
+ .by_ref()
+ .take(self.rest_num_chars)
+ .last()
+ .map_or(0, |(i, _)| i + 1);
+ let len = s.len() - chars.as_str().len();
+ let truncated = &s[..len];
+ write!(
+ self.inner,
+ "{}",
+ PercentEncoded::<_, S>::unreserve(truncated)
+ )?;
+ self.rest_num_chars -= skip_count;
+ Ok(())
+ }
+}
+
+/// A writer that writes a prefix only once if and only if some value is written.
+struct PrefixOnceWriter<'a, W> {
+ /// Inner writer.
+ inner: &'a mut W,
+ /// Prefix to write.
+ prefix: Option<&'a str>,
+}
+
+impl<'a, W: fmt::Write> PrefixOnceWriter<'a, W> {
+ /// Creates a new writer with no prefix.
+ #[inline]
+ #[must_use]
+ fn new(inner: &'a mut W) -> Self {
+ Self {
+ inner,
+ prefix: None,
+ }
+ }
+
+ /// Creates a new writer with a prefix.
+ #[inline]
+ #[must_use]
+ fn with_prefix(inner: &'a mut W, prefix: &'a str) -> Self {
+ Self {
+ inner,
+ prefix: Some(prefix),
+ }
+ }
+
+ /// Returns true if the writer have not yet written the prefix.
+ #[inline]
+ #[must_use]
+ fn has_unwritten_prefix(&self) -> bool {
+ self.prefix.is_some()
+ }
+}
+
+impl<W: fmt::Write> fmt::Write for PrefixOnceWriter<'_, W> {
+ #[inline]
+ fn write_str(&mut self, s: &str) -> fmt::Result {
+ if let Some(prefix) = self.prefix.take() {
+ self.inner.write_str(prefix)?;
+ }
+ self.inner.write_str(s)
+ }
+}
+
+/// An opaque token value that proves some variable is visited.
+// This should not be able to be created by any means other than `VarVisitor::visit_foo()`.
+// Do not derive any traits that allows the value to be generated or cloned.
+struct VisitDoneToken<'a, S, W>(ValueVisitor<'a, S, W>);
+
+impl<'a, S: Spec, W: fmt::Write> VisitDoneToken<'a, S, W> {
+ /// Creates a new token.
+ #[inline]
+ #[must_use]
+ fn new(visitor: ValueVisitor<'a, S, W>) -> Self {
+ Self(visitor)
+ }
+
+ /// Returns the raw pointer to the backend formatter.
+ #[inline]
+ #[must_use]
+ fn writer_ptr(&self) -> *const W {
+ self.0.writer_ptr()
+ }
+}
+
+impl<S: Spec, W: fmt::Write> fmt::Debug for VisitDoneToken<'_, S, W> {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("VisitDoneToken")
+ }
+}
+
+/// Visitor to retrieve a variable value.
+// Single `ValueVisitor` should be used for single expansion.
+// Do not derive any traits that allows the value to be generated or cloned.
+struct ValueVisitor<'a, S, W> {
+ /// Formatter.
+ writer: &'a mut W,
+ /// Varspec.
+ varspec: VarSpec<'a>,
+ /// Operator.
+ op: Operator,
+ /// Whether the variable to visit is the first one in an expression.
+ is_first_varspec: &'a mut bool,
+ /// Spec.
+ _spec: PhantomData<fn() -> S>,
+}
+
+impl<'a, S: Spec, W: fmt::Write> ValueVisitor<'a, S, W> {
+ /// Creates a visitor.
+ #[inline]
+ #[must_use]
+ fn new(
+ f: &'a mut W,
+ varspec: VarSpec<'a>,
+ op: Operator,
+ is_first_varspec: &'a mut bool,
+ ) -> Self {
+ Self {
+ writer: f,
+ varspec,
+ op,
+ is_first_varspec,
+ _spec: PhantomData,
+ }
+ }
+
+ /// Returns the raw pointer to the backend formatter.
+ #[inline]
+ #[must_use]
+ fn writer_ptr(&self) -> *const W {
+ self.writer as &_ as *const _
+ }
+}
+
+impl<S: Spec, W: fmt::Write> VisitorSealed for ValueVisitor<'_, S, W> {}
+
+impl<'a, S: Spec, W: fmt::Write> Visitor for ValueVisitor<'a, S, W> {
+ type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
+ type ListVisitor = ListValueVisitor<'a, S, W>;
+ type AssocVisitor = AssocValueVisitor<'a, S, W>;
+
+ /// Returns the name of the variable to visit.
+ #[inline]
+ #[must_use]
+ fn var_name(&self) -> VarName<'a> {
+ self.varspec.name()
+ }
+
+ #[inline]
+ fn purpose(&self) -> VisitPurpose {
+ VisitPurpose::Expand
+ }
+
+ /// Visits an undefined variable, i.e. indicates that the requested variable is unavailable.
+ #[inline]
+ fn visit_undefined(self) -> Self::Result {
+ Ok(VisitDoneToken::new(self))
+ }
+
+ /// Visits a string variable.
+ #[inline]
+ fn visit_string<T: fmt::Display>(self, v: T) -> Self::Result {
+ let oppr = OpProps::from_op(self.op);
+
+ if mem::replace(self.is_first_varspec, false) {
+ self.writer.write_str(oppr.first)?;
+ } else {
+ self.writer.write_str(oppr.sep)?;
+ }
+ let mut writer = if oppr.named {
+ self.writer.write_str(self.varspec.name().as_str())?;
+ PrefixOnceWriter::with_prefix(self.writer, "=")
+ } else {
+ PrefixOnceWriter::new(self.writer)
+ };
+
+ let max_len = match self.varspec.modifier() {
+ Modifier::None | Modifier::Explode => None,
+ Modifier::MaxLen(max_len) => Some(max_len),
+ };
+ escape_write_with_maxlen::<S, T, W>(&mut writer, v, oppr.allow_reserved, max_len)?;
+ if writer.has_unwritten_prefix() {
+ self.writer.write_str(oppr.ifemp)?;
+ }
+ Ok(VisitDoneToken::new(self))
+ }
+
+ /// Visits a list variable.
+ #[inline]
+ #[must_use]
+ fn visit_list(self) -> Self::ListVisitor {
+ let oppr = OpProps::from_op(self.op);
+ ListValueVisitor {
+ visitor: self,
+ num_elems: 0,
+ oppr,
+ }
+ }
+
+ /// Visits an associative array variable.
+ #[inline]
+ #[must_use]
+ fn visit_assoc(self) -> Self::AssocVisitor {
+ let oppr = OpProps::from_op(self.op);
+ AssocValueVisitor {
+ visitor: self,
+ num_elems: 0,
+ oppr,
+ }
+ }
+}
+
+/// Visitor to retrieve value of a list variable.
+// RFC 6570 section 2.3:
+//
+// > A variable defined as a list value is considered undefined if the
+// > list contains zero members. A variable defined as an associative
+// > array of (name, value) pairs is considered undefined if the array
+// > contains zero members or if all member names in the array are
+// > associated with undefined values.
+//
+// Single variable visitor should be used for single expansion.
+// Do not derive any traits that allows the value to be generated or cloned.
+struct ListValueVisitor<'a, S, W> {
+ /// Visitor.
+ visitor: ValueVisitor<'a, S, W>,
+ /// Number of already emitted elements.
+ num_elems: usize,
+ /// Operator props.
+ oppr: &'static OpProps,
+}
+
+impl<S: Spec, W: fmt::Write> ListValueVisitor<'_, S, W> {
+ /// Visits an item.
+ fn visit_item_impl<T: fmt::Display>(&mut self, item: T) -> fmt::Result {
+ let modifier = self.visitor.varspec.modifier();
+ let is_explode = match modifier {
+ Modifier::MaxLen(_) => panic!(
+ "value type changed since `UriTemplateStr::expand()`: \
+ prefix modifier is not applicable to a list"
+ ),
+ Modifier::None => false,
+ Modifier::Explode => true,
+ };
+
+ // Write prefix for each variable.
+ if self.num_elems == 0 {
+ if mem::replace(self.visitor.is_first_varspec, false) {
+ self.visitor.writer.write_str(self.oppr.first)?;
+ } else {
+ self.visitor.writer.write_str(self.oppr.sep)?;
+ }
+ if self.oppr.named {
+ self.visitor
+ .writer
+ .write_str(self.visitor.varspec.name().as_str())?;
+ self.visitor.writer.write_char('=')?;
+ }
+ } else {
+ // Write prefix for the non-first item.
+ match (self.oppr.named, is_explode) {
+ (_, false) => self.visitor.writer.write_char(',')?,
+ (false, true) => self.visitor.writer.write_str(self.oppr.sep)?,
+ (true, true) => {
+ self.visitor.writer.write_str(self.oppr.sep)?;
+ escape_write::<S, _, _>(
+ self.visitor.writer,
+ self.visitor.varspec.name().as_str(),
+ self.oppr.allow_reserved,
+ )?;
+ self.visitor.writer.write_char('=')?;
+ }
+ }
+ }
+
+ escape_write::<S, _, _>(self.visitor.writer, item, self.oppr.allow_reserved)?;
+
+ self.num_elems += 1;
+ Ok(())
+ }
+}
+
+impl<S: Spec, W: fmt::Write> VisitorSealed for ListValueVisitor<'_, S, W> {}
+
+impl<'a, S: Spec, W: fmt::Write> ListVisitor for ListValueVisitor<'a, S, W> {
+ type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
+
+ /// Visits an item.
+ #[inline]
+ fn visit_item<T: fmt::Display>(&mut self, item: T) -> ControlFlow<Self::Result> {
+ match self.visit_item_impl(item) {
+ Ok(_) => ControlFlow::Continue(()),
+ Err(e) => ControlFlow::Break(Err(e)),
+ }
+ }
+
+ /// Finishes visiting the list.
+ #[inline]
+ fn finish(self) -> Self::Result {
+ Ok(VisitDoneToken::new(self.visitor))
+ }
+}
+
+/// Visitor to retrieve entries of an associative array variable.
+// RFC 6570 section 2.3:
+//
+// > A variable defined as a list value is considered undefined if the
+// > list contains zero members. A variable defined as an associative
+// > array of (name, value) pairs is considered undefined if the array
+// > contains zero members or if all member names in the array are
+// > associated with undefined values.
+//
+// Single variable visitor should be used for single expansion.
+// Do not derive any traits that allows the value to be generated or cloned.
+struct AssocValueVisitor<'a, S, W> {
+ /// Visitor.
+ visitor: ValueVisitor<'a, S, W>,
+ /// Number of already emitted elements.
+ num_elems: usize,
+ /// Operator props.
+ oppr: &'static OpProps,
+}
+
+impl<S: Spec, W: fmt::Write> AssocValueVisitor<'_, S, W> {
+ /// Visits an entry.
+ fn visit_entry_impl<K: fmt::Display, V: fmt::Display>(
+ &mut self,
+ key: K,
+ value: V,
+ ) -> fmt::Result {
+ let modifier = self.visitor.varspec.modifier();
+ let is_explode = match modifier {
+ Modifier::MaxLen(_) => panic!(
+ "value type changed since `UriTemplateStr::expand()`: \
+ prefix modifier is not applicable to an associative array"
+ ),
+ Modifier::None => false,
+ Modifier::Explode => true,
+ };
+
+ // Write prefix for each variable.
+ if self.num_elems == 0 {
+ if mem::replace(self.visitor.is_first_varspec, false) {
+ self.visitor.writer.write_str(self.oppr.first)?;
+ } else {
+ self.visitor.writer.write_str(self.oppr.sep)?;
+ }
+ if is_explode {
+ escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
+ self.visitor.writer.write_char('=')?;
+ } else {
+ if self.oppr.named {
+ escape_write::<S, _, _>(
+ self.visitor.writer,
+ self.visitor.varspec.name().as_str(),
+ self.oppr.allow_reserved,
+ )?;
+ self.visitor.writer.write_char('=')?;
+ }
+ escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
+ self.visitor.writer.write_char(',')?;
+ }
+ } else {
+ // Write prefix for the non-first item.
+ match (self.oppr.named, is_explode) {
+ (_, false) => {
+ self.visitor.writer.write_char(',')?;
+ escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
+ self.visitor.writer.write_char(',')?;
+ }
+ (false, true) => {
+ self.visitor.writer.write_str(self.oppr.sep)?;
+ escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
+ self.visitor.writer.write_char('=')?;
+ }
+ (true, true) => {
+ self.visitor.writer.write_str(self.oppr.sep)?;
+ escape_write::<S, _, _>(self.visitor.writer, key, self.oppr.allow_reserved)?;
+ self.visitor.writer.write_char('=')?;
+ }
+ }
+ }
+
+ escape_write::<S, _, _>(self.visitor.writer, value, self.oppr.allow_reserved)?;
+
+ self.num_elems += 1;
+ Ok(())
+ }
+}
+
+impl<S: Spec, W: fmt::Write> VisitorSealed for AssocValueVisitor<'_, S, W> {}
+
+impl<'a, S: Spec, W: fmt::Write> AssocVisitor for AssocValueVisitor<'a, S, W> {
+ type Result = Result<VisitDoneToken<'a, S, W>, fmt::Error>;
+
+ /// Visits an entry.
+ #[inline]
+ fn visit_entry<K: fmt::Display, V: fmt::Display>(
+ &mut self,
+ key: K,
+ value: V,
+ ) -> ControlFlow<Self::Result> {
+ match self.visit_entry_impl(key, value) {
+ Ok(_) => ControlFlow::Continue(()),
+ Err(e) => ControlFlow::Break(Err(e)),
+ }
+ }
+
+ /// Finishes visiting the associative array.
+ #[inline]
+ fn finish(self) -> Self::Result {
+ Ok(VisitDoneToken::new(self.visitor))
+ }
+}
+
+/// Visitor to retrieve effective type of a variable.
+struct TypeVisitor<'a> {
+ /// Variable name.
+ var_name: VarName<'a>,
+}
+
+impl<'a> TypeVisitor<'a> {
+ /// Creates a new type visitor.
+ #[inline]
+ #[must_use]
+ fn new(var_name: VarName<'a>) -> Self {
+ Self { var_name }
+ }
+}
+
+impl VisitorSealed for TypeVisitor<'_> {}
+
+impl<'a> Visitor for TypeVisitor<'a> {
+ type Result = ValueType;
+ type ListVisitor = ListTypeVisitor;
+ type AssocVisitor = AssocTypeVisitor;
+
+ #[inline]
+ fn var_name(&self) -> VarName<'a> {
+ self.var_name
+ }
+ #[inline]
+ fn purpose(&self) -> VisitPurpose {
+ VisitPurpose::Typecheck
+ }
+ #[inline]
+ fn visit_undefined(self) -> Self::Result {
+ ValueType::undefined()
+ }
+ #[inline]
+ fn visit_string<T: fmt::Display>(self, _: T) -> Self::Result {
+ ValueType::string()
+ }
+ #[inline]
+ fn visit_list(self) -> Self::ListVisitor {
+ ListTypeVisitor
+ }
+ #[inline]
+ fn visit_assoc(self) -> Self::AssocVisitor {
+ AssocTypeVisitor
+ }
+}
+
+/// Visitor to retrieve effective type of a list variable.
+struct ListTypeVisitor;
+
+impl VisitorSealed for ListTypeVisitor {}
+
+impl ListVisitor for ListTypeVisitor {
+ type Result = ValueType;
+
+ /// Visits an item.
+ #[inline]
+ fn visit_item<T: fmt::Display>(&mut self, _item: T) -> ControlFlow<Self::Result> {
+ ControlFlow::Break(ValueType::nonempty_list())
+ }
+
+ /// Finishes visiting the list.
+ #[inline]
+ fn finish(self) -> Self::Result {
+ ValueType::empty_list()
+ }
+}
+
+/// Visitor to retrieve effective type of an associative array variable.
+struct AssocTypeVisitor;
+
+impl VisitorSealed for AssocTypeVisitor {}
+
+impl AssocVisitor for AssocTypeVisitor {
+ type Result = ValueType;
+
+ /// Visits an item.
+ #[inline]
+ fn visit_entry<K: fmt::Display, V: fmt::Display>(
+ &mut self,
+ _key: K,
+ _value: V,
+ ) -> ControlFlow<Self::Result> {
+ ControlFlow::Break(ValueType::nonempty_assoc())
+ }
+
+ /// Finishes visiting the list.
+ #[inline]
+ fn finish(self) -> Self::Result {
+ ValueType::empty_assoc()
+ }
+}
diff --git a/vendor/iri-string/src/template/parser.rs b/vendor/iri-string/src/template/parser.rs
new file mode 100644
index 00000000..6d5443a8
--- /dev/null
+++ b/vendor/iri-string/src/template/parser.rs
@@ -0,0 +1,6 @@
+//! URI Template parser.
+
+pub(super) mod char;
+pub(super) mod validate;
+
+pub(super) use self::validate::validate_template_str;
diff --git a/vendor/iri-string/src/template/parser/char.rs b/vendor/iri-string/src/template/parser/char.rs
new file mode 100644
index 00000000..9ad4a6d8
--- /dev/null
+++ b/vendor/iri-string/src/template/parser/char.rs
@@ -0,0 +1,190 @@
+//! Characters.
+
+/// Properties of ASCII characters.
+///
+/// About `'` (single quote) being considered as a literal: see
+/// [Errata ID 6937](https://www.rfc-editor.org/errata/eid6937).
+const CHARS_TABLE: [u8; 128] = [
+ 0b_0000_0000, // NUL
+ 0b_0000_0000, // SOH
+ 0b_0000_0000, // STX
+ 0b_0000_0000, // ETX
+ 0b_0000_0000, // EOT
+ 0b_0000_0000, // ENQ
+ 0b_0000_0000, // ACK
+ 0b_0000_0000, // BEL
+ 0b_0000_0000, // BS
+ 0b_0000_0000, // HT
+ 0b_0000_0000, // LF
+ 0b_0000_0000, // VT
+ 0b_0000_0000, // FF
+ 0b_0000_0000, // CR
+ 0b_0000_0000, // SO
+ 0b_0000_0000, // SI
+ 0b_0000_0000, // DLE
+ 0b_0000_0000, // DC1
+ 0b_0000_0000, // DC2
+ 0b_0000_0000, // DC3
+ 0b_0000_0000, // DC4
+ 0b_0000_0000, // NAK
+ 0b_0000_0000, // SYN
+ 0b_0000_0000, // ETB
+ 0b_0000_0000, // CAN
+ 0b_0000_0000, // EM
+ 0b_0000_0000, // SUB
+ 0b_0000_0000, // ESC
+ 0b_0000_0000, // FS
+ 0b_0000_0000, // GS
+ 0b_0000_0000, // RS
+ 0b_0000_0000, // US
+ 0b_0000_0000, // SPACE
+ 0b_0000_0001, // !
+ 0b_0000_0000, // "
+ 0b_0000_0001, // #
+ 0b_0000_0001, // $
+ 0b_0000_0000, // %
+ 0b_0000_0001, // &
+ 0b_0000_0001, // '
+ 0b_0000_0001, // (
+ 0b_0000_0001, // )
+ 0b_0000_0001, // *
+ 0b_0000_0001, // +
+ 0b_0000_0001, // ,
+ 0b_0000_0001, // -
+ 0b_0000_0101, // .
+ 0b_0000_0001, // /
+ 0b_0000_0111, // 0
+ 0b_0000_0111, // 1
+ 0b_0000_0111, // 2
+ 0b_0000_0111, // 3
+ 0b_0000_0111, // 4
+ 0b_0000_0111, // 5
+ 0b_0000_0111, // 6
+ 0b_0000_0111, // 7
+ 0b_0000_0111, // 8
+ 0b_0000_0111, // 9
+ 0b_0000_0001, // :
+ 0b_0000_0001, // ;
+ 0b_0000_0000, // <
+ 0b_0000_0001, // =
+ 0b_0000_0000, // >
+ 0b_0000_0001, // ?
+ 0b_0000_0001, // @
+ 0b_0000_0111, // A
+ 0b_0000_0111, // B
+ 0b_0000_0111, // C
+ 0b_0000_0111, // D
+ 0b_0000_0111, // E
+ 0b_0000_0111, // F
+ 0b_0000_0111, // G
+ 0b_0000_0111, // H
+ 0b_0000_0111, // I
+ 0b_0000_0111, // J
+ 0b_0000_0111, // K
+ 0b_0000_0111, // L
+ 0b_0000_0111, // M
+ 0b_0000_0111, // N
+ 0b_0000_0111, // O
+ 0b_0000_0111, // P
+ 0b_0000_0111, // Q
+ 0b_0000_0111, // R
+ 0b_0000_0111, // S
+ 0b_0000_0111, // T
+ 0b_0000_0111, // U
+ 0b_0000_0111, // V
+ 0b_0000_0111, // W
+ 0b_0000_0111, // X
+ 0b_0000_0111, // Y
+ 0b_0000_0111, // Z
+ 0b_0000_0001, // [
+ 0b_0000_0000, // \
+ 0b_0000_0001, // ]
+ 0b_0000_0000, // ^
+ 0b_0000_0111, // _
+ 0b_0000_0000, // `
+ 0b_0000_0111, // a
+ 0b_0000_0111, // b
+ 0b_0000_0111, // c
+ 0b_0000_0111, // d
+ 0b_0000_0111, // e
+ 0b_0000_0111, // f
+ 0b_0000_0111, // g
+ 0b_0000_0111, // h
+ 0b_0000_0111, // i
+ 0b_0000_0111, // j
+ 0b_0000_0111, // k
+ 0b_0000_0111, // l
+ 0b_0000_0111, // m
+ 0b_0000_0111, // n
+ 0b_0000_0111, // o
+ 0b_0000_0111, // p
+ 0b_0000_0111, // q
+ 0b_0000_0111, // r
+ 0b_0000_0111, // s
+ 0b_0000_0111, // t
+ 0b_0000_0111, // u
+ 0b_0000_0111, // v
+ 0b_0000_0111, // w
+ 0b_0000_0111, // x
+ 0b_0000_0111, // y
+ 0b_0000_0111, // z
+ 0b_0000_0000, // {
+ 0b_0000_0000, // |
+ 0b_0000_0000, // }
+ 0b_0000_0001, // ~
+ 0b_0000_0000, // DEL
+];
+
+/// A mask to test whether the character matches `literals` rule defined in [RFC 6570].
+///
+/// [RFC 6570]: https://www.rfc-editor.org/rfc/rfc6570.html#section-2.1
+const CHARS_TABLE_MASK_LITERAL: u8 = 1 << 0;
+
+/// A mask to test whether the character matches `varchar` rule defined in [RFC 6570].
+///
+/// [RFC 6570]: https://www.rfc-editor.org/rfc/rfc6570.html#section-2.3
+const CHARS_TABLE_MASK_VARCHAR_START: u8 = 1 << 1;
+
+/// A mask to test whether the character matches `varchar` rule defined in [RFC 6570] or a period.
+///
+/// [RFC 6570]: https://www.rfc-editor.org/rfc/rfc6570.html#section-2.3
+const CHARS_TABLE_MASK_VARCHAR_CONTINUE: u8 = 1 << 2;
+
+/// Returns true if the given ASCII character is allowed in a literal string.
+///
+/// # Precondition
+///
+/// The given byte should be an ASCII character, i.e. should be less than 128.
+#[inline]
+#[must_use]
+pub(super) const fn is_ascii_literal_char(c: u8) -> bool {
+ (CHARS_TABLE[c as usize] & CHARS_TABLE_MASK_LITERAL) != 0
+}
+
+/// Returns true if the given ASCII character is allowed as the beginning of the `varname`.
+///
+/// Note that this does not return true for `%` character. It is caller's
+/// responsibility to test validity of percent-encoded triplets.
+///
+/// # Precondition
+///
+/// The given byte should be an ASCII character, i.e. should be less than 128.
+#[inline]
+#[must_use]
+pub(super) const fn is_ascii_varchar_start(c: u8) -> bool {
+ (CHARS_TABLE[c as usize] & CHARS_TABLE_MASK_VARCHAR_START) != 0
+}
+
+/// Returns true if the given ASCII character is allowed as the non-beginning of the `varname`.
+///
+/// Note that this does not return true for `%` character. It is caller's
+/// responsibility to test validity of percent-encoded triplets.
+///
+/// # Precondition
+///
+/// The given byte should be an ASCII character, i.e. should be less than 128.
+#[inline]
+#[must_use]
+pub(super) const fn is_ascii_varchar_continue(c: u8) -> bool {
+ (CHARS_TABLE[c as usize] & CHARS_TABLE_MASK_VARCHAR_CONTINUE) != 0
+}
diff --git a/vendor/iri-string/src/template/parser/validate.rs b/vendor/iri-string/src/template/parser/validate.rs
new file mode 100644
index 00000000..67ab6c01
--- /dev/null
+++ b/vendor/iri-string/src/template/parser/validate.rs
@@ -0,0 +1,161 @@
+//! Validating parsers.
+
+use crate::parser::str::{
+ find_split2_hole, find_split_hole, satisfy_chars_with_pct_encoded, starts_with_double_hexdigits,
+};
+use crate::template::components::MaybeOperator;
+use crate::template::error::{Error, ErrorKind};
+
+use crate::template::parser::char::{
+ is_ascii_literal_char, is_ascii_varchar_continue, is_ascii_varchar_start,
+};
+
+/// Returns `Ok(())` if the given string is a valid literal.
+fn validate_literal(s: &str, offset: usize) -> Result<(), Error> {
+ match s
+ .chars()
+ .position(|c| !c.is_ascii() || !is_ascii_literal_char(c as u8))
+ {
+ Some(pos) => Err(Error::new(ErrorKind::InvalidCharacter, offset + pos)),
+ None => Ok(()),
+ }
+}
+
+/// Returns `Ok(())` if the given string is a valid varspec.
+fn validate_varspec(s: &str, offset: usize) -> Result<(), Error> {
+ match find_split2_hole(s, b':', b'*') {
+ Some((maybe_varname, b':', maybe_len)) => {
+ validate_varname(maybe_varname, offset)?;
+ if !(1..=5).contains(&maybe_len.len()) {
+ return Err(Error::new(
+ ErrorKind::InvalidExpression,
+ offset + maybe_varname.len() + 2,
+ ));
+ }
+ if let Some(pos) = maybe_len.bytes().position(|b| !b.is_ascii_digit()) {
+ return Err(Error::new(
+ ErrorKind::InvalidExpression,
+ offset + maybe_varname.len() + 2 + pos,
+ ));
+ }
+ }
+ Some((maybe_varname, b'*', extra)) => {
+ validate_varname(maybe_varname, offset)?;
+ if !extra.is_empty() {
+ return Err(Error::new(
+ ErrorKind::InvalidExpression,
+ offset + maybe_varname.len() + 1,
+ ));
+ }
+ }
+ Some((_, sep, _)) => unreachable!("[consistency] the byte {sep:#02x} is not searched"),
+ None => validate_varname(s, offset)?,
+ }
+ Ok(())
+}
+
+/// Returns `Ok(())` if the given string is a valid varname.
+pub(crate) fn validate_varname(s: &str, offset: usize) -> Result<(), Error> {
+ let rest = match s.as_bytes().first() {
+ Some(b'%') if starts_with_double_hexdigits(&s.as_bytes()[1..]) => &s[3..],
+ Some(b) if b.is_ascii() && is_ascii_varchar_start(*b) => &s[1..],
+ _ => return Err(Error::new(ErrorKind::InvalidExpression, offset)),
+ };
+ let is_valid = satisfy_chars_with_pct_encoded(rest, is_ascii_varchar_continue, |_| false);
+ if !is_valid {
+ return Err(Error::new(ErrorKind::InvalidExpression, offset));
+ }
+ Ok(())
+}
+
+/// Returns `Ok(())` if the given string is a valid expression.
+///
+/// "Expression" here is the expression body inside `{` and `}`, but not including braces.
+fn validate_expr_body(s: &str, mut offset: usize) -> Result<(), Error> {
+ if s.is_empty() {
+ return Err(Error::new(ErrorKind::InvalidExpression, offset));
+ }
+
+ // Skip the operator.
+ let maybe_variable_list = match MaybeOperator::from_byte(s.as_bytes()[0]) {
+ Some(MaybeOperator::Operator(_)) => {
+ offset += 1;
+ &s[1..]
+ }
+ Some(MaybeOperator::Reserved(_)) => {
+ return Err(Error::new(ErrorKind::UnsupportedOperator, offset));
+ }
+ None => s,
+ };
+
+ // Validate varspecs.
+ for (spec_i, maybe_varspec) in maybe_variable_list.split(',').enumerate() {
+ if spec_i != 0 {
+ // Add the length of the leading separator `,`.
+ offset += 1;
+ }
+ validate_varspec(maybe_varspec, offset)?;
+ offset += maybe_varspec.len();
+ }
+
+ Ok(())
+}
+
+/// Validates whether the given string is valid as a URI template.
+///
+/// Returns `Ok(())` if the given string is a valid URI template.
+pub(in crate::template) fn validate_template_str(s: &str) -> Result<(), Error> {
+ let mut rest = s;
+ let mut offset = 0;
+ while !rest.is_empty() {
+ rest = match find_split2_hole(rest, b'%', b'{') {
+ Some((literal, b'%', xdigits2_and_rest)) => {
+ validate_literal(literal, offset)?;
+
+ if xdigits2_and_rest.len() < 2 {
+ return Err(Error::new(
+ ErrorKind::InvalidPercentEncoding,
+ offset + literal.len(),
+ ));
+ }
+ let (xdigits2, new_rest) = xdigits2_and_rest.split_at(2);
+ if !xdigits2.as_bytes()[0].is_ascii_hexdigit() {
+ return Err(Error::new(
+ ErrorKind::InvalidPercentEncoding,
+ offset + literal.len() + 1,
+ ));
+ }
+ if !xdigits2.as_bytes()[1].is_ascii_hexdigit() {
+ return Err(Error::new(
+ ErrorKind::InvalidPercentEncoding,
+ offset + literal.len() + 2,
+ ));
+ }
+ new_rest
+ }
+ Some((literal, b'{', expr_and_rest)) => {
+ validate_literal(literal, offset)?;
+
+ let (expr, new_rest) = match find_split_hole(expr_and_rest, b'}') {
+ Some(v) => v,
+ None => {
+ return Err(Error::new(
+ ErrorKind::ExpressionNotClosed,
+ offset + literal.len(),
+ ))
+ }
+ };
+
+ // +1 is `+ "{".len()`.
+ validate_expr_body(expr, offset + literal.len() + 1)?;
+
+ new_rest
+ }
+ Some(_) => unreachable!("[consistency] searching only `%` and `{{`"),
+ None => return validate_literal(rest, offset),
+ };
+ offset = s.len() - rest.len();
+ }
+
+ Ok(())
+}
diff --git a/vendor/iri-string/src/template/simple_context.rs b/vendor/iri-string/src/template/simple_context.rs
new file mode 100644
index 00000000..5c19dc79
--- /dev/null
+++ b/vendor/iri-string/src/template/simple_context.rs
@@ -0,0 +1,218 @@
+//! Simple general-purpose context type.
+
+use core::ops::ControlFlow;
+
+use alloc::collections::BTreeMap;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::string::String;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::vec::Vec;
+
+use crate::template::context::{Context, VarName, Visitor};
+
+/// Value.
+#[derive(Debug, Clone)]
+pub enum Value {
+ /// Undefined (i.e. null).
+ Undefined,
+ /// String value.
+ String(String),
+ /// List.
+ List(Vec<String>),
+ /// Associative array.
+ Assoc(Vec<(String, String)>),
+}
+
+impl From<&str> for Value {
+ #[inline]
+ fn from(v: &str) -> Self {
+ Self::String(v.into())
+ }
+}
+
+impl From<String> for Value {
+ #[inline]
+ fn from(v: String) -> Self {
+ Self::String(v)
+ }
+}
+
+/// Simple template expansion context.
+#[derive(Default, Debug, Clone)]
+pub struct SimpleContext {
+ /// Variable values.
+ // Any map types (including `HashMap`) is ok, but the hash map is not provided by `alloc`.
+ //
+ // QUESTION: Should hexdigits in percent-encoded triplets in varnames be
+ // compared case sensitively?
+ variables: BTreeMap<String, Value>,
+}
+
+impl SimpleContext {
+ /// Creates a new empty context.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// use iri_string::spec::UriSpec;
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::SimpleContext;
+ ///
+ /// let empty_ctx = SimpleContext::new();
+ /// let template = UriTemplateStr::new("{no_such_variable}")?;
+ /// let expanded = template.expand::<UriSpec, _>(&empty_ctx)?;
+ ///
+ /// assert_eq!(
+ /// expanded.to_string(),
+ /// ""
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Inserts a variable.
+ ///
+ /// Passing [`Value::Undefined`] removes the value from the context.
+ ///
+ /// The entry will be inserted or removed even if the key is invalid as a
+ /// variable name. Such entries will be simply ignored on expansion.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// use iri_string::spec::UriSpec;
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::SimpleContext;
+ ///
+ /// let mut context = SimpleContext::new();
+ /// context.insert("username", "foo");
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// let expanded = template.expand::<UriSpec, _>(&context)?;
+ ///
+ /// assert_eq!(
+ /// expanded.to_string(),
+ /// "/users/foo"
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ ///
+ /// Passing [`Value::Undefined`] removes the value from the context.
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// ## [cfg(feature = "alloc")] {
+ /// use iri_string::spec::UriSpec;
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::{SimpleContext, Value};
+ ///
+ /// let mut context = SimpleContext::new();
+ /// context.insert("username", "foo");
+ /// context.insert("username", Value::Undefined);
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// let expanded = template.expand::<UriSpec, _>(&context)?;
+ ///
+ /// assert_eq!(
+ /// expanded.to_string(),
+ /// "/users/"
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ pub fn insert<K, V>(&mut self, key: K, value: V) -> Option<Value>
+ where
+ K: Into<String>,
+ V: Into<Value>,
+ {
+ let key = key.into();
+ match value.into() {
+ Value::Undefined => self.variables.remove(&key),
+ value => self.variables.insert(key, value),
+ }
+ }
+
+ /// Removes all entries in the context.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// use iri_string::spec::UriSpec;
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::SimpleContext;
+ ///
+ /// let template = UriTemplateStr::new("{foo,bar}")?;
+ /// let mut context = SimpleContext::new();
+ ///
+ /// context.insert("foo", "FOO");
+ /// context.insert("bar", "BAR");
+ /// assert_eq!(
+ /// template.expand::<UriSpec, _>(&context)?.to_string(),
+ /// "FOO,BAR"
+ /// );
+ ///
+ /// context.clear();
+ /// assert_eq!(
+ /// template.expand::<UriSpec, _>(&context)?.to_string(),
+ /// ""
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn clear(&mut self) {
+ self.variables.clear();
+ }
+
+ /// Returns a reference to the value for the key.
+ //
+ // QUESTION: Should hexdigits in percent-encoded triplets in varnames be
+ // compared case sensitively?
+ #[inline]
+ #[must_use]
+ pub fn get(&self, key: VarName<'_>) -> Option<&Value> {
+ self.variables.get(key.as_str())
+ }
+}
+
+impl Context for SimpleContext {
+ fn visit<V: Visitor>(&self, visitor: V) -> V::Result {
+ use crate::template::context::{AssocVisitor, ListVisitor};
+
+ let name = visitor.var_name().as_str();
+ match self.variables.get(name) {
+ None | Some(Value::Undefined) => visitor.visit_undefined(),
+ Some(Value::String(s)) => visitor.visit_string(s),
+ Some(Value::List(list)) => {
+ let mut visitor = visitor.visit_list();
+ if let ControlFlow::Break(res) =
+ list.iter().try_for_each(|item| visitor.visit_item(item))
+ {
+ return res;
+ }
+ visitor.finish()
+ }
+ Some(Value::Assoc(list)) => {
+ let mut visitor = visitor.visit_assoc();
+ if let ControlFlow::Break(res) =
+ list.iter().try_for_each(|(k, v)| visitor.visit_entry(k, v))
+ {
+ return res;
+ }
+ visitor.finish()
+ }
+ }
+ }
+}
diff --git a/vendor/iri-string/src/template/string.rs b/vendor/iri-string/src/template/string.rs
new file mode 100644
index 00000000..9ba53a75
--- /dev/null
+++ b/vendor/iri-string/src/template/string.rs
@@ -0,0 +1,647 @@
+//! Template string types.
+
+use core::fmt;
+
+#[cfg(feature = "alloc")]
+use alloc::borrow::Cow;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::boxed::Box;
+#[cfg(feature = "alloc")]
+use alloc::rc::Rc;
+#[cfg(feature = "alloc")]
+use alloc::string::String;
+#[cfg(feature = "alloc")]
+use alloc::sync::Arc;
+
+use crate::spec::Spec;
+use crate::template::components::{VarListIter, VarName};
+use crate::template::context::{Context, DynamicContext};
+use crate::template::error::{Error, ErrorKind};
+use crate::template::expand::{expand_whole_dynamic, Chunk, Chunks, Expanded};
+use crate::template::parser::validate_template_str;
+
+#[cfg(feature = "alloc")]
+pub use self::owned::UriTemplateString;
+
+/// Implements `PartialEq` and `PartialOrd`.
+macro_rules! impl_cmp {
+ ($ty_common:ty, $ty_lhs:ty, $ty_rhs:ty) => {
+ impl PartialEq<$ty_rhs> for $ty_lhs {
+ #[inline]
+ fn eq(&self, o: &$ty_rhs) -> bool {
+ <$ty_common as PartialEq<$ty_common>>::eq(self.as_ref(), o.as_ref())
+ }
+ }
+ impl PartialEq<$ty_lhs> for $ty_rhs {
+ #[inline]
+ fn eq(&self, o: &$ty_lhs) -> bool {
+ <$ty_common as PartialEq<$ty_common>>::eq(self.as_ref(), o.as_ref())
+ }
+ }
+ impl PartialOrd<$ty_rhs> for $ty_lhs {
+ #[inline]
+ fn partial_cmp(&self, o: &$ty_rhs) -> Option<core::cmp::Ordering> {
+ <$ty_common as PartialOrd<$ty_common>>::partial_cmp(self.as_ref(), o.as_ref())
+ }
+ }
+ impl PartialOrd<$ty_lhs> for $ty_rhs {
+ #[inline]
+ fn partial_cmp(&self, o: &$ty_lhs) -> Option<core::cmp::Ordering> {
+ <$ty_common as PartialOrd<$ty_common>>::partial_cmp(self.as_ref(), o.as_ref())
+ }
+ }
+ };
+}
+
+#[cfg(feature = "alloc")]
+mod owned;
+
+/// A borrowed slice of a URI template.
+///
+/// URI Template is defined by [RFC 6570].
+///
+/// Note that "URI Template" can also be used for IRI.
+///
+/// [RFC 6570]: https://www.rfc-editor.org/rfc/rfc6570.html
+///
+/// # Valid values
+///
+/// This type can have a URI template string.
+///
+/// # Applied errata
+///
+/// [Errata ID 6937](https://www.rfc-editor.org/errata/eid6937) is applied, so
+/// single quotes are allowed to appear in an URI template.
+///
+/// ```
+/// # use iri_string::template::Error;
+/// use iri_string::template::UriTemplateStr;
+///
+/// let template = UriTemplateStr::new("'quoted'")?;
+/// # Ok::<_, Error>(())
+/// ```
+#[cfg_attr(feature = "serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "serde", serde(transparent))]
+#[repr(transparent)]
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct UriTemplateStr {
+ /// The raw string.
+ inner: str,
+}
+
+impl UriTemplateStr {
+ /// Creates a new string.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::UriTemplateStr;
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn new(s: &str) -> Result<&Self, Error> {
+ TryFrom::try_from(s)
+ }
+
+ /// Creates a new string without validation.
+ ///
+ /// This does not validate the given string, so it is caller's
+ /// responsibility to ensure the given string is valid.
+ ///
+ /// # Safety
+ ///
+ /// The given string must be syntactically valid as `Self` type.
+ /// If not, any use of the returned value or the call of this
+ /// function itself may result in undefined behavior.
+ #[inline]
+ #[must_use]
+ pub unsafe fn new_unchecked(s: &str) -> &Self {
+ // SAFETY: `new_always_unchecked` requires the same precondition
+ // as `new_always_unchecked`.
+ unsafe { Self::new_always_unchecked(s) }
+ }
+
+ /// Creates a new string without any validation.
+ ///
+ /// This does not validate the given string at any time.
+ ///
+ /// Intended for internal use.
+ ///
+ /// # Safety
+ ///
+ /// The given string must be valid.
+ #[inline]
+ #[must_use]
+ unsafe fn new_always_unchecked(s: &str) -> &Self {
+ // SAFETY: the cast is safe since `Self` type has `repr(transparent)`
+ // attribute and the content is guaranteed as valid by the
+ // precondition of the function.
+ unsafe { &*(s as *const str as *const Self) }
+ }
+
+ /// Returns the template as a plain `&str`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::UriTemplateStr;
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// assert_eq!(template.as_str(), "/users/{username}");
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn as_str(&self) -> &str {
+ self.as_ref()
+ }
+
+ /// Returns the template string length.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::UriTemplateStr;
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// assert_eq!(template.len(), "/users/{username}".len());
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn len(&self) -> usize {
+ self.as_str().len()
+ }
+
+ /// Returns whether the string is empty.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::UriTemplateStr;
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// assert!(!template.is_empty());
+ ///
+ /// let empty = UriTemplateStr::new("")?;
+ /// assert!(empty.is_empty());
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn is_empty(&self) -> bool {
+ self.as_str().is_empty()
+ }
+}
+
+impl UriTemplateStr {
+ /// Expands the template with the given context.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// use iri_string::spec::UriSpec;
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::SimpleContext;
+ ///
+ /// let mut context = SimpleContext::new();
+ /// context.insert("username", "foo");
+ ///
+ /// let template = UriTemplateStr::new("/users/{username}")?;
+ /// let expanded = template.expand::<UriSpec, _>(&context)?;
+ ///
+ /// assert_eq!(
+ /// expanded.to_string(),
+ /// "/users/foo"
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ ///
+ /// You can control allowed characters in the output by changing spec type.
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// use iri_string::spec::{IriSpec, UriSpec};
+ /// use iri_string::template::UriTemplateStr;
+ /// use iri_string::template::simple_context::SimpleContext;
+ ///
+ /// let mut context = SimpleContext::new();
+ /// context.insert("alpha", "\u{03B1}");
+ ///
+ /// let template = UriTemplateStr::new("{?alpha}")?;
+ ///
+ /// assert_eq!(
+ /// template.expand::<UriSpec, _>(&context)?.to_string(),
+ /// "?alpha=%CE%B1",
+ /// "a URI cannot contain Unicode alpha (U+03B1), so it should be escaped"
+ /// );
+ /// assert_eq!(
+ /// template.expand::<IriSpec, _>(&context)?.to_string(),
+ /// "?alpha=\u{03B1}",
+ /// "an IRI can contain Unicode alpha (U+03B1), so it written as is"
+ /// );
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ pub fn expand<'a, S: Spec, C: Context>(
+ &'a self,
+ context: &'a C,
+ ) -> Result<Expanded<'a, S, C>, Error> {
+ Expanded::new(self, context)
+ }
+
+ /// Expands the template with the given dynamic context.
+ ///
+ #[cfg_attr(
+ feature = "alloc",
+ doc = concat!(
+ "If you need the allocated [`String`], use",
+ "[`expand_dynamic_to_string`][`Self::expand_dynamic_to_string`]."
+ )
+ )]
+ ///
+ /// See the documentation for [`DynamicContext`] for usage.
+ pub fn expand_dynamic<S: Spec, W: fmt::Write, C: DynamicContext>(
+ &self,
+ writer: &mut W,
+ context: &mut C,
+ ) -> Result<(), Error> {
+ expand_whole_dynamic::<S, _, _>(self, writer, context)
+ }
+
+ /// Expands the template into a string, with the given dynamic context.
+ ///
+ /// This is basically [`expand_dynamic`][`Self::expand_dynamic`] method
+ /// that returns an owned string instead of writing to the given writer.
+ ///
+ /// See the documentation for [`DynamicContext`] for usage.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #[cfg(feature = "alloc")]
+ /// # extern crate alloc;
+ /// # use iri_string::template::Error;
+ /// # #[cfg(feature = "alloc")] {
+ /// # use alloc::string::String;
+ /// use iri_string::template::UriTemplateStr;
+ /// # use iri_string::template::context::{DynamicContext, Visitor, VisitPurpose};
+ /// use iri_string::spec::UriSpec;
+ ///
+ /// struct MyContext<'a> {
+ /// // See the documentation for `DynamicContext`.
+ /// # /// Target path.
+ /// # target: &'a str,
+ /// # /// Username.
+ /// # username: Option<&'a str>,
+ /// # /// A flag to remember whether the URI template
+ /// # /// attempted to use `username` variable.
+ /// # username_visited: bool,
+ /// }
+ /// #
+ /// # impl DynamicContext for MyContext<'_> {
+ /// # fn on_expansion_start(&mut self) {
+ /// # // Reset the state.
+ /// # self.username_visited = false;
+ /// # }
+ /// # fn visit_dynamic<V: Visitor>(&mut self, visitor: V) -> V::Result {
+ /// # match visitor.var_name().as_str() {
+ /// # "target" => visitor.visit_string(self.target),
+ /// # "username" => {
+ /// # if visitor.purpose() == VisitPurpose::Expand {
+ /// # // The variable `username` is being used
+ /// # // on the template expansion.
+ /// # // Don't care whether `username` is defined or not.
+ /// # self.username_visited = true;
+ /// # }
+ /// # if let Some(username) = &self.username {
+ /// # visitor.visit_string(username)
+ /// # } else {
+ /// # visitor.visit_undefined()
+ /// # }
+ /// # }
+ /// # _ => visitor.visit_undefined(),
+ /// # }
+ /// # }
+ /// # }
+ ///
+ /// let mut context = MyContext {
+ /// target: "/posts/1",
+ /// username: Some("the_admin"),
+ /// username_visited: false,
+ /// };
+ ///
+ /// // No access to the variable `username`.
+ /// let template = UriTemplateStr::new("{+target}{?username}")?;
+ /// let s = template.expand_dynamic_to_string::<UriSpec, _>(&mut context)?;
+ /// assert_eq!(s, "/posts/1?username=the_admin");
+ /// assert!(context.username_visited);
+ /// # }
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[cfg(feature = "alloc")]
+ pub fn expand_dynamic_to_string<S: Spec, C: DynamicContext>(
+ &self,
+ context: &mut C,
+ ) -> Result<String, Error> {
+ let mut buf = String::new();
+ expand_whole_dynamic::<S, _, _>(self, &mut buf, context)?;
+ Ok(buf)
+ }
+
+ /// Returns an iterator of variables in the template.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use iri_string::template::Error;
+ /// use iri_string::template::UriTemplateStr;
+ ///
+ /// let template = UriTemplateStr::new("foo{/bar*,baz:4}{?qux}{&bar*}")?;
+ /// let mut vars = template.variables();
+ /// assert_eq!(vars.next().map(|var| var.as_str()), Some("bar"));
+ /// assert_eq!(vars.next().map(|var| var.as_str()), Some("baz"));
+ /// assert_eq!(vars.next().map(|var| var.as_str()), Some("qux"));
+ /// assert_eq!(vars.next().map(|var| var.as_str()), Some("bar"));
+ /// # Ok::<_, Error>(())
+ /// ```
+ #[inline]
+ #[must_use]
+ pub fn variables(&self) -> UriTemplateVariables<'_> {
+ UriTemplateVariables::new(self)
+ }
+}
+
+impl fmt::Debug for UriTemplateStr {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_tuple("UriTemplateStr").field(&&self.inner).finish()
+ }
+}
+
+impl AsRef<str> for UriTemplateStr {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ &self.inner
+ }
+}
+
+impl AsRef<UriTemplateStr> for UriTemplateStr {
+ #[inline]
+ fn as_ref(&self) -> &UriTemplateStr {
+ self
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl<'a> From<&'a UriTemplateStr> for Cow<'a, UriTemplateStr> {
+ #[inline]
+ fn from(s: &'a UriTemplateStr) -> Self {
+ Cow::Borrowed(s)
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl From<&UriTemplateStr> for Arc<UriTemplateStr> {
+ fn from(s: &UriTemplateStr) -> Self {
+ let inner: &str = s.as_str();
+ let buf = Arc::<str>::from(inner);
+ // SAFETY: `UriTemplateStr` has `repr(transparent)` attribute, so
+ // the memory layouts of `Arc<str>` and `Arc<UriTemplateStr>` are
+ // compatible.
+ unsafe {
+ let raw: *const str = Arc::into_raw(buf);
+ Self::from_raw(raw as *const UriTemplateStr)
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl From<&UriTemplateStr> for Box<UriTemplateStr> {
+ fn from(s: &UriTemplateStr) -> Self {
+ let inner: &str = s.as_str();
+ let buf = Box::<str>::from(inner);
+ // SAFETY: `UriTemplateStr` has `repr(transparent)` attribute, so
+ // the memory layouts of `Box<str>` and `Box<UriTemplateStr>` are
+ // compatible.
+ unsafe {
+ let raw: *mut str = Box::into_raw(buf);
+ Self::from_raw(raw as *mut UriTemplateStr)
+ }
+ }
+}
+
+#[cfg(feature = "alloc")]
+impl From<&UriTemplateStr> for Rc<UriTemplateStr> {
+ fn from(s: &UriTemplateStr) -> Self {
+ let inner: &str = s.as_str();
+ let buf = Rc::<str>::from(inner);
+ // SAFETY: `UriTemplateStr` has `repr(transparent)` attribute, so
+ // the memory layouts of `Rc<str>` and `Rc<UriTemplateStr>` are
+ // compatible.
+ unsafe {
+ let raw: *const str = Rc::into_raw(buf);
+ Self::from_raw(raw as *const UriTemplateStr)
+ }
+ }
+}
+
+impl<'a> From<&'a UriTemplateStr> for &'a str {
+ #[inline]
+ fn from(s: &'a UriTemplateStr) -> &'a str {
+ s.as_ref()
+ }
+}
+
+impl<'a> TryFrom<&'a str> for &'a UriTemplateStr {
+ type Error = Error;
+
+ #[inline]
+ fn try_from(s: &'a str) -> Result<Self, Self::Error> {
+ match validate_template_str(s) {
+ // SAFETY: just checked the string is valid.
+ Ok(()) => Ok(unsafe { UriTemplateStr::new_always_unchecked(s) }),
+ Err(e) => Err(e),
+ }
+ }
+}
+
+impl<'a> TryFrom<&'a [u8]> for &'a UriTemplateStr {
+ type Error = Error;
+
+ #[inline]
+ fn try_from(bytes: &'a [u8]) -> Result<Self, Self::Error> {
+ let s = core::str::from_utf8(bytes)
+ .map_err(|e| Error::new(ErrorKind::InvalidUtf8, e.valid_up_to()))?;
+ match validate_template_str(s) {
+ // SAFETY: just checked the string is valid.
+ Ok(()) => Ok(unsafe { UriTemplateStr::new_always_unchecked(s) }),
+ Err(e) => Err(e),
+ }
+ }
+}
+
+impl_cmp!(str, str, UriTemplateStr);
+impl_cmp!(str, &str, UriTemplateStr);
+impl_cmp!(str, str, &UriTemplateStr);
+
+impl fmt::Display for UriTemplateStr {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+/// Serde deserializer implementation.
+#[cfg(feature = "serde")]
+mod __serde_slice {
+ use super::UriTemplateStr;
+
+ use core::fmt;
+
+ use serde::{
+ de::{self, Visitor},
+ Deserialize, Deserializer,
+ };
+
+ /// Custom borrowed string visitor.
+ #[derive(Debug, Clone, Copy)]
+ struct CustomStrVisitor;
+
+ impl<'de> Visitor<'de> for CustomStrVisitor {
+ type Value = &'de UriTemplateStr;
+
+ #[inline]
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("URI template string")
+ }
+
+ #[inline]
+ fn visit_borrowed_str<E>(self, v: &'de str) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ <&'de UriTemplateStr as TryFrom<&'de str>>::try_from(v).map_err(E::custom)
+ }
+ }
+
+ // About `'de` and `'a`, see
+ // <https://serde.rs/lifetimes.html#the-deserializede-lifetime>.
+ impl<'a, 'de: 'a> Deserialize<'de> for &'a UriTemplateStr {
+ #[inline]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_string(CustomStrVisitor)
+ }
+ }
+}
+
+/// An iterator of variables in a URI template.
+#[derive(Debug, Clone)]
+pub struct UriTemplateVariables<'a> {
+ /// Chunks iterator.
+ chunks: Chunks<'a>,
+ /// Variables in the last chunk.
+ vars_in_chunk: Option<VarListIter<'a>>,
+}
+
+impl<'a> UriTemplateVariables<'a> {
+ /// Creates a variables iterator from the URI template.
+ #[inline]
+ #[must_use]
+ fn new(template: &'a UriTemplateStr) -> Self {
+ Self {
+ chunks: Chunks::new(template),
+ vars_in_chunk: None,
+ }
+ }
+}
+
+impl<'a> Iterator for UriTemplateVariables<'a> {
+ type Item = VarName<'a>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ loop {
+ if let Some(vars) = &mut self.vars_in_chunk {
+ match vars.next() {
+ Some((_len, spec)) => return Some(spec.name()),
+ None => self.vars_in_chunk = None,
+ }
+ }
+ let expr = self.chunks.find_map(|chunk| match chunk {
+ Chunk::Literal(_) => None,
+ Chunk::Expr(v) => Some(v),
+ });
+ self.vars_in_chunk = match expr {
+ Some(expr) => Some(expr.decompose().1.into_iter()),
+ None => return None,
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ use crate::spec::IriSpec;
+ use crate::template::context::{AssocVisitor, ListVisitor, Visitor};
+
+ struct TestContext;
+ impl Context for TestContext {
+ fn visit<V: Visitor>(&self, visitor: V) -> V::Result {
+ match visitor.var_name().as_str() {
+ "str" => visitor.visit_string("string"),
+ "list" => visitor
+ .visit_list()
+ .visit_items_and_finish(["item0", "item1", "item2"]),
+ "assoc" => visitor
+ .visit_assoc()
+ .visit_entries_and_finish([("key0", "value0"), ("key1", "value1")]),
+ _ => visitor.visit_undefined(),
+ }
+ }
+ }
+
+ #[test]
+ fn expand_error_pos() {
+ {
+ let e = UriTemplateStr::new("foo{list:4}")
+ .unwrap()
+ .expand::<IriSpec, _>(&TestContext)
+ .err()
+ .map(|e| e.location());
+ assert_eq!(e, Some("foo{".len()));
+ }
+
+ {
+ let e = UriTemplateStr::new("foo{/list*,list:4}")
+ .unwrap()
+ .expand::<IriSpec, _>(&TestContext)
+ .err()
+ .map(|e| e.location());
+ assert_eq!(e, Some("foo{/list*,".len()));
+ }
+
+ {
+ let e = UriTemplateStr::new("foo{/str:3,list*,assoc:4}")
+ .unwrap()
+ .expand::<IriSpec, _>(&TestContext)
+ .err()
+ .map(|e| e.location());
+ assert_eq!(e, Some("foo{/str:3,list*,".len()));
+ }
+ }
+}
diff --git a/vendor/iri-string/src/template/string/owned.rs b/vendor/iri-string/src/template/string/owned.rs
new file mode 100644
index 00000000..afd201b3
--- /dev/null
+++ b/vendor/iri-string/src/template/string/owned.rs
@@ -0,0 +1,296 @@
+//! Owned `UriTemplateString`.
+
+use core::fmt;
+
+use alloc::borrow::Cow;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::borrow::ToOwned;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::boxed::Box;
+#[cfg(all(feature = "alloc", not(feature = "std")))]
+use alloc::string::String;
+
+use crate::template::error::{CreationError, Error, ErrorKind};
+use crate::template::parser::validate_template_str;
+use crate::template::string::UriTemplateStr;
+
+/// An owned slice of a URI template.
+///
+/// URI Template is defined by [RFC 6570].
+///
+/// Note that "URI Template" can also be used for IRI.
+///
+/// [RFC 6570]: https://www.rfc-editor.org/rfc/rfc6570.html
+///
+/// # Valid values
+///
+/// This type can have a URI template string.
+// Note that `From<$ty> for {Arc,Rc}<$slice>` is currently not implemented since
+// this won't reuse allocated memory and hides internal memory reallocation. See
+// <https://github.com/lo48576/iri-string/issues/20#issuecomment-1105207849>.
+// However, this is not decided with firm belief or opinion, so there would be
+// a chance that they are implemented in future.
+#[cfg_attr(feature = "serde", derive(serde::Serialize))]
+#[cfg_attr(feature = "serde", serde(transparent))]
+#[derive(Default, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct UriTemplateString {
+ /// Inner data.
+ inner: String,
+}
+
+impl UriTemplateString {
+ /// Creates a new string without validation.
+ ///
+ /// This does not validate the given string, so it is caller's
+ /// responsibility to ensure the given string is valid.
+ ///
+ /// # Safety
+ ///
+ /// The given string must be syntactically valid as `Self` type.
+ /// If not, any use of the returned value or the call of this
+ /// function itself may result in undefined behavior.
+ #[inline]
+ #[must_use]
+ pub unsafe fn new_unchecked(s: alloc::string::String) -> Self {
+ // The construction itself can be written in safe Rust, but
+ // every other place including unsafe functions expects
+ // `self.inner` to be syntactically valid as `Self`. In order to
+ // make them safe, the construction should validate the value
+ // or at least should require users to validate the value by
+ // making the function `unsafe`.
+ Self { inner: s }
+ }
+
+ /// Shrinks the capacity of the inner buffer to match its length.
+ #[inline]
+ pub fn shrink_to_fit(&mut self) {
+ self.inner.shrink_to_fit()
+ }
+
+ /// Returns the internal buffer capacity in bytes.
+ #[inline]
+ #[must_use]
+ pub fn capacity(&self) -> usize {
+ self.inner.capacity()
+ }
+
+ /// Returns the borrowed IRI string slice.
+ ///
+ /// This is equivalent to `&*self`.
+ #[inline]
+ #[must_use]
+ pub fn as_slice(&self) -> &UriTemplateStr {
+ self.as_ref()
+ }
+
+ /// Appends the template string.
+ #[inline]
+ pub fn append(&mut self, other: &UriTemplateStr) {
+ self.inner.push_str(other.as_str());
+ debug_assert!(validate_template_str(self.as_str()).is_ok());
+ }
+}
+
+impl AsRef<str> for UriTemplateString {
+ #[inline]
+ fn as_ref(&self) -> &str {
+ &self.inner
+ }
+}
+
+impl AsRef<UriTemplateStr> for UriTemplateString {
+ #[inline]
+ fn as_ref(&self) -> &UriTemplateStr {
+ // SAFETY: `UriTemplateString and `UriTemplateStr` requires same validation,
+ // so the content of `self: &UriTemplateString` must be valid as `UriTemplateStr`.
+ unsafe { UriTemplateStr::new_always_unchecked(AsRef::<str>::as_ref(self)) }
+ }
+}
+
+impl core::borrow::Borrow<str> for UriTemplateString {
+ #[inline]
+ fn borrow(&self) -> &str {
+ self.as_ref()
+ }
+}
+
+impl core::borrow::Borrow<UriTemplateStr> for UriTemplateString {
+ #[inline]
+ fn borrow(&self) -> &UriTemplateStr {
+ self.as_ref()
+ }
+}
+
+impl ToOwned for UriTemplateStr {
+ type Owned = UriTemplateString;
+
+ #[inline]
+ fn to_owned(&self) -> Self::Owned {
+ self.into()
+ }
+}
+
+impl From<&'_ UriTemplateStr> for UriTemplateString {
+ #[inline]
+ fn from(s: &UriTemplateStr) -> Self {
+ // This is safe because `s` must be valid.
+ Self {
+ inner: alloc::string::String::from(s.as_str()),
+ }
+ }
+}
+
+impl From<UriTemplateString> for alloc::string::String {
+ #[inline]
+ fn from(s: UriTemplateString) -> Self {
+ s.inner
+ }
+}
+
+impl<'a> From<UriTemplateString> for Cow<'a, UriTemplateStr> {
+ #[inline]
+ fn from(s: UriTemplateString) -> Cow<'a, UriTemplateStr> {
+ Cow::Owned(s)
+ }
+}
+
+impl From<UriTemplateString> for Box<UriTemplateStr> {
+ #[inline]
+ fn from(s: UriTemplateString) -> Box<UriTemplateStr> {
+ let inner: String = s.into();
+ let buf = Box::<str>::from(inner);
+ // SAFETY: `UriTemplateStr` has `repr(transparent)` attribute, so
+ // the memory layouts of `Box<str>` and `Box<UriTemplateStr>` are
+ // compatible. Additionally, `UriTemplateString` and `UriTemplateStr`
+ // require the same syntax.
+ unsafe {
+ let raw: *mut str = Box::into_raw(buf);
+ Box::<UriTemplateStr>::from_raw(raw as *mut UriTemplateStr)
+ }
+ }
+}
+
+impl TryFrom<&'_ str> for UriTemplateString {
+ type Error = Error;
+
+ #[inline]
+ fn try_from(s: &str) -> Result<Self, Self::Error> {
+ <&UriTemplateStr>::try_from(s).map(Into::into)
+ }
+}
+
+impl TryFrom<&'_ [u8]> for UriTemplateString {
+ type Error = Error;
+
+ #[inline]
+ fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
+ let s = core::str::from_utf8(bytes)
+ .map_err(|e| Error::new(ErrorKind::InvalidUtf8, e.valid_up_to()))?;
+ <&UriTemplateStr>::try_from(s).map(Into::into)
+ }
+}
+
+impl core::convert::TryFrom<alloc::string::String> for UriTemplateString {
+ type Error = CreationError<String>;
+
+ #[inline]
+ fn try_from(s: alloc::string::String) -> Result<Self, Self::Error> {
+ match <&UriTemplateStr>::try_from(s.as_str()) {
+ Ok(_) => {
+ // This is safe because `<&UriTemplateStr>::try_from(s)?` ensures
+ // that the string `s` is valid.
+ Ok(Self { inner: s })
+ }
+ Err(e) => Err(CreationError::new(e, s)),
+ }
+ }
+}
+
+impl alloc::str::FromStr for UriTemplateString {
+ type Err = Error;
+
+ #[inline]
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ TryFrom::try_from(s)
+ }
+}
+
+impl core::ops::Deref for UriTemplateString {
+ type Target = UriTemplateStr;
+
+ #[inline]
+ fn deref(&self) -> &UriTemplateStr {
+ self.as_ref()
+ }
+}
+
+impl_cmp!(str, UriTemplateStr, Cow<'_, str>);
+impl_cmp!(str, &UriTemplateStr, Cow<'_, str>);
+
+impl_cmp!(str, str, UriTemplateString);
+impl_cmp!(str, &str, UriTemplateString);
+impl_cmp!(str, Cow<'_, str>, UriTemplateString);
+impl_cmp!(str, String, UriTemplateString);
+
+impl fmt::Display for UriTemplateString {
+ #[inline]
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str(self.as_str())
+ }
+}
+
+/// Serde deserializer implementation.
+#[cfg(feature = "serde")]
+mod __serde_owned {
+ use super::UriTemplateString;
+
+ use core::fmt;
+
+ #[cfg(all(feature = "alloc", feature = "serde", not(feature = "std")))]
+ use alloc::string::String;
+
+ use serde::{
+ de::{self, Visitor},
+ Deserialize, Deserializer,
+ };
+
+ /// Custom owned string visitor.
+ #[derive(Debug, Clone, Copy)]
+ struct CustomStringVisitor;
+
+ impl Visitor<'_> for CustomStringVisitor {
+ type Value = UriTemplateString;
+
+ #[inline]
+ fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("URI template string")
+ }
+
+ #[inline]
+ fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ <UriTemplateString as TryFrom<&str>>::try_from(v).map_err(E::custom)
+ }
+
+ #[cfg(feature = "serde")]
+ #[inline]
+ fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ <UriTemplateString as TryFrom<String>>::try_from(v).map_err(E::custom)
+ }
+ }
+
+ impl<'de> Deserialize<'de> for UriTemplateString {
+ #[inline]
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ deserializer.deserialize_str(CustomStringVisitor)
+ }
+ }
+}