pavex::blueprint::nesting

Struct NestingConditions

source
pub struct NestingConditions<'a> { /* private fields */ }
Expand description

The type returned by Blueprint::prefix and Blueprint::domain.

It allows you to customize how nested routes should behave.

Implementations§

source§

impl<'a> NestingConditions<'a>

source

pub fn domain(self, domain: &str) -> Self

Only requests to the specified domain will be forwarded to routes nested under this condition.

Check out Blueprint::domain for more details.

source

pub fn prefix(self, prefix: &str) -> Self

Prepends a common prefix to all routes nested under this condition.

If a prefix has already been set, it will be overridden.

Check out Blueprint::prefix for more details.

source

pub fn nest(self, bp: Blueprint)

Nest a Blueprint, optionally applying a common prefix and a domain restriction to all its routes.

Nesting also has consequences when it comes to constructors’ visibility.

§Constructors

Constructors registered against the parent blueprint will be available to the nested blueprint—they are inherited. Constructors registered against the nested blueprint will not be available to other sibling blueprints that are nested under the same parent—they are private.

Check out the example below to better understand the implications of nesting blueprints.

§Visibility
use pavex::f;
use pavex::blueprint::{Blueprint, router::GET};

fn app() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.singleton(f!(crate::db_connection_pool));
    bp.nest(home_bp());
    bp.nest(user_bp());
    bp
}

/// All property-related routes and constructors.
fn home_bp() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.route(GET, "/home", f!(crate::v1::get_home));
    bp
}

/// All user-related routes and constructors.
fn user_bp() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.request_scoped(f!(crate::user::get_session));
    bp.route(GET, "/user", f!(crate::user::get_user));
    bp
}

This example registers two routes:

  • GET /home
  • GET /user

It also registers two constructors:

  • crate::user::get_session, for Session;
  • crate::db_connection_pool, for ConnectionPool.

Since we are nesting the user_bp blueprint, the get_session constructor will only be available to the routes declared in the user_bp blueprint. If a route declared in home_bp tries to inject a Session, Pavex will report an error at compile-time, complaining that there is no registered constructor for Session. In other words, all constructors declared against the user_bp blueprint are private and isolated from the rest of the application.

The db_connection_pool constructor, instead, is declared against the parent blueprint and will therefore be available to all routes declared in home_bp and user_bp—i.e. nested blueprints inherit all the constructors declared against their parent(s).

§Precedence

If a constructor is declared against both the parent and one of its nested blueprints, the one declared against the nested blueprint takes precedence.

use pavex::f;
use pavex::blueprint::{Blueprint, router::GET};

fn app() -> Blueprint {
    let mut bp = Blueprint::new();
    // This constructor is registered against the root blueprint and it's visible
    // to all nested blueprints.
    bp.request_scoped(f!(crate::global::get_session));
    bp.nest(user_bp());
    // [..]
    bp
}

fn user_bp() -> Blueprint {
    let mut bp = Blueprint::new();
    // It can be overridden by a constructor for the same type registered
    // against a nested blueprint.
    // All routes in `user_bp` will use `user::get_session` instead of `global::get_session`.
    bp.request_scoped(f!(crate::user::get_session));
    // [...]
    bp
}
§Singletons

There is one exception to the precedence rule: singletons. Pavex guarantees that there will be only one instance of a singleton type for the entire lifecycle of the application. What should happen if two different constructors are registered for the same Singleton type by two nested blueprints that share the same parent? We can’t honor both constructors without ending up with two different instances of the same type, which would violate the singleton contract.

It goes one step further! Even if those two constructors are identical, what is the expected behaviour? Does the user expect the same singleton instance to be injected in both blueprints? Or does the user expect two different singleton instances to be injected in each nested blueprint?

To avoid this ambiguity, Pavex takes a conservative approach: a singleton constructor must be registered exactly once for each type. If multiple nested blueprints need access to the singleton, the constructor must be registered against a common parent blueprint—the root blueprint, if necessary.

Auto Trait Implementations§

§

impl<'a> Freeze for NestingConditions<'a>

§

impl<'a> RefUnwindSafe for NestingConditions<'a>

§

impl<'a> Send for NestingConditions<'a>

§

impl<'a> Sync for NestingConditions<'a>

§

impl<'a> Unpin for NestingConditions<'a>

§

impl<'a> !UnwindSafe for NestingConditions<'a>

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T> Same for T

source§

type Output = T

Should always be Self
source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more