Blueprint

Struct Blueprint 

Source
pub struct Blueprint { /* private fields */ }
Expand description

The structure of your Pavex application.

§Guide

Check out the “Project structure” section of Pavex’s guide for more details on the role of Blueprint in Pavex applications.

§Overview

A blueprint keeps track of:

You can also decompose your application into smaller sub-components using .nest(), .prefix() and .domain().

A blueprint can be serialized via .persist() and forwarded to Pavex’s CLI to (re)generate the server SDK crate.

§Example

use pavex::{Blueprint, blueprint::from};

let mut bp = Blueprint::new();
// Bring into scope constructors, error handlers and configuration
// types defined in the crates listed via `from!`.
bp.import(from![
    // Local components, defined in this crate
    crate,
    // Components defined in the `pavex` crate,
    // by the framework itself.
    pavex,
]);

// Attach a `tracing` span to every incoming request.
bp.wrap(LOGGER);
// Log the status code of every response.
bp.post_process(RESPONSE_LOGGER);
// Capture the error message and source chain
// of every unhandled error.
bp.error_observer(ERROR_LOGGER);

// Register all routes defined in this crate,
// prepending `/api` to their paths.
bp.prefix("/api").routes(from![crate]);

Implementations§

Source§

impl Blueprint

Source

pub fn new() -> Self

Create a new Blueprint.

Source

pub fn import(&mut self, import: Import) -> RegisteredImport<'_>

Import all constructors, error handlers, configuration and prebuilt types defined in the target modules.

Components that have been annotated with Pavex’s macros (e.g. #[singleton]) aren’t automatically considered when resolving the dependency graph for your application.
They need to be explicitly imported using one or more invocations of this method.

§Guide

Check out the “Dependency Injection” section of Pavex’s guide for a thorough introduction to dependency injection in Pavex applications.

§Wildcard import

You can import all components defined in the current crate and its direct dependencies using the wildcard source, *:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
bp.import(from![*]);
§All local components

Use crate as source to import all components defined in the current crate:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
bp.import(from![crate]);
§Specific modules

You can restrict the import to modules:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
// It will only import components defined
// in the `crate::a` and `crate::b` modules.
bp.import(from![crate::a, crate::b]);
§Dependencies

You can import components from a dependency using the same mechanism:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
// Import components from the `pavex_session` and
// `pavex_session_sqlx` crates.
bp.import(from![pavex_session, pavex_session_sqlx]);

The specified crates must be direct dependencies of the current crate.

Source

pub fn routes(&mut self, import: Import) -> RegisteredRoutes<'_>

Register all the routes defined in the target modules.

Components that have been annotated with Pavex’s macros (e.g. #[pavex::get]) aren’t automatically added to your application.
They need to be explicitly imported using this method or .route().

§Guide

Check out the “Routing” section of Pavex’s guide for a thorough introduction to routing in Pavex applications.

Check out .route()’s documentation to learn how routes are defined.

§All local routes

Use crate as source to register all the routes defined in the current crate:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
bp.routes(from![crate]);
§Specific modules

You can restrict the scope to specific modules:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
// It will only register routes defined
// in the `crate::routes::user` and `crate::routes::post` modules.
bp.routes(from![
    crate::routes::user,
    crate::routes::post
]);
§Dependencies

You can register routes defined in one of your dependencies using the same mechanism:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
// Register request handlers from the `pavex_session` crate
bp.routes(from![pavex_session]);

The specified crates must be direct dependencies of the current crate.

§Wildcard import

You can import all routes defined in the current crate and its direct dependencies using the wildcard source, *:

use pavex::{blueprint::from, Blueprint};

let mut bp = Blueprint::new();
bp.routes(from![*]);

This is generally discouraged.

Source

pub fn route(&mut self, route: Route) -> RegisteredRoute<'_>

Register a route to handle incoming requests.

You can register at most one route for any given path and method pair.

§Guide

Check out the “Routing” section of Pavex’s guide for a thorough introduction to routing in Pavex applications.

§Example: function route

Add the get attribute to a function to create a route matching GET requests to the given path:

use pavex::get;
use pavex::{request::RequestHead, Response};

#[get(path = "/")]
pub fn get_root(request_head: &RequestHead) -> Response {
    // [...]
}

The get attribute will define a new constant, named GET_ROOT.
Pass the constant to Blueprint::route to add the newly-defined route to your application:

let mut bp = Blueprint::new();
bp.route(GET_ROOT);
§Method-specific attributes

Pavex provides attributes for the most common HTTP methods: get, post, put, patch, delete, head, and options. Use the route attribute, instead, to define routes that match multiple methods, non-standard methods or arbitrary methods.

§Example: method route

You’re not limited to free functions. Methods can be used as routes too:

use pavex::methods;
use pavex::request::RequestHead;

pub struct LoginController(/* .. */);

#[methods]
impl LoginController {
    #[get(path = "/login")]
    pub fn get(head: &RequestHead) -> Self {
        // [...]
    }

    #[post(path = "/login")]
    pub fn post(head: &RequestHead) -> Self {
        // [...]
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the verb annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.route(LOGIN_CONTROLLER_GET);
bp.route(LOGIN_CONTROLLER_POST);
§Imports

If you have defined multiple routes, you can invoke .routes() to register them in bulk:

use pavex::{Blueprint, blueprint::from};

let mut bp = Blueprint::new();
// Import all the routes defined in the current crate.
// It's equivalent to invoking `bp.route` for every
// single route defined in the current crate.
bp.routes(from![crate]);

Check out the documentation for .routes() for more information.

Source

pub fn config(&mut self, config: Config) -> RegisteredConfig<'_>

Add a new type to the application’s configuration.

§Required traits

Configuration types must implement Debug, Clone and serde::Deserialize.

§Guide

Check out the “Configuration” section of Pavex’s guide for a thorough introduction to Pavex’s configuration system.

§Example

Add the config attribute to the type you want to include in the configuration for your application:

use pavex::config;

#[config(key = "pool")]
#[derive(serde::Deserialize, Debug, Clone)]
pub struct PoolConfig {
    pub max_n_connections: u32,
    pub min_n_connections: u32,
}

The config attribute will define a new constant, named POOL_CONFIG.
Pass the constant to Blueprint::config to add the new configuration type to your application:

let mut bp = Blueprint::new();
bp.config(POOL_CONFIG);

A new field, named pool with type PoolConfig, will be added to the generated ApplicationConfig struct.

§Imports

If you have defined multiple configuration types, you can use an import to register them in bulk:

use pavex::{Blueprint, blueprint::from};

let mut bp = Blueprint::new();
// Import all the types from the current crate that
// have been annotated with `#[config]`.
// It's equivalent to calling `bp.config` for
// every single configuration type defined in the current crate.
bp.import(from![crate]);

Check out the documentation for Blueprint::import for more information.

Source

pub fn constructor( &mut self, constructor: Constructor, ) -> RegisteredConstructor<'_>

Register a constructor.

If a constructor for the same type has already been registered, it will be overwritten.

§Guide

Check out the “Dependency injection” section of Pavex’s guide for a thorough introduction to dependency injection in Pavex applications.

§Example: function constructor

Add the request_scoped attribute to a function to mark it as a request-scoped constructor:

use pavex::request_scoped;
use pavex::request::RequestHead;

pub struct AuthorizationHeader(/* .. */);

#[request_scoped]
pub fn extract_authorization(head: &RequestHead) -> AuthorizationHeader {
    // [...]
}

The request_scoped attribute will define a new constant, named EXTRACT_AUTHORIZATION.
Pass the constant to Blueprint::constructor to allow other components to inject an instance of the AuthorizationHeader type as an input parameter.

let mut bp = Blueprint::new();
bp.constructor(EXTRACT_AUTHORIZATION);
§Lifecycles

You can also register constructors with singleton and transient lifecycles. Check out the respective macros (singleton and transient) for more details.

§Example: method constructor

You’re not limited to free functions. Methods can be used as constructors too:

use pavex::methods;
use pavex::request::RequestHead;

pub struct AuthorizationHeader(/* .. */);

#[methods]
impl AuthorizationHeader {
    #[request_scoped]
    pub fn new(head: &RequestHead) -> Self {
        // [...]
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[request_scoped] annotation on the method itself.\

The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.constructor(AUTHORIZATION_HEADER_NEW);
§Imports

If you have defined multiple constructors, you can use an import to register them in bulk:

use pavex::{Blueprint, blueprint::from};

let mut bp = Blueprint::new();
// Import all the types from the current crate that
// have been annotated with either `#[singleton]`,
// `#[request_scoped]`, `#[transient]` or `#[constructor]`.
// It's equivalent to invoking `bp.constructor` for every
// single constructor defined in the current crate.
bp.import(from![crate]);

Check out the documentation for Blueprint::import for more information.

Source

pub fn wrap( &mut self, m: WrappingMiddleware, ) -> RegisteredWrappingMiddleware<'_>

Register a wrapping middleware.

§Guide

Check out the “Middleware” section of Pavex’s guide for a thorough introduction to middlewares in Pavex applications.

§Example: function wrapper

Add the wrap attribute to a function to mark it as a a wrapping middleware:

use pavex::{middleware::Next, Response, wrap};
use std::time::Duration;
use tokio::time::{timeout, error::Elapsed};

#[wrap]
pub async fn timeout_wrapper<C>(next: Next<C>) -> Result<Response, Elapsed>
where
    C: IntoFuture<Output = Response>
{
    timeout(Duration::from_secs(2), next.into_future()).await
}

The wrap attribute will define a new constant, named TIMEOUT_WRAPPER.
Pass the constant to Blueprint::wrap to add the newly-defined middleware to your application:

let mut bp = Blueprint::new();
bp.wrap(TIMEOUT_WRAPPER);
§Example: method middleware

You’re not limited to free functions. Methods can be used as middlewares too:

use pavex::{middleware::Next, Response, methods};
use std::time::Duration;
use tokio::time::{timeout, error::Elapsed};

pub struct TimeoutMiddleware {
    timeout: Duration,
}

#[methods]
impl TimeoutMiddleware {
    #[wrap]
    pub async fn execute<C>(&self, next: Next<C>) -> Result<Response, Elapsed>
    where
        C: IntoFuture<Output = Response>
    {
        timeout(self.timeout, next.into_future()).await
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[wrap] annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.wrap(TIMEOUT_MIDDLEWARE_EXECUTE);
Source

pub fn post_process( &mut self, m: PostProcessingMiddleware, ) -> RegisteredPostProcessingMiddleware<'_>

Register a post-processing middleware.

§Guide

Check out the “Middleware” section of Pavex’s guide for a thorough introduction to middlewares in Pavex applications.

§Example: function middleware

Add the post_process attribute to a function to mark it as a a post-processing middleware:

use pavex::{post_process, Response};
use pavex_tracing::{
    RootSpan,
    fields::{http_response_status_code, HTTP_RESPONSE_STATUS_CODE}
};

#[post_process]
pub fn response_logger(response: Response, root_span: &RootSpan) -> Response
{
    root_span.record(
        HTTP_RESPONSE_STATUS_CODE,
        http_response_status_code(&response),
    );
    response
}

The post_process attribute will define a new constant, named RESPONSE_LOGGER.
Pass the constant to Blueprint::post_process to add the newly-defined middleware to your application:

let mut bp = Blueprint::new();
bp.post_process(RESPONSE_LOGGER);
§Example: method middleware

You’re not limited to free functions. Methods can be used as middlewares too:

use pavex::{methods, Response};
use pavex_tracing::{
    RootSpan,
    fields::{http_response_status_code, HTTP_RESPONSE_STATUS_CODE}
};

pub struct ResponseLogger {
    log_body_size: bool,
}

#[methods]
impl ResponseLogger {
    #[post_process]
    pub fn log(&self, response: Response, root_span: &RootSpan) -> Response
    {
        if self.log_body_size {
            // [...]
        }
        root_span.record(
            HTTP_RESPONSE_STATUS_CODE,
            http_response_status_code(&response),
        );
        response
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[post_process] annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.post_process(RESPONSE_LOGGER_LOG);
Source

pub fn pre_process( &mut self, m: PreProcessingMiddleware, ) -> RegisteredPreProcessingMiddleware<'_>

Register a pre-processing middleware.

§Guide

Check out the “Middleware” section of Pavex’s guide for a thorough introduction to middlewares in Pavex applications.

§Example: function middleware

Add the pre_process attribute to a function to mark it as a a pre-processing middleware:

use pavex::{Blueprint, pre_process, Response};
use pavex::middleware::Processing;
use pavex::http::{HeaderValue, header::LOCATION};
use pavex::request::RequestHead;

/// If the request path ends with a `/`,
/// redirect to the same path without the trailing `/`.
#[pre_process]
pub fn redirect_to_normalized(request_head: &RequestHead) -> Processing
{
    let Some(normalized_path) = request_head.target.path().strip_suffix('/') else {
        // No need to redirect, we continue processing the request.
        return Processing::Continue;
    };
    let location = HeaderValue::from_str(normalized_path).unwrap();
    let redirect = Response::temporary_redirect().insert_header(LOCATION, location);
    // Short-circuit the request processing pipeline and return the redirect response
    // to the client without invoking downstream middlewares and the request handler.
    Processing::EarlyReturn(redirect)
}

The pre_process attribute will define a new constant, named REDIRECT_TO_NORMALIZED.
Pass the constant to Blueprint::pre_process to add the newly-defined middleware to your application:

let mut bp = Blueprint::new();
bp.pre_process(REDIRECT_TO_NORMALIZED);
§Example: method middleware

You’re not limited to free functions. Methods can be used as middlewares too:

use pavex::{methods, Response};
use pavex::middleware::Processing;
use pavex::http::{HeaderValue, header::LOCATION};
use pavex::request::RequestHead;

pub struct PathNormalizer {
    // [...]
}

#[methods]
impl PathNormalizer {
    #[pre_process]
    pub fn redirect(request_head: &RequestHead) -> Processing
    {
        // [...]
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[pre_process] annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.pre_process(PATH_NORMALIZER_REDIRECT);
Source

pub fn nest(&mut self, blueprint: Blueprint)

Nest a Blueprint under the current Blueprint (the parent), without adding a common path prefix nor a domain restriction to its routes.

Check out RoutingModifiers::nest for more details on nesting.

Source

pub fn prefix(&mut self, prefix: &str) -> RoutingModifiers<'_>

A common prefix will be prepended to the path of routes nested under this condition.

use pavex::Blueprint;
use pavex::get;
use pavex::Response;

fn app() -> Blueprint {
    let mut bp = Blueprint::new();
    // Adding `/api` as common prefix here
    bp.prefix("/api").nest(api_bp());
    bp
}

#[get(path = "/version")]
pub fn get_api_version() -> Response {
    // [...]
}

fn api_bp() -> Blueprint {
    let mut bp = Blueprint::new();
    // This will match `GET` requests to `/api/version`.
    bp.route(GET_API_VERSION);
    bp
}

You can also add a (sub)domain constraint, in addition to the common prefix:

use pavex::Blueprint;
use pavex::get;
use pavex::Response;

fn app() -> Blueprint {
   let mut bp = Blueprint::new();
   bp.prefix("/v1").domain("api.mybusiness.com").nest(api_bp());
   bp
}

#[get(path = "/about")]
pub fn get_about() -> Response {
    // [...]
}

fn api_bp() -> Blueprint {
   let mut bp = Blueprint::new();
  // This will match `GET` requests to `api.mybusiness.com/v1/about`.
  bp.route(GET_ABOUT);
  bp
}

Check out Blueprint::domain for more details on domain restrictions.

§Restrictions

prefix must be non-empty and it must start with a /. If you don’t want to add a common prefix, check out Blueprint::nest or Blueprint::domain.

§Trailing slashes

prefix can’t end with a trailing /. This would result in routes with two consecutive / in their paths—e.g. /prefix//path—which is rarely desirable. If you actually need consecutive slashes in your route, you can add them explicitly to the path of the route registered in the nested blueprint:

use pavex::Blueprint;
use pavex::get;
use pavex::Response;

fn app() -> Blueprint {
    let mut bp = Blueprint::new();
    bp.prefix("/api").nest(api_bp());
    bp
}

#[get(path = "//version")]
pub fn get_api_version() -> Response {
    // [...]
}

fn api_bp() -> Blueprint {
    let mut bp = Blueprint::new();
    // This will match `GET` requests to `/api//version`.
    bp.route(GET_API_VERSION);
    bp
}
Source

pub fn domain(&mut self, domain: &str) -> RoutingModifiers<'_>

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

§Example
use pavex::Blueprint;

let mut bp = Blueprint::new();

// We split UI and API routes into separate blueprints,
// and we serve them using different subdomains.
bp.domain("api.mybusiness.com")
  .nest(api_routes());
bp.domain("console.mybusiness.com")
  .nest(console_routes());

You can also prepend a common path prefix to all registered routes, in addition to the domain constraint:

use pavex::Blueprint;
use pavex::get;
use pavex::Response;

fn app() -> Blueprint {
   let mut bp = Blueprint::new();
   bp.prefix("/v1").domain("api.mybusiness.com").nest(api_bp());
   bp
}

#[get(path = "/about")]
pub fn get_about() -> Response {
    // [...]
}

fn api_bp() -> Blueprint {
   let mut bp = Blueprint::new();
  // This will match `GET` requests to `api.mybusiness.com/v1/about`.
  bp.route(GET_ABOUT);
  bp
}

Check out Blueprint::prefix for more details on path prefixes.

§Domain detection

Domain detection is based on the value of Host header. If the header is not present in the request, the condition will be considered as not met.

Keep in mind that the Host header can be easily spoofed by the client, so you should not rely on its value for auth or other security-sensitive operations.

Source

pub fn fallback(&mut self, fallback: Fallback) -> RegisteredFallback<'_>

Register a fallback handler to be invoked when an incoming request does not match any of the routes you registered with Blueprint::route.

If you don’t register a fallback handler, the default framework fallback will be used instead.

If a fallback handler has already been registered against this Blueprint, it will be overwritten.

§Example
use pavex::{get, fallback, Blueprint};
use pavex::Response;

#[get(path = "/path")]
pub fn get_path() -> Response {
    // [...]
}
#[fallback]
pub fn fallback_handler() -> Response {
    // [...]
}

let mut bp = Blueprint::new();
bp.route(GET_PATH);
// The fallback handler will be invoked for all the requests that don't match `/path`.
// E.g. `GET /home`, `POST /home`, `GET /home/123`, etc.
bp.fallback(FALLBACK_HANDLER);
§Signature

A fallback handler is a function (or a method) that returns a Response, either directly (if infallible) or wrapped in a Result (if fallible).

Fallback handlers can take advantage of dependency injection, like any other component. You list what you want to see injected as function parameters and Pavex will inject them for you in the generated code.

§Nesting

You can register a single fallback handler for each blueprint. If your application takes advantage of nesting, you can register a fallback against each nested blueprint in your application as well as one for the top-level blueprint.

Let’s explore how nesting affects the invocation of fallback handlers.

§Nesting without prefix

The fallback registered against a blueprint will be invoked for all the requests that match the path of a route that was directly registered against that blueprint, but don’t satisfy their method guards.

use pavex::{get, fallback, Blueprint};
use pavex::Response;

#[get(path = "/home")]
pub fn get_home() -> Response {
    // [...]
}

#[get(path = "/room")]
pub fn get_room() -> Response {
    // [...]
}

#[fallback]
pub fn fallback_handler() -> Response {
    // [...]
}

let mut bp = Blueprint::new();
bp.route(GET_HOME);
bp.nest({
    let mut bp = Blueprint::new();
    bp.route(GET_ROOM);
    bp.fallback(FALLBACK_HANDLER);
    bp
});

In the example above, fallback_handler will be invoked for incoming POST /room requests: the path matches the path of a route registered against the nested blueprint (GET /room), but the method guard doesn’t (POST vs GET). If the incoming requests don’t have /room as their path instead (e.g. GET /street or GET /room/123), they will be handled by the fallback registered against the parent blueprint—the top-level one in this case. Since no fallback has been explicitly registered against the top-level blueprint, the default framework fallback will be used instead.

§Nesting with prefix

If the nested blueprint includes a nesting prefix (e.g. bp.nest_at("/api", api_bp)), its fallback will also be invoked for all the requests that start with the prefix but don’t match any of the route paths registered against the nested blueprint.

use pavex::{get, fallback, Blueprint};
use pavex::Response;

#[get(path = "/home")]
pub fn get_home() -> Response {
    // [...]
}

#[get(path = "/")]
pub fn list_rooms() -> Response {
    // [...]
}

#[fallback]
pub fn fallback_handler() -> Response {
    // [...]
}

let mut bp = Blueprint::new();
bp.route(GET_HOME);
bp.prefix("/room").nest({
    let mut bp = Blueprint::new();
    bp.route(LIST_ROOMS);
    bp.fallback(FALLBACK_HANDLER);
    bp
});

In the example above, fallback_handler will be invoked for both POST /room and POST /room/123 requests: the path of the latter doesn’t match the path of the only route registered against the nested blueprint (GET /room/), but it starts with the prefix of the nested blueprint (/room).

Source

pub fn error_observer( &mut self, error_observer: ErrorObserver, ) -> RegisteredErrorObserver<'_>

Register an error observer to intercept and report errors that occur during request handling.

§Guide

Check out the “Error observers” section of Pavex’s guide for a thorough introduction to error observers in Pavex applications.

§Example: function observer
use pavex::error_observer;
use tracing_log_error::log_error;

#[error_observer]
pub fn error_logger(e: &pavex::Error) {
    log_error!(e, "An error occurred while handling a request");
}

The error_observer attribute will define a new constant, named ERROR_LOGGER.
Pass the constant to .error_observer() to register the newly defined error observer:

let mut bp = Blueprint::new();
bp.error_observer(ERROR_LOGGER);
§Example: method observer

You’re not limited to free functions. Methods can be used as error observers too:

use pavex::methods;
use tracing_log_error::log_error;

pub struct ErrorLogger;

#[methods]
impl ErrorLogger {
    #[error_observer]
    pub fn log(e: &pavex::Error) {
        log_error!(e, "An error occurred while handling a request");
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[error_observer] annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.error_observer(ERROR_LOGGER_LOG);
Source

pub fn error_handler(&mut self, m: ErrorHandler) -> RegisteredErrorHandler<'_>

Register an error handler.

§Guide

Check out the “Error handlers” section of Pavex’s guide for a thorough introduction to error handlers in Pavex applications.

§Example: function handler

Add the error_handler attribute to a function to mark it as an error handler:

use pavex::error_handler;
use pavex::Response;

pub enum LoginError {
    InvalidCredentials,
    DatabaseError,
}

#[error_handler]
pub fn login_error_handler(e: &LoginError) -> Response {
    match e {
        LoginError::InvalidCredentials => Response::unauthorized(),
        LoginError::DatabaseError => Response::internal_server_error(),
    }
}

The error_handler attribute will define a new constant, named LOGIN_ERROR_HANDLER.
Pass the constant to .error_handler() to register the newly defined error handler:

let mut bp = Blueprint::new();
bp.error_handler(LOGIN_ERROR_HANDLER);
§Example: method handler

You’re not limited to free functions. Methods can be used as error handlers too:

use pavex::methods;
use pavex::Response;

pub enum LoginError {
    InvalidCredentials,
    DatabaseError,
}

#[methods]
impl LoginError {
    #[error_handler]
    pub fn to_response(&self) -> Response {
        match self {
            LoginError::InvalidCredentials => Response::unauthorized(),
            LoginError::DatabaseError => Response::internal_server_error(),
        }
    }
}

For methods, you must add a #[methods] annotation on the impl block it belongs to, in addition to the #[error_handler] annotation on the method itself.
The generated constant is named <type_name>_<method_name>, in constant case:

let mut bp = Blueprint::new();
bp.error_handler(LOGIN_ERROR_TO_RESPONSE);
Source

pub fn prebuilt(&mut self, prebuilt: Prebuilt) -> RegisteredPrebuilt<'_>

Register a type to be used as input parameter to the (generated) ApplicationState::new method.

§Guide

Check out the “Dependency injection” section of Pavex’s guide for a thorough introduction to dependency injection in Pavex applications.

Source§

impl Blueprint

Methods to serialize and deserialize a Blueprint. These are used to pass the blueprint data to Pavex’s CLI.

Source

pub fn persist(&self, filepath: &Path) -> Result<(), Error>

Serialize the Blueprint to a file in RON format.

The file is only written to disk if the content of the blueprint has changed.

Source

pub fn load(filepath: &Path) -> Result<Self, Error>

Read a RON-encoded Blueprint from a file.

Trait Implementations§

Source§

impl Default for Blueprint

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

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.

§

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

§

fn fg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the foreground set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like red() and green(), which have the same functionality but are pithier.

§Example

Set foreground color to white using fg():

use yansi::{Paint, Color};

painted.fg(Color::White);

Set foreground color to white using white().

use yansi::Paint;

painted.white();
§

fn primary(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Primary].

§Example
println!("{}", value.primary());
§

fn fixed(&self, color: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Fixed].

§Example
println!("{}", value.fixed(color));
§

fn rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the fg() set to [Color :: Rgb].

§Example
println!("{}", value.rgb(r, g, b));
§

fn black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Black].

§Example
println!("{}", value.black());
§

fn red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Red].

§Example
println!("{}", value.red());
§

fn green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Green].

§Example
println!("{}", value.green());
§

fn yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Yellow].

§Example
println!("{}", value.yellow());
§

fn blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Blue].

§Example
println!("{}", value.blue());
§

fn magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Magenta].

§Example
println!("{}", value.magenta());
§

fn cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: Cyan].

§Example
println!("{}", value.cyan());
§

fn white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: White].

§Example
println!("{}", value.white());
§

fn bright_black(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlack].

§Example
println!("{}", value.bright_black());
§

fn bright_red(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightRed].

§Example
println!("{}", value.bright_red());
§

fn bright_green(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightGreen].

§Example
println!("{}", value.bright_green());
§

fn bright_yellow(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightYellow].

§Example
println!("{}", value.bright_yellow());
§

fn bright_blue(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightBlue].

§Example
println!("{}", value.bright_blue());
§

fn bright_magenta(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.bright_magenta());
§

fn bright_cyan(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightCyan].

§Example
println!("{}", value.bright_cyan());
§

fn bright_white(&self) -> Painted<&T>

Returns self with the fg() set to [Color :: BrightWhite].

§Example
println!("{}", value.bright_white());
§

fn bg(&self, value: Color) -> Painted<&T>

Returns a styled value derived from self with the background set to value.

This method should be used rarely. Instead, prefer to use color-specific builder methods like on_red() and on_green(), which have the same functionality but are pithier.

§Example

Set background color to red using fg():

use yansi::{Paint, Color};

painted.bg(Color::Red);

Set background color to red using on_red().

use yansi::Paint;

painted.on_red();
§

fn on_primary(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Primary].

§Example
println!("{}", value.on_primary());
§

fn on_fixed(&self, color: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Fixed].

§Example
println!("{}", value.on_fixed(color));
§

fn on_rgb(&self, r: u8, g: u8, b: u8) -> Painted<&T>

Returns self with the bg() set to [Color :: Rgb].

§Example
println!("{}", value.on_rgb(r, g, b));
§

fn on_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Black].

§Example
println!("{}", value.on_black());
§

fn on_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Red].

§Example
println!("{}", value.on_red());
§

fn on_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Green].

§Example
println!("{}", value.on_green());
§

fn on_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Yellow].

§Example
println!("{}", value.on_yellow());
§

fn on_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Blue].

§Example
println!("{}", value.on_blue());
§

fn on_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Magenta].

§Example
println!("{}", value.on_magenta());
§

fn on_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: Cyan].

§Example
println!("{}", value.on_cyan());
§

fn on_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: White].

§Example
println!("{}", value.on_white());
§

fn on_bright_black(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlack].

§Example
println!("{}", value.on_bright_black());
§

fn on_bright_red(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightRed].

§Example
println!("{}", value.on_bright_red());
§

fn on_bright_green(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightGreen].

§Example
println!("{}", value.on_bright_green());
§

fn on_bright_yellow(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightYellow].

§Example
println!("{}", value.on_bright_yellow());
§

fn on_bright_blue(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightBlue].

§Example
println!("{}", value.on_bright_blue());
§

fn on_bright_magenta(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightMagenta].

§Example
println!("{}", value.on_bright_magenta());
§

fn on_bright_cyan(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightCyan].

§Example
println!("{}", value.on_bright_cyan());
§

fn on_bright_white(&self) -> Painted<&T>

Returns self with the bg() set to [Color :: BrightWhite].

§Example
println!("{}", value.on_bright_white());
§

fn attr(&self, value: Attribute) -> Painted<&T>

Enables the styling [Attribute] value.

This method should be used rarely. Instead, prefer to use attribute-specific builder methods like bold() and underline(), which have the same functionality but are pithier.

§Example

Make text bold using attr():

use yansi::{Paint, Attribute};

painted.attr(Attribute::Bold);

Make text bold using using bold().

use yansi::Paint;

painted.bold();
§

fn bold(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Bold].

§Example
println!("{}", value.bold());
§

fn dim(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Dim].

§Example
println!("{}", value.dim());
§

fn italic(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Italic].

§Example
println!("{}", value.italic());
§

fn underline(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Underline].

§Example
println!("{}", value.underline());

Returns self with the attr() set to [Attribute :: Blink].

§Example
println!("{}", value.blink());

Returns self with the attr() set to [Attribute :: RapidBlink].

§Example
println!("{}", value.rapid_blink());
§

fn invert(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Invert].

§Example
println!("{}", value.invert());
§

fn conceal(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Conceal].

§Example
println!("{}", value.conceal());
§

fn strike(&self) -> Painted<&T>

Returns self with the attr() set to [Attribute :: Strike].

§Example
println!("{}", value.strike());
§

fn quirk(&self, value: Quirk) -> Painted<&T>

Enables the yansi [Quirk] value.

This method should be used rarely. Instead, prefer to use quirk-specific builder methods like mask() and wrap(), which have the same functionality but are pithier.

§Example

Enable wrapping using .quirk():

use yansi::{Paint, Quirk};

painted.quirk(Quirk::Wrap);

Enable wrapping using wrap().

use yansi::Paint;

painted.wrap();
§

fn mask(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Mask].

§Example
println!("{}", value.mask());
§

fn wrap(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Wrap].

§Example
println!("{}", value.wrap());
§

fn linger(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Linger].

§Example
println!("{}", value.linger());
§

fn clear(&self) -> Painted<&T>

👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear(). The clear() method will be removed in a future release.

Returns self with the quirk() set to [Quirk :: Clear].

§Example
println!("{}", value.clear());
§

fn resetting(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Resetting].

§Example
println!("{}", value.resetting());
§

fn bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: Bright].

§Example
println!("{}", value.bright());
§

fn on_bright(&self) -> Painted<&T>

Returns self with the quirk() set to [Quirk :: OnBright].

§Example
println!("{}", value.on_bright());
§

fn whenever(&self, value: Condition) -> Painted<&T>

Conditionally enable styling based on whether the [Condition] value applies. Replaces any previous condition.

See the crate level docs for more details.

§Example

Enable styling painted only when both stdout and stderr are TTYs:

use yansi::{Paint, Condition};

painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);
§

fn new(self) -> Painted<Self>
where Self: Sized,

Create a new [Painted] with a default [Style]. Read more
§

fn paint<S>(&self, style: S) -> Painted<&Self>
where S: Into<Style>,

Apply a style wholesale to self. Any previous style is replaced. Read more
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