pub struct Branch<T> { /* private fields */ }
Expand description
Main cataclysm structure for route handling
Branches are cataclysm’s main building block. It is a really simple pattern matching system, with the following priorities. They are named branches to avoid conflict with the Path extractor.
- Exact matching
- Pattern matching
- Default branches (a.k.a, variable handling in branches)
In the case of exact matching, the path constructor is pretty straight forward
let branch: Branch<()> = Branch::new("/hello/world");
Pattern matching is a bit more complex
// matches any route that starts with `/hello/` and then words of 3 or 4 letters, no numbers
let branch: Branch<()> = Branch::new("/hello/{regex:^[A-Za-z\\d]{3,4}$}");
Last but not least, we have variable detection, with no regex
// matches any route that contains "/hello/{:variable}"
let branch: Branch<()> = Branch::new("/hello/{:variable}");
There is an important thing to note about most methods of this structure. When you create a branch with multiple parts in the path, a tree gets spawned containing each token in the path, however, methods like layer
, nest
, and with
operatoe on the top-level-token of the path, i.e., if you create a branch like this
let branch: Branch<()> = Branch::new("/path/with/many/tokens");
And then you execute the with
method, the callback you provide will reply only to /path/with/many/tokens
.
Implementations
sourceimpl<T: Sync + Send> Branch<T>
impl<T: Sync + Send> Branch<T>
sourcepub fn new<A: AsRef<str>>(trail: A) -> Branch<T>
pub fn new<A: AsRef<str>>(trail: A) -> Branch<T>
Creates a new branch
let branch: Branch<()> = Branch::new("/hello/world");
sourcepub fn with(self, method_callback: MethodHandler<T>) -> Self
pub fn with(self, method_callback: MethodHandler<T>) -> Self
Adds a callback to a branch
This function is the main building block for callbacks in the branch. A MethodHandler consists of a Method, and a callback function. Se the Method structure to see how to construct them.
// Example index function
async fn index() -> Response {
Response::ok().body("hello")
}
// Branch that will reply go a get method in `/scope`
let branch: Branch<()> = Branch::new("/scope").with(Method::Get.to(index));
sourcepub fn unmatched_method_to<F: Callback<A> + Send + Sync + 'static, A: Extractor<T>>(
self,
callback: F
) -> Self
pub fn unmatched_method_to<F: Callback<A> + Send + Sync + 'static, A: Extractor<T>>(
self,
callback: F
) -> Self
Adds a default method responder, in case no specific handler is found for the requested method.
By default, unmatched methods reply with a 405 Method Not Allowed
, but this function allows override of such behaviour.
let branch: Branch<()> = Branch::new("/").with(Method::Get.to(|| async {
Response::ok().body("Supported!")
})).unmatched_method_to(|| async {
Response::ok().body("Unsupported, please try with GET")
});
sourcepub fn defaults_to<F: Callback<A> + Send + Sync + 'static, A: Extractor<T>>(
self,
callback: F
) -> Self
pub fn defaults_to<F: Callback<A> + Send + Sync + 'static, A: Extractor<T>>(
self,
callback: F
) -> Self
Adds a default callback, in case of no nested matching.
// This branch will reply in any of `/hello`, `/hello/world`, `/hello/a/b` ,etc.
let branch: Branch<()> = Branch::new("/hello").defaults_to(|| async {
Response::ok().body("Are you lost?")
});
sourcepub fn files<A: Into<PathBuf>>(self, files_location: A) -> Self
pub fn files<A: Into<PathBuf>>(self, files_location: A) -> Self
Allows static file serving.
// This branch will reply with the default function to any
// path that has no extension. If it has extension, static files
// are served from ./static
let branch: Branch<()> = Branch::new("/").defaults_to(|| async {
Response::ok().body("Is this an SPA?")
}).files("./static");
sourcepub fn defaults_to_file<A: Into<PathBuf>>(self, file_location: A) -> Self
pub fn defaults_to_file<A: Into<PathBuf>>(self, file_location: A) -> Self
Helper for creating a file-loader default endpoint, for one specific file.
This is useful for single page applications.
// This is an SPA.
let branch: Branch<()> = Branch::new("/")
.defaults_to_file("./static/index.html")
.files("./static");
sourcepub fn merge(self, other: Branch<T>) -> Branch<T>
pub fn merge(self, other: Branch<T>) -> Branch<T>
Merges two branches from their bases, in case you find it useful
let branch_1: Branch<()> = Branch::new("/hello/world")
.with(Method::Get.to(|| async {Response::ok()}));
let branch_2 = Branch::new("/hallo/welt")
.with(Method::Get.to(|| async {Response::unauthorized()}));
// Merged branch will reply in `/hello/world` and in `/hallo/welt`
let merged_branch = branch_1.merge(branch_2);
Importance is held by the caller branch (lhs
). That means that the following will hold true:
- Method callbacks from
rhs
are only merged if not already present inlhs
. - Exact matches from
rhs
will be merged if already found inlhs
, else they get inserted. - Pattern matches from
rhs
will be marged if matched literally to another regex, else they will be inserted at the end of the evaluation queue. - Variable match from
rhs
is ignored iflhs
already contains one. - Static file serving from
rhs
is ignored iflhs
already contains one.
sourcepub fn nest(self, other: Branch<T>) -> Self
pub fn nest(self, other: Branch<T>) -> Self
Nests one branch in the top node of the first one
The “top node” is defined as the one following the path given to the branch constructor.
let to_be_nested: Branch<()> = Branch::new("/world");
// This one will reply in `/hello/world`
let branch = Branch::new("/hello").nest(to_be_nested);
sourcepub fn layer<F: 'static + Fn(Request, Box<Pipeline<T>>, Arc<Additional<T>>) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync>(
self,
layer_fn: F
) -> Self
pub fn layer<F: 'static + Fn(Request, Box<Pipeline<T>>, Arc<Additional<T>>) -> Pin<Box<dyn Future<Output = Response> + Send>> + Send + Sync>(
self,
layer_fn: F
) -> Self
Adds a processing layer to the callbacks contained in this branch
A layer is what is commonly known as middleware. The passed layer methods act as a wrap to the core handling functions of this branch. It is important to note that layer functions have a very specific structure: each one receives a Request
and a boxed Pipeline
. The function must return a pinned boxed future. A Timing Layer/Middleware function is provided as an example.
use cataclysm::{Branch, Additional, Pipeline, http::{Request, Response, Method}};
use futures::future::FutureExt;
use std::sync::Arc;
let branch = Branch::new("/hello")
.with(Method::Get.to(|| async {Response::ok().body("¡Hola!")}))
.layer(|req: Request, pipeline: Box<Pipeline<()>>, ad: Arc<Additional<()>>| async {
// Example of timing layer / middleware
let now = std::time::Instant::now();
// Execute the deeper layers of the pipeline, passing the request
let response = pipeline.execute(req, ad).await;
// Measure and print time
let elapsed = now.elapsed().as_nanos();
println!("Process time: {} ns", elapsed);
// We return the request for further possible processing.
response
}.boxed()
);
Calling the function multiple times will wrap the preceeding layer (or core handlers), like an onion 🧅.
Trait Implementations
Auto Trait Implementations
impl<T> !RefUnwindSafe for Branch<T>
impl<T> Send for Branch<T>
impl<T> Sync for Branch<T>
impl<T> Unpin for Branch<T>
impl<T> !UnwindSafe for Branch<T>
Blanket Implementations
sourceimpl<T> BorrowMut<T> for T where
T: ?Sized,
impl<T> BorrowMut<T> for T where
T: ?Sized,
const: unstable · sourcepub fn borrow_mut(&mut self) -> &mut T
pub fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more