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:
- Routes, registered via
.routes()and.route() - Middlewares, registered via
.pre_process(),.wrap()and.post_process() - Error observers, registered via
.error_observer() - Constructors, imported via
.import()or registered via.constructor() - Configuration types, imported via
.import()or registered via.config() - Prebuilt types, imported via
.import()or registered via.prebuilt() - Error handlers, imported via
.import()or registered via.error_handler() - Fallback routes, registered via
.fallback()
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
impl Blueprint
Sourcepub fn import(&mut self, import: Import) -> RegisteredImport<'_>
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.
Sourcepub fn routes(&mut self, import: Import) -> RegisteredRoutes<'_>
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.
Sourcepub fn route(&mut self, route: Route) -> RegisteredRoute<'_>
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.
Sourcepub fn config(&mut self, config: Config) -> RegisteredConfig<'_>
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.
Sourcepub fn constructor(
&mut self,
constructor: Constructor,
) -> RegisteredConstructor<'_>
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.
Sourcepub fn wrap(
&mut self,
m: WrappingMiddleware,
) -> RegisteredWrappingMiddleware<'_>
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);Sourcepub fn post_process(
&mut self,
m: PostProcessingMiddleware,
) -> RegisteredPostProcessingMiddleware<'_>
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);Sourcepub fn pre_process(
&mut self,
m: PreProcessingMiddleware,
) -> RegisteredPreProcessingMiddleware<'_>
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);Sourcepub fn nest(&mut self, blueprint: Blueprint)
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.
Sourcepub fn prefix(&mut self, prefix: &str) -> RoutingModifiers<'_>
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
}Sourcepub fn domain(&mut self, domain: &str) -> RoutingModifiers<'_>
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.
Sourcepub fn fallback(&mut self, fallback: Fallback) -> RegisteredFallback<'_>
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).
Sourcepub fn error_observer(
&mut self,
error_observer: ErrorObserver,
) -> RegisteredErrorObserver<'_>
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);Sourcepub fn error_handler(&mut self, m: ErrorHandler) -> RegisteredErrorHandler<'_>
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);Sourcepub fn prebuilt(&mut self, prebuilt: Prebuilt) -> RegisteredPrebuilt<'_>
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.
Trait Implementations§
Auto Trait Implementations§
impl Freeze for Blueprint
impl RefUnwindSafe for Blueprint
impl Send for Blueprint
impl Sync for Blueprint
impl Unpin for Blueprint
impl UnwindSafe for Blueprint
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
§fn fg(&self, value: Color) -> Painted<&T>
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 bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
§fn bg(&self, value: Color) -> Painted<&T>
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>
fn on_primary(&self) -> Painted<&T>
§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
§fn attr(&self, value: Attribute) -> Painted<&T>
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 rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
§fn quirk(&self, value: Quirk) -> Painted<&T>
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 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.
fn clear(&self) -> Painted<&T>
resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.§fn whenever(&self, value: Condition) -> Painted<&T>
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);