aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2026-03-28 22:11:33 +0200
committerrtkay123 <dev@kanjala.com>2026-03-28 22:11:33 +0200
commit0274b33510864945fdcc2df4f3828c4e8682ac0d (patch)
tree05fb74ee4a0be11a58515f5a73c348358e07e177
parent66290428604e72d5a1e587419d3e8b4514ce2f64 (diff)
downloadwarden-0274b33510864945fdcc2df4f3828c4e8682ac0d.tar.bz2
warden-0274b33510864945fdcc2df4f3828c4e8682ac0d.zip
feat: generate config file
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock595
-rw-r--r--Cargo.toml6
-rw-r--r--warden/Cargo.toml11
-rw-r--r--warden/src/config/cli/mod.rs68
-rw-r--r--warden/src/config/log_level.rs68
-rw-r--r--warden/src/config/mod.rs108
-rw-r--r--warden/src/main.rs46
8 files changed, 900 insertions, 3 deletions
diff --git a/.gitignore b/.gitignore
index e3dc09e..bdf118e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
/target
/docs/book/book
+warden.toml
diff --git a/Cargo.lock b/Cargo.lock
index bfa7b82..0d0c44a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,5 +3,600 @@
version = 4
[[package]]
+name = "aho-corasick"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "anstream"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
+dependencies = [
+ "anstyle",
+ "anstyle-parse",
+ "anstyle-query",
+ "anstyle-wincon",
+ "colorchoice",
+ "is_terminal_polyfill",
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle"
+version = "1.0.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
+
+[[package]]
+name = "anstyle-parse"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
+dependencies = [
+ "utf8parse",
+]
+
+[[package]]
+name = "anstyle-query"
+version = "1.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "anstyle-wincon"
+version = "3.0.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
+dependencies = [
+ "anstyle",
+ "once_cell_polyfill",
+ "windows-sys",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.102"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
+
+[[package]]
+name = "clap"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351"
+dependencies = [
+ "clap_builder",
+ "clap_derive",
+]
+
+[[package]]
+name = "clap_builder"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
+dependencies = [
+ "anstream",
+ "anstyle",
+ "clap_lex",
+ "strsim",
+]
+
+[[package]]
+name = "clap_derive"
+version = "4.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1110bd8a634a1ab8cb04345d8d878267d57c3cf1b38d91b71af6686408bbca6a"
+dependencies = [
+ "heck",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "clap_lex"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
+
+[[package]]
+name = "colorchoice"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
+
+[[package]]
+name = "crossbeam-channel"
+version = "0.5.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "deranged"
+version = "0.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
+dependencies = [
+ "powerfmt",
+]
+
+[[package]]
+name = "equivalent"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+
+[[package]]
+name = "hashbrown"
+version = "0.16.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+
+[[package]]
+name = "heck"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+
+[[package]]
+name = "indexmap"
+version = "2.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017"
+dependencies = [
+ "equivalent",
+ "hashbrown",
+]
+
+[[package]]
+name = "is_terminal_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
+
+[[package]]
+name = "itoa"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+
+[[package]]
+name = "lazy_static"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
+
+[[package]]
+name = "log"
+version = "0.4.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
+
+[[package]]
+name = "matchers"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
+dependencies = [
+ "regex-automata",
+]
+
+[[package]]
+name = "memchr"
+version = "2.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
+
+[[package]]
+name = "nu-ansi-term"
+version = "0.50.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
+dependencies = [
+ "windows-sys",
+]
+
+[[package]]
+name = "num-conv"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967"
+
+[[package]]
+name = "once_cell"
+version = "1.21.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+
+[[package]]
+name = "once_cell_polyfill"
+version = "1.70.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.17"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+
+[[package]]
+name = "powerfmt"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.106"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a"
+
+[[package]]
+name = "serde"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+dependencies = [
+ "serde_core",
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_core"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.228"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "serde_spanned"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "sharded-slab"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6"
+dependencies = [
+ "lazy_static",
+]
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "strsim"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
+
+[[package]]
+name = "syn"
+version = "2.0.117"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "thiserror"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "2.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "thread_local"
+version = "1.1.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
+dependencies = [
+ "cfg-if",
+]
+
+[[package]]
+name = "time"
+version = "0.3.47"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
+dependencies = [
+ "deranged",
+ "itoa",
+ "num-conv",
+ "powerfmt",
+ "serde_core",
+ "time-core",
+ "time-macros",
+]
+
+[[package]]
+name = "time-core"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca"
+
+[[package]]
+name = "time-macros"
+version = "0.2.27"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215"
+dependencies = [
+ "num-conv",
+ "time-core",
+]
+
+[[package]]
+name = "tokio"
+version = "1.50.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d"
+dependencies = [
+ "pin-project-lite",
+ "tokio-macros",
+]
+
+[[package]]
+name = "tokio-macros"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "toml"
+version = "1.1.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc"
+dependencies = [
+ "indexmap",
+ "serde_core",
+ "serde_spanned",
+ "toml_datetime",
+ "toml_parser",
+ "toml_writer",
+ "winnow",
+]
+
+[[package]]
+name = "toml_datetime"
+version = "1.1.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011"
+dependencies = [
+ "winnow",
+]
+
+[[package]]
+name = "toml_writer"
+version = "1.1.0+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed"
+
+[[package]]
+name = "tracing"
+version = "0.1.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
+dependencies = [
+ "pin-project-lite",
+ "tracing-attributes",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-appender"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf"
+dependencies = [
+ "crossbeam-channel",
+ "thiserror",
+ "time",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "tracing-attributes"
+version = "0.1.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "tracing-core"
+version = "0.1.36"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
+dependencies = [
+ "once_cell",
+ "valuable",
+]
+
+[[package]]
+name = "tracing-log"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3"
+dependencies = [
+ "log",
+ "once_cell",
+ "tracing-core",
+]
+
+[[package]]
+name = "tracing-subscriber"
+version = "0.3.23"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319"
+dependencies = [
+ "matchers",
+ "nu-ansi-term",
+ "once_cell",
+ "regex-automata",
+ "sharded-slab",
+ "smallvec",
+ "thread_local",
+ "tracing",
+ "tracing-core",
+ "tracing-log",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.24"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+
+[[package]]
name = "warden"
version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "clap",
+ "serde",
+ "tokio",
+ "toml",
+ "tracing",
+ "tracing-appender",
+ "tracing-subscriber",
+]
+
+[[package]]
+name = "windows-link"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+
+[[package]]
+name = "windows-sys"
+version = "0.61.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
+dependencies = [
+ "windows-link",
+]
+
+[[package]]
+name = "winnow"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8"
diff --git a/Cargo.toml b/Cargo.toml
index 0bb4411..024a6a6 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,9 +1,13 @@
[workspace]
resolver = "3"
-members = [ "warden" ]
+members = ["warden"]
[workspace.package]
license = "AGPL-3.0-only"
readme = "README.md"
documentation = "https://books.kanjala.com/warden"
homepage = "https://git.kanjala.com/warden"
+
+[workspace.dependencies]
+serde = "1.0.228"
+tracing = "0.1.44"
diff --git a/warden/Cargo.toml b/warden/Cargo.toml
index 4fa8c3a..f3255e7 100644
--- a/warden/Cargo.toml
+++ b/warden/Cargo.toml
@@ -7,3 +7,14 @@ documentation.workspace = true
homepage.workspace = true
[dependencies]
+anyhow = "1.0.102"
+clap = { version = "4.6.0", features = ["derive", "env"] }
+serde = { workspace = true, features = ["derive"] }
+toml = "1.1.0"
+tracing.workspace = true
+tracing-appender = "0.2.4"
+tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
+
+[dependencies.tokio]
+version = "1.50.0"
+features = ["macros", "rt", "rt-multi-thread"]
diff --git a/warden/src/config/cli/mod.rs b/warden/src/config/cli/mod.rs
new file mode 100644
index 0000000..aa98ca8
--- /dev/null
+++ b/warden/src/config/cli/mod.rs
@@ -0,0 +1,68 @@
+use std::path::PathBuf;
+
+use clap::{Parser, Subcommand, ValueEnum};
+use serde::{Deserialize, Serialize};
+
+#[derive(Parser, Serialize, Deserialize, Default, Debug)]
+#[command(version, about, long_about = None)]
+/// Real-time transaction monitoring
+pub struct Cli {
+ /// Sets a custom config file
+ #[arg(short, long, value_name = "FILE")]
+ #[serde(skip)]
+ pub config: Option<PathBuf>,
+
+ #[command(flatten)]
+ pub server: Server,
+ #[command(subcommand)]
+ #[serde(skip)]
+ pub command: Option<Commands>,
+}
+
+#[derive(Subcommand, Debug)]
+pub enum Commands {
+ /// Generates a default configuration file
+ Init {
+ /// Path to save the config
+ #[arg(short, long, default_value = "warden.toml")]
+ path: String,
+ },
+}
+
+#[derive(Parser, Deserialize, Serialize, Debug)]
+#[serde(rename_all = "kebab-case")]
+pub struct Server {
+ /// Sets the port that the server listens to
+ #[arg(short, long, value_name = "PORT", env = "PORT", default_value = "2210")]
+ #[arg(value_parser = clap::value_parser!(u16).range(1..=65535))]
+ pub port: Option<u16>,
+ /// Runtime environment
+ #[arg(short, long, value_name = "ENV", default_value = "prod")]
+ pub environment: Option<CliEnvironment>,
+ /// Log Level
+ #[arg(short, long, value_name = "LOG_LEVEL")]
+ pub log_level: Option<String>,
+}
+
+impl Default for Server {
+ fn default() -> Self {
+ Self {
+ port: Some(2210),
+ environment: Default::default(),
+ log_level: Some(format!(
+ "{}=debug,tower_http=debug,axum::rejection=trace",
+ env!("CARGO_CRATE_NAME")
+ )),
+ }
+ }
+}
+
+#[derive(Deserialize, ValueEnum, Serialize, Clone, Copy, Default, Debug)]
+#[serde(rename_all = "lowercase")]
+pub enum CliEnvironment {
+ #[default]
+ Dev,
+ Development,
+ Prod,
+ Production,
+}
diff --git a/warden/src/config/log_level.rs b/warden/src/config/log_level.rs
new file mode 100644
index 0000000..3edfbc3
--- /dev/null
+++ b/warden/src/config/log_level.rs
@@ -0,0 +1,68 @@
+use clap::ValueEnum;
+use serde::Deserialize;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum, Debug, Default, Deserialize)]
+#[serde(rename_all = "lowercase")]
+pub enum LogLevel {
+ /// The "trace" level.
+ ///
+ /// Designates very low priority, often extremely verbose, information.
+ Trace = 0,
+ /// The "debug" level.
+ ///
+ /// Designates lower priority information.
+ #[default]
+ Debug = 1,
+ /// The "info" level.
+ ///
+ /// Designates useful information.
+ Info = 2,
+ /// The "warn" level.
+ ///
+ /// Designates hazardous situations.
+ Warn = 3,
+ /// The "error" level.
+ ///
+ /// Designates very serious errors.
+ Error = 4,
+}
+
+impl From<LogLevel> for tracing::Level {
+ fn from(value: LogLevel) -> Self {
+ match value {
+ LogLevel::Trace => tracing::Level::TRACE,
+ LogLevel::Debug => tracing::Level::DEBUG,
+ LogLevel::Info => tracing::Level::INFO,
+ LogLevel::Warn => tracing::Level::WARN,
+ LogLevel::Error => tracing::Level::ERROR,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::LogLevel;
+
+ fn check(level: LogLevel, value: &str) {
+ let level = tracing::Level::from(level);
+ assert_eq!(level.to_string().to_lowercase(), value);
+ }
+
+ #[test]
+ fn loglevel() {
+ let level = LogLevel::Trace;
+ check(level, "trace");
+
+ let level = LogLevel::Debug;
+ check(level, "debug");
+
+ let level = LogLevel::Info;
+ check(level, "info");
+
+ let level = LogLevel::Warn;
+ check(level, "warn");
+
+ let level = LogLevel::Error;
+ check(level, "error");
+ }
+}
diff --git a/warden/src/config/mod.rs b/warden/src/config/mod.rs
new file mode 100644
index 0000000..b2e5307
--- /dev/null
+++ b/warden/src/config/mod.rs
@@ -0,0 +1,108 @@
+mod cli;
+mod log_level;
+
+use clap::ValueEnum;
+pub use cli::Cli;
+pub use cli::Commands;
+use serde::Deserialize;
+use tracing_subscriber::EnvFilter;
+
+use crate::config::cli::CliEnvironment;
+
+#[derive(Deserialize, Default, Debug, ValueEnum, Clone, Copy)]
+#[serde(rename_all = "lowercase")]
+pub enum Environment {
+ Development,
+ #[default]
+ Production,
+}
+
+impl From<CliEnvironment> for Environment {
+ fn from(value: CliEnvironment) -> Self {
+ match value {
+ CliEnvironment::Dev | CliEnvironment::Development => Self::Development,
+ CliEnvironment::Prod | CliEnvironment::Production => Self::Production,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Default)]
+pub struct Configuration {
+ pub server: Server,
+}
+
+#[derive(Debug, Clone)]
+pub struct Server {
+ pub port: u16,
+ pub environment: Environment,
+ pub log_level: EnvFilter,
+}
+
+impl Default for Server {
+ fn default() -> Self {
+ Self {
+ port: 2210,
+ environment: Default::default(),
+ log_level: tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
+ // axum logs rejections from built-in extractors with the `axum::rejection`
+ // target, at `TRACE` level. `axum::rejection=trace` enables showing those events
+ format!(
+ "{}=debug,tower_http=debug,axum::rejection=trace",
+ env!("CARGO_CRATE_NAME")
+ )
+ .into()
+ }),
+ }
+ }
+}
+
+impl Configuration {
+ pub fn merge(cli: &Cli, file: &Cli) -> anyhow::Result<Self> {
+ let mut missing = Vec::new();
+ let port = cli.server.port.or(file.server.port);
+
+ if port.is_none() {
+ missing.push("server.port");
+ }
+
+ let log_level = cli
+ .server
+ .log_level
+ .as_ref()
+ .or(file.server.log_level.as_ref())
+ .map(ToOwned::to_owned)
+ .unwrap_or_default();
+
+ tracing_subscriber::EnvFilter::try_from_default_env().unwrap_or_else(|_| {
+ // axum logs rejections from built-in extractors with the `axum::rejection`
+ // target, at `TRACE` level. `axum::rejection=trace` enables showing those events
+ log_level.to_string().into()
+ });
+
+ let environment = cli
+ .server
+ .environment
+ .or(file.server.environment)
+ .unwrap_or_default()
+ .into();
+
+ if !missing.is_empty() {
+ anyhow::bail!(
+ "Missing required configuration values:\n{}",
+ missing
+ .iter()
+ .map(|f| format!(" - {}", f))
+ .collect::<Vec<_>>()
+ .join("\n")
+ );
+ }
+
+ Ok(Self {
+ server: Server {
+ port: port.unwrap(),
+ environment,
+ log_level: log_level.into(),
+ },
+ })
+ }
+}
diff --git a/warden/src/main.rs b/warden/src/main.rs
index e7a11a9..bedc254 100644
--- a/warden/src/main.rs
+++ b/warden/src/main.rs
@@ -1,3 +1,45 @@
-fn main() {
- println!("Hello, world!");
+use std::io::Write;
+
+use anyhow::Context as _;
+use clap::Parser as _;
+
+use crate::config::Commands;
+
+mod config;
+
+#[tokio::main]
+async fn main() -> anyhow::Result<()> {
+ let cli = config::Cli::parse();
+
+ if let Some(command) = cli.command {
+ return check_subcommand(command);
+ }
+
+ let config = if let Some(file) = cli.config.as_ref() {
+ let contents = std::fs::read_to_string(file)
+ .with_context(|| format!("Failed to read config file: {file:?}"))?;
+ toml::from_str(&contents)?
+ } else {
+ config::Cli::default()
+ };
+ dbg!(config);
+
+ Ok(())
+}
+
+fn check_subcommand(command: Commands) -> anyhow::Result<()> {
+ match command {
+ Commands::Init { path } => {
+ let default_config = config::Cli::default();
+ let mut file = std::fs::OpenOptions::new()
+ .write(true)
+ .create_new(true)
+ .open(&path)?;
+ let str = toml::to_string_pretty(&default_config)?;
+ let _ = file.write_all(str.as_bytes());
+ println!("configuration file saved to {path}");
+ }
+ }
+
+ Ok(())
}