[][src]Struct roa_core::App

pub struct App<M: Model> { /* fields omitted */ }

The Application of roa.

Example

use roa_core::App;
use log::info;
use async_std::fs::File;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let server = App::new(())
        .gate_fn(|ctx, next| async move {
            info!("{} {}", ctx.method().await, ctx.uri().await);
            next().await
        })
        .end(|ctx| async move {
            ctx.resp_mut().await.write(File::open("assets/welcome.html").await?);
            Ok(())
        })
        .listen("127.0.0.1:8000", |addr| {
            info!("Server is listening on {}", addr)
        })?;
    server.await;
    Ok(())
}

Model

The Model and its State is designed to share data or handler between middlewares. The only one type implemented Model by this crate is (), you can implement your custom Model if neccassary.

use roa_core::{App, Model};
use log::info;
use futures::lock::Mutex;
use std::sync::Arc;
use std::collections::HashMap;

struct AppModel {
    default_id: u64,
    database: Arc<Mutex<HashMap<u64, String>>>,
}

struct AppState {
    id: u64,
    database: Arc<Mutex<HashMap<u64, String>>>,
}

impl AppModel {
    fn new() -> Self {
        Self {
            default_id: 0,
            database: Arc::new(Mutex::new(HashMap::new()))
        }
    }
}

impl Model for AppModel {
    type State = AppState;
    fn new_state(&self) -> Self::State {
        AppState {
            id: self.default_id,
            database: self.database.clone(),
        }
    }
}

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let server = App::new(AppModel::new())
        .gate_fn(|ctx, next| async move {
            ctx.state_mut().await.id = 1;
            next().await
        })
        .end(|ctx| async move {
            let id = ctx.state().await.id;
            ctx.state().await.database.lock().await.get(&id);
            Ok(())
        })
        .listen("127.0.0.1:8000", |addr| {
            info!("Server is listening on {}", addr)
        })?;
    server.await;
    Ok(())
}

Graceful Shutdown

App::listen returns a hyper::Server, which supports graceful shutdown.

use roa_core::App;
use log::info;
use futures::channel::oneshot;

#[async_std::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Prepare some signal for when the server should start shutting down...
    let (tx, rx) = oneshot::channel::<()>();
    let server = App::new(())
        .listen("127.0.0.1:8000", |addr| {
            info!("Server is listening on {}", addr)
        })?
        .with_graceful_shutdown(async {
            rx.await.ok();
        });
    // Await the `server` receiving the signal...
    server.await;
     
    // And later, trigger the signal by calling `tx.send(())`.
    let _ = tx.send(());
    Ok(())
}

Methods

impl<M: Model> App<M>[src]

pub fn new(model: M) -> Self[src]

Construct an application from a model.

pub fn gate(&mut self, middleware: impl Middleware<M::State>) -> &mut Self[src]

Use a middleware.

pub fn gate_fn<F>(
    &mut self,
    middleware: impl 'static + Sync + Send + Fn(Context<M::State>, Next) -> F
) -> &mut Self where
    F: 'static + Send + Future<Output = Result>, 
[src]

A sugar to match a lambda as a middleware.

App::gate cannot match a lambda without parameter type indication.

use roa_core::{App, Next};

let mut app = App::new(());
// app.gate(|_ctx, next| async move { next().await }); compile fails.
app.gate(|_ctx, next: Next| async move { next().await });

However, with App::gate_fn, you can match a lambda without type indication.

use roa_core::{App, Next};

let mut app = App::new(());
app.gate_fn(|_ctx, next| async move { next().await });

pub fn end<F>(&mut self, endpoint: fn(_: Context<M::State>) -> F) -> &mut Self where
    F: 'static + Send + Future<Output = Result>, 
[src]

A sugar to match a function pointer like async fn(Context<S>) -> impl Future and use it as a middleware(endpoint).

As the ducument of Middleware, an endpoint is defined as a template:

use roa_core::{App, Context, Result};
use std::future::Future;

fn endpoint<F>(ctx: Context<()>) -> F
where F: 'static + Send + Future<Output=Result> {
    unimplemented!()
}

However, an async function is not a template, it needs a transfer function to suit for App::gate.

use roa_core::{App, Context, Result, State, Middleware};
use std::future::Future;

async fn endpoint(ctx: Context<()>) -> Result {
    Ok(())
}

fn transfer<S, F>(endpoint: fn(Context<S>) -> F) -> impl Middleware<S>
where S: State,
      F: 'static + Send + Future<Output=Result> {
    endpoint
}

App::new(()).gate(transfer(endpoint));

And App::end is a wrapper of App::gate with this transfer function.

use roa_core::App;
App::new(()).end(|_ctx| async { Ok(()) });

Trait Implementations

impl<M: Model> Clone for App<M>[src]

Auto Trait Implementations

impl<M> !RefUnwindSafe for App<M>

impl<M> Send for App<M>

impl<M> Sync for App<M>

impl<M> Unpin for App<M>

impl<M> !UnwindSafe for App<M>

Blanket Implementations

impl<T> Any for T where
    T: 'static + ?Sized
[src]

impl<T> Borrow<T> for T where
    T: ?Sized
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized
[src]

impl<T> From<T> for T[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

impl<T> ToOwned for T where
    T: Clone
[src]

type Owned = T

The resulting type after obtaining ownership.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

type Error = Infallible

The type returned in the event of a conversion error.

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.