1use serde::Deserialize;
25use std::collections::HashMap;
26use std::path::Path;
27
28use crate::{Error, Result};
29
30#[derive(Debug, Clone, Deserialize)]
32pub struct Config {
33 #[serde(default)]
35 pub includes: HashMap<String, String>,
36
37 #[serde(default)]
39 pub defs: HashMap<String, Def>,
40}
41
42#[derive(Debug, Clone, Deserialize)]
44pub struct Def {
45 #[serde(default)]
47 pub extends: Option<String>,
48
49 #[serde(default)]
51 pub traits: Vec<String>,
52
53 #[serde(default)]
55 pub attrs: Vec<String>,
56}
57
58pub fn parse_file<P: AsRef<Path>>(path: P) -> Result<Config> {
72 let content = std::fs::read_to_string(path.as_ref()).map_err(Error::ConfigRead)?;
73 parse(&content)
74}
75
76pub fn parse(content: &str) -> Result<Config> {
96 toml::from_str(content).map_err(Error::TomlParse)
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn test_parse_basic() {
105 let toml = r#"
106[defs.serialization]
107traits = ["Clone", "Serialize", "Deserialize"]
108attrs = ['#[serde(rename_all = "camelCase")]']
109
110[defs.model]
111traits = ["Debug", "Clone", "PartialEq"]
112"#;
113
114 let config = parse(toml).unwrap();
115 assert_eq!(config.defs.len(), 2);
116 assert!(config.defs.contains_key("serialization"));
117 assert!(config.defs.contains_key("model"));
118
119 let serialization = &config.defs["serialization"];
120 assert_eq!(serialization.traits.len(), 3);
121 assert_eq!(serialization.attrs.len(), 1);
122 assert!(serialization.extends.is_none());
123
124 let model = &config.defs["model"];
125 assert_eq!(model.traits.len(), 3);
126 assert!(model.attrs.is_empty());
127 }
128
129 #[test]
130 fn test_parse_with_extends() {
131 let toml = r#"
132[defs.base]
133traits = ["Debug", "Clone"]
134
135[defs.extended]
136extends = "base"
137traits = ["PartialEq"]
138"#;
139
140 let config = parse(toml).unwrap();
141 assert_eq!(config.defs.len(), 2);
142
143 let extended = &config.defs["extended"];
144 assert_eq!(extended.extends.as_deref(), Some("base"));
145 assert_eq!(extended.traits.len(), 1);
146 }
147
148 #[test]
149 fn test_parse_with_includes() {
150 let toml = r#"
151[includes]
152common = "shared/common.toml"
153
154[defs.test]
155traits = ["Debug"]
156"#;
157
158 let config = parse(toml).unwrap();
159 assert_eq!(config.includes.len(), 1);
160 assert_eq!(
161 config.includes.get("common"),
162 Some(&"shared/common.toml".to_string())
163 );
164 }
165
166 #[test]
167 fn test_parse_empty() {
168 let config = parse("").unwrap();
169 assert!(config.defs.is_empty());
170 assert!(config.includes.is_empty());
171 }
172
173 #[test]
174 fn test_parse_invalid_toml() {
175 let result = parse("invalid toml [[");
176 assert!(result.is_err());
177 }
178}