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.

  1. Exact matching
  2. Pattern matching
  3. 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 in lhs.
  • Exact matches from rhs will be merged if already found in lhs, 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 if lhs already contains one.
  • Static file serving from rhs is ignored if lhs 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 🧅.

Trait Implementations

Formats the value using the given formatter. Read more

Auto Trait Implementations

Blanket Implementations

Gets the TypeId of self. Read more

Immutably borrows from an owned value. Read more

Mutably borrows from an owned value. Read more

Returns the argument unchanged.

Calls U::from(self).

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

Converts the given value to a String. Read more

The type returned in the event of a conversion error.

Performs the conversion.

The type returned in the event of a conversion error.

Performs the conversion.