From 90fbdc990c21b2cd97012ce94d6b26ee4b7688ba Mon Sep 17 00:00:00 2001 From: lyderic Date: Wed, 4 Sep 2024 10:53:41 +0200 Subject: [PATCH 1/6] Added command line arguments --- Cargo.lock | 176 ++++++++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 1 + src/app_config.rs | 19 +++++ 3 files changed, 190 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5c678f36..98e7b00b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -608,6 +608,17 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi 0.1.19", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.3.0" @@ -833,6 +844,45 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "clap" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" +dependencies = [ + "atty", + "bitflags 1.3.2", + "clap_derive", + "clap_lex", + "indexmap 1.9.3", + "once_cell", + "strsim 0.10.0", + "termcolor", + "textwrap", +] + +[[package]] +name = "clap_derive" +version = "3.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" +dependencies = [ + "heck 0.4.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "clap_lex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2850f2f5a82cbf437dd5af4d49848fbdfc27c157c3d010345776f952765261c5" +dependencies = [ + "os_str_bytes", +] + [[package]] name = "colorchoice" version = "1.0.2" @@ -1054,7 +1104,7 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.11.1", "syn 2.0.76", ] @@ -1503,7 +1553,7 @@ dependencies = [ "futures-sink", "futures-util", "http 0.2.12", - "indexmap", + "indexmap 2.5.0", "slab", "tokio", "tokio-util", @@ -1524,6 +1574,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.13.2" @@ -1549,12 +1605,27 @@ dependencies = [ "hashbrown 0.14.5", ] +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + [[package]] name = "hermit-abi" version = "0.3.9" @@ -1729,6 +1800,16 @@ dependencies = [ "quote", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", +] + [[package]] name = "indexmap" version = "2.5.0" @@ -2144,6 +2225,12 @@ dependencies = [ "hashbrown 0.13.2", ] +[[package]] +name = "os_str_bytes" +version = "6.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2355d85b9a3786f481747ced0e0ff2ba35213a1f9bd406ed906554d7af805a1" + [[package]] name = "parking" version = "2.2.0" @@ -2372,6 +2459,30 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.86" @@ -2752,7 +2863,7 @@ version = "1.0.127" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8043c06d9f82bd7271361ed64f415fe5e12a77fdb52e573e7f06a516dea329ad" dependencies = [ - "indexmap", + "indexmap 2.5.0", "itoa", "memchr", "ryu", @@ -2906,6 +3017,7 @@ dependencies = [ "awc", "base64 0.22.1", "chrono", + "clap", "config", "csv-async", "dotenvy", @@ -2984,7 +3096,7 @@ dependencies = [ "hex", "hkdf", "hmac", - "indexmap", + "indexmap 2.5.0", "itoa", "libc", "libsqlite3-sys", @@ -3024,7 +3136,7 @@ checksum = "187f41faa1ab00c8a149d3c66b4fe1ce609ca6f5d96d64a68dff983a13547cc4" dependencies = [ "dotenvy", "either", - "heck", + "heck 0.5.0", "once_cell", "proc-macro2", "quote", @@ -3068,6 +3180,12 @@ dependencies = [ "unicode-properties", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + [[package]] name = "strsim" version = "0.11.1" @@ -3127,6 +3245,21 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" + [[package]] name = "thiserror" version = "1.0.63" @@ -3293,7 +3426,7 @@ version = "0.22.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d" dependencies = [ - "indexmap", + "indexmap 2.5.0", "serde", "serde_spanned", "toml_datetime", @@ -3596,6 +3729,37 @@ dependencies = [ "web-sys", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-core" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index 26352278..4420e3ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ csv-async = { version = "1.2.6", features = ["tokio"] } rustls = { version = "0.22.0" } # keep in sync with actix-web, awc, rustls-acme, and sqlx rustls-native-certs = "0.7.0" awc = { version = "3", features = ["rustls-0_22-webpki-roots"] } +clap = { version = "3.1.18", features = ["derive"] } [build-dependencies] awc = { version = "3", features = ["rustls-0_22-webpki-roots"] } diff --git a/src/app_config.rs b/src/app_config.rs index be88fcd7..84048afd 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -5,6 +5,24 @@ use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize}; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; +use clap::Parser; + +#[derive(Parser)] +#[clap(author, version, about, long_about = None)] +pub struct Cli { + /// web root + #[clap(short,long,default_value=".")] + web_root: PathBuf, + /// database url + #[clap(short,long,default_value="sqlite::memory")] + database_url: String, + /// port + #[clap(short,long,default_value_t=8080)] + port: u16, + /// interface to listen on + #[clap(short,long,default_value="127.0.0.1")] + listen_on: String, +} #[cfg(not(feature = "lambda-web"))] const DEFAULT_DATABASE_FILE: &str = "sqlpage.db"; @@ -149,6 +167,7 @@ fn cannonicalize_if_possible(path: &std::path::Path) -> PathBuf { /// Parses and loads the configuration from the `sqlpage.json` file in the current directory. /// This should be called only once at the start of the program. pub fn load() -> anyhow::Result { + let _cli = Cli::parse(); let configuration_directory = &configuration_directory(); load_from_directory(configuration_directory) } From 85f53664434dd7d81607a10819e06f7be64cb66c Mon Sep 17 00:00:00 2001 From: lyderic Date: Wed, 4 Sep 2024 11:53:29 +0200 Subject: [PATCH 2/6] Getting values from CLI, how to use them now? --- src/app_config.rs | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/src/app_config.rs b/src/app_config.rs index 84048afd..626c7692 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -12,16 +12,10 @@ use clap::Parser; pub struct Cli { /// web root #[clap(short,long,default_value=".")] - web_root: PathBuf, - /// database url - #[clap(short,long,default_value="sqlite::memory")] - database_url: String, - /// port - #[clap(short,long,default_value_t=8080)] - port: u16, - /// interface to listen on - #[clap(short,long,default_value="127.0.0.1")] - listen_on: String, + pub web_root: PathBuf, + /// configuration directory + #[clap(short,long,default_value="sqlpage")] + pub config_dir: String, } #[cfg(not(feature = "lambda-web"))] @@ -169,7 +163,7 @@ fn cannonicalize_if_possible(path: &std::path::Path) -> PathBuf { pub fn load() -> anyhow::Result { let _cli = Cli::parse(); let configuration_directory = &configuration_directory(); - load_from_directory(configuration_directory) + load_from_directory(&configuration_directory) } /// Parses and loads the configuration from the given directory. From 63baf3ca8a3c717f7041c2abd7d39b9d5dc0b284 Mon Sep 17 00:00:00 2001 From: lyderic Date: Wed, 4 Sep 2024 11:56:19 +0200 Subject: [PATCH 3/6] little typo --- src/app_config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app_config.rs b/src/app_config.rs index 626c7692..beab0a7e 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -163,7 +163,7 @@ fn cannonicalize_if_possible(path: &std::path::Path) -> PathBuf { pub fn load() -> anyhow::Result { let _cli = Cli::parse(); let configuration_directory = &configuration_directory(); - load_from_directory(&configuration_directory) + load_from_directory(configuration_directory) } /// Parses and loads the configuration from the given directory. From e4127660d38fe0884d60826638de160e62fde946 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 5 Sep 2024 22:39:58 +0200 Subject: [PATCH 4/6] format --- src/app_config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/app_config.rs b/src/app_config.rs index beab0a7e..bdcbd0c2 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -1,20 +1,20 @@ use anyhow::Context; +use clap::Parser; use config::Config; use percent_encoding::AsciiSet; use serde::de::Error; use serde::{Deserialize, Deserializer, Serialize}; use std::net::{SocketAddr, ToSocketAddrs}; use std::path::{Path, PathBuf}; -use clap::Parser; #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { /// web root - #[clap(short,long,default_value=".")] + #[clap(short, long, default_value = ".")] pub web_root: PathBuf, /// configuration directory - #[clap(short,long,default_value="sqlpage")] + #[clap(short, long, default_value = "sqlpage")] pub config_dir: String, } From c78f764b8123d7391da659d77997526355178804 Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 5 Sep 2024 22:56:31 +0200 Subject: [PATCH 5/6] improve cli parsing --- src/app_config.rs | 62 +++++++++++++++++++++++++++++++++++++---------- src/main.rs | 2 +- 2 files changed, 50 insertions(+), 14 deletions(-) diff --git a/src/app_config.rs b/src/app_config.rs index bdcbd0c2..d5081fec 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -10,17 +10,41 @@ use std::path::{Path, PathBuf}; #[derive(Parser)] #[clap(author, version, about, long_about = None)] pub struct Cli { - /// web root - #[clap(short, long, default_value = ".")] - pub web_root: PathBuf, - /// configuration directory - #[clap(short, long, default_value = "sqlpage")] - pub config_dir: String, + /// The directory where the .sql files are located. + #[clap(short, long)] + pub web_root: Option, + /// The directory where the sqlpage.json configuration, the templates, and the migrations are located. + #[clap(short, long)] + pub config_dir: Option, } #[cfg(not(feature = "lambda-web"))] const DEFAULT_DATABASE_FILE: &str = "sqlpage.db"; +impl AppConfig { + pub fn from_cli(cli: &Cli) -> anyhow::Result { + let mut config = if let Some(config_dir) = &cli.config_dir { + load_from_directory(config_dir)? + } else { + load_from_env()? + }; + if let Some(web_root) = &cli.web_root { + config.web_root.clone_from(web_root); + } + Ok(config) + } +} + +pub fn load_from_cli() -> anyhow::Result { + let cli = Cli::parse(); + AppConfig::from_cli(&cli) +} + +pub fn load_from_env() -> anyhow::Result { + let config_dir = configuration_directory(); + load_from_directory(&config_dir) +} + #[derive(Debug, Deserialize, PartialEq, Clone)] pub struct AppConfig { #[serde(default = "default_database_url")] @@ -160,13 +184,6 @@ fn cannonicalize_if_possible(path: &std::path::Path) -> PathBuf { /// Parses and loads the configuration from the `sqlpage.json` file in the current directory. /// This should be called only once at the start of the program. -pub fn load() -> anyhow::Result { - let _cli = Cli::parse(); - let configuration_directory = &configuration_directory(); - load_from_directory(configuration_directory) -} - -/// Parses and loads the configuration from the given directory. pub fn load_from_directory(directory: &Path) -> anyhow::Result { let cannonical = cannonicalize_if_possible(directory); log::debug!("Loading configuration from {:?}", cannonical); @@ -407,4 +424,23 @@ mod test { assert_eq!(normalize_site_prefix("a b"), "/a%20b/"); assert_eq!(normalize_site_prefix("a b/c"), "/a%20b/c/"); } + + #[test] + fn test_cli() { + let cli = Cli::parse_from(&[ + "sqlpage", + "--web-root", + "/path/to/web", + "--config-dir", + "custom_config", + ]); + assert_eq!(cli.web_root, Some(PathBuf::from("/path/to/web"))); + assert_eq!(cli.config_dir, Some(PathBuf::from("custom_config"))); + } + + #[test] + fn verify_cli() { + use clap::CommandFactory; + Cli::command().debug_assert(); + } } diff --git a/src/main.rs b/src/main.rs index 4337b23e..5286b288 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,7 +14,7 @@ async fn main() { } async fn start() -> anyhow::Result<()> { - let app_config = app_config::load()?; + let app_config = app_config::load_from_cli()?; let db = Database::init(&app_config).await?; webserver::database::migrations::apply(&app_config, &db).await?; let state = AppState::init_with_db(&app_config, db).await?; From e615052d851972f037b86745da64bf1e52c5c76d Mon Sep 17 00:00:00 2001 From: lovasoa Date: Thu, 5 Sep 2024 23:02:19 +0200 Subject: [PATCH 6/6] add a config-file cli argument --- src/app_config.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/app_config.rs b/src/app_config.rs index d5081fec..c7f2b029 100644 --- a/src/app_config.rs +++ b/src/app_config.rs @@ -14,8 +14,11 @@ pub struct Cli { #[clap(short, long)] pub web_root: Option, /// The directory where the sqlpage.json configuration, the templates, and the migrations are located. - #[clap(short, long)] + #[clap(short = 'd', long)] pub config_dir: Option, + /// The path to the configuration file. + #[clap(short = 'c', long)] + pub config_file: Option, } #[cfg(not(feature = "lambda-web"))] @@ -23,7 +26,9 @@ const DEFAULT_DATABASE_FILE: &str = "sqlpage.db"; impl AppConfig { pub fn from_cli(cli: &Cli) -> anyhow::Result { - let mut config = if let Some(config_dir) = &cli.config_dir { + let mut config = if let Some(config_file) = &cli.config_file { + load_from_file(config_file)? + } else if let Some(config_dir) = &cli.config_dir { load_from_directory(config_dir)? } else { load_from_env()? @@ -134,7 +139,8 @@ pub struct AppConfig { #[serde(default = "default_compress_responses")] pub compress_responses: bool, - /// Content-Security-Policy header to send to the client. If not set, a default policy allowing scripts from the same origin is used and from jsdelivr.net + /// Content-Security-Policy header to send to the client. + /// If not set, a default policy allowing scripts from the same origin is used and from jsdelivr.net pub content_security_policy: Option, /// Whether `sqlpage.fetch` should load trusted certificates from the operating system's certificate store