sara_core/config/
settings.rs1use serde::{Deserialize, Serialize};
4use std::path::PathBuf;
5
6use crate::error::ConfigError;
7
8#[derive(Debug, Clone, Serialize, Deserialize, Default)]
10pub struct Config {
11 #[serde(default)]
13 pub repositories: RepositoryConfig,
14
15 #[serde(default)]
17 pub validation: ValidationConfig,
18
19 #[serde(default)]
21 pub output: OutputConfig,
22
23 #[serde(default)]
25 pub templates: TemplatesConfig,
26}
27
28impl Config {
29 pub fn new() -> Self {
31 Self::default()
32 }
33
34 pub fn add_repository(&mut self, path: impl Into<PathBuf>) {
36 self.repositories.paths.push(path.into());
37 }
38
39 pub fn expand_template_paths(&self) -> Result<Vec<PathBuf>, ConfigError> {
41 expand_glob_patterns(&self.templates.paths)
42 }
43}
44
45#[derive(Debug, Clone, Default, Serialize, Deserialize)]
47pub struct RepositoryConfig {
48 #[serde(default)]
50 pub paths: Vec<PathBuf>,
51}
52
53#[derive(Debug, Clone, Serialize, Deserialize, Default)]
55pub struct ValidationConfig {
56 #[serde(default)]
58 pub strict_orphans: bool,
59
60 #[serde(default)]
62 pub allowed_custom_fields: Vec<String>,
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub struct OutputConfig {
68 #[serde(default = "default_true")]
70 pub colors: bool,
71
72 #[serde(default = "default_true")]
74 pub emojis: bool,
75}
76
77impl Default for OutputConfig {
78 fn default() -> Self {
79 Self {
80 colors: true,
81 emojis: true,
82 }
83 }
84}
85
86fn default_true() -> bool {
87 true
88}
89
90#[derive(Debug, Clone, Default, Serialize, Deserialize)]
92pub struct TemplatesConfig {
93 #[serde(default)]
98 pub paths: Vec<String>,
99}
100
101pub fn expand_glob_patterns(patterns: &[String]) -> Result<Vec<PathBuf>, ConfigError> {
103 let mut result = Vec::new();
104
105 for pattern in patterns {
106 match glob::glob(pattern) {
107 Ok(paths) => {
108 for entry in paths {
109 match entry {
110 Ok(path) => result.push(path),
111 Err(e) => {
112 return Err(ConfigError::InvalidGlobPattern {
113 pattern: pattern.clone(),
114 reason: e.to_string(),
115 });
116 }
117 }
118 }
119 }
120 Err(e) => {
121 return Err(ConfigError::InvalidGlobPattern {
122 pattern: pattern.clone(),
123 reason: e.to_string(),
124 });
125 }
126 }
127 }
128
129 Ok(result)
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_default_config() {
138 let config = Config::default();
139 assert!(config.repositories.paths.is_empty());
140 assert!(!config.validation.strict_orphans);
141 assert!(config.output.colors);
142 assert!(config.output.emojis);
143 }
144
145 #[test]
146 fn test_add_repository() {
147 let mut config = Config::default();
148 config.add_repository("/path/to/repo");
149 assert_eq!(config.repositories.paths.len(), 1);
150 }
151
152 #[test]
153 fn test_config_serialization() {
154 let config = Config::default();
155 let toml_str = toml::to_string(&config).unwrap();
156 assert!(toml_str.contains("[repositories]"));
157 }
158}