diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 22 | ||||
-rw-r--r-- | src/main.rs | 43 | ||||
-rw-r--r-- | src/models/mod.rs | 43 | ||||
-rw-r--r-- | src/templates/index.html | 92 |
4 files changed, 165 insertions, 35 deletions
diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..7798967 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,22 @@ +use once_cell::sync::Lazy; +use std::{env::var, error::Error, net::SocketAddr}; + +pub static INSTANCE: Lazy<AppConfig> = + Lazy::new(|| AppConfig::from_env().expect("Error loading config")); + +#[derive(Clone, Debug)] +pub struct AppConfig { + pub addr: SocketAddr, + pub db_uri: String, +} + +impl AppConfig { + pub fn from_env() -> Result<Self, Box<dyn Error>> { + let new = Self { + addr: var("TWINH_ADDR")?.parse()?, + db_uri: var("TWINH_DB_URI")?, + }; + + Ok(new) + } +} diff --git a/src/main.rs b/src/main.rs index 2f5bdf4..b71df18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,22 @@ use hyper::StatusCode; use hyper::{Body, Request, Response, Server}; use serde::{Deserialize, Serialize}; use std::convert::Infallible; -use std::net::SocketAddr; +mod config; +mod models; mod templates; #[tokio::main] -async fn main() { - // bind to 127.0.0.1:3000 - let addr = SocketAddr::from(([127, 0, 0, 1], 3000)); +async fn main() -> Result<(), Box<dyn std::error::Error>> { + let addr = config::INSTANCE.addr; - let make_svc = make_service_fn(|_conn| async { Ok::<_, Infallible>(service_fn(router)) }); + let client = mongodb::Client::with_uri_str(&config::INSTANCE.db_uri).await?; + + let make_svc = make_service_fn(move |_conn| { + let client = client.clone(); + + async { Ok::<_, Infallible>(service_fn(move |req| router(req, client.to_owned()))) } + }); let server = Server::bind(&addr).serve(make_svc); let graceful = server.with_graceful_shutdown(shutdown_signal()); @@ -23,12 +29,17 @@ async fn main() { if let Err(e) = graceful.await { eprintln!("server error: {}", e); } + + Ok(()) } -async fn router(req: Request<Body>) -> Result<Response<Body>, Infallible> { +async fn router( + req: Request<Body>, + _client: mongodb::Client, +) -> Result<Response<Body>, Infallible> { match (req.method(), req.uri().path()) { (&Method::GET, "/") | (&Method::GET, "/index.html") => Ok(Response::new("Welcome!".into())), - (&Method::GET, "/parts") | (&Method::GET, "/parts/index.html") => parts(req).await, + (&Method::GET, "/parts") | (&Method::GET, "/parts/index.html") => get_parts(req).await, _ => Ok(Response::builder() .status(StatusCode::NOT_FOUND) .body("Not found.".into()) @@ -36,14 +47,14 @@ async fn router(req: Request<Body>) -> Result<Response<Body>, Infallible> { } } -async fn parts(req: Request<Body>) -> Result<Response<Body>, Infallible> { +async fn get_parts(req: Request<Body>) -> Result<Response<Body>, Infallible> { let query = req.uri().query().unwrap_or_default(); - let filter = serde_urlencoded::de::from_str::<PartsRequest>(query).unwrap(); + let filter = serde_urlencoded::de::from_str::<PartsQuery>(query).unwrap(); let mut reg = Handlebars::new(); reg.register_template_string("index", templates::INDEX_T) .unwrap(); - let mut data = PartsView::default(); - data.makes = Some(vec!["Hudson".into(), "Essex".into()]); + let mut data = PartsData::default(); + data.makes = Some(vec!["Hudson".into(), "Essex".into(), "Terraplane".into()]); if let Some(make) = filter.make { if make.eq("Hudson") { @@ -72,16 +83,22 @@ async fn shutdown_signal() { } #[derive(Debug, Deserialize)] -struct PartsRequest { +struct PartsQuery { make: Option<String>, + year: Option<String>, model: Option<String>, + engine: Option<String>, } #[derive(Debug, Serialize, Default)] -struct PartsView { +struct PartsData { makes: Option<Vec<String>>, + years: Option<Vec<String>>, models: Option<Vec<String>>, + engines: Option<Vec<String>>, selected_make: Option<String>, + selected_year: Option<String>, selected_model: Option<String>, + selected_engine: Option<String>, parts: Option<Vec<String>>, } diff --git a/src/models/mod.rs b/src/models/mod.rs new file mode 100644 index 0000000..d7ff47a --- /dev/null +++ b/src/models/mod.rs @@ -0,0 +1,43 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct Car { + year: u16, // Yes, in 30767 years I will address this. + make: String, + model: String, + engine: Engine, + doors: u8, + transmission: Transmission, +} + +#[derive(Serialize, Deserialize)] +pub struct Part { + name: String, + compatible_cars: Vec<Car>, + sources: Vec<Source>, + categories: Vec<Category>, +} + +#[derive(Serialize, Deserialize)] +pub enum Source { + Uri(String), +} + +#[derive(Serialize, Deserialize)] +pub struct Engine { + cylinders: u8, + displacement: u16, + layout: Layout, +} + +#[derive(Serialize, Deserialize)] +pub enum Layout { + I, + V, +} + +#[derive(Serialize, Deserialize)] +pub struct Transmission(String); + +#[derive(Serialize, Deserialize)] +pub struct Category(String); diff --git a/src/templates/index.html b/src/templates/index.html index 683891c..4663f5d 100644 --- a/src/templates/index.html +++ b/src/templates/index.html @@ -4,34 +4,82 @@ <meta charset="UTF-8" /> <style> - input[type="submit"] { + .make { display: block; + background: none; + border: 0 none; + cursor: pointer; + } + + .model { + display: block; + background: none; + border: 0 none; + cursor: pointer; + margin-left: 20px; + } + + .sidebar { + width: 300px; + height: 100%; + position: fixed; + z-index: 1; + } + + .main { + margin-left: 300px; + height: 100%; } </style> </head> <body> - <h1>Parts Catalog</h1> - {{#each makes}} - <form action="/parts"> - {{#if (eq this ../selected_make)}} - <input disabled type="submit" name="make" value="{{ this }}" /> - <input type="hidden" name="make" value="{{ ../selected_make }}" /> - {{#each ../models}} - {{#if (eq this ../../selected_model)}} - <input disabled type="submit" name="model" value="{{ this }}" /> - Parts for {{ this }} - {{else}} - <input type="submit" name="model" value="{{ this }}" /> - {{/if}} + <div class="sidebar"> + <h1>Parts Catalog</h1> + {{#each makes}} + <form action="/parts"> + {{#if (eq this ../selected_make)}} + <button disabled class="make"> + {{ this }} + </button> + <input type="hidden" name="make" value="{{ ../selected_make }}" /> + {{#each ../models}} + {{#if (eq this ../../selected_model)}} + <button disabled class="model">{{ this }}</button> + {{else}} + <button class="model" name="model" value="{{ this }}"> + {{ this }} + </button> + {{/if}} + {{else}} + <button disabled class="model">(No models found)</button> + {{/each}} + {{else}} + <button class="make" name="make" value="{{ this }}">{{ this }}</button> + {{/if}} + </form> {{else}} - No models found. + No makes found. {{/each}} - {{else}} - <input type="submit" name="make" value="{{ this }}" /> - {{/if}} - </form> - {{else}} - No makes found. - {{/each}} + </div> + + <div class="main"> + <form action="/parts"> + <input type="hidden" name="make" value="{{ selected_make }}" /> + <input type="hidden" name="model" value="{{ selected_model }}" /> + <input + type="search" + name="search" + placeholder="Part #, Name, Source, etc." + /> + <button>Search</button> + </form> + <table> + {{#each parts}} + <tr> + <td>{{ this }}</td> + </tr> + {{/each}} + </table> + </div> </body> </html> |