diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/config.rs | 54 | ||||
-rw-r--r-- | src/error.rs | 23 | ||||
-rw-r--r-- | src/main.rs | 76 | ||||
-rw-r--r-- | src/repo/mod.rs | 25 |
4 files changed, 83 insertions, 95 deletions
diff --git a/src/config.rs b/src/config.rs deleted file mode 100644 index da65385..0000000 --- a/src/config.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::TwinHError; -use once_cell::sync::Lazy; -use std::{env, error::Error, net::IpAddr, net::Ipv4Addr, net::SocketAddr}; - -pub static CONFIG_INSTANCE: Lazy<Config> = Lazy::new(|| match Config::new() { - Ok(c) => c, - Err(e) => panic!("twinh: config error: {}", e), -}); - -#[derive(Clone, Debug)] -pub struct Config { - pub bind_addr: SocketAddr, - pub db_path: String, -} - -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<Self, Box<dyn Error>> { - 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, - }; - - 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 943c3ec..ef0ef1d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,6 +1,9 @@ use bincode::Error as bincode_e; +use hyper::Error as hyper_e; use sled::Error as sled_e; use std::fmt; +use std::net::AddrParseError; +use std::num::ParseIntError; #[derive(Debug)] pub struct TwinHError(pub String); @@ -9,7 +12,7 @@ impl std::error::Error for TwinHError {} impl std::fmt::Display for TwinHError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { - write!(f, "Twin H-Power: {}", self.0) + write!(f, "twinh: {}", self.0) } } @@ -24,3 +27,21 @@ impl From<bincode_e> for TwinHError { Self(format!("(de)serialization error: {}", e)) } } + +impl From<AddrParseError> for TwinHError { + fn from(e: AddrParseError) -> Self { + Self(format!("failed to parse addr: {}", e)) + } +} + +impl From<ParseIntError> for TwinHError { + fn from(e: ParseIntError) -> Self { + Self(format!("failed to parse port: {}", e)) + } +} + +impl From<hyper_e> for TwinHError { + fn from(e: hyper_e) -> Self { + Self(format!("server error: {}", e)) + } +} diff --git a/src/main.rs b/src/main.rs index 02fdc2e..0c7766a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,10 @@ -use crate::{config::CONFIG_INSTANCE, error::TwinHError}; +use crate::error::TwinHError; use hyper::{ service::{make_service_fn, service_fn}, Server, }; -use std::env; +use std::{env, net::IpAddr, net::Ipv4Addr, net::SocketAddr}; -mod config; mod error; mod import; mod models; @@ -14,9 +13,19 @@ mod routes; mod templates; #[tokio::main] -async fn main() -> Result<(), Box<dyn std::error::Error>> { - // handle non-config args - for arg in env::args().skip(1) { +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 @@ -25,34 +34,27 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { } "--import" => { // import CSV data into database + let _source = args + .next() + .ok_or_else(|| TwinHError(String::from("import source not provided")))?; todo!(); } - "--help" | "-h" => { - // print help - print!( - "twinh: a home-grown classic car parts catalog\n\ - \nUsage: twinh <dir> [options]\n\ - <dir> 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; <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\ - " - ); - return Ok(()); + "--addr" => { + bind_addr.set_ip(args.next().unwrap_or_default().parse()?); + } + "--port" => { + bind_addr.set_port(args.next().unwrap_or_default().parse()?); } - unknown => { - panic!("unknown option: {}", unknown); + _ => { + // if not the last argument (the database) then it's unknown + if args.peek().is_some() { + print_help(); + return Ok(()); + } } }; } - // 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)) }); @@ -62,10 +64,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> { let graceful = server.with_graceful_shutdown(shutdown_signal()); // start and run until signal - if let Err(e) = graceful.await { - eprintln!("server error: {}", e); - } - + graceful.await?; Ok(()) } @@ -75,3 +74,18 @@ async fn shutdown_signal() { .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\ + " + ); +} diff --git a/src/repo/mod.rs b/src/repo/mod.rs index 45e9ce3..c7992c9 100644 --- a/src/repo/mod.rs +++ b/src/repo/mod.rs @@ -1,4 +1,3 @@ -use crate::config::CONFIG_INSTANCE; use crate::error::TwinHError; use crate::models::Car; use crate::models::Part; @@ -6,20 +5,28 @@ use bincode::deserialize; use constants::*; use once_cell::sync::Lazy; use sled::{Config, Db}; +use std::env; mod constants; -static REPO_INSTANCE: Lazy<Db> = Lazy::new(|| { - Config::default() - .path(&CONFIG_INSTANCE.db_path) - .temporary(true) - .open() - .expect("Couldn't open DB!") +pub static DB_PATH_INSTANCE: Lazy<String> = Lazy::new(|| { + env::args() + .skip(1) + .last() + .expect("database directory not provided") }); +static REPO_INSTANCE: Lazy<Db> = + Lazy::new( + || match Config::default().path(DB_PATH_INSTANCE.as_str()).open() { + Err(e) => panic!("failed to open database: {}", e), + Ok(db) => db, + }, + ); + pub fn create_demo_db() -> Result<(), TwinHError> { let db = sled::Config::default() - .path(&CONFIG_INSTANCE.db_path) + .path(DB_PATH_INSTANCE.as_str()) .create_new(true) .open()?; let cars_tree = db.open_tree(CARS_TREE)?; @@ -28,7 +35,7 @@ pub fn create_demo_db() -> Result<(), TwinHError> { pub fn create_new_db() -> Result<(), TwinHError> { sled::Config::default() - .path(&CONFIG_INSTANCE.db_path) + .path(DB_PATH_INSTANCE.as_str()) .create_new(true) .open()?; Ok(()) |