diff --git a/src/main.rs b/src/main.rs index 5160a42..a914f71 100644 --- a/src/main.rs +++ b/src/main.rs @@ -64,7 +64,6 @@ pub struct State { groups: Vec, /// Cached, flattened mapping of all routes and their destinations. routes: HashMap, - renderer: Handlebars, } #[actix_rt::main] @@ -83,13 +82,11 @@ async fn main() -> Result<(), BunBunError> { // config has default location provided, unwrapping is fine. let conf_file_location = String::from(matches.value_of("config").unwrap()); let conf = read_config(&conf_file_location)?; - let renderer = compile_templates(); let state = Arc::from(RwLock::new(State { public_address: conf.public_address, default_route: conf.default_route, routes: cache_routes(&conf.groups), groups: conf.groups, - renderer, })); // Daemonize after trying to read from config and before watching; allow user @@ -106,6 +103,7 @@ async fn main() -> Result<(), BunBunError> { HttpServer::new(move || { App::new() .data(state.clone()) + .app_data(compile_templates()) .wrap(Logger::default()) .service(routes::hop) .service(routes::list) @@ -195,6 +193,9 @@ fn read_config(config_file_path: &str) -> Result { Ok(serde_yaml::from_str(&config_str)?) } +/// Generates a hashmap of routes from the data structure created by the config +/// file. This should improve runtime performance and is a better solution than +/// just iterating over the config object for every hop resolution. fn cache_routes(groups: &[RouteGroup]) -> HashMap { let mut mapping = HashMap::new(); for group in groups { @@ -233,6 +234,15 @@ fn compile_templates() -> Handlebars { handlebars } +/// Starts the watch on a file, if possible. This will only return an Error if +/// the notify library (used by Hotwatch) fails to initialize, which is +/// considered to be a more serve error as it may be indicative of a low-level +/// problem. If a watch was unsuccessfully obtained (the most common is due to +/// the file not existing), then this will simply warn before returning a watch +/// object. +/// +/// This watch object should be kept in scope as dropping it releases all +/// watches. fn start_watch( state: Arc>, config_file_path: String, @@ -267,6 +277,7 @@ fn start_watch( "Couldn't watch {}: {}. Changes to this file won't be seen!", &config_file_path, e ), - }; + } + Ok(watch) } diff --git a/src/routes.rs b/src/routes.rs index 12ea23c..d07db7f 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -3,7 +3,8 @@ use crate::State; use actix_web::get; use actix_web::http::header; use actix_web::web::{Data, Query}; -use actix_web::{HttpResponse, Responder}; +use actix_web::{HttpRequest, HttpResponse, Responder}; +use handlebars::Handlebars; use itertools::Itertools; use log::debug; use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; @@ -21,9 +22,18 @@ const FRAGMENT_ENCODE_SET: &AsciiSet = &CONTROLS .add(b'+'); #[get("/ls")] -pub async fn list(data: Data>>) -> impl Responder { +pub async fn list( + data: Data>>, + req: HttpRequest, +) -> impl Responder { let data = data.read().unwrap(); - HttpResponse::Ok().body(data.renderer.render("list", &data.groups).unwrap()) + HttpResponse::Ok().body( + req + .app_data::() + .unwrap() + .render("list", &data.groups) + .unwrap(), + ) } #[derive(Deserialize)] @@ -34,6 +44,7 @@ pub struct SearchQuery { #[get("/hop")] pub async fn hop( data: Data>>, + req: HttpRequest, query: Query, ) -> impl Responder { let data = data.read().unwrap(); @@ -42,8 +53,9 @@ pub async fn hop( (Some(path), args) => HttpResponse::Found() .header( header::LOCATION, - data - .renderer + req + .app_data::() + .unwrap() .render_template( &path, &template_args::query( @@ -109,11 +121,15 @@ fn resolve_hop( } #[get("/")] -pub async fn index(data: Data>>) -> impl Responder { +pub async fn index( + data: Data>>, + req: HttpRequest, +) -> impl Responder { let data = data.read().unwrap(); HttpResponse::Ok().body( - data - .renderer + req + .app_data::() + .unwrap() .render( "index", &template_args::hostname(data.public_address.clone()), @@ -123,7 +139,10 @@ pub async fn index(data: Data>>) -> impl Responder { } #[get("/bunbunsearch.xml")] -pub async fn opensearch(data: Data>>) -> impl Responder { +pub async fn opensearch( + data: Data>>, + req: HttpRequest, +) -> impl Responder { let data = data.read().unwrap(); HttpResponse::Ok() .header( @@ -131,8 +150,9 @@ pub async fn opensearch(data: Data>>) -> impl Responder { "application/opensearchdescription+xml", ) .body( - data - .renderer + req + .app_data::() + .unwrap() .render( "opensearch", &template_args::hostname(data.public_address.clone()),