A parser for the google.api.http path-template grammar.
A path template is the pattern that appears in a google.api.http
annotation, for example /shelves/{shelf}/books/{book=**}:archive. This crate
turns such a string into a validated, structured PathTemplate — an
abstract syntax tree of Segments (literals, *, **, and
{field.path=sub-template} Variable bindings) plus an optional custom
:verb.
Parsing is zero-copy: the returned PathTemplate borrows from the input
string (every literal, field name, and verb is a slice into it), so a parse
copies no text and allocates only the top-level segment list.
A template must begin with / and is a /-separated sequence of segments —
each / delimits one segment. Literal segments, variable field names, and the
custom :verb are preserved and compared verbatim, so the grammar is
case-sensitive; the parser performs no case folding.
The grammar mirrors the reference google.api.HttpRule path syntax:
- a literal segment (
shelves) must match verbatim and contain only RFC 3986pcharcharacters, with valid%HHescapes; raw*is reserved for the wildcard atoms below and must be percent-encoded in a literal; *(Segment::Single) matches exactly one non-empty segment;**(Segment::Rest) matches the remaining segments and may only appear as the final element;{field.path=sub-template}(Segment::Variable) captures the portion of the path matched by its sub-template into a dotted message field; the shorthand{field}is{field=*}and nested variables are rejected;- a trailing
:verbdeclares a custom method verb.
PathTemplate::parse takes a Grammar argument. The default grammar is
the strict google.api.http syntax above; passing a Grammar with
Grammar::with_segment_affixes enabled additionally allows intra-segment
prefix/suffix parameters: a single segment may wrap one {field.path}
variable in literal text, for example /files/{name}.json, /v{version}/x,
or /img-{id}.png. Such a segment parses to a Segment::Affix. The strict
grammar rejects this syntax.
Parsing /shelves/{shelf}/books/{book=**}:archive yields four top-level
Segments plus the custom verb archive:
shelves— aSegment::Literal;{shelf}— aSegment::Variablebinding fieldshelfto a single segment (*, i.e.Segment::Single);books— aSegment::Literal;{book=**}— aSegment::Variablebinding fieldbookto the remaining segments (**, i.e.Segment::Rest).
use http_path_template::{Grammar, PathTemplate, Segment};
let template = PathTemplate::parse(
"/shelves/{shelf}/books/{book=**}:archive",
Grammar::default(),
)?;
assert_eq!(template.segments().len(), 4);
assert_eq!(template.verb(), Some("archive"));
assert_eq!(template.segments()[0], Segment::Literal("shelves"));
assert_eq!(template.segments()[2], Segment::Literal("books"));
let Segment::Variable(shelf) = template.segments()[1] else {
panic!("expected variable")
};
assert_eq!(shelf.field_path(), "shelf");
assert!(shelf.segments().eq([Segment::Single]));
let Segment::Variable(book) = template.segments()[3] else {
panic!("expected variable")
};
assert_eq!(book.field_path(), "book");
assert!(book.segments().eq([Segment::Rest]));std(default) — captures astd::backtrace::Backtraceinto aParseErrorfor richer diagnostics. Disable (default-features = false) for#![no_std]use; the crate then requires onlyallocand captures no backtrace.ParseErrorimplementscore::error::Erroreither way.
This crate was developed as part of The Oxidizer Project. Browse this crate's source code.