archetect_core/config/
answers.rs1use std::fs;
2use std::path::PathBuf;
3
4use linked_hash_map::LinkedHashMap;
5use log::debug;
6use pest::error::Error as PestError;
7use pest::iterators::Pair;
8use pest::Parser;
9
10use crate::config::VariableInfo;
11
12pub type AnswerInfo = VariableInfo;
13
14#[derive(Debug, Deserialize, Serialize)]
15pub struct AnswerConfig {
16 #[serde(skip_serializing_if = "LinkedHashMap::is_empty")]
17 answers: LinkedHashMap<String, AnswerInfo>,
18}
19
20#[derive(Debug, PartialEq, thiserror::Error)]
21pub enum AnswerConfigError {
22 #[error("Error parsing answer config: {0}")]
23 ParseError(String),
24 #[error("Missing answer config")]
25 MissingError,
26}
27
28impl From<serde_yaml::Error> for AnswerConfigError {
29 fn from(error: serde_yaml::Error) -> Self {
30 AnswerConfigError::ParseError(error.to_string())
31 }
32}
33
34impl From<std::io::Error> for AnswerConfigError {
35 fn from(_: std::io::Error) -> Self {
36 AnswerConfigError::MissingError
38 }
39}
40
41impl AnswerConfig {
42 pub fn load<P: Into<PathBuf>>(path: P) -> Result<AnswerConfig, AnswerConfigError> {
43 let path = path.into();
44 if path.is_dir() {
45 let answer_file_names = vec![
46 "archetect.yml",
47 ".archetect.yml",
48 "archetect.yaml",
49 ".archetect.yaml",
50 ".answers.yaml",
51 "answers.yaml",
52 ];
53 for answer_file_name in answer_file_names {
54 let answers = path.join(answer_file_name);
55 if answers.exists() {
56 debug!("Reading Archetect config from '{}'", &answers.display());
57 let config = fs::read_to_string(answers)?;
58 let config = serde_yaml::from_str::<AnswerConfig>(&config)?;
59 return Ok(config);
60 }
61 }
62 } else {
63 let config = fs::read_to_string(path)?;
64 let config = serde_yaml::from_str::<AnswerConfig>(&config)?;
65 return Ok(config);
66 }
67
68 Err(AnswerConfigError::MissingError)
70 }
71
72 pub fn add_answer(&mut self, identifier: &str, value: &str) {
73 self.answers
74 .insert(identifier.to_owned(), AnswerInfo::with_value(value).build());
75 }
76
77 pub fn with_answer(mut self, identifier: &str, value: &str) -> AnswerConfig {
78 self.add_answer(identifier, value);
79 self
80 }
81
82 pub fn answers(&self) -> &LinkedHashMap<String, AnswerInfo> {
83 &self.answers
84 }
85}
86
87impl Default for AnswerConfig {
88 fn default() -> Self {
89 AnswerConfig {
90 answers: LinkedHashMap::new(),
91 }
92 }
93}
94
95#[derive(Parser)]
96#[grammar = "config/answer_grammar.pest"]
97struct AnswerParser;
98
99#[derive(Debug, PartialEq)]
100pub enum AnswerParseError {
101 PestError(PestError<Rule>),
102}
103
104impl From<PestError<Rule>> for AnswerParseError {
105 fn from(error: PestError<Rule>) -> Self {
106 AnswerParseError::PestError(error)
107 }
108}
109
110fn parse(source: &str) -> Result<(String, AnswerInfo), AnswerParseError> {
111 let mut pairs = AnswerParser::parse(Rule::answer, source)?;
112 Ok(parse_answer(pairs.next().unwrap()))
113}
114
115fn parse_answer(pair: Pair<Rule>) -> (String, AnswerInfo) {
116 assert_eq!(pair.as_rule(), Rule::answer);
117 let mut iter = pair.into_inner();
118 let identifier_pair = iter.next().unwrap();
119 let value_pair = iter.next().unwrap();
120 (
121 parse_identifier(identifier_pair),
122 AnswerInfo::with_value(parse_value(value_pair)).build(),
123 )
124}
125
126fn parse_identifier(pair: Pair<Rule>) -> String {
127 assert_eq!(pair.as_rule(), Rule::identifier);
128 pair.as_str().to_owned()
129}
130
131fn parse_value(pair: Pair<Rule>) -> String {
132 assert_eq!(pair.as_rule(), Rule::string);
133 pair.into_inner().next().unwrap().as_str().to_owned()
134}
135
136impl AnswerInfo {
137 pub fn parse(input: &str) -> Result<(String, AnswerInfo), AnswerParseError> {
138 parse(input)
139 }
140}
141
142#[cfg(test)]
143mod tests {
144 use super::*;
145
146 #[test]
147 fn test_parse_success() {
148 assert_eq!(
149 parse("key=value"),
150 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
151 );
152
153 assert_eq!(
154 parse("key = value"),
155 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
156 );
157
158 assert_eq!(
159 parse("key = value set"),
160 Ok(("key".to_owned(), AnswerInfo::with_value("value set").build()))
161 );
162
163 assert_eq!(
164 parse("key='value'"),
165 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
166 );
167
168 assert_eq!(
169 parse("key='value set'"),
170 Ok(("key".to_owned(), AnswerInfo::with_value("value set").build()))
171 );
172
173 assert_eq!(
174 parse("key = 'value'"),
175 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
176 );
177
178 assert_eq!(
179 parse("key=\"value\""),
180 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
181 );
182
183 assert_eq!(
184 parse("key=\"value set\""),
185 Ok(("key".to_owned(), AnswerInfo::with_value("value set").build()))
186 );
187
188 assert_eq!(
189 parse("key = \"value\""),
190 Ok(("key".to_owned(), AnswerInfo::with_value("value").build()))
191 );
192
193 assert_eq!(
194 parse("key ="),
195 Ok(("key".to_owned(), AnswerInfo::with_value("").build()))
196 );
197
198 assert_eq!(
199 parse("key =''"),
200 Ok(("key".to_owned(), AnswerInfo::with_value("").build()))
201 );
202
203 assert_eq!(
204 parse(" key =\"\""),
205 Ok(("key".to_owned(), AnswerInfo::with_value("").build()))
206 );
207 }
208
209 #[test]
210 fn test_parse_fail() {
211 match parse("key") {
212 Err(AnswerParseError::PestError(_)) => (),
213 _ => panic!("Error expected"),
214 }
215 }
216
217 #[test]
218 fn test_parse_answer() {
219 assert_eq!(
220 parse_answer(AnswerParser::parse(Rule::answer, "key=value").unwrap().next().unwrap()),
221 ("key".to_owned(), AnswerInfo::with_value("value").build())
222 );
223
224 assert_eq!(
225 parse_answer(
226 AnswerParser::parse(Rule::answer, "key='value'")
227 .unwrap()
228 .next()
229 .unwrap()
230 ),
231 ("key".to_owned(), AnswerInfo::with_value("value").build())
232 );
233
234 assert_eq!(
235 parse_answer(
236 AnswerParser::parse(Rule::answer, "key=\"value\"")
237 .unwrap()
238 .next()
239 .unwrap()
240 ),
241 ("key".to_owned(), AnswerInfo::with_value("value").build())
242 );
243 }
244
245 #[test]
246 fn test_parse_identifier() {
247 assert_eq!(
248 parse_identifier(AnswerParser::parse(Rule::identifier, "key").unwrap().next().unwrap()),
249 "key"
250 );
251 }
252
253 #[test]
254 fn test_parse_value() {
255 assert_eq!(
256 parse_value(AnswerParser::parse(Rule::string, "value").unwrap().next().unwrap()),
257 "value"
258 );
259
260 assert_eq!(
261 parse_value(AnswerParser::parse(Rule::string, "\"value\"").unwrap().next().unwrap()),
262 "value"
263 );
264
265 assert_eq!(
266 parse_value(AnswerParser::parse(Rule::string, "'value'").unwrap().next().unwrap()),
267 "value"
268 );
269 }
270
271 #[test]
272 fn test_serialize_answer_config() {
273 let config = AnswerConfig::default()
274 .with_answer("name", "Order Service")
275 .with_answer("author", "Jane Doe");
276
277 println!("{}", serde_yaml::to_string(&config).unwrap());
278 }
279}