Struct cataclysm::Branch [−][src]
pub struct Branch<T> { /* fields omitted */ }
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
Creates a new branch
let branch: Branch<()> = Branch::new("/hello/world");
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));
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")
});
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?")
});
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");
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");
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.
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);
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 🧅.