Skip to main content

praxis_core/config/
bootstrap.rs

1// SPDX-License-Identifier: MIT
2// Copyright (c) 2024 Praxis Contributors
3
4//! Configuration loading with fallback resolution.
5
6use super::Config;
7use crate::errors::ProxyError;
8
9// -----------------------------------------------------------------------------
10// Constants
11// -----------------------------------------------------------------------------
12
13/// Built-in fallback configuration (static JSON response on `/`).
14///
15/// ```
16/// let config =
17///     praxis_core::config::Config::from_yaml(praxis_core::config::DEFAULT_CONFIG).unwrap();
18/// assert!(!config.listeners.is_empty());
19/// ```
20pub const DEFAULT_CONFIG: &str = include_str!("default.yaml");
21
22// -----------------------------------------------------------------------------
23// Configuration Loading
24// -----------------------------------------------------------------------------
25
26/// Load configuration from an explicit path, falling back to
27/// `praxis.yaml` in the working directory, then the built-in
28/// default.
29///
30/// # Errors
31///
32/// Returns [`ProxyError::Config`] if the resolved config source
33/// cannot be loaded or is invalid.
34///
35/// ```no_run
36/// let config = praxis_core::config::load_config(None).unwrap();
37/// assert!(!config.listeners.is_empty());
38/// ```
39///
40/// [`ProxyError::Config`]: crate::errors::ProxyError::Config
41pub fn load_config(explicit_path: Option<&str>) -> Result<Config, ProxyError> {
42    Config::load(explicit_path, DEFAULT_CONFIG)
43}
44
45// -----------------------------------------------------------------------------
46// Tests
47// -----------------------------------------------------------------------------
48
49#[cfg(test)]
50#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
51#[allow(
52    clippy::unwrap_used,
53    clippy::expect_used,
54    clippy::indexing_slicing,
55    clippy::needless_raw_strings,
56    clippy::needless_raw_string_hashes,
57    reason = "tests use unwrap/expect/indexing/raw strings for brevity"
58)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn default_config_parses_successfully() {
64        let config = Config::from_yaml(DEFAULT_CONFIG).expect("DEFAULT_CONFIG should parse");
65        assert!(
66            !config.listeners.is_empty(),
67            "default config should define at least one listener"
68        );
69    }
70
71    #[test]
72    fn load_config_nonexistent_explicit_path_returns_error() {
73        let result = Config::load(Some("/nonexistent/path/praxis.yaml"), DEFAULT_CONFIG);
74        assert!(
75            result.is_err(),
76            "loading a nonexistent explicit path should return an error"
77        );
78    }
79
80    #[test]
81    fn load_config_none_with_valid_fallback_succeeds() {
82        let config = Config::load(None, DEFAULT_CONFIG).expect("fallback YAML should parse successfully");
83        assert!(
84            !config.listeners.is_empty(),
85            "fallback config should define at least one listener"
86        );
87        assert_eq!(
88            config.listeners[0].name, "default",
89            "fallback config listener name should be 'default'"
90        );
91    }
92}