Skip to main content

ephemeris_documents/yaml/source/read/
value.rs

1//! Telling apart the mappings the tree can edit from raw text.
2
3use yaml_rt::{CollectionStyle, NodeId, SemanticKind};
4
5use super::fit::Parsed;
6use super::{Bounds, Mapping, Reader, Text, Value};
7use crate::error::DocumentError;
8use crate::path::Segment;
9
10/// Where a value sits in the source.
11#[derive(Clone, Copy)]
12pub(super) enum Site {
13    /// The root of the document, whose text starts at `line`.
14    Root { line: usize },
15    /// The value after the colon of a key whose line has `indent` spaces
16    /// before it.
17    Entry { colon_end: usize, indent: usize },
18}
19
20impl Site {
21    /// Where the text before an opening brace starts.
22    pub(super) const fn before(self) -> usize {
23        match self {
24            Self::Root { line } => line,
25            Self::Entry {
26                colon_end,
27                indent: _,
28            } => colon_end,
29        }
30    }
31
32    /// Where the entries of a flow mapping would sit as a block.
33    pub(super) const fn flow_indent(self, step: usize) -> usize {
34        match self {
35            Self::Root { line: _ } => 0,
36            Self::Entry {
37                colon_end: _,
38                indent,
39            } => indent.saturating_add(step),
40        }
41    }
42}
43
44impl Reader<'_> {
45    /// The value of a key as a node, and where its text ends. A mapping the
46    /// tree cannot edit is kept as raw text.
47    pub(super) fn value(
48        &self,
49        name: &str,
50        node: NodeId,
51        span: Bounds,
52        colon_end: usize,
53        indent: usize,
54    ) -> Result<Parsed<Value>, DocumentError> {
55        if self.mapping_style(node).is_none() {
56            return Ok(self.leaf(colon_end, node, span)?.map(Value::Leaf));
57        }
58        let site = Site::Entry { colon_end, indent };
59        let fit = self
60            .mapping(node, span, site)
61            .map_err(|error| error.under(Segment::Key(name.to_owned())))?;
62        fit.map_or_else(
63            || {
64                self.opaque(site, node, span)
65                    .map_err(|error| error.under(Segment::Key(name.to_owned())))
66            },
67            |parsed| Ok(parsed.map(Value::Mapping)),
68        )
69    }
70
71    /// The style of `node` when it is a mapping.
72    pub(super) fn mapping_style(&self, node: NodeId) -> Option<CollectionStyle> {
73        let Some(SemanticKind::Mapping { style }) = self.document.semantic_kind(node) else {
74            return None;
75        };
76        Some(style)
77    }
78
79    /// `node` as an editable mapping, or `None` when it is not a mapping, is a
80    /// flow mapping with entries, or does not fit the tree.
81    pub(super) fn mapping(
82        &self,
83        node: NodeId,
84        span: Bounds,
85        site: Site,
86    ) -> Result<Option<Parsed<Mapping>>, DocumentError> {
87        match self.mapping_style(node) {
88            Some(CollectionStyle::Block) => self.block_mapping(node, site),
89            Some(CollectionStyle::Flow) => self.empty_flow_mapping(node, span, site),
90            None => Ok(None),
91        }
92    }
93
94    fn block_mapping(
95        &self,
96        node: NodeId,
97        site: Site,
98    ) -> Result<Option<Parsed<Mapping>>, DocumentError> {
99        match site {
100            Site::Root { line: _ } => Ok(self.block(node, 0)?.map(|parsed| {
101                parsed.map(|block| {
102                    Mapping::block(Text::default(), block.entries, block.indent, self.step)
103                })
104            })),
105            Site::Entry { colon_end, indent } => {
106                let opening_end = self.line_end(colon_end);
107                match self.block(node, opening_end)? {
108                    Some(Parsed { value: block, end }) if block.indent > indent => {
109                        let opening = Text::from(self.slice(colon_end, opening_end)?);
110                        Ok(Some(Parsed {
111                            value: Mapping::block(opening, block.entries, block.indent, self.step),
112                            end,
113                        }))
114                    }
115                    Some(_) | None => Ok(None),
116                }
117            }
118        }
119    }
120
121    fn empty_flow_mapping(
122        &self,
123        node: NodeId,
124        span: Bounds,
125        site: Site,
126    ) -> Result<Option<Parsed<Mapping>>, DocumentError> {
127        let indent = site.flow_indent(self.step);
128        let Some(brace) = self.empty_braces(node, span) else {
129            return Ok(None);
130        };
131        let end = self.line_end(span.end);
132        let mapping = Mapping::empty_flow(
133            Text::from(self.slice(site.before(), brace)?),
134            Text::from(self.slice(brace, span.end)?),
135            Text::from(self.slice(span.end, end)?),
136            indent,
137            self.step,
138        );
139        Ok(Some(Parsed {
140            value: mapping,
141            end,
142        }))
143    }
144}