diff --git a/Cargo.lock b/Cargo.lock index 5c4902e7..f7222315 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1061,6 +1061,7 @@ dependencies = [ "serde_json", "tokio", "toml", + "walkdir", ] [[package]] @@ -1270,6 +1271,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -1627,6 +1637,16 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1718,6 +1738,15 @@ 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.52.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/Cargo.toml b/Cargo.toml index e64883cf..f3ae52ad 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ futures = "0.3" # runtime tokio = { version = "1", features = ["rt"] } +walkdir = "2" # logger log = "0.4" diff --git a/src/conf/mod.rs b/src/conf/mod.rs index fa3c55c9..25b66fbf 100644 --- a/src/conf/mod.rs +++ b/src/conf/mod.rs @@ -1,6 +1,7 @@ use std::fs; use std::io::{Result, Error, ErrorKind}; +use walkdir::WalkDir; use clap::ArgMatches; use serde::{Serialize, Deserialize}; @@ -80,11 +81,33 @@ impl FullConf { } pub fn from_conf_file(file: &str) -> Self { - let conf = fs::read_to_string(file).unwrap_or_else(|e| panic!("unable to open {}: {}", file, &e)); - match Self::from_conf_str(&conf) { - Ok(x) => x, - Err(e) => panic!("failed to parse {}: {}", file, &e), + let mtd = fs::metadata(file).unwrap_or_else(|e| panic!("failed to open {}: {}", file, e)); + + if mtd.is_file() { + let conf = fs::read_to_string(file).unwrap_or_else(|e| panic!("failed to open {}: {}", file, e)); + match Self::from_conf_str(&conf) { + Ok(x) => return x, + Err(e) => panic!("failed to parse {}: {}", file, &e), + } + } + + let mut full_conf = FullConf::default(); + for entry in WalkDir::new(file) + .follow_links(true) + .into_iter() + .filter_entry(|e| e.file_name().to_str().is_some_and(|x| x.starts_with('.'))) + .filter_map(|e| e.ok()) + .filter(|e| e.file_type().is_file()) + .filter(|e| e.path().extension().map_or(false, |s| s == "toml" || s == "json")) + { + let conf_part = fs::read_to_string(entry.path()) + .unwrap_or_else(|e| panic!("failed to open {}: {}", entry.path().to_string_lossy(), e)); + + let conf_part = Self::from_conf_str(&conf_part) + .unwrap_or_else(|e| panic!("failed to parse {}: {}", entry.path().to_string_lossy(), e)); + full_conf.take_fields(conf_part); } + full_conf } pub fn from_conf_str(s: &str) -> Result { @@ -116,6 +139,13 @@ impl FullConf { )) } + fn take_fields(&mut self, other: Self) { + self.log.take_field(&other.log); + self.dns.take_field(&other.dns); + self.network.take_field(&other.network); + self.endpoints.extend(other.endpoints); + } + pub fn add_endpoint(&mut self, endpoint: EndpointConf) -> &mut Self { self.endpoints.push(endpoint); self