From b343fbccea94145cc0bf57df53a145054e29c909 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sun, 23 Feb 2025 14:08:22 +0100 Subject: [PATCH] feat: started clap cli interface --- Cargo.lock | 124 +++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/cli/mod.rs | 32 ++++++++++++ src/cli/serve.rs | 56 +++++++++++++++++++++ src/main.rs | 28 +++-------- 5 files changed, 216 insertions(+), 25 deletions(-) create mode 100644 src/cli/mod.rs create mode 100644 src/cli/serve.rs diff --git a/Cargo.lock b/Cargo.lock index 88e0186..1cc157f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,56 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +[[package]] +name = "anstream" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" + +[[package]] +name = "anstyle-parse" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" +dependencies = [ + "anstyle", + "once_cell", + "windows-sys 0.59.0", +] + [[package]] name = "argon2" version = "0.5.3" @@ -190,6 +240,52 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b7b18d71fad5313a1e320fa9897994228ce274b60faa4d694fe0ea89cd9e6d" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a35db2071778a7344791a4fb4f95308b5673d219dee3ae348b86642574ecc90c" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" + +[[package]] +name = "colorchoice" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" + [[package]] name = "cookie" version = "0.18.1" @@ -554,6 +650,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "itoa" version = "1.0.14" @@ -655,7 +757,7 @@ checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", "wasi", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -696,6 +798,7 @@ dependencies = [ "argon2", "axum", "axum-extra", + "clap", "diesel", "diesel_migrations", "libsqlite3-sys", @@ -998,7 +1101,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1086,7 +1189,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -1248,6 +1351,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00e2473a93778eb0bad35909dff6a10d28e63f792f16ed15e404fca9d5eeedbe" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.1" @@ -1303,6 +1412,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 560e28b..6473565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" argon2 = "0.5.3" axum = "0.8.1" axum-extra = { version = "0.10", features = ["cookie", "typed-header"] } +clap = { version = "4.5.30", features = ["derive", "env"] } diesel = { version = "2.2.7", features = ["r2d2", "sqlite", "returning_clauses_for_sqlite_3_35"] } diesel_migrations = { version = "2.2.0", features = ["sqlite"] } libsqlite3-sys = { version = "0.31.0", features = ["bundled"] } diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 0000000..2efff8b --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,32 @@ +mod serve; + +use std::path::PathBuf; + +use clap::{Parser, Subcommand}; + +#[derive(Parser)] +pub struct Cli { + #[arg( + long = "data", + default_value = "./data", + value_name = "DATA_DIR", + env = "OTTER_DATA_DIR" + )] + pub data_dir: PathBuf, + + #[command(subcommand)] + pub cmd: Command, +} + +#[derive(Subcommand)] +pub enum Command { + Serve(serve::ServeCommand), +} + +impl Cli { + pub fn run(&self) -> u8 { + match &self.cmd { + Command::Serve(cmd) => cmd.run(self), + } + } +} diff --git a/src/cli/serve.rs b/src/cli/serve.rs new file mode 100644 index 0000000..8d7fde4 --- /dev/null +++ b/src/cli/serve.rs @@ -0,0 +1,56 @@ +use clap::Args; + +use crate::{db, server}; + +const DB_FILENAME: &str = "otter.sqlite3"; + +#[derive(Args)] +pub struct ServeCommand { + #[arg( + short, + long, + default_value = "127.0.0.1", + value_name = "DOMAIN", + env = "OTTER_DOMAIN" + )] + domain: String, + + #[arg( + short, + long, + default_value = "8080", + value_name = "PORT", + env = "OTTER_PORT" + )] + port: u16, +} + +impl ServeCommand { + pub fn run(&self, cli: &super::Cli) -> u8 { + tracing_subscriber::fmt::init(); + + tracing::info!("Initializing database and running migrations"); + + let pool = db::initialize_db(cli.data_dir.join(DB_FILENAME), true).unwrap(); + + let ctx = server::Context { pool }; + let app = server::app().with_state(ctx); + + let rt = tokio::runtime::Builder::new_multi_thread() + .enable_all() + .build() + .unwrap(); + + let address = format!("{}:{}", self.domain, self.port); + tracing::info!("Starting server on {address}"); + + rt.block_on(async { + let listener = tokio::net::TcpListener::bind(address).await.unwrap(); + axum::serve(listener, app.into_make_service()) + .await + .unwrap() + }); + + 0 + } +} diff --git a/src/main.rs b/src/main.rs index 29da765..0d12122 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,13 @@ +mod cli; mod db; mod server; -fn main() { - tracing_subscriber::fmt::init(); +use clap::Parser; - tracing::info!("Initializing database and running migrations"); +use std::process::ExitCode; - let pool = db::initialize_db("data/db.sqlite3", true).unwrap(); +fn main() -> ExitCode { + let args = cli::Cli::parse(); - let ctx = server::Context { pool }; - let app = server::app().with_state(ctx); - - let rt = tokio::runtime::Builder::new_multi_thread() - .enable_all() - .build() - .unwrap(); - - let address = "0.0.0.0:8080"; - - tracing::info!("Starting server on {address}"); - - rt.block_on(async { - let listener = tokio::net::TcpListener::bind(address).await.unwrap(); - axum::serve(listener, app.into_make_service()) - .await - .unwrap() - }); + ExitCode::from(args.run()) }