Skip to main content

ephemeris_documents/
backend.rs

1//! Parsing, building and reconciling documents for a format.
2
3use std::fmt;
4
5use crate::annotation::Annotated;
6use crate::error::{DocumentError, MergeError};
7use crate::format::Format;
8use crate::header::{Applied, Existing, Header};
9use crate::merge::{Node, merge};
10use crate::ownership::Ownership;
11use crate::source::Source;
12
13/// A parsed document whose root node can be edited in place.
14pub(crate) trait Editable {
15    type Node: Node;
16
17    /// Replace the leading comment block of this document with the header
18    /// of `desired`.
19    fn apply_header(&mut self, desired: &Self, existing: Existing) {
20        let header = Header::new(desired.header_text());
21        let (root, first) = self.leading();
22        let applied = header
23            .applied_to_parts(root, first, existing)
24            .map(Applied::into_owned);
25        if let Some(applied) = applied {
26            self.set_leading(applied);
27        }
28    }
29
30    /// The text of the header comment of this document.
31    fn header_text(&self) -> &str;
32
33    /// The text before the first entry as the document root holds it and as
34    /// the first entry holds it. A format that keeps all of it in one place
35    /// returns an empty root text.
36    fn leading(&self) -> (&str, &str);
37
38    /// The root node.
39    fn root(&self) -> &Self::Node;
40
41    /// The root node the merge works on.
42    fn root_mut(&mut self) -> &mut Self::Node;
43
44    /// Put the leading text of an applied header in place.
45    fn set_leading(&mut self, applied: Applied<'_>);
46}
47
48/// The node type of the documents of backend `B`.
49pub(crate) type NodeOf<B> = <<B as Backend>::Document as Editable>::Node;
50
51/// A structured format that can be parsed, edited in place, and rendered.
52pub(crate) trait Backend {
53    type Document: fmt::Display + Editable;
54
55    /// Build the desired document from its annotated value.
56    ///
57    /// # Errors
58    /// Returns an error when the value cannot be represented in this format.
59    fn build(&self, desired: &Annotated) -> Result<Self::Document, DocumentError>;
60
61    /// The child `key` of `node`, which sits at `path`. Backends fail here
62    /// when a node on the way cannot be read through.
63    ///
64    /// # Errors
65    /// Returns an error when `node` cannot be descended into.
66    fn descend<'n>(
67        &self,
68        node: &'n NodeOf<Self>,
69        key: &str,
70        _path: &[String],
71    ) -> Result<Option<&'n NodeOf<Self>>, DocumentError> {
72        Ok(node.child(key))
73    }
74
75    /// The error for a merge that failed against `current`. Backends can
76    /// name a cause more precisely than the generic conflict.
77    fn explain(&self, _current: &Self::Document, error: MergeError) -> DocumentError {
78        DocumentError::Merge(error)
79    }
80
81    /// Adjust the merged document before it is rendered.
82    fn finish(&self, current: &mut Self::Document, desired: &Self::Document, existing: Existing);
83
84    /// The format this backend reads and writes.
85    fn format(&self) -> Format;
86
87    /// Parse an existing source document.
88    ///
89    /// # Errors
90    /// Returns an error when the source is malformed.
91    fn parse(&self, source: &str) -> Result<Self::Document, DocumentError>;
92
93    /// The string at `path` in `source`, or `None` when a key on the way is
94    /// absent.
95    ///
96    /// # Errors
97    /// Returns an error for malformed source, and a not-a-string error when
98    /// the value at `path` is not a string.
99    fn read_string(&self, source: &str, path: &[String]) -> Result<Option<String>, DocumentError> {
100        let document = self.parse(source)?;
101        let mut node = document.root();
102        for (index, key) in path.iter().enumerate() {
103            match self.descend(node, key, path.get(..index).unwrap_or_default())? {
104                Some(child) => node = child,
105                None => return Ok(None),
106            }
107        }
108        self.string_leaf(node)?
109            .map(Some)
110            .ok_or_else(|| DocumentError::not_string(path))
111    }
112
113    /// Reconcile a desired value against the current source.
114    ///
115    /// When the whole document is owned, the source holds no text and cannot
116    /// affect the output.
117    ///
118    /// # Errors
119    /// Returns an error for malformed source, values the format cannot
120    /// represent, or an incompatible merge.
121    fn reconcile(
122        &self,
123        desired: &Annotated,
124        source: &Source<'_>,
125        ownership: &Ownership,
126    ) -> Result<Vec<u8>, DocumentError> {
127        desired.validate(self.format())?;
128        let mut desired = self.build(desired)?;
129        let mut document = self.parse(source.text().unwrap_or_default())?;
130        merge(document.root_mut(), &*desired.root_mut(), ownership)
131            .map_err(|error| self.explain(&document, error))?;
132        self.finish(&mut document, &desired, Existing::under(ownership));
133        Ok(source.write(&document))
134    }
135
136    /// The text of `node` when it is a string, `None` for any other kind of
137    /// value.
138    ///
139    /// # Errors
140    /// Returns an error when the string cannot be decoded.
141    fn string_leaf(&self, node: &NodeOf<Self>) -> Result<Option<String>, DocumentError>;
142}