An exact matcher for the RFC 6570 URI template shapes that can be matched without ambiguity.
Snodo.Resource compiles a template at build time and generates
matches?/1 from the result. A template outside the supported shapes is a
compile error naming the shape, unless the module implements matches?/1
itself. This module matches URIs against templates; it does not expand them.
Supported shapes
The scheme is a literal, matched case-insensitively.
The authority is one literal, one
{var}, or empty when a literal/follows it, as infile:///{+path}. An empty authority matches only an empty host.Each path segment is one literal or one
{var}. A{var}binds exactly one whole, non-empty segment.At most one variable-length path expression, anywhere in the path:
/{+var}(reserved expansion) binds one or more whole segments, joined with/;{/var}(path segment expansion) binds zero or one segment;{/var*}(exploded path segments) binds zero or more segments, joined with/.
The literal and
{var}segments before and after it are matched from each end, so there is never more than one way to split a URI.Query expansion at the end of the template: one
{?a,b}followed by any number of{&c,d}. Each named parameter may appear at most once, in any order, and may be absent. A parameter that the template does not name, a repeated parameter, a pair without=, or an empty query does not match.q=binds"".
A variable that is not bound, an absent {/var}, {/var*}, or query
parameter, is left out of the returned map, as RFC 6570 leaves undefined
variables out of an expansion.
Rejected shapes
compile/1 returns {:error, reason}, and the reason names the shape, for:
fragment expansion {#var}, label expansion {.var}, path-style parameters
{;var}, the reserved operators = , ! @ |, prefix modifiers {var:n},
explode on simple, reserved, or query variables, more than one variable in a
simple, reserved, or path expression ({a,b}, {/a,b}: a missing value
cannot be assigned to one variable), more than one variable-length path
expression, an expression that shares a path segment with literal text or
another expression (v{version}, {name}.json), any expression in the
scheme, a reserved expression in the authority, an empty authority not
followed by /, a {/var} straight after a literal /, a query expression
that is not at the end, a literal query, fragment, port, or userinfo, empty
segments,
variables named uri or _meta (the request owns those keys), and
malformed literals.
Values
Matched values are percent-decoded once and must be valid UTF-8. Malformed
percent escapes and empty path segments do not match. + is not decoded as
a space. Repeated variables must bind the same decoded value. Literal
authority and path segments match exactly, without percent-decoding or
slash normalization, and dot segments (. and ..) are not removed.
A decoded value can therefore contain / (from %2F, or from the segments
that {+var} and {/var*} join), can be .. or contain ../, and can
contain a NUL byte (from %00). An application that maps a value to a file
must treat it as untrusted: reject values containing NUL, and resolve the
rest with Path.safe_relative/2 against the directory it serves (or reject
values containing /, \, or .. segments) before touching the file
system.
Limits
A template is at most 1,024 bytes with at most 32 variables, checked
when it compiles. Matching is linear in the length of the URI: the URI is
parsed once, split once on /, and each fixed segment and query pair is
examined once, with no backtracking. The span a variable-length expression
binds is checked and decoded as one binary, not segment by segment. A query with more pairs than the template names
is refused before the rest is read. The length of the URI is bounded by the
transport's message size limit.
Summary
Functions
Compiles a URI template, or reports why it is outside the supported shapes.
Matches a concrete URI, returning the bound variables.
Returns the variable names a compiled template binds, in template order.
Types
Functions
Compiles a URI template, or reports why it is outside the supported shapes.
URI.new/1 rejects the braces, so the template is parsed textually. Its
literals are checked as a concrete URI with safe placeholders for variables,
so an invalid literal cannot produce an unreachable generated matcher.
Matches a concrete URI, returning the bound variables.
Returns the variable names a compiled template binds, in template order.