summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam T. Carpenter <atc@53hor.net>2021-04-15 20:03:01 -0400
committerAdam T. Carpenter <atc@53hor.net>2021-04-15 20:03:01 -0400
commitd83fe68ed51016bbb87d83aa512ef8b9d3f0780e (patch)
tree05f93d34464692c706288f7bb3497f55e4539b09
parent93d9a3b8d6984a8f113a30fea523607162e71589 (diff)
downloadtwinh-d83fe68ed51016bbb87d83aa512ef8b9d3f0780e.tar.xz
twinh-d83fe68ed51016bbb87d83aa512ef8b9d3f0780e.zip
split config into modules that actually need it, started parsing args
-rw-r--r--artifacts/layout.html28
-rw-r--r--artifacts/ui-demo.html210
-rw-r--r--src/config.rs54
-rw-r--r--src/error.rs23
-rw-r--r--src/main.rs76
-rw-r--r--src/repo/mod.rs25
6 files changed, 83 insertions, 333 deletions
diff --git a/artifacts/layout.html b/artifacts/layout.html
deleted file mode 100644
index a06d6d0..0000000
--- a/artifacts/layout.html
+++ /dev/null
@@ -1,28 +0,0 @@
-<!DOCTYPE html>
-<html>
- <body>
- <ul>
- <li>/: root, filter, search</li>
- <li>/parts: raw parts table</li>
- <li>/parts.csv: CSV download of parts table</li>
- <li>/cars: raw cars table</li>
- <li>/cars.csv: CSV download of cars table</li>
- <li>/suggestions: suggestion input form and appoval list</li>
- <li>
- /login: all the logins
- <ul>
- <li>/login/facebook</li>
- <li>/login/google</li>
- <li>/login/microsoft</li>
- <li>/login/gitea</li>
- </ul>
- </li>
- <li>/api/v1: JSON API</li>
- <ul>
- <li>/api/v1/parts</li>
- <li>/api/v1/cars</li>
- <li>/api/v1/suggestions</li>
- </ul>
- </ul>
- </body>
-</html>
diff --git a/artifacts/ui-demo.html b/artifacts/ui-demo.html
deleted file mode 100644
index 09dc9cb..0000000
--- a/artifacts/ui-demo.html
+++ /dev/null
@@ -1,210 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <style>
- :root {
- --balboa: #195970;
- }
-
- body {
- margin: 0;
- padding: 0;
- }
-
- .model::before {
- content: "➥";
- }
-
- .topbar {
- background-color: var(--balboa);
- box-shadow: 0 0 0.5em black;
- position: relative;
- height: 3em;
- z-index: 1;
- }
-
- .topbar ul {
- margin: 0;
- padding: 0;
- overflow: hidden;
- }
-
- .topbar li {
- float: left;
- display: block;
- padding: 14px 14px;
- }
-
- .topbar li.right {
- float: right;
- }
-
- .topbar li a {
- color: white;
- text-decoration: none;
- }
-
- .sidebar {
- background-color: var(--balboa);
- box-shadow: inset -0.5em 0 0.5em -0.5em black;
- z-index: -1;
- position: fixed;
- top: 3em;
- left: 0;
- width: 20%;
- height: 100%;
- transition: 0.25s;
- }
-
- .sidebar button {
- display: block;
- background: none;
- border: 0 none;
- cursor: pointer;
- color: white;
- }
-
- .model {
- margin-left: 20px;
- }
-
- .model::before {
- content: "➥";
- }
-
- #hidden:target .sidebar {
- left: -20%;
- }
-
- .menuToggle a.openMenu {
- display: none;
- }
-
- #hidden:target .menuToggle a.closeMenu {
- display: none;
- }
-
- #hidden:target .menuToggle a.openMenu {
- display: block;
- }
-
- #hidden:target section {
- margin-left: 0;
- }
-
- .catalogTable {
- width: 100%;
- border-collapse: collapse;
- }
- .catalogTable td,
- .catalogTable th {
- padding: 0.5em;
- }
-
- .catalogTable tr:nth-child(even) {
- background-color: #f2f2f2;
- }
- .catalogTable tr:hover {
- background-color: #ddd;
- }
- .catalogTable th {
- padding-top: 12px;
- padding-bottom: 12px;
- text-align: left;
- background-color: var(--balboa);
- color: white;
- }
-
- input[type="search"] {
- text-align: center;
- width: 100%;
- }
-
- section {
- margin-left: 20%;
- transition: margin-left 0.25s;
- padding-top: 2em;
- padding-left: 2em;
- padding-right: 2em;
- }
- </style>
- </head>
- <body id="hidden">
- <nav class="topbar">
- <ul>
- <li>
- <div class="menuToggle">
- <a class="openMenu" href="#"
- ><span
- ><svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
- <path
- fill="currentColor"
- d="M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z"
- /></svg></span
- ></a>
- <a class="closeMenu" href="#hidden"
- ><span
- ><svg style="width: 24px; height: 24px" viewBox="0 0 24 24">
- <path
- fill="currentColor"
- d="M21,15.61L19.59,17L14.58,12L19.59,7L21,8.39L17.44,12L21,15.61M3,6H16V8H3V6M3,13V11H13V13H3M3,18V16H16V18H3Z"
- /></svg></span
- ></a>
- </div>
- </li>
- <li><a href="#">Index</a></li>
- <li><a href="#">Parts</a></li>
- <li><a href="#">Cars</a></li>
- <li><a href="#">Suggestions</a></li>
- <li><a href="#">About</a></li>
- <li class="right"><a href="#">Login</a></li>
- </ul>
- </nav>
-
- <nav class="sidebar">
- {{#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 makes found.
- {{/each}}
- </nav>
-
- <section>
- <form>
- <input autofocus type="search" placeholder="hit enter to search" />
- </form>
- </section>
-
- <section>
- <table class="catalogTable">
- <tbody>
- {{#each parts}}
- <tr>
- <td>{{ this }}</td>
- </tr>
- {{/each}}
- </tbody>
- </table>
- </section>
- </body>
-</html>
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(())