std/uri/route
std/uri/src/route.trb
TemplateCase, TemplateRoute and TemplateRoutes: URI templates typed against the cases of one type, which is
what a router of std/web is built from (docs/design/WEB.md, decision D5; docs/design/URI.md section 9a).
The compiler checks a route where it is written: in route("/orders/{id}", to: Route.Order) the template literal
gives the names and the case gives the fields, and a variable that is no field of the case, or a field without a
default that is no variable, is an error at the literal. TemplateRoutes.of checks the same when the program
starts, for routes made at run time, and it is what says that a case has no route.
type TemplateCase
type TemplateCase<Value> with Show, Equals, Hash
One case of Value, named where it is written: Route.Order where a TemplateCase<Route> is expected is the case
Order, and not the function that constructs one (docs/design/URI.md section 9a). It is what a route pairs its
template with.
Examples
type Route {
case Home
case Order(id: Int)
}
const order: TemplateCase<Route> = Route.Order
print order.name()
fn named
static fn named(name: String): TemplateCase<Value>
The case of Value called name, for a route made at run time. Nothing checks here that Value has such a case;
TemplateRoutes.of does.
fn name
fn name(): String
The name of the case.
fn show
fn show(): String
Not documented.
type TemplateRoute
type TemplateRoute<Value> with Show
A template and the case of Value its variables fill: one line of a router.
field template
template: UriTemplate<Value>
The template, typed against Value.
field target
target: TemplateCase<Value>
The case a matched reference becomes, and the case whose values TemplateRoutes.link expands.
fn show
fn show(): String
Not documented.
fn route
fn route<Value>(template: UriTemplate<Value>, to: TemplateCase<Value>): TemplateRoute<Value>
The route of one case: route("/orders/{id}", to: Route.Order). Written with a literal and a case, it is checked
where it is written: every variable is a field of the case, every field without a default is a variable, and a field
with a default is at most a query variable ({?page}), which a request may leave out.
Examples
type Route {
case Home
case Order(id: Int)
}
const routes = TemplateRoutes.of([
route("/", to: Route.Home),
route("/orders/{id}", to: Route.Order),
])?
type TemplateRoutes
type TemplateRoutes<Value> with Show
Every route of Value, checked to cover each of its cases: a reference is matched against them in order and read
into the case of the first that matches, and a value is linked through the route of its own case. Both directions
come from the one template, so they cannot disagree.
Examples
type Route {
case Home
case Order(id: Int)
}
const routes = TemplateRoutes.of([
route("/", to: Route.Home),
route("/orders/{id}", to: Route.Order),
])?
const order: UriReference = "/orders/7"
print routes.matched(order)
print routes.link(Route.Order(7))
Pitfalls
- The order matters where two templates match one reference: the first route wins.
fn of
static fn of(routes: List<TemplateRoute<Value>>): Result<TemplateRoutes<Value>, UriError> where Value: Describe
The routes, once every case of Value has one and every route fits its case: its case exists, every variable is
a field of it, every field without a default is a variable, and a field with a default is at most a query
variable. This is the check a program makes when it starts, and the one that finds a case without a route.
Errors
UriError.InvalidRoutesnaming every problem, one per line.
fn matched
fn matched(reference: UriReference): Value? where Value: Decode
The case the first matching route reads reference into, or None where no route matches it.
fn link
fn link(value: Value): Result<UriReference, UriError> where Value: Encode
The reference of value: the template of the first route of its case, expanded with its fields.
Errors
UriError.InvalidRoutesfor a value that is no case, which aValueTemplateRoutes.ofaccepted never is.- As for
UriTemplate.expanded.
fn show
fn show(): String
Not documented.