Skip to content

Commit

Permalink
Rename path-related extractors.
Browse files Browse the repository at this point in the history
  • Loading branch information
LukeMathWalker committed Jan 3, 2024
1 parent 2bbde9e commit 03a2233
Show file tree
Hide file tree
Showing 90 changed files with 517 additions and 518 deletions.
16 changes: 8 additions & 8 deletions docs/getting_started/quickstart/dependency_injection.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@ it knows how to construct them.

## Constructor registration

Let's zoom in on [`RouteParams`][RouteParams]: how does the framework know how to construct it?
Let's zoom in on [`PathParams`][PathParams]: how does the framework know how to construct it?
You need to go back to the [`Blueprint`][Blueprint] to find out:

--8<-- "doc_examples/quickstart/04-register_common_invocation.snap"

The `register_common_constructors` function takes care of registering constructors for a set of types that
are defined in the `pavex` crate itself and commonly used in Pavex applications.
If you check out its definition, you'll see that it registers a constructor for [`RouteParams`][RouteParams]:
If you check out its definition, you'll see that it registers a constructor for [`PathParams`][PathParams]:

--8<-- "doc_examples/quickstart/04-route_params_constructor.snap"

Inside [`RouteParams::register`][RouteParams::register] you'll find:
Inside [`PathParams::register`][PathParams::register] you'll find:

```rust
use crate::blueprint::constructor::{Constructor, Lifecycle};
use crate::blueprint::Blueprint;
use crate::f;

impl RouteParams<()> {
impl PathParams<()> {
pub fn register(bp: &mut Blueprint) -> Constructor {
bp.constructor(
f!(pavex::request::route::RouteParams::extract),
f!(pavex::request::path::PathParams::extract),
Lifecycle::RequestScoped,
)
.error_handler(f!(
pavex::request::route::errors::ExtractRouteParamsError::into_response
pavex::request::path::errors::ExtractPathParamsError::into_response
))
}
}
Expand Down Expand Up @@ -107,8 +107,8 @@ Make sure that the project compiles successfully now.

[Blueprint]: ../../api_reference/pavex/blueprint/struct.Blueprint.html

[RouteParams]: ../../api_reference/pavex/request/route/struct.RouteParams.html
[RouteParams::register]: ../../api_reference/pavex/request/route/struct.RouteParams.html#method.register
[PathParams]: ../../api_reference/pavex/request/route/struct.PathParams.html
[PathParams::register]: ../../api_reference/pavex/request/route/struct.PathParams.html#method.register

[Lifecycle::Singleton]: ../../api_reference/pavex/blueprint/constructor/enum.Lifecycle.html#variant.Singleton

Expand Down
2 changes: 1 addition & 1 deletion docs/getting_started/quickstart/going_further.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ need-to-know basis.

[IntoResponse]: ../../api_reference/pavex/response/trait.IntoResponse.html

[RouteParams]: ../../api_reference/pavex/request/route/struct.RouteParams.html
[PathParams]: ../../api_reference/pavex/request/route/struct.PathParams.html

[Response::ok]: ../../api_reference/pavex/response/struct.Response.html#method.ok

Expand Down
10 changes: 5 additions & 5 deletions docs/getting_started/quickstart/routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ Let's register the new route with the [`Blueprint`][Blueprint] in the meantime:

--8<-- "doc_examples/quickstart/02-register_new_route.snap"

1. Dynamic route parameters are prefixed with a colon (`:`).
1. Dynamic path parameters are prefixed with a colon (`:`).

## Extract route parameters
## Extract path parameters

To access the `name` route parameter from your new handler you must use the [`RouteParams`][RouteParams] extractor:
To access the `name` route parameter from your new handler you must use the [`PathParams`][PathParams] extractor:


--8<-- "doc_examples/quickstart/03-route_def.snap"

1. The name of the field must match the name of the route parameter as it appears in the path we registered with
the [`Blueprint`][Blueprint].
2. The [`RouteParams`][RouteParams] extractor is generic over the type of the route parameters.
2. The [`PathParams`][PathParams] extractor is generic over the type of the path parameters.
In this case, we're using the `GreetParams` type we just defined.

You can now return the expected response from the `greet` handler:
Expand Down Expand Up @@ -92,7 +92,7 @@ You should see `Hello, Ursula!` in your terminal if everything went well.

[IntoResponse]: ../../api_reference/pavex/response/trait.IntoResponse.html

[RouteParams]: ../../api_reference/pavex/request/route/struct.RouteParams.html
[PathParams]: ../../api_reference/pavex/request/route/struct.PathParams.html

[Response::ok]: ../../api_reference/pavex/response/struct.Response.html#method.ok

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Let's look at a few common scenarios to build some intuition around lifecycles:
|--------------------------|-------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Database connection pool | [Singleton][Lifecycle::Singleton] | The entire application should use the same pool. <br/>Each request will fetch a connection from the pool when needed. |
| HTTP client | [Singleton][Lifecycle::Singleton] | Most HTTP clients keep, under the hood, a connection pool. <br/>You want to reuse those connections across requests to minimise latency and the number of open file descriptors. |
| Route parameters | [RequestScoped][Lifecycle::RequestScoped] | Route parameters are extracted from the incoming request. <br/> They must not be shared across requests, therefore they can't be a [`Singleton`][Lifecycle::Singleton].<br/> They could be [`Transient`][Lifecycle::Transient], but re-parsing the parameters before every use would be expensive.<br/>[`RequestScoped`][Lifecycle::RequestScoped] is the optimal choice. |
| Path parameters | [RequestScoped][Lifecycle::RequestScoped] | Path parameters are extracted from the incoming request. <br/> They must not be shared across requests, therefore they can't be a [`Singleton`][Lifecycle::Singleton].<br/> They could be [`Transient`][Lifecycle::Transient], but re-parsing the parameters before every use would be expensive.<br/>[`RequestScoped`][Lifecycle::RequestScoped] is the optimal choice. |
| Database connection | [Transient][Lifecycle::Transient] | The connection is retrieved from a shared pool.<br/>It could be [`RequestScoped`][Lifecycle::RequestScoped], but you might end up keeping the connection booked (i.e. outside of the pool) for longer than it's strictly necessary.<br/>[`Transient`][Lifecycle::Transient] is the optimal choice: you only remove the connection from the pool when it's needed, put it back when idle. | |

## Recursive dependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ The framework primitives are:

- [`RequestHead`][RequestHead]. The incoming request data, minus the body.
- [`RawIncomingBody`][RawIncomingBody]. The raw body of the incoming request.
- [`RouteParams`][RouteParams]. The route parameters extracted from the incoming request.
- [`PathParams`][PathParams]. The path parameters extracted from the incoming request.
- [`AllowedMethods`][AllowedMethods]. The HTTP methods allowed for the current request path.

They represent raw data from the incoming request ([`RequestHead`][RequestHead], [`RawIncomingBody`][RawIncomingBody])
or information coming from the routing system ([`AllowedMethods`][AllowedMethods], [`RouteParams`][RouteParams]).
or information coming from the routing system ([`AllowedMethods`][AllowedMethods], [`PathParams`][PathParams]).

## Convenient, but inflexible

Expand All @@ -26,7 +26,7 @@ You lose this flexibility with framework primitives: you can't customize how the
That's why we try to keep their number to a minimum.

[RequestHead]: ../../../api_reference/pavex/request/struct.RequestHead.html
[RouteParams]: ../../../api_reference/pavex/request/route/struct.RouteParams.html
[PathParams]: ../../../api_reference/pavex/request/route/struct.PathParams.html
[AllowedMethods]: ../../../api_reference/pavex/router/enum.AllowedMethods.html
[RawIncomingBody]: ../../../api_reference/pavex/request/body/struct.RawIncomingBody.html
[JsonBody]: ../../../api_reference/pavex/request/body/struct.JsonBody.html
2 changes: 1 addition & 1 deletion docs/guide/request_data/path/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The **path** is a component of the [request target](../request_target.md).
E.g. `/foo/bar` is the path component in `https://example.com/foo/bar?baz=qux` or `/foo/bar?baz=qux`.

The path is primarily used for [routing requests to the right handlers](../../routing/index.md).
The path can also be used to encode dynamic data—check out ["Route parameters"](route_parameters.md) for
The path can also be used to encode dynamic data—check out ["Path parameters"](path_parameters.md) for
more details.

## Injection
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# Route parameters
# Path parameters

In REST APIs, the [path](index.md) is often used to identify a resource.
For example, in `https://example.com/users/123`, the path is `/users/123` and the resource is the user with ID `123`.

Those dynamic path segments are called **route parameters**.
In Pavex, you must declare the route parameters for a given path in the route definition—see [Route parameters](../../routing/path_patterns.md#route-parameters)
Those dynamic path segments are called **path parameters**.
In Pavex, you must declare the path parameters for a given path in the route definition—see [Path parameters](../../routing/path_patterns.md#route-parameters)
for more details.

## Overview
Expand All @@ -16,16 +16,16 @@ To extract `123` from the path, you register `/users/:id` as the path pattern fo

1. The path pattern for the route.

You can then access the `id` value for an incoming request by injecting [`RouteParams<T>`][RouteParams] in your handler:
You can then access the `id` value for an incoming request by injecting [`PathParams<T>`][PathParams] in your handler:

--8<-- "doc_examples/guide/request_data/route_params/project-route_params_extraction.snap"

There are a few moving parts here. Let's break them down!

### Fields names

[`RouteParams<T>`][RouteParams] is a generic wrapper around a struct[^why-struct] that models the route parameters for a given path.
All struct fields must be named after the route parameters declared in the path pattern[^wrong-name].
[`PathParams<T>`][PathParams] is a generic wrapper around a struct[^why-struct] that models the path parameters for a given path.
All struct fields must be named after the path parameters declared in the path pattern[^wrong-name].

In our example, the path pattern is `/users/:id`.
Our extraction type, `GetUserParams`, must have a matching field named `id`.
Expand All @@ -35,15 +35,15 @@ Our extraction type, `GetUserParams`, must have a matching field named `id`.
### Deserialization

The newly defined struct must be **deserializable**—i.e. it must implement the [`serde::Deserialize`][serde::Deserialize] trait.
The [`#[RouteParams]`][RouteParamsMacro] attribute macro will automatically derive [`serde::Deserialize`][serde::Deserialize] for you. Alternatively, you can derive or implement [`serde::Deserialize`][serde::Deserialize] directly.
The [`#[PathParams]`][PathParamsMacro] attribute macro will automatically derive [`serde::Deserialize`][serde::Deserialize] for you. Alternatively, you can derive or implement [`serde::Deserialize`][serde::Deserialize] directly.

--8<-- "doc_examples/guide/request_data/route_params/project-route_params_struct_with_attr.snap"

If you rely on [`#[RouteParams]`][RouteParamsMacro], Pavex can perform more advanced checks at compile time[^structural-deserialize] (e.g. detect unsupported types).
If you rely on [`#[PathParams]`][PathParamsMacro], Pavex can perform more advanced checks at compile time[^structural-deserialize] (e.g. detect unsupported types).

### Parsing

From a protocol perspective, all route parameters are strings.
From a protocol perspective, all path parameters are strings.
From an application perspective, you might want to enforce stricter constraints.

In our example, we expect `id` parameter to be a number.
Expand All @@ -57,7 +57,7 @@ Everything works as expected because `u64` implements the [`serde::Deserialize`]

### Unsupported field types

Route parameters are best used to encode **values**, such as numbers, strings, or dates.
Path parameters are best used to encode **values**, such as numbers, strings, or dates.
There is no standard way to encode more complex types such as collections (e.g. `Vec<T>`, tuples) in a route parameter.
As a result, Pavex doesn't support them.

Expand All @@ -66,19 +66,19 @@ Pavex will do its best to catch unsupported types at compile time, but it's not
## Avoiding allocations

If you want to squeeze out the last bit of performance from your application,
you can try to avoid heap memory allocations when extracting string-like route parameters.
you can try to avoid heap memory allocations when extracting string-like path parameters.
Pavex supports this use case—**you can borrow from the request's path**.

### Percent-encoding

It is not always possible to avoid allocations when handling route parameters.
Route parameters must comply with the restriction of the URI specification:
It is not always possible to avoid allocations when handling path parameters.
Path parameters must comply with the restriction of the URI specification:
you can only use [a limited set of characters](https://datatracker.ietf.org/doc/html/rfc3986#section-2).
If you want to use a character not allowed in a URI, you must [percent-encode it](https://developer.mozilla.org/en-US/docs/Glossary/Percent-encoding).
For example, if you want to use a space in a route parameter, you must encode it as `%20`.
A string like `John Doe` becomes `John%20Doe` when percent-encoded.

[`RouteParams<T>`][RouteParams] automatically decodes percent-encoded strings for you. But that comes at a cost:
[`PathParams<T>`][PathParams] automatically decodes percent-encoded strings for you. But that comes at a cost:
Pavex _must_ allocate a new `String` if the route parameter is percent-encoded.

### Cow
Expand All @@ -89,32 +89,32 @@ It borrows from the request's path if possible, it allocates a new `String` if i
[`Cow<'_, str>`][Cow] strikes a balance between performance and robustness: you don't have to worry about a runtime error if the route parameter
is percent-encoded, but you tried to use `&str` as its field type.

## `RawRouteParams`
## `RawPathParams`

[`RouteParams<T>`][RouteParams] is a high-level interface: it bundles together compile-time checks,
[`PathParams<T>`][PathParams] is a high-level interface: it bundles together compile-time checks,
extraction and parsing.
If you want to opt out of all those utilities, reach for [`RawRouteParams`][RawRouteParams].
If you want to opt out of all those utilities, reach for [`RawPathParams`][RawPathParams].
It is a lower-level interface[^relationship]: it gives you access to the dynamic
path segments as they appear right after extraction.
It doesn't perform percent-decoding not deserialization.

### Injection

[`RawRouteParams`][RawRouteParams] is a [framework primitive](../../dependency_injection/core_concepts/framework_primitives.md),
[`RawPathParams`][RawPathParams] is a [framework primitive](../../dependency_injection/core_concepts/framework_primitives.md),
you don't have to register a constructor to inject it.

--8<-- "doc_examples/guide/request_data/route_params/project-raw_route_params.snap"

### Allocations

[`RawRouteParams`][RawRouteParams] tries to avoid heap memory allocations.
[`RawPathParams`][RawPathParams] tries to avoid heap memory allocations.
Parameter names are borrowed from the server routing machinery.
Parameter values are borrowed from the [raw path](index.md) of the incoming request.

You might have to allocate when you decode [percent-encoded parameters](#percent-encoding).

[^why-struct]: Pavex made a deliberate choice of _not_ supporting tuples or other sequence-like types for extracting route parameters.
Check out [the API reference](../../../api_reference/pavex/request/route/struct.RouteParams.html#unsupported-types)
[^why-struct]: Pavex made a deliberate choice of _not_ supporting tuples or other sequence-like types for extracting path parameters.
Check out [the API reference](../../../api_reference/pavex/request/route/struct.PathParams.html#unsupported-types)
to learn more about the rationale behind this decision.

[^wrong-name]: If a field name doesn't match a route parameter name, Pavex will detect it at compile time and return
Expand All @@ -124,13 +124,13 @@ No more runtime errors because you misspelled a field name!
[^structural-deserialize]: Check the documentation for [`StructuralDeserialize`][StructuralDeserialize] if you want
to know more about the underlying mechanism.

[^relationship]: [`RouteParams<T>`][RouteParams] is built on top of [`RawRouteParams`][RawRouteParams].
[^relationship]: [`PathParams<T>`][PathParams] is built on top of [`RawPathParams`][RawPathParams].

[RequestHead]: ../../../api_reference/pavex/request/struct.RequestHead.html
[RequestHead::target]: ../../../api_reference/pavex/request/struct.RequestHead.html#structfield.target
[RouteParams]: ../../../api_reference/pavex/request/route/struct.RouteParams.html
[RouteParamsMacro]: ../../../api_reference/pavex/request/route/attr.RouteParams.html
[PathParams]: ../../../api_reference/pavex/request/route/struct.PathParams.html
[PathParamsMacro]: ../../../api_reference/pavex/request/route/attr.PathParams.html
[serde::Deserialize]: https://docs.rs/serde/latest/serde/trait.Deserialize.html
[StructuralDeserialize]: ../../../api_reference/pavex/serialization/trait.StructuralDeserialize.html
[Cow]: https://doc.rust-lang.org/std/borrow/enum.Cow.html
[RawRouteParams]: ../../../api_reference/pavex/request/route/struct.RawRouteParams.html
[RawPathParams]: ../../../api_reference/pavex/request/route/struct.RawPathParams.html
2 changes: 1 addition & 1 deletion docs/guide/request_data/request_target.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Inject [`RequestHead`][RequestHead] to access the request target via its [`targe

The raw target and its components are primarily useful for logging purposes.
Rely on higher-level abstractions
to perform more advanced processing—e.g. parsing query parameters or [route parameters](path/route_parameters.md).
to perform more advanced processing—e.g. parsing query parameters or [path parameters](path/path_parameters.md).

[^rfc]: [RFC 7230](https://datatracker.ietf.org/doc/html/rfc7230#section-5.3) allows two other formats of request target,
authority form (e.g. `example.com:443`) and asterisk form (e.g. `*`).
Expand Down
Loading

0 comments on commit 03a2233

Please sign in to comment.