diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2020-06-04 02:25:35 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2020-06-04 02:25:35 -0400 |
| commit | deb89e35533ecada8d0e668bd5a93c62a2ae5549 (patch) | |
| tree | fd4244d86cecc8df70163813dc97d20b737b8e3a | |
| parent | 0910b26375542ed3d49e54d9ce61855d2eb8cb84 (diff) | |
fmt
| -rw-r--r-- | src/bin/web.rs | 13 | ||||
| -rw-r--r-- | src/twelve.rs | 68 | ||||
| -rw-r--r-- | src/view.rs | 107 |
3 files changed, 85 insertions, 103 deletions
diff --git a/src/bin/web.rs b/src/bin/web.rs index 3186207..6913263 100644 --- a/src/bin/web.rs +++ b/src/bin/web.rs @@ -23,14 +23,9 @@ async fn main() -> Result { let service = view::make_service()?; - let app_factory = move || - App::new() - .configure(|cfg| service(cfg)); - - HttpServer::new(app_factory) - .bind(port)? - .run() - .await?; - + let app_factory = move || App::new().configure(|cfg| service(cfg)); + + HttpServer::new(app_factory).bind(port)?.run().await?; + Ok(()) } diff --git a/src/twelve.rs b/src/twelve.rs index ea63bf4..08bccf0 100644 --- a/src/twelve.rs +++ b/src/twelve.rs @@ -1,27 +1,27 @@ //! A [twelve-factor application][1] reads [its configuration][2] from the environment. -//! +//! //! In many cases, "read" directly maps to the target binary inspecting the //! OS-provided environment dictionary. This module provides supporting tools //! for reading configuration data from the environment, via `std::env`, and //! converting it to useful types. -//! +//! //! [1]: https://12factor.net/ //! [2]: https://12factor.net/config use std::env; use std::io; -use std::net::{Ipv4Addr, Ipv6Addr, IpAddr, SocketAddr, ToSocketAddrs}; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs}; use std::num; use thiserror::Error; /// Errors that can arise when reading a port number from the environment. -/// +/// /// For convenience when returning errors into `main`, this type can be /// converted to std::io::Error. #[derive(Error, Debug)] pub enum Error { /// PORT was set, but contained a non-unicode value that sys::env can't parse. - /// + /// /// For obvious reasons, this cannot be converted to a port number. Rather /// than ignoring this error, we report it, so that misconfiguration can be /// detected early. @@ -31,7 +31,7 @@ pub enum Error { source: env::VarError, }, /// PORT was set, but was set to a non-numeric value.FnOnce - /// + /// /// PORT can only be used to select a port number if numeric. Rather than /// ignoring this error, we report it, so that misconfiguration can be /// detected early. @@ -39,18 +39,18 @@ pub enum Error { ParseError { #[from] source: num::ParseIntError, - } + }, } /// A listen address consisting of only a port number. -/// +/// /// Listening on this address will bind to both the ip4 and ip6 addresses on the /// current host, assuming both ip4 and ip6 are supported. #[derive(Debug, Clone)] pub struct PortAddr { /// When used in an std::net::SocketAddr context, this is the port number to /// bind on. - port: u16 + port: u16, } fn v4(port_addr: &PortAddr) -> SocketAddr { @@ -65,28 +65,25 @@ impl ToSocketAddrs for PortAddr { type Iter = std::vec::IntoIter<SocketAddr>; fn to_socket_addrs(&self) -> io::Result<Self::Iter> { - let addrs = vec![ - v6(self), - v4(self), - ]; + let addrs = vec![v6(self), v4(self)]; Ok(addrs.into_iter()) } } /// Query the environment for a port number. -/// +/// /// This will read the PORT environment variable. If set, it will use the value /// (as a number). If it's unset, then this will use the passed `default_port` /// number to choose the app's default port. If the PORT environment variable /// is set but cannot be interpreted as a port number, this will return an error /// indicating why, to assist the user in correcting their configuration. /// # Examples -/// +/// /// ``` /// use std::net::TcpListener; /// mod twelve; -/// +/// /// // Listen on port 3000 (or $PORT if set), on global ip4 and ip6 interfaces. /// let port = twelve::port(3000)?; /// let listener = TcpListener::bind(port); @@ -100,9 +97,7 @@ pub fn port(default_port: u16) -> Result<PortAddr, Error> { }, }; - Ok(PortAddr{ - port, - }) + Ok(PortAddr { port }) } #[cfg(test)] @@ -127,31 +122,28 @@ mod tests { #[quickcheck] fn port_addr_as_socket_addr_has_v4(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs() - .unwrap() - .collect::<Vec<_>>(); + let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); - socket_addrs.iter() + socket_addrs + .iter() .any(|&socket_addr| socket_addr.is_ipv4()) } #[quickcheck] fn port_addr_as_socket_addr_has_v6(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs() - .unwrap() - .collect::<Vec<_>>(); + let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); - socket_addrs.iter() + socket_addrs + .iter() .any(|&socket_addr| socket_addr.is_ipv6()) } #[quickcheck] fn port_addr_as_socket_addr_all_have_port(addr: PortAddr) -> bool { - let socket_addrs = addr.to_socket_addrs() - .unwrap() - .collect::<Vec<_>>(); + let socket_addrs = addr.to_socket_addrs().unwrap().collect::<Vec<_>>(); - socket_addrs.iter() + socket_addrs + .iter() .all(|&socket_addr| socket_addr.port() == addr.port) } @@ -172,16 +164,14 @@ mod tests { // attempts to accelerate testing by running multiple threads, but this // causes race conditions as test A stomps on state used by test B. // Serialize tests through a mutex. - // + // // Huge hack. static ref ENV_MUTEX: Mutex<Runner> = Mutex::new(Runner::default()); } // Runs a body with ENV_MUTEX locked. Easier to write. fn env_locked<T>(f: impl FnOnce() -> T) -> T { - ENV_MUTEX.lock() - .unwrap() - .run(f) + ENV_MUTEX.lock().unwrap().run(f) } #[quickcheck] @@ -193,8 +183,7 @@ mod tests { env_locked(|| { env::set_var("PORT", env_port.to_string()); - let read_port = port(default_port) - .unwrap(); + let read_port = port(default_port).unwrap(); TestResult::from_bool(read_port.port == env_port) }) @@ -220,8 +209,7 @@ mod tests { env_locked(|| { env::remove_var("PORT"); - let read_port = port(default_port) - .unwrap(); + let read_port = port(default_port).unwrap(); read_port.port == default_port }) @@ -239,4 +227,4 @@ mod tests { assert!(result.is_err()); }) } -}
\ No newline at end of file +} diff --git a/src/view.rs b/src/view.rs index eb61ba9..5f2e3d9 100644 --- a/src/view.rs +++ b/src/view.rs @@ -1,54 +1,54 @@ //! HTML resources that help users troubleshoot problems. -//! +//! //! This provides a single endpoint, as well as necessary application data to //! power it. The endpoint can be mounted on an actix_web App using the exposed //! `make_service(…)` function. -//! +//! //! # Examples -//! +//! //! ``` //! let service = view::make_service()?; //! let app_factory = move || //! App::new() //! .configure(|cfg| service(cfg)); -//! +//! //! HttpServer::new(app_factory) //! .bind(port)? //! .run() //! .await?; //! ``` -//! +//! //! # Endpoints -//! +//! //! * `/` (`GET`): an HTML page suggesting one thing to check. -//! +//! //! Takes an optional `item` URL parameter, which must be an integer between 0 //! and the number of options available (not provided). If `item` is provided, //! this endpoint returns a fixed result (the `item`th suggestion in the //! backing data); otherwise, it returns a randomly-selected result, for //! fortuitous suggesting. -//! +//! //! The returned page is always `text/html` on success. Invalid `item` indices //! will return an error. -//! +//! //! # Data -//! +//! //! This module creates a data item in the configured application, consisting of //! a list of strings loaded from a YAML constant. The data comes from a file in //! this module parsed at compile time — our target deployment environments //! don't support modifying it without triggering a rebuild anyways. It's parsed //! on startup, however, and invalid data can cause `make_service` to fail. -//! +//! //! When adding suggestions, add them at the end. This will ensure that existing //! links to existing items are not invalidated or changed - the `item` //! parameter to the `/` endpoint is a literal index into this list. -use actix_web::{get, error, web, Responder}; -use maud::{DOCTYPE, html, Markup, PreEscaped}; -use pulldown_cmark::{Parser, Options, html}; -use rand::thread_rng; +use actix_web::{error, get, web, Responder}; +use maud::{html, Markup, PreEscaped, DOCTYPE}; +use pulldown_cmark::{html, Options, Parser}; use rand::seq::SliceRandom; -use serde::{Serialize, Deserialize}; +use rand::thread_rng; +use serde::{Deserialize, Serialize}; use serde_urlencoded::ser; use std::iter; use thiserror::Error; @@ -100,18 +100,13 @@ struct ItemQuery { impl From<&usize> for ItemQuery { fn from(idx: &usize) -> Self { - ItemQuery { - item: Some(*idx), - } + ItemQuery { item: Some(*idx) } } } type MarkupResult = Result<Markup, error::Error>; -fn page( - head: impl FnOnce() -> MarkupResult, - body: impl FnOnce() -> MarkupResult, -) -> MarkupResult { +fn page(head: impl FnOnce() -> MarkupResult, body: impl FnOnce() -> MarkupResult) -> MarkupResult { Ok(html! { (DOCTYPE) html { @@ -126,7 +121,7 @@ fn page( } fn stylesheet() -> Markup { - html!{ + html! { style { (PreEscaped(" body { @@ -165,7 +160,11 @@ fn og_card(title: &str, description: &str) -> Markup { } } -fn suggestion_link(req: &impl Urls, query: ItemQuery, body: impl FnOnce() -> MarkupResult) -> MarkupResult { +fn suggestion_link( + req: &impl Urls, + query: ItemQuery, + body: impl FnOnce() -> MarkupResult, +) -> MarkupResult { Ok(html! { p { a href=( req.index_url(query)? ) { (body()?) } @@ -187,23 +186,27 @@ fn github_badge(repo: &str) -> Markup { fn index_view(req: impl Urls, idx: &usize, thing: &Thing) -> MarkupResult { page( - || Ok(html! { - title { (thing.markdown) } - (stylesheet()) - (og_card("Troubleshooting suggestion", &thing.markdown)) - }), - || Ok(html! { - section { - (PreEscaped(&thing.html)) - (suggestion_link(&req, ItemQuery::default(), || Ok(html! { - "That wasn't it, suggest something else." - }))?) - (suggestion_link(&req, ItemQuery::from(idx), || Ok(html! { - "Share this troubleshooting suggestion." - }))?) - } - (github_badge("ojacobson/things-to-check")) - }) + || { + Ok(html! { + title { (thing.markdown) } + (stylesheet()) + (og_card("Troubleshooting suggestion", &thing.markdown)) + }) + }, + || { + Ok(html! { + section { + (PreEscaped(&thing.html)) + (suggestion_link(&req, ItemQuery::default(), || Ok(html! { + "That wasn't it, suggest something else." + }))?) + (suggestion_link(&req, ItemQuery::from(idx), || Ok(html! { + "Share this troubleshooting suggestion." + }))?) + } + (github_badge("ojacobson/things-to-check")) + }) + }, ) } @@ -223,8 +226,7 @@ async fn index( None => return Err(error::ErrorNotFound("Not found")), }; - Ok(index_view(req, index, thing)? - .with_header("Cache-Control", "no-store")) + Ok(index_view(req, index, thing)?.with_header("Cache-Control", "no-store")) } const THINGS: &str = include_str!("things-to-check.yml"); @@ -243,10 +245,7 @@ impl From<String> for Thing { let mut html = String::new(); html::push_html(&mut html, parser); - Thing{ - markdown, - html, - } + Thing { markdown, html } } } @@ -257,10 +256,11 @@ fn load_things(src: &str) -> serde_yaml::Result<Things> { let raw_things: Vec<String> = serde_yaml::from_str(src)?; Ok(Things( - raw_things.into_iter() + raw_things + .into_iter() .map(Thing::from) .enumerate() - .collect() + .collect(), )) } @@ -270,18 +270,17 @@ pub enum Error { /// Indicates that the included YAML was invalid in some way. This is only /// fixable by recompiling the program with correct YAML. #[error("Unable to load Things To Check YAML: {0}")] - DeserializeError(#[from] serde_yaml::Error) + DeserializeError(#[from] serde_yaml::Error), } /// Set up an instance of this service. -/// +/// /// The returned function will configure any actix-web App with the necessary /// state to tell people how to troubleshoot problems. pub fn make_service() -> Result<impl Fn(&mut web::ServiceConfig) + Clone, Error> { let things = load_things(THINGS)?; Ok(move |cfg: &mut web::ServiceConfig| { - cfg.data(things.clone()) - .service(index); + cfg.data(things.clone()).service(index); }) } |
