1use core::fmt;
40use core::str::FromStr;
41
42use crate::ConfigContent;
43use crate::error::{ConfigError, ConfigResult};
44use crate::key::{Key, KeySegment};
45use crate::value::Value;
46use serde_yaml::Value as YamlValue;
47
48#[derive(Debug, Clone)]
52pub struct YamlContent {
53 data: YamlValue,
54}
55
56impl PartialEq for YamlContent {
57 fn eq(&self, other: &Self) -> bool {
58 self.data == other.data
60 }
61}
62
63#[allow(dead_code)]
64impl YamlContent {
65 pub fn from_value(value: YamlValue) -> Self {
67 Self { data: value }
68 }
69
70 pub fn as_value(&self) -> &YamlValue {
72 &self.data
73 }
74
75 pub fn into_value(self) -> YamlValue {
77 self.data
78 }
79
80 fn get_value_at_key(&self, key: &Key) -> ConfigResult<&YamlValue> {
81 let mut current = &self.data;
82
83 for segment in key.segments() {
84 match segment {
85 KeySegment::Key(s) => {
86 current = match current {
87 YamlValue::Mapping(m) => {
88 let yaml_key = YamlValue::String(s.clone());
89 m.get(&yaml_key)
90 .ok_or_else(|| ConfigError::KeyNotFound(Key::to_key_string(key)))?
91 }
92 _ => {
93 return Err(ConfigError::KeyNotFound(Key::to_key_string(key)));
94 }
95 };
96 }
97 KeySegment::Index(i) => {
98 current = match current {
99 YamlValue::Sequence(seq) => seq
100 .get(*i)
101 .ok_or_else(|| ConfigError::KeyNotFound(Key::to_key_string(key)))?,
102 _ => {
103 return Err(ConfigError::KeyNotFound(Key::to_key_string(key)));
104 }
105 };
106 }
107 KeySegment::Attribute(_) => {
108 return Err(ConfigError::InvalidKey(
109 "YAML does not support attributes".to_string(),
110 ));
111 }
112 }
113 }
114
115 Ok(current)
116 }
117
118 fn contains_key_at(&self, key: &Key) -> bool {
119 self.get_value_at_key(key).is_ok()
120 }
121
122 fn keys_at(&self) -> Vec<Key> {
123 match &self.data {
124 YamlValue::Mapping(m) => m
125 .keys()
126 .filter_map(|k| {
127 if let YamlValue::String(s) = k {
128 Key::from_str(&format!(".{}", s)).ok()
129 } else {
130 None
131 }
132 })
133 .collect(),
134 YamlValue::Sequence(seq) => (0..seq.len())
135 .filter_map(|i| Key::from_str(&format!(".[{i}]")).ok())
136 .collect(),
137 _ => vec![],
138 }
139 }
140
141 fn len_at(&self) -> usize {
142 match &self.data {
143 YamlValue::Mapping(m) => m.len(),
144 YamlValue::Sequence(seq) => seq.len(),
145 _ => 0,
146 }
147 }
148
149 fn delete_at(&mut self, key: &Key) -> ConfigResult<()> {
150 if key.segments().is_empty() {
151 return Err(ConfigError::DeleteError("Cannot delete root".to_string()));
152 }
153
154 let segments = key.segments();
155 let last_idx = segments.len() - 1;
156
157 let mut current = &mut self.data;
158
159 for (i, segment) in segments.iter().enumerate() {
160 if i == last_idx {
161 match segment {
162 KeySegment::Key(s) => {
163 if let YamlValue::Mapping(m) = current {
164 let yaml_key = YamlValue::String(s.clone());
165 drop(m.remove(&yaml_key));
166 return Ok(());
167 }
168 }
169 KeySegment::Index(idx) => {
170 if let YamlValue::Sequence(seq) = current {
171 if *idx < seq.len() {
172 drop(seq.remove(*idx));
173 return Ok(());
174 }
175 }
176 }
177 KeySegment::Attribute(_) => {
178 return Err(ConfigError::InvalidKey(
179 "YAML does not support attributes".to_string(),
180 ));
181 }
182 }
183 } else {
184 match segment {
185 KeySegment::Key(s) => {
186 if let YamlValue::Mapping(m) = current {
187 let yaml_key = YamlValue::String(s.clone());
188 if let Some(next) = m.get_mut(&yaml_key) {
189 current = next;
190 } else {
191 return Err(ConfigError::KeyNotFound(key.to_key_string()));
192 }
193 } else {
194 return Err(ConfigError::KeyNotFound(key.to_key_string()));
195 }
196 }
197 KeySegment::Index(idx) => {
198 if let YamlValue::Sequence(seq) = current {
199 if let Some(next) = seq.get_mut(*idx) {
200 current = next;
201 } else {
202 return Err(ConfigError::KeyNotFound(key.to_key_string()));
203 }
204 } else {
205 return Err(ConfigError::KeyNotFound(key.to_key_string()));
206 }
207 }
208 KeySegment::Attribute(_) => {
209 return Err(ConfigError::InvalidKey(
210 "YAML does not support attributes".to_string(),
211 ));
212 }
213 }
214 }
215 }
216
217 Err(ConfigError::KeyNotFound(key.to_key_string()))
218 }
219
220 fn upsert_at(&mut self, key: &Key, value: &Value) -> ConfigResult<()> {
221 if key.segments().is_empty() {
222 self.data = yaml_value_from_value(value);
223 return Ok(());
224 }
225
226 let segments = key.segments();
227 let last_idx = segments.len() - 1;
228
229 let mut current = &mut self.data;
230
231 for (i, segment) in segments.iter().enumerate() {
232 if i == last_idx {
233 match segment {
234 KeySegment::Key(s) => {
235 if let YamlValue::Mapping(m) = current {
236 drop(m.insert(
237 YamlValue::String(s.clone()),
238 yaml_value_from_value(value),
239 ));
240 return Ok(());
241 }
242 }
243 KeySegment::Index(idx) => {
244 if let YamlValue::Sequence(seq) = current {
245 if *idx < seq.len() {
246 seq[*idx] = yaml_value_from_value(value);
247 } else if *idx == seq.len() {
248 seq.push(yaml_value_from_value(value));
249 } else {
250 return Err(ConfigError::KeyNotFound(key.to_key_string()));
251 }
252 return Ok(());
253 }
254 }
255 KeySegment::Attribute(_) => {
256 return Err(ConfigError::InvalidKey(
257 "YAML does not support attributes".to_string(),
258 ));
259 }
260 }
261 } else {
262 match segment {
263 KeySegment::Key(s) => {
264 let next = match current {
265 YamlValue::Mapping(m) => {
266 let yaml_key = YamlValue::String(s.clone());
267 let key_exists = m.contains_key(&yaml_key);
268 if !key_exists {
269 drop(m.insert(
270 yaml_key.clone(),
271 YamlValue::Mapping(serde_yaml::Mapping::new()),
272 ));
273 }
274 m.get_mut(&yaml_key).ok_or_else(|| {
275 ConfigError::KeyNotFound(Key::to_key_string(key))
276 })?
277 }
278 _ => {
279 return Err(ConfigError::KeyNotFound(Key::to_key_string(key)));
280 }
281 };
282 current = next;
283 }
284 KeySegment::Index(idx) => {
285 let next = match current {
286 YamlValue::Sequence(seq) => {
287 if *idx < seq.len() {
288 &mut seq[*idx]
289 } else if *idx == seq.len() {
290 seq.push(YamlValue::Null);
291 seq.last_mut().ok_or_else(|| {
292 ConfigError::KeyNotFound(Key::to_key_string(key))
293 })?
294 } else {
295 return Err(ConfigError::KeyNotFound(Key::to_key_string(key)));
296 }
297 }
298 _ => {
299 return Err(ConfigError::KeyNotFound(Key::to_key_string(key)));
300 }
301 };
302 current = next;
303 }
304 KeySegment::Attribute(_) => {
305 return Err(ConfigError::InvalidKey(
306 "YAML does not support attributes".to_string(),
307 ));
308 }
309 }
310 }
311 }
312
313 Ok(())
314 }
315}
316
317impl ConfigContent for YamlContent {
318 fn select(&self, key: &Key) -> ConfigResult<Value> {
319 let yaml_value = self.get_value_at_key(key)?;
320 Ok(value_from_yaml_value(yaml_value))
321 }
322
323 fn insert(&mut self, key: &Key, value: &Value) -> ConfigResult<()> {
324 if self.contains_key_at(key) {
325 return Err(ConfigError::KeyAlreadyExists(key.to_key_string()));
326 }
327 self.upsert_at(key, value)
328 }
329
330 fn update(&mut self, key: &Key, value: &Value) -> ConfigResult<()> {
331 if !self.contains_key_at(key) {
332 return Err(ConfigError::KeyDoesNotExist(key.to_key_string()));
333 }
334 self.upsert_at(key, value)
335 }
336
337 fn delete(&mut self, key: &Key) -> ConfigResult<()> {
338 self.delete_at(key)
339 }
340
341 fn upsert(&mut self, key: &Key, value: &Value) -> ConfigResult<()> {
342 self.upsert_at(key, value)
343 }
344}
345
346impl FromStr for YamlContent {
347 type Err = ConfigError;
348
349 fn from_str(s: &str) -> Result<Self, Self::Err> {
350 let value = serde_yaml::from_str(s).map_err(|e| ConfigError::ParseError(e.to_string()))?;
351 Ok(Self { data: value })
352 }
353}
354
355impl fmt::Display for YamlContent {
356 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
357 let output = serde_yaml::to_string(&self.data).map_err(|_| fmt::Error)?;
358 write!(f, "{}", output)
359 }
360}
361
362fn value_from_yaml_value(yaml: &YamlValue) -> Value {
363 match yaml {
364 YamlValue::Null => Value::Null,
365 YamlValue::Bool(b) => Value::Bool(*b),
366 YamlValue::Number(n) => {
367 if let Some(i) = n.as_i64() {
368 Value::Integer(i)
369 } else if let Some(f) = n.as_f64() {
370 Value::Float(f)
371 } else {
372 Value::Null
373 }
374 }
375 YamlValue::String(s) => Value::String(s.clone()),
376 YamlValue::Sequence(seq) => Value::Array(seq.iter().map(value_from_yaml_value).collect()),
377 YamlValue::Mapping(m) => Value::Map(
378 m.iter()
379 .filter_map(|(k, v)| {
380 if let YamlValue::String(s) = k {
381 Some((s.clone(), value_from_yaml_value(v)))
382 } else {
383 None
384 }
385 })
386 .collect(),
387 ),
388 YamlValue::Tagged(tagged) => value_from_yaml_value(&tagged.value),
389 }
390}
391
392fn yaml_value_from_value(value: &Value) -> YamlValue {
393 match value {
394 Value::Null => YamlValue::Null,
395 Value::Bool(b) => YamlValue::Bool(*b),
396 Value::Integer(i) => YamlValue::Number(serde_yaml::Number::from(*i)),
397 Value::Float(f) => {
398 if f.is_nan() {
400 YamlValue::String(".nan".to_string())
401 } else if f.is_infinite() {
402 if f.is_sign_positive() {
403 YamlValue::String(".inf".to_string())
404 } else {
405 YamlValue::String("-.inf".to_string())
406 }
407 } else {
408 YamlValue::Number(serde_yaml::Number::from(*f))
409 }
410 }
411 Value::String(s) => YamlValue::String(s.clone()),
412 Value::Array(arr) => YamlValue::Sequence(arr.iter().map(yaml_value_from_value).collect()),
413 Value::Map(m) => {
414 let mut mapping = serde_yaml::Mapping::new();
415 for (k, v) in m {
416 drop(mapping.insert(YamlValue::String(k.clone()), yaml_value_from_value(v)));
417 }
418 YamlValue::Mapping(mapping)
419 }
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426
427 #[test]
428 fn test_yaml_display_is_stable_and_pretty() {
429 let content = YamlContent::from_str("b: 2\na:\n x: 1").expect("test should succeed");
430 let first = content.to_string();
431 let second = content.to_string();
432 assert_eq!(first, second);
433 assert!(first.contains("\n"));
434 }
435
436 #[test]
437 fn test_yaml_from_str() {
438 let content = YamlContent::from_str("key: value").expect("test should succeed");
439 assert!(
440 content
441 .select(&Key::from_str(".key").expect("test should succeed"))
442 .is_ok()
443 );
444 }
445
446 #[test]
447 fn test_yaml_select() {
448 let content = YamlContent::from_str("name: test").expect("test should succeed");
449 let value = content
450 .select(&Key::from_str(".name").expect("test should succeed"))
451 .expect("test should succeed");
452 assert_eq!(value.as_str(), Some("test"));
453 }
454
455 #[test]
456 fn test_yaml_insert() {
457 let mut content = YamlContent::from_str("{}").expect("test should succeed");
458 content
459 .insert(
460 &Key::from_str(".name").expect("test should succeed"),
461 &Value::string("test"),
462 )
463 .expect("test should succeed");
464 let value = content
465 .select(&Key::from_str(".name").expect("test should succeed"))
466 .expect("test should succeed");
467 assert_eq!(value.as_str(), Some("test"));
468 }
469
470 #[test]
471 fn test_yaml_update() {
472 let mut content = YamlContent::from_str("name: old").expect("test should succeed");
473 content
474 .update(
475 &Key::from_str(".name").expect("test should succeed"),
476 &Value::string("new"),
477 )
478 .expect("test should succeed");
479 let value = content
480 .select(&Key::from_str(".name").expect("test should succeed"))
481 .expect("test should succeed");
482 assert_eq!(value.as_str(), Some("new"));
483 }
484
485 #[test]
486 fn test_yaml_delete() {
487 let mut content = YamlContent::from_str("name: test").expect("test should succeed");
488 content
489 .delete(&Key::from_str(".name").expect("test should succeed"))
490 .expect("test should succeed");
491 assert!(
492 content
493 .select(&Key::from_str(".name").expect("test should succeed"))
494 .is_err()
495 );
496 }
497
498 #[test]
499 fn test_yaml_upsert() {
500 let mut content = YamlContent::from_str("{}").expect("test should succeed");
501 content
502 .upsert(
503 &Key::from_str(".name").expect("test should succeed"),
504 &Value::string("test"),
505 )
506 .expect("test should succeed");
507 let value = content
508 .select(&Key::from_str(".name").expect("test should succeed"))
509 .expect("test should succeed");
510 assert_eq!(value.as_str(), Some("test"));
511 }
512
513 #[test]
514 fn test_yaml_nested() {
515 let content =
516 YamlContent::from_str("database:\n url: localhost").expect("test should succeed");
517 let value = content
518 .select(&Key::from_str(".database.url").expect("test should succeed"))
519 .expect("test should succeed");
520 assert_eq!(value.as_str(), Some("localhost"));
521 }
522
523 #[test]
524 fn test_yaml_array() {
525 let content =
526 YamlContent::from_str("items:\n - 1\n - 2\n - 3").expect("test should succeed");
527 let value = content
528 .select(&Key::from_str(".items[0]").expect("test should succeed"))
529 .expect("test should succeed");
530 assert_eq!(value.as_integer(), Some(1));
531 }
532
533 #[test]
534 fn test_yaml_string() {
535 let content = YamlContent::from_str("text: hello").expect("test should succeed");
536 let value = content
537 .select(&Key::from_str(".text").expect("test should succeed"))
538 .expect("test should succeed");
539 assert_eq!(value.as_str(), Some("hello"));
540 }
541
542 #[test]
543 fn test_yaml_integer() {
544 let content = YamlContent::from_str("num: 42").expect("test should succeed");
545 let value = content
546 .select(&Key::from_str(".num").expect("test should succeed"))
547 .expect("test should succeed");
548 assert_eq!(value.as_integer(), Some(42));
549 }
550
551 #[test]
552 fn test_yaml_negative_integer() {
553 let content = YamlContent::from_str("num: -17").expect("test should succeed");
554 let value = content
555 .select(&Key::from_str(".num").expect("test should succeed"))
556 .expect("test should succeed");
557 assert_eq!(value.as_integer(), Some(-17));
558 }
559
560 #[test]
561 fn test_yaml_float() {
562 let content = YamlContent::from_str("num: 3.14").expect("test should succeed");
563 let value = content
564 .select(&Key::from_str(".num").expect("test should succeed"))
565 .expect("test should succeed");
566 let f = value.as_float().expect("test should succeed");
567 assert!((f - 3.14).abs() < 1e-6);
568 }
569
570 #[test]
571 fn test_yaml_boolean() {
572 let content = YamlContent::from_str("t: true\nf: false").expect("test should succeed");
573 assert_eq!(
574 content
575 .select(&Key::from_str(".t").expect("test should succeed"))
576 .expect("test should succeed")
577 .as_bool(),
578 Some(true)
579 );
580 assert_eq!(
581 content
582 .select(&Key::from_str(".f").expect("test should succeed"))
583 .expect("test should succeed")
584 .as_bool(),
585 Some(false)
586 );
587 }
588
589 #[test]
590 fn test_yaml_null() {
591 let content = YamlContent::from_str("value: null").expect("test should succeed");
592 let value = content
593 .select(&Key::from_str(".value").expect("test should succeed"))
594 .expect("test should succeed");
595 assert!(value.is_null());
596 }
597
598 #[test]
599 fn test_yaml_unicode() {
600 let content = YamlContent::from_str("text: hello").expect("test should succeed");
601 let value = content
602 .select(&Key::from_str(".text").expect("test should succeed"))
603 .expect("test should succeed");
604 assert_eq!(value.as_str(), Some("hello"));
605 }
606
607 #[test]
608 fn test_yaml_nested_array() {
609 let content =
610 YamlContent::from_str("matrix:\n - [1, 2]\n - [3, 4]").expect("test should succeed");
611 let value = content
612 .select(&Key::from_str(".matrix[0][1]").expect("test should succeed"))
613 .expect("test should succeed");
614 assert_eq!(value.as_integer(), Some(2));
615 }
616
617 #[test]
618 fn test_yaml_mixed_array() {
619 let content = YamlContent::from_str("data:\n - name: Alice\n - name: Bob")
620 .expect("test should succeed");
621 let value = content
622 .select(&Key::from_str(".data[0].name").expect("test should succeed"))
623 .expect("test should succeed");
624 assert_eq!(value.as_str(), Some("Alice"));
625 }
626
627 #[test]
628 fn test_yaml_delete_nested() {
629 let mut content = YamlContent::from_str("server:\n host: localhost\n port: 8080")
630 .expect("test should succeed");
631 content
632 .delete(&Key::from_str(".server.port").expect("test should succeed"))
633 .expect("test should succeed");
634 assert!(
635 content
636 .select(&Key::from_str(".server.port").expect("test should succeed"))
637 .is_err()
638 );
639 }
640
641 #[test]
642 fn test_yaml_update_nested() {
643 let mut content =
644 YamlContent::from_str("server:\n host: old").expect("test should succeed");
645 content
646 .update(
647 &Key::from_str(".server.host").expect("test should succeed"),
648 &Value::string("new"),
649 )
650 .expect("test should succeed");
651 let value = content
652 .select(&Key::from_str(".server.host").expect("test should succeed"))
653 .expect("test should succeed");
654 assert_eq!(value.as_str(), Some("new"));
655 }
656
657 #[test]
658 fn test_yaml_explicit_tag() {
659 let content = YamlContent::from_str("num: !!int 42").expect("test should succeed");
660 let value = content
661 .select(&Key::from_str(".num").expect("test should succeed"))
662 .expect("test should succeed");
663 assert_eq!(value.as_integer(), Some(42));
664 }
665}