From 93d9a3b8d6984a8f113a30fea523607162e71589 Mon Sep 17 00:00:00 2001 From: "Adam T. Carpenter" Date: Fri, 9 Apr 2021 20:26:19 -0400 Subject: better startup configuration --- src/config.rs | 56 +++++++++++++++++++++++++++++++++++++++++++------------ src/error.rs | 15 ++++++++------- src/import/mod.rs | 1 + src/main.rs | 48 +++++++++++++++++++++++++++++++++++++++++++---- src/repo/mod.rs | 6 +++--- 5 files changed, 100 insertions(+), 26 deletions(-) create mode 100644 src/import/mod.rs diff --git a/src/config.rs b/src/config.rs index b3c9715..da65385 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,22 +1,54 @@ +use crate::TwinHError; use once_cell::sync::Lazy; -use std::{env::var, error::Error, net::SocketAddr}; +use std::{env, error::Error, net::IpAddr, net::Ipv4Addr, net::SocketAddr}; -pub static CONFIG_INSTANCE: Lazy = - Lazy::new(|| AppConfig::from_env().expect("Error loading config")); +pub static CONFIG_INSTANCE: Lazy = Lazy::new(|| match Config::new() { + Ok(c) => c, + Err(e) => panic!("twinh: config error: {}", e), +}); #[derive(Clone, Debug)] -pub struct AppConfig { - pub addr: SocketAddr, - pub db_uri: String, +pub struct Config { + pub bind_addr: SocketAddr, + pub db_path: String, } -impl AppConfig { - pub fn from_env() -> Result> { - let new = Self { - addr: var("TWINH_ADDR")?.parse()?, - db_uri: var("TWINH_DB_URI")?, +impl Default for Config { + fn default() -> Self { + Self { + bind_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5353), + db_path: String::from("/var/db/twinh"), + } + } +} + +impl Config { + fn new() -> Result> { + let mut args = env::args().skip(1); + + let db_path = args + .next() + .ok_or_else(|| TwinHError(String::from("database directory not provided")))?; + + let mut config = Config { + bind_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 5353), + db_path, }; - Ok(new) + while let Some(arg) = args.next() { + match arg.as_str() { + "--addr" => { + let addr = args.next().unwrap_or_default().parse()?; + config.bind_addr.set_ip(addr); + } + "--port" => { + let port = args.next().unwrap_or_default().parse()?; + config.bind_addr.set_port(port); + } + _ => {} + }; + } + + Ok(config) } } diff --git a/src/error.rs b/src/error.rs index 06d7d1e..943c3ec 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,25 +1,26 @@ use bincode::Error as bincode_e; use sled::Error as sled_e; +use std::fmt; #[derive(Debug)] -pub struct TwinHError; +pub struct TwinHError(pub String); impl std::error::Error for TwinHError {} impl std::fmt::Display for TwinHError { - fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> { - todo!() + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + write!(f, "Twin H-Power: {}", self.0) } } impl From for TwinHError { - fn from(_: sled_e) -> Self { - todo!() + fn from(e: sled_e) -> Self { + Self(format!("database error: {}", e)) } } impl From for TwinHError { - fn from(_: bincode_e) -> Self { - todo!() + fn from(e: bincode_e) -> Self { + Self(format!("(de)serialization error: {}", e)) } } diff --git a/src/import/mod.rs b/src/import/mod.rs new file mode 100644 index 0000000..bac9966 --- /dev/null +++ b/src/import/mod.rs @@ -0,0 +1 @@ +const test: i32 = 4; diff --git a/src/main.rs b/src/main.rs index 4a5a8b1..02fdc2e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,13 @@ -use crate::config::CONFIG_INSTANCE; -use crate::error::TwinHError; +use crate::{config::CONFIG_INSTANCE, error::TwinHError}; use hyper::{ service::{make_service_fn, service_fn}, Server, }; +use std::env; mod config; mod error; +mod import; mod models; mod repo; mod routes; @@ -14,14 +15,53 @@ mod templates; #[tokio::main] async fn main() -> Result<(), Box> { - let addr = CONFIG_INSTANCE.addr; + // handle non-config args + for arg in env::args().skip(1) { + match arg.as_str() { + "--create-db" => { + // create a fresh database and quit + repo::create_new_db()?; + return Ok(()); + } + "--import" => { + // import CSV data into database + todo!(); + } + "--help" | "-h" => { + // print help + print!( + "twinh: a home-grown classic car parts catalog\n\ + \nUsage: twinh [options]\n\ + your database directory (e.g. /var/db/twinh)\n\ + \nOptions:\n\ + --help | -h prints this message and exits\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; cannot exist yet\n\ + --import-cars imports CSV car data into the database\n\ + --import-parts imports CSV parts data into the database\n\ + " + ); + return Ok(()); + } + unknown => { + panic!("unknown option: {}", unknown); + } + }; + } + + // gather config + let bind_addr = CONFIG_INSTANCE.bind_addr; + // create primary listener let make_svc = make_service_fn(move |_conn| async { Ok::<_, TwinHError>(service_fn(routes::router)) }); - let server = Server::bind(&addr).serve(make_svc); + // bind server + let server = Server::bind(&bind_addr).serve(make_svc); let graceful = server.with_graceful_shutdown(shutdown_signal()); + // start and run until signal if let Err(e) = graceful.await { eprintln!("server error: {}", e); } diff --git a/src/repo/mod.rs b/src/repo/mod.rs index f0bb95c..45e9ce3 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -11,7 +11,7 @@ mod constants; static REPO_INSTANCE: Lazy = Lazy::new(|| { Config::default() - .path(&CONFIG_INSTANCE.db_uri) + .path(&CONFIG_INSTANCE.db_path) .temporary(true) .open() .expect("Couldn't open DB!") @@ -19,7 +19,7 @@ static REPO_INSTANCE: Lazy = Lazy::new(|| { pub fn create_demo_db() -> Result<(), TwinHError> { let db = sled::Config::default() - .path(&CONFIG_INSTANCE.db_uri) + .path(&CONFIG_INSTANCE.db_path) .create_new(true) .open()?; let cars_tree = db.open_tree(CARS_TREE)?; @@ -28,7 +28,7 @@ pub fn create_demo_db() -> Result<(), TwinHError> { pub fn create_new_db() -> Result<(), TwinHError> { sled::Config::default() - .path(&CONFIG_INSTANCE.db_uri) + .path(&CONFIG_INSTANCE.db_path) .create_new(true) .open()?; Ok(()) -- cgit v1.2.3