Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ chrono = "0.4.13"
colored = "2.0.0"
flate2 = "1.0.16"
futures = "0.3.5"
hyper = {version = "0.14", features = ["server", "http1", "http2", "tcp"]}
hyper = {version = "0.14", features = ["server", "stream", "http1", "http2", "tcp"]}
log = "0.4.11"
regex = "1.3.9"
serde = "1.0.114"
Expand All @@ -34,3 +34,8 @@ tokio = {version = "1", features = ["full"]}
handlebars = {version = "3.5.1", optional = true}
multer = {version = "1.2.2", optional = true}
rust-s3 = {version = "0.26.4", optional = true}
tokio-util = { version = "0.7.1", features = ["codec"] }

[dev-dependencies]
serde = { version = "1.0", features = ["derive"] }
uuid = { version = "0.8", features = ["serde", "v4"] }
63 changes: 63 additions & 0 deletions examples/hello.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use std::{
net::{Ipv4Addr, SocketAddr},
pin::Pin,
};

use eve_rs::{
listen,
App,
Context,
DefaultContext,
DefaultMiddleware,
Error,
NextHandler,
Request,
};
use futures::Future;

async fn say_hello(
mut context: DefaultContext,
_: NextHandler<DefaultContext, ()>,
) -> Result<DefaultContext, Error<()>> {
let val = "Hello 👋";
context.body(val);
context.content_type("application/html");
Ok(context)
}

#[tokio::main]
async fn main() {
println!("Starting server ...");

let mut app = App::<DefaultContext, DefaultMiddleware<()>, (), ()>::create(
|request: Request, _state: &()| DefaultContext::new(request),
(),
);

app.get(
"/hello",
[DefaultMiddleware::new(|context, next| {
Box::pin(async move { say_hello(context, next).await })
})],
);

let bind_addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 8080));
println!("Listening for connection on port {bind_addr}");

listen(
app,
bind_addr,
None::<Pin<Box<dyn Future<Output = ()>>>>,
)
.await;
}

#[allow(dead_code)]
async fn shutdown_signal() {
tokio::signal::ctrl_c()
.await
.expect("Expected installing Ctrl+C signal handler");

// Handle cleanup tasks
println!("Shutting down the server !!!");
}
48 changes: 48 additions & 0 deletions examples/static_server.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use std::{
env,
net::{Ipv4Addr, SocketAddr},
pin::Pin,
};

use eve_rs::{
default_middlewares::static_file_server::StaticFileServer,
listen,
App,
DefaultContext,
DefaultMiddleware,
Request,
};
use futures::Future;

#[tokio::main]
async fn main() {
println!("Starting server ...");

let mut app = App::<DefaultContext, DefaultMiddleware<()>, (), ()>::create(
|request: Request, _state: &()| DefaultContext::new(request),
(),
);

app.get(
"**",
[DefaultMiddleware::new(move |context, next| {
Box::pin(async move {
StaticFileServer::create(
env::current_dir().unwrap().to_str().unwrap(),
)
.serve(context, next)
.await
})
})],
);

let bind_addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 8080));
println!("Listening for connection on port {bind_addr}");

listen(
app,
bind_addr,
None::<Pin<Box<dyn Future<Output = ()>>>>,
)
.await;
}
118 changes: 118 additions & 0 deletions examples/todos/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use std::{
collections::HashMap,
pin::Pin,
sync::{Arc, RwLock},
};

use eve_rs::{Context, Error, Middleware, NextHandler, Request, Response};
use futures::Future;
use serde::Serialize;
use uuid::Uuid;

// Middleware used by Eve
#[derive(Debug, Clone)]
pub struct Middler {
handler: MiddlewareHandler,
}

type MiddlewareHandler =
fn(
Ctx,
NextHandler<Ctx, ()>,
) -> Pin<Box<dyn Future<Output = Result<Ctx, Error<()>>> + Send>>;

impl Middler {
pub fn new(handler: MiddlewareHandler) -> Self {
Middler { handler }
}
}

#[async_trait::async_trait]
impl Middleware<Ctx, ()> for Middler {
async fn run_middleware(
&self,
context: Ctx,
next: NextHandler<Ctx, ()>,
) -> Result<Ctx, Error<()>> {
(self.handler)(context, next).await
}
}

// Context used by every middleware
#[derive(Debug)]
pub struct Ctx {
// basic data
req: Request,
res: Response,

// buffering streaming data
buffered_request_body: Option<Vec<u8>>,

// additional data
pub state: AppState,
}

impl Ctx {
pub fn new(req: Request, state: &AppState) -> Self {
Ctx {
req,
res: Default::default(),
state: state.clone(),
buffered_request_body: None,
}
}
}

#[async_trait::async_trait]
impl Context for Ctx {
fn get_request(&self) -> &Request {
&self.req
}

fn get_request_mut(&mut self) -> &mut Request {
&mut self.req
}

fn get_response(&self) -> &Response {
&self.res
}

fn take_response(self) -> Response {
self.res
}

fn get_response_mut(&mut self) -> &mut Response {
&mut self.res
}

type ResBodyBuffer = Vec<u8>;
async fn get_buffered_request_body(&mut self) -> &Self::ResBodyBuffer {
match self.buffered_request_body {
Some(ref body) => body,
None => {
let body = hyper::body::to_bytes(self.req.take_body())
.await
.unwrap()
.to_vec();
self.buffered_request_body.get_or_insert(body)
}
}
}
}

// AppState for sharing and persistance
#[derive(Debug, Default, Clone)]
pub struct AppState {
// Since state is cloned for every request,
// size of struct fields should be small
pub db: Db,
}

type Db = Arc<RwLock<HashMap<Uuid, Todo>>>;

#[derive(Debug, Clone, Serialize)]
pub struct Todo {
pub id: Uuid,
pub text: String,
pub completed: bool,
}
52 changes: 52 additions & 0 deletions examples/todos/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
mod app;
mod routes;

use std::{
net::{Ipv4Addr, SocketAddr},
pin::Pin,
};

use eve_rs::{listen, App};
use futures::Future;

use crate::{
app::{AppState, Ctx, Middler},
routes::{create_todos, delete_todos, get_todos, update_todos},
};

macro_rules! middleware {
( $ ($func: ident),+ ) => {
[
$ (
Middler::new(|ctx, next| {
Box::pin(async move { $func(ctx, next).await })
})
),+
]
};
}

#[tokio::main]
async fn main() {
println!("Starting server ...");

let mut app = App::<Ctx, Middler, AppState, ()>::create(
Ctx::new,
AppState::default(),
);

app.get("/todos", middleware![get_todos]);
app.post("/todos", middleware![create_todos]);
app.patch("/todos/:id", middleware![update_todos]);
app.delete("/todos/:id", middleware![delete_todos]);

let bind_addr = SocketAddr::from((Ipv4Addr::LOCALHOST, 8080));
println!("Listening on {bind_addr}");

listen(
app,
bind_addr,
None::<Pin<Box<dyn Future<Output = ()>>>>,
)
.await;
}
Loading