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}