From 5513042b1b9d5b47f9fd7363603d182f1bc315cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mekina?= Date: Wed, 12 Mar 2025 23:08:40 +0100 Subject: [PATCH] extract config to external files --- Cargo.lock | 87 +++++++++++++++++++++++++++++++++++++++--- Cargo.toml | 1 + Makefile | 22 ++--------- config.json | 22 ++++++++--- image/image.dockerfile | 3 +- image/image.mk | 7 +++- image/run.sh | 7 +++- lib/config.mk | 11 ++++++ src/config.rs | 45 ++++++++++++++++++++++ src/main.rs | 34 ++++++++++------- 10 files changed, 193 insertions(+), 46 deletions(-) create mode 100644 lib/config.mk create mode 100644 src/config.rs diff --git a/Cargo.lock b/Cargo.lock index 60e6bf4..820a780 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,12 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "fs2" version = "0.4.3" @@ -217,6 +223,12 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + [[package]] name = "humansize" version = "2.1.3" @@ -249,6 +261,16 @@ dependencies = [ "cc", ] +[[package]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inferium" version = "0.1.0" @@ -284,9 +306,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.170" +version = "0.2.171" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "875b3680cb2f8f71bdcf9a30f38d48282f5d3c95cbf9b3fa57269bb5d5c06828" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" [[package]] name = "libm" @@ -388,9 +410,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.20.3" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "parking_lot" @@ -444,9 +466,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.39" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] @@ -516,6 +538,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + [[package]] name = "shlex" version = "1.3.0" @@ -591,6 +622,40 @@ dependencies = [ "syn", ] +[[package]] +name = "toml" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicase" version = "2.8.1" @@ -777,6 +842,15 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" +dependencies = [ + "memchr", +] + [[package]] name = "zmp24" version = "0.1.0" @@ -789,4 +863,5 @@ dependencies = [ "serde_json", "sled", "tokio", + "toml", ] diff --git a/Cargo.toml b/Cargo.toml index a9c0686..9ed2f4f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,6 +16,7 @@ serde = { version = "1.0.218", features = ["derive"] } serde_json = "1.0.140" sled = "0.34.7" tokio = { version = "1.43.0", features = ["macros", "sync", "rt", "rt-multi-thread"] } +toml = "0.8.20" [features] dev = [] diff --git a/Makefile b/Makefile index d3c932d..8c9fd7e 100644 --- a/Makefile +++ b/Makefile @@ -6,25 +6,16 @@ ARCH := $(shell uname -m) FILES_RS := $(shell find src -type f -regex '^.*\.rs$$') FILES_CLIENT := client/dst/index.html client/dst/script.js client/dst/style.css -MAIN_RS_SRCS := $(FILES_RS) $(FILES_CLIENT) Cargo.toml config.json +MAIN_RS_SRCS := $(FILES_RS) $(FILES_CLIENT) Cargo.toml SEARCH_REPLACE := lib/search_and_replace/target/release/search_and_replace -DEV_DB := $(shell jq -r .dev.db config.json) -DEV_BIND := $(shell jq -r .dev.bind_to config.json) -PROD_DB := $(shell jq -r .prod.db config.json) -PROD_SOCK_PATH := $(shell jq -r .prod.sock_path config.json) - .PHONY: run -run: $(MAIN_RS_SRCS) - DB_PATH="$(DEV_DB)" \ - BIND_TO="$(DEV_BIND)" \ - cargo run --features dev +run: $(MAIN_RS_SRCS) dst/dev_config.toml + cargo run --features dev -- dst/dev_config.toml .PHONY: test test: target/debug/zmp24 - DB_PATH="$(DEV_DB)" \ - BIND_TO="$(DEV_BIND)" \ cargo test .PHONY: test_tabs @@ -47,22 +38,17 @@ clean: client_clean dst: mkdir dst +include lib/config.mk include client/client.mk include image/image.mk target/release/zmp24: $(MAIN_RS_SRCS) - DB_PATH="$(PROD_DB)" \ - BIND_TO="$(PROD_SOCK_PATH)" \ cargo build --release target/debug/zmp24: $(MAIN_RS_SRCS) - DB_PATH="$(DEV_DB)" \ - BIND_TO="$(DEV_BIND)" \ cargo build target/$(ARCH)-unknown-linux-musl/release/zmp24: $(MAIN_RS_SRCS) - DB_PATH="$(PROD_DB)" \ - BIND_TO="$(PROD_SOCK_PATH)" \ cargo build --target $(ARCH)-unknown-linux-musl --release $(SEARCH_REPLACE): lib/search_and_replace/src/main.rs lib/search_and_replace/Cargo.toml diff --git a/config.json b/config.json index 4bda4b7..2eeabdb 100644 --- a/config.json +++ b/config.json @@ -1,16 +1,28 @@ { "prod": { - "sock_path": "/run/zmp24/zmp24.sock", - "db": "/data/db" + "io": { + "sock_path": "/run/zmp24/zmp24.sock" + }, + + "database": { + "path": "/data/db" + } }, + "dev": { - "bind_to": "[::1]:8080", - "db": "/tmp/zmp24db" + "io": { + "bind_to": "[::1]:8080" + }, + + "database": { + "path": "/tmp/zmp24db" + } }, + "image": { "name": "zmp24", - "version": "0.1" + "version": "0.2" } } diff --git a/image/image.dockerfile b/image/image.dockerfile index 7908204..e121c36 100644 --- a/image/image.dockerfile +++ b/image/image.dockerfile @@ -2,6 +2,7 @@ FROM docker.io/alpine:latest COPY ./zmp24 /usr/bin/zmp24 COPY ./run.sh /run.sh -RUN apk update && apk add gcompat && mkdir /data +RUN apk update && apk add gcompat && mkdir /data && mkdir /default +COPY ./config.toml /default/config.toml CMD [ "/run.sh" ] diff --git a/image/image.mk b/image/image.mk index e8c302b..a0e1785 100644 --- a/image/image.mk +++ b/image/image.mk @@ -1,10 +1,10 @@ .PHONY: image -image: dst/image/zmp24 dst/image/Containerfile dst/image/run.sh config.json +image: dst/image/zmp24 dst/image/Containerfile dst/image/run.sh dst/image/config.toml config.json cd dst/image && podman build . -t \ "$$(jq -r .image.name ../../config.json):$$(jq -r .image.version ../../config.json)" dst/image: dst - mkdir dst/image + mkdir -p dst/image dst/image/Containerfile: dst/image image/image.dockerfile ln -sf $$(pwd)/image/image.dockerfile dst/image/Containerfile @@ -13,5 +13,8 @@ dst/image/run.sh: dst/image image/run.sh ln -f image/run.sh dst/image/run.sh chmod +x dst/image/run.sh +dst/image/config.toml: dst/image dst/prod_config.toml + ln -f dst/prod_config.toml dst/image/config.toml + dst/image/zmp24: dst/image target/$(ARCH)-unknown-linux-musl/release/zmp24 ln -f target/$(ARCH)-unknown-linux-musl/release/zmp24 dst/image/zmp24 diff --git a/image/run.sh b/image/run.sh index 0215ca9..19870b2 100755 --- a/image/run.sh +++ b/image/run.sh @@ -1,10 +1,15 @@ #!/bin/sh +if [ ! -f /data/config.toml ] +then + cp /default/config.toml /data/config.toml +fi + _term() { kill -TERM "$server_pid" 2>/dev/null } trap _term SIGTERM -/usr/bin/zmp24 & +/usr/bin/zmp24 /data/config.toml & server_pid=$! wait "$server_pid" diff --git a/lib/config.mk b/lib/config.mk new file mode 100644 index 0000000..c5738a7 --- /dev/null +++ b/lib/config.mk @@ -0,0 +1,11 @@ +JQ_INTO_TOML := '.prod|to_entries.[]|if (.value | type == "object") then "\n[" + .key + "]\n" + (.value|to_entries.[] | if (.value | type == "string") then .key + " = \"" + (.value|tostring) + "\"" else .key + " = " + (.value|tostring) end) + "\n" else if (.value | type == "string") then .key + " = \"" + (.value|tostring) + "\"" else .key + " = " + (.value|tostring) end end' + +define to_toml + jq -r '.$(1)|to_entries.[]|if (.value | type == "object") then "\n[" + .key + "]\n" + (.value|to_entries.[] | if (.value | type == "string") then .key + " = \"" + (.value|tostring) + "\"" else .key + " = " + (.value|tostring) end) + "\n" else if (.value | type == "string") then .key + " = \"" + (.value|tostring) + "\"" else .key + " = " + (.value|tostring) end end' $(2) > $(3) +endef + +dst/dev_config.toml: config.json dst + $(call to_toml,dev,$<,$@) + +dst/prod_config.toml: config.json dst + $(call to_toml,prod,$<,$@) diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..7499010 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,45 @@ +use serde::Deserialize; + +#[derive(Deserialize)] +pub struct Config { + pub io: IO, + pub database: Database, +} + +#[derive(Deserialize)] +pub struct IO { + #[cfg(feature = "dev")] + pub bind_to: String, + #[cfg(not(feature = "dev"))] + pub sock_path: String, +} + +#[derive(Deserialize)] +pub struct Database { + pub path: String, +} + +impl IO { + pub fn listen_on(&self) -> &str { + #[cfg(feature = "dev")] + return &self.bind_to; + #[cfg(not(feature = "dev"))] + return &self.sock_path; + } +} + +pub struct Args { + pub config_path: String, +} + +impl Args { + pub fn new() -> Result { + let mut args = std::env::args(); + args.next(); + let config_path = args.next().ok_or(())?; + if args.next() != None { + return Err(()); + } + Ok(Args { config_path }) + } +} diff --git a/src/main.rs b/src/main.rs index 3a6ca23..923a255 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use std::sync::Arc; use chrono::{SecondsFormat, Utc}; +use config::Config; #[cfg(feature = "dev")] use { inferium::TokioInet, @@ -16,6 +17,7 @@ use inferium::h1::AsyncServer; use tokio::sync::mpsc::{self, Sender}; use std::sync::atomic::AtomicU32; +mod config; mod mime_types; mod response_builder; mod router; @@ -98,13 +100,15 @@ async fn server_handler( pub struct State { visit_count: AtomicU32, db: sled::Db, + config: Config, } impl State { - pub fn new(db_path: &str) -> Self { + pub fn new(config: Config) -> Self { Self { visit_count: 0.into(), - db: sled::open(db_path).expect("could not open the database") + db: sled::open(&config.database.path).expect("could not open the database"), + config, } } } @@ -123,26 +127,30 @@ fn delete_if_exists(filepath: &str) { } } +fn load_config(config_path: &str) -> Config { + let config = std::fs::read_to_string(config_path).expect("could not load config file"); + toml::from_str(&config).expect("could not parse config file") +} + #[tokio::main(flavor = "multi_thread", worker_threads = 4)] async fn main() { - const BIND_TO: &str = std::env!("BIND_TO"); - const DB_PATH: &str = std::env!("DB_PATH"); + let args = config::Args::new().expect("invalid arguments"); + let conf = load_config(&args.config_path); - - logger!(debug "binding to {:?}", BIND_TO); + logger!(debug "binding to {:?}", conf.io.listen_on()); #[cfg(not(feature = "dev"))] - delete_if_exists(BIND_TO); + delete_if_exists(&conf.io.sock_path); #[cfg(not(feature = "dev"))] - let listener = UnixListener::bind(BIND_TO) - .expect(&format!("could not bind to {:?}", BIND_TO)); + let listener = UnixListener::bind(&conf.io.sock_path) + .expect(&format!("could not bind to {:?}", conf.io.listen_on())); #[cfg(feature = "dev")] - let listener = TcpListener::bind(BIND_TO).await - .expect(&format!("could not bind to {:?}", BIND_TO)); + let listener = TcpListener::bind(&conf.io.bind_to).await + .expect(&format!("could not bind to {:?}", conf.io.listen_on())); let (tx, mut rx) = mpsc::channel::<()>(1); - let state = Arc::new(State::new(DB_PATH)); + let state = Arc::new(State::new(conf)); - logger!(info "server running on {:?}", BIND_TO); + logger!(info "server running on {:?}", state.config.io.listen_on()); tokio::select!( _ = server_handler(listener, tx, state) => {} _ = rx.recv() => {},