diff --git a/src/main.rs b/src/main.rs index 13a8799..3576caf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,103 +1,22 @@ +mod server; mod simulation; mod state; -use std::{sync::Arc}; - -use futures::SinkExt; -use serde_json::json; -use tera::{Context, Tera}; -use tokio::sync::watch; -use warp::{Filter, reject::Reject, Rejection, ws::{Message, WebSocket}}; - use simulation::Simulation; -use state::State; - -struct WithTemplate { - name: &'static str, - context: Context, -} +use server::setup_server; #[tokio::main] async fn main() { let mut simulation = Simulation::new(); let state_channel = simulation.state(); - let tr = match Tera::new("templates/**/*.tera") { - Ok(t) => t, - Err(e) => { - println!("Parsing error(s): {}", e); - ::std::process::exit(1); - } - }; - let tr = Arc::new(tr); - let tera = move || { - let tr = tr.clone(); - move |with_template| render(with_template, tr.clone()) - }; - let state = move || { - let state_channel = state_channel.clone(); - move || state_channel.clone() - }; - let sim = tokio::spawn(async move { simulation.run().await; }); - let hello = warp::get() - .and(warp::path::end()) - .map(state()) - .and_then(|channel| async move { get_state(channel).await }) - .map(|state| WithTemplate { - name: "stuff.tera", - context: Context::from_serialize(state).unwrap(), - }) - .map(tera()); - - let wsstate = state(); - let ws = warp::path("state") - .and(warp::ws()) - .map(move |ws: warp::ws::Ws| (ws, wsstate())) - .map(|(ws, state): (warp::ws::Ws, watch::Receiver)| { - ws.on_upgrade(move |websocket| watch_state(websocket, state)) - }); - - let server = - tokio::spawn(async move { warp::serve(hello.or(ws)).run(([127, 0, 0, 1], 3030)).await }); + let server = tokio::spawn(async move { + setup_server(state_channel).await; + }); tokio::try_join!(sim, server).unwrap(); } - -async fn watch_state(mut ws: WebSocket, mut state_channel: watch::Receiver) { - loop { - let state = state_channel.recv().await; - - let result = match state { - Some(state) => ws.send(Message::text(json!(state).to_string())).await, - None => break - }; - - if let Err(_) = result { - break; - } - }; -} - -#[derive(Debug)] -struct SimulationUnavailable; - -impl Reject for SimulationUnavailable {} - -async fn get_state(mut channel: watch::Receiver) -> Result { - match channel.recv().await { - Some(state) => Ok(state), - None => Err(warp::reject::custom(SimulationUnavailable)) - } -} - -fn render(template: WithTemplate, tera: Arc) -> impl warp::Reply { - let render = tera - .render(template.name, &template.context) - .unwrap_or_else(|err| err.to_string()); - - warp::reply::html(render) -} diff --git a/src/server.rs b/src/server.rs new file mode 100644 index 0000000..a1802fe --- /dev/null +++ b/src/server.rs @@ -0,0 +1,90 @@ + +use std::sync::Arc; + +use futures::SinkExt; +use serde_json::json; +use tera::{Context, Tera}; +use tokio::sync::watch; +use warp::{Filter, reject::Reject, Rejection, ws::{Message, WebSocket}}; + +use crate::state::State; + +pub async fn setup_server(state_channel: watch::Receiver) { + let tr = match Tera::new("templates/**/*.tera") { + Ok(t) => t, + Err(e) => { + println!("Parsing error(s): {}", e); + ::std::process::exit(1); + } + }; + let tr = Arc::new(tr); + let tera = move || { + let tr = tr.clone(); + move |with_template| render(with_template, tr.clone()) + }; + let state = move || { + let state_channel = state_channel.clone(); + move || state_channel.clone() + }; + + let index = warp::get() + .and(warp::path::end()) + .map(state()) + .and_then(|channel| async move { get_state(channel).await }) + .map(|state| WithTemplate { + name: "stuff.tera", + context: Context::from_serialize(state).unwrap(), + }) + .map(tera()); + + let wsstate = state(); + let ws = warp::path("state") + .and(warp::ws()) + .map(move |ws: warp::ws::Ws| (ws, wsstate())) + .map(|(ws, state): (warp::ws::Ws, watch::Receiver)| { + ws.on_upgrade(move |websocket| watch_state(websocket, state)) + }); + + warp::serve(index.or(ws)).run(([127, 0, 0, 1], 3030)).await; +} + +struct WithTemplate { + name: &'static str, + context: Context, +} + +async fn watch_state(mut ws: WebSocket, mut state_channel: watch::Receiver) { + loop { + let state = state_channel.recv().await; + + let result = match state { + Some(state) => ws.send(Message::text(json!(state).to_string())).await, + None => break + }; + + if let Err(_) = result { + break; + } + }; +} + + +#[derive(Debug)] +struct SimulationUnavailable; + +impl Reject for SimulationUnavailable {} + +async fn get_state(mut channel: watch::Receiver) -> Result { + match channel.recv().await { + Some(state) => Ok(state), + None => Err(warp::reject::custom(SimulationUnavailable)) + } +} + +fn render(template: WithTemplate, tera: Arc) -> impl warp::Reply { + let render = tera + .render(template.name, &template.context) + .unwrap_or_else(|err| err.to_string()); + + warp::reply::html(render) +}