Skip to main content

ephemeris_documents/yaml/
backend.rs

1//! The YAML backend of the generic merge.
2
3use super::YamlDocument;
4use super::source::{Entry, Tree, Value};
5use crate::annotation::Annotated;
6use crate::backend::{Backend, Editable as _};
7use crate::error::{DocumentError, InternalError, MergeError, YamlError};
8use crate::format::Format;
9use crate::header::Existing;
10use crate::merge::Node as _;
11use crate::path::KeyPath;
12
13/// The YAML backend. It edits the source tree and keeps what it does not
14/// touch byte for byte.
15#[derive(Debug, Clone, Copy)]
16pub(crate) struct Yaml;
17
18impl Backend for Yaml {
19    type Document = Tree;
20
21    fn parse(&self, source: &str) -> Result<Tree, DocumentError> {
22        Tree::parse(source)
23    }
24
25    fn string_leaf(&self, node: &Entry) -> Result<Option<String>, DocumentError> {
26        Ok(match node.value() {
27            Value::Leaf(leaf) => leaf.decoded().map(str::to_owned),
28            Value::Mapping(_) | Value::Opaque(_) => None,
29        })
30    }
31
32    fn descend<'n>(
33        &self,
34        node: &'n Entry,
35        key: &str,
36        path: &[String],
37    ) -> Result<Option<&'n Entry>, DocumentError> {
38        if node.value().is_unsupported() {
39            return Err(YamlError::UnsupportedLayout {
40                path: KeyPath::from(path),
41            }
42            .into());
43        }
44        Ok(node.child(key))
45    }
46
47    fn explain(&self, current: &Tree, error: MergeError) -> DocumentError {
48        match error {
49            MergeError::TypeConflict { path } if current.is_opaque_at(&path) => {
50                YamlError::UnsupportedLayout { path }.into()
51            }
52            MergeError::TypeConflict { path } => MergeError::TypeConflict { path }.into(),
53        }
54    }
55
56    fn format(&self) -> Format {
57        Format::Yaml
58    }
59
60    fn build(&self, desired: &Annotated) -> Result<Tree, DocumentError> {
61        let canonical = YamlDocument::new(desired).to_string();
62        Tree::parse(&canonical).map_err(|source| InternalError::yaml_rendered(source).into())
63    }
64
65    /// Put the generated header comment above the user's, once.
66    fn finish(&self, current: &mut Tree, desired: &Tree, existing: Existing) {
67        current.collapse_empty_root();
68        current.apply_header(desired, existing);
69    }
70}
71
72#[cfg(test)]
73mod tests;