Skip to main content

apollo_saphyr/annotated/
marked_yaml_owned.rs

1//! A YAML node with position in the source document.
2//!
3//! This is set aside so as to not clutter `annotated.rs`.
4
5use alloc::{
6    borrow::Cow,
7    boxed::Box,
8    string::{String, ToString},
9    vec::Vec,
10};
11
12use hashlink::LinkedHashMap;
13use saphyr_parser::{ScalarStyle, Span, Tag};
14
15use crate::{
16    index::SafelyIndexMut, Accessor, LoadableYamlNode, SafelyIndex, ScalarOwned, Yaml,
17    YamlDataOwned,
18};
19
20/// A YAML node with [`Span`]s pointing to the start of the node.
21///
22/// This structure does not implement functions to operate on the YAML object. To access those,
23/// refer to the [`Self::data`] field.
24///
25/// # Warning
26/// In order to allow indexing by content in mappings, equality comparisons for this structure
27/// **ignore** the [`Span`].
28#[derive(Clone, Debug)]
29pub struct MarkedYamlOwned {
30    /// The span indicating where in the input stream the object is.
31    ///
32    /// The markers are relative to the start of the input stream that was given to the parser, not
33    /// to the start of the document within the input stream.
34    pub span: Span,
35    /// The YAML contents of the node.
36    pub data: YamlDataOwned<MarkedYamlOwned>,
37}
38
39impl MarkedYamlOwned {
40    /// Convert a string to a scalar node.
41    ///
42    /// See [`YamlData::value_from_str`] for more details.
43    ///
44    /// The returned node is created with a default [`Span`].
45    ///
46    /// [`YamlData::value_from_str`]: crate::YamlData::value_from_str
47    #[must_use]
48    pub fn value_from_str(v: &str) -> Self {
49        Self::value_from_cow(v.into())
50    }
51
52    /// Same as [`Self::value_from_str`] but uses a [`String`] instead.
53    ///
54    /// See [`YamlData::value_from_str`] for more details.
55    ///
56    /// The returned node is created with a default [`Span`].
57    ///
58    /// [`YamlData::value_from_str`]: crate::YamlData::value_from_str
59    #[must_use]
60    pub fn scalar_from_string(v: String) -> Self {
61        Self::value_from_cow(v.into())
62    }
63
64    /// Same as [`Self::value_from_str`] but uses a [`Cow`] instead.
65    ///
66    /// See [`YamlData::value_from_str`] for more details.
67    ///
68    /// The returned node is created with a default [`Span`].
69    ///
70    /// [`YamlData::value_from_str`]: crate::YamlData::value_from_str
71    #[must_use]
72    pub fn value_from_cow(v: Cow<'_, str>) -> Self {
73        Self {
74            data: YamlDataOwned::Value(ScalarOwned::parse_from_cow(v)),
75            span: Span::default(),
76        }
77    }
78
79    /// Convert a string to a  scalar node, abiding by the given metadata.
80    ///
81    /// The variant returned by this function will always be a [`YamlDataOwned::Value`], unless the
82    /// tag forces a particular type and the representation cannot be parsed as this type, in which
83    /// case it returns a [`YamlDataOwned::BadValue`].
84    ///
85    /// The returned node is created with a default [`Span`].
86    #[must_use]
87    pub fn value_from_cow_and_metadata(
88        v: Cow<'_, str>,
89        style: ScalarStyle,
90        tag: Option<&Cow<'_, Tag>>,
91    ) -> Self {
92        ScalarOwned::parse_from_cow_and_metadata(v, style, tag).map_or_else(
93            || Self {
94                data: YamlDataOwned::BadValue,
95                span: Span::default(),
96            },
97            |v| Self {
98                data: YamlDataOwned::Value(v),
99                span: Span::default(),
100            },
101        )
102    }
103}
104
105impl super::AnnotatedNodeOwned for MarkedYamlOwned {
106    type HashKey = MarkedYamlOwned;
107
108    fn parse_representation_recursive(&mut self) -> bool {
109        self.data.parse_representation_recursive()
110    }
111}
112
113impl From<YamlDataOwned<MarkedYamlOwned>> for MarkedYamlOwned {
114    fn from(value: YamlDataOwned<MarkedYamlOwned>) -> Self {
115        Self {
116            span: Span::default(),
117            data: value,
118        }
119    }
120}
121
122impl PartialEq<MarkedYamlOwned> for MarkedYamlOwned {
123    fn eq(&self, other: &MarkedYamlOwned) -> bool {
124        self.data.eq(&other.data)
125    }
126}
127
128// I don't know if it's okay to implement that, but we need it for the hashmap.
129impl Eq for MarkedYamlOwned {}
130
131impl core::hash::Hash for MarkedYamlOwned {
132    fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
133        self.data.hash(state);
134    }
135}
136
137impl SafelyIndex for MarkedYamlOwned {
138    fn get(&self, key: impl Into<Accessor>) -> Option<&Self> {
139        match key.into() {
140            Accessor::Field(f) => self.data.as_mapping_get(f.as_str()),
141            Accessor::Index(i) => self.data.as_sequence_get(i),
142        }
143    }
144}
145
146impl SafelyIndexMut for MarkedYamlOwned {
147    fn get_mut(&mut self, key: impl Into<Accessor>) -> Option<&mut Self> {
148        match key.into() {
149            Accessor::Field(f) => self.data.as_mapping_get_mut(f.as_str()),
150            Accessor::Index(i) => self.data.as_sequence_get_mut(i),
151        }
152    }
153}
154
155impl LoadableYamlNode<'_> for MarkedYamlOwned {
156    type HashKey = MarkedYamlOwned;
157
158    fn from_bare_yaml(yaml: Yaml) -> Self {
159        Self {
160            span: Span::default(),
161            data: match yaml {
162                // Sequence and Mapping will always have their container empty.
163                Yaml::Sequence(_) => YamlDataOwned::Sequence(vec![]),
164                Yaml::Mapping(_) => YamlDataOwned::Mapping(LinkedHashMap::new()),
165                Yaml::Alias(x) => YamlDataOwned::Alias(x),
166                Yaml::BadValue => YamlDataOwned::BadValue,
167                Yaml::Representation(v, style, tag) => {
168                    YamlDataOwned::Representation(v.to_string(), style, tag.map(Cow::into_owned))
169                }
170                Yaml::Tagged(tag, node) => {
171                    YamlDataOwned::Tagged(tag.into_owned(), Box::new(Self::from_bare_yaml(*node)))
172                }
173                Yaml::Value(x) => YamlDataOwned::Value(x.into_owned()),
174            },
175        }
176    }
177
178    fn is_sequence(&self) -> bool {
179        self.data.is_sequence()
180    }
181
182    fn is_mapping(&self) -> bool {
183        self.data.is_mapping()
184    }
185
186    fn is_badvalue(&self) -> bool {
187        self.data.is_badvalue()
188    }
189
190    fn into_tagged(self, tag: Cow<'_, Tag>) -> Self {
191        Self {
192            span: self.span,
193            data: YamlDataOwned::Tagged(tag.into_owned(), Box::new(self)),
194        }
195    }
196
197    fn sequence_mut(&mut self) -> &mut Vec<Self> {
198        self.data
199            .as_vec_mut()
200            .expect("Called sequence_mut on a non-array")
201    }
202
203    fn mapping_mut(&mut self) -> &mut LinkedHashMap<Self::HashKey, Self> {
204        self.data
205            .as_mapping_mut()
206            .expect("Called mapping_mut on a non-hash")
207    }
208
209    fn take(&mut self) -> Self {
210        let mut taken_out = MarkedYamlOwned {
211            span: Span::default(),
212            data: YamlDataOwned::BadValue,
213        };
214        core::mem::swap(&mut taken_out, self);
215        taken_out
216    }
217
218    fn with_span(mut self, span: Span) -> Self {
219        self.span = span;
220        self
221    }
222
223    fn with_start_marker(mut self, start: saphyr_parser::Marker) -> Self {
224        self.span.start = start;
225        self
226    }
227
228    fn with_end_marker(mut self, end: saphyr_parser::Marker) -> Self {
229        self.span.end = end;
230        self
231    }
232}