summaryrefslogtreecommitdiff
path: root/src/main.rs
blob: 0c7766a556e9f778ead778338783b2376d8a7ce7 (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
use crate::error::TwinHError;
use hyper::{
    service::{make_service_fn, service_fn},
    Server,
};
use std::{env, net::IpAddr, net::Ipv4Addr, net::SocketAddr};

mod error;
mod import;
mod models;
mod repo;
mod routes;
mod templates;

#[tokio::main]
async fn main() -> Result<(), TwinHError> {
    // print help if there are no arguments
    let mut args = env::args().skip(1).peekable();
    if args.peek().is_none() {
        print_help();
        return Ok(());
    }

    // set default bind addr
    let mut bind_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5353);

    // handle arguments and options
    while let Some(arg) = args.next() {
        match arg.as_str() {
            "--create-db" => {
                // create a fresh database and quit
                repo::create_new_db()?;
                return Ok(());
            }
            "--import" => {
                // import CSV data into database
                let _source = args
                    .next()
                    .ok_or_else(|| TwinHError(String::from("import source not provided")))?;
                todo!();
            }
            "--addr" => {
                bind_addr.set_ip(args.next().unwrap_or_default().parse()?);
            }
            "--port" => {
                bind_addr.set_port(args.next().unwrap_or_default().parse()?);
            }
            _ => {
                // if not the last argument (the database) then it's unknown
                if args.peek().is_some() {
                    print_help();
                    return Ok(());
                }
            }
        };
    }

    // create primary listener
    let make_svc =
        make_service_fn(move |_conn| async { Ok::<_, TwinHError>(service_fn(routes::router)) });

    // bind server
    let server = Server::bind(&bind_addr).serve(make_svc);
    let graceful = server.with_graceful_shutdown(shutdown_signal());

    // start and run until signal
    graceful.await?;
    Ok(())
}

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

fn print_help() {
    print!(
        "twinh: a home-grown classic car parts catalog\n\
                    \nUsage: twinh [options] <dir>\n\
                    <dir>           your database directory (e.g. /var/db/twinh)\n\
                    \nOptions:\n\
                    --addr          an ip address to bind to (e.g. 127.0.0.1)\n\
                    --port          a port to bind to (e.g. 5353)\n\
                    --create-db     creates a fresh empty database; <dir> cannot exist yet\n\
                    --import-cars   imports CSV car data into the database\n\
                    --import-parts  imports CSV parts data into the database\n\
                    "
    );
}