Skip to main content

grapsus_config/multi_file/
directory.rs

1//! Convention-based configuration directory support.
2//!
3//! This module provides `ConfigDirectory` for loading configuration
4//! from a structured directory layout.
5
6use anyhow::Result;
7use std::path::{Path, PathBuf};
8
9use crate::Config;
10
11use super::loader::MultiFileLoader;
12
13/// Configuration directory structure support.
14///
15/// Provides convention-based loading from a standard directory layout:
16///
17/// ```text
18/// config/
19///   ├── grapsus.kdl         # Main config
20///   ├── listeners/           # Listener definitions
21///   │   ├── http.kdl
22///   │   └── https.kdl
23///   ├── routes/              # Route definitions
24///   │   ├── api.kdl
25///   │   └── static.kdl
26///   ├── upstreams/           # Upstream definitions
27///   │   ├── backend.kdl
28///   │   └── cache.kdl
29///   ├── agents/              # Agent configurations
30///   │   ├── waf.kdl
31///   │   └── auth.kdl
32///   └── environments/        # Environment overrides
33///       ├── development.kdl
34///       ├── staging.kdl
35///       └── production.kdl
36/// ```
37pub struct ConfigDirectory {
38    root: PathBuf,
39}
40
41impl ConfigDirectory {
42    /// Create a new config directory handler.
43    pub fn new(root: impl AsRef<Path>) -> Self {
44        Self {
45            root: root.as_ref().to_path_buf(),
46        }
47    }
48
49    /// Load configuration using convention-based structure.
50    ///
51    /// Optionally applies environment-specific overrides.
52    pub fn load(&self, environment: Option<&str>) -> Result<Config> {
53        let mut loader = MultiFileLoader::new(&self.root);
54
55        // Load main configuration
56        if self.root.join("grapsus.kdl").exists() {
57            loader = loader.with_include("grapsus.kdl");
58        }
59
60        // Load from subdirectories
61        let subdirs = ["listeners", "routes", "upstreams", "agents"];
62        for subdir in subdirs {
63            let dir = self.root.join(subdir);
64            if dir.exists() {
65                loader = loader.with_include(format!("{}/*.kdl", subdir));
66            }
67        }
68
69        // Load environment-specific overrides
70        if let Some(env) = environment {
71            let env_file = format!("environments/{}.kdl", env);
72            let env_path = self.root.join(&env_file);
73            if env_path.exists() {
74                loader = loader.with_include(env_file);
75            }
76        }
77
78        // Exclude example and backup files
79        loader = loader
80            .with_exclude("*.example.kdl")
81            .with_exclude("*.bak")
82            .with_exclude("*~");
83
84        loader.load()
85    }
86}