summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: b71df18cb1a265de68d2c6f65c7332667eda818b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
use handlebars::Handlebars;
use hyper::service::{make_service_fn, service_fn};
use hyper::Method;
use hyper::StatusCode;
use hyper::{Body, Request, Response, Server};
use serde::{Deserialize, Serialize};
use std::convert::Infallible;

mod config;
mod models;
mod templates;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = config::INSTANCE.addr;

    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());

    // run until shutdown signal
    if let Err(e) = graceful.await {
        eprintln!("server error: {}", e);
    }

    Ok(())
}

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") => get_parts(req).await,
        _ => Ok(Response::builder()
            .status(StatusCode::NOT_FOUND)
            .body("Not found.".into())
            .unwrap()),
    }
}

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::<PartsQuery>(query).unwrap();
    let mut reg = Handlebars::new();
    reg.register_template_string("index", templates::INDEX_T)
        .unwrap();
    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") {
            data.models = Some(vec!["Hornet".into(), "Wasp".into(), "Jet".into()]);
        } else if make.eq("Essex") {
            data.models = Some(vec!["Super Six".into()]);
        }
        data.selected_make = Some(make);
    }

    data.parts = Some(Vec::new());
    if let Some(model) = filter.model {
        data.parts = Some(vec!["1".into(), "2".into(), "3".into()]);
        data.selected_model = Some(model);
    }

    let result = reg.render("index", &data).unwrap();
    Ok(Response::new(result.into()))
}

async fn shutdown_signal() {
    // Wait for CTRL+C
    tokio::signal::ctrl_c()
        .await
        .expect("failed to install CTRL+C signal handler");
}

#[derive(Debug, Deserialize)]
struct PartsQuery {
    make: Option<String>,
    year: Option<String>,
    model: Option<String>,
    engine: Option<String>,
}

#[derive(Debug, Serialize, Default)]
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>>,
}