Skip to main content

apollo_saphyr/annotated/
yaml_data_owned.rs

1use alloc::{
2    boxed::Box,
3    string::{String, ToString},
4    vec::Vec,
5};
6use core::{
7    hash::{BuildHasher, Hasher},
8    ops::{Index, IndexMut},
9};
10
11use hashlink::LinkedHashMap;
12use saphyr_parser::{ScalarStyle, Tag};
13
14use crate::{annotated::AnnotatedNodeOwned, ScalarOwned};
15
16/// YAML data for nodes that will contain annotations.
17///
18/// Owned version of [`YamlData`].
19///
20/// [`YamlData`]: `crate::YamlData`
21#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord, Hash)]
22pub enum YamlDataOwned<Node>
23where
24    Node: core::hash::Hash + Eq + From<Self> + AnnotatedNodeOwned,
25{
26    /// The raw string from the input.
27    ///
28    /// See [`YamlData::Representation`].
29    ///
30    /// [`YamlData::Representation`]: `crate::YamlData::Representation`
31    Representation(String, ScalarStyle, Option<Tag>),
32    /// The resolved value from the representation.
33    ///
34    /// See [`YamlData::Value`].
35    ///
36    /// [`YamlData::Value`]: `crate::YamlData::Value`
37    Value(ScalarOwned),
38    /// YAML sequence, can be accessed as a `Vec`.
39    ///
40    /// See [`YamlData::Sequence`].
41    ///
42    /// [`YamlData::Sequence`]: `crate::YamlData::Sequence`
43    Sequence(AnnotatedSequenceOwned<Node>),
44    /// YAML mapping, can be accessed as a [`LinkedHashMap`].
45    ///
46    /// See [`YamlData::Mapping`].
47    ///
48    /// [`YamlData::Mapping`]: `crate::YamlData::Mapping`
49    Mapping(AnnotatedMappingOwned<Node>),
50    /// A tagged node.
51    ///
52    /// See [`YamlData::Tagged`].
53    ///
54    /// [`YamlData::Tagged`]: `crate::YamlData::Tagged`
55    Tagged(Tag, Box<Node>),
56    /// Alias, not fully supported yet.
57    ///
58    /// See [`YamlData::Alias`].
59    ///
60    /// [`YamlData::Alias`]: `crate::YamlData::Alias`
61    Alias(usize),
62    /// A variant used when parsing the representation of a scalar node fails.
63    ///
64    /// See [`YamlData::BadValue`].
65    ///
66    /// [`YamlData::BadValue`]: `crate::YamlData::BadValue`
67    BadValue,
68}
69
70// This defines most common operations on a YAML object. See macro definition for details.
71define_yaml_object_impl!(
72    YamlDataOwned<Node>,
73    < Node>,
74    where {
75        Node: core::hash::Hash
76            + Eq
77            + From<Self>
78            + AnnotatedNodeOwned
79            + PartialEq<Node::HashKey>,
80    },
81    mappingtype = AnnotatedMappingOwned<Node>,
82    sequencetype = AnnotatedSequenceOwned<Node>,
83    nodetype = Node,
84    scalartype = { ScalarOwned },
85    selfname = "YamlDataOwned",
86    owned
87);
88
89impl<Node> YamlDataOwned<Node>
90where
91    Node: core::hash::Hash + Eq + From<Self> + AnnotatedNodeOwned + PartialEq<Node::HashKey>,
92{
93    /// Take the contained node out of `Self`, leaving a `BadValue` in its place.
94    #[must_use]
95    fn take(&mut self) -> Self {
96        core::mem::replace(self, Self::BadValue)
97    }
98
99    /// Implementation detail for [`Self::as_mapping_get`], which is generated from a macro.
100    fn as_mapping_get_impl(&self, key: &str) -> Option<&Node> {
101        use core::hash::Hash;
102
103        match self {
104            Self::Mapping(mapping) => {
105                let needle =
106                    Node::HashKey::from(YamlDataOwned::Value(ScalarOwned::String(key.into())));
107
108                // In order to work around `needle`'s lifetime being different from `h`'s, we need
109                // to manually compute the hash. Otherwise, we'd use `h.get()`, which complains the
110                // needle's lifetime doesn't match that of the key in `h`.
111                let mut hasher = mapping.hasher().build_hasher();
112                needle.hash(&mut hasher);
113                let hash = hasher.finish();
114
115                mapping
116                    .raw_entry()
117                    .from_hash(hash, |candidate| *candidate == needle)
118                    .map(|(_, v)| v)
119            }
120            _ => None,
121        }
122    }
123
124    /// Implementation detail for [`Self::as_mapping_get_mut`], which is generated from a macro.
125    #[must_use]
126    fn as_mapping_get_mut_impl(&mut self, key: &str) -> Option<&mut Node> {
127        match self.as_mapping_mut() {
128            Some(mapping) => {
129                use core::hash::Hash;
130                use hashlink::linked_hash_map::RawEntryMut::{Occupied, Vacant};
131
132                // In order to work around `needle`'s lifetime being different from `h`'s, we need
133                // to manually compute the hash. Otherwise, we'd use `h.get()`, which complains the
134                // needle's lifetime doesn't match that of the key in `h`.
135                let needle =
136                    Node::HashKey::from(YamlDataOwned::Value(ScalarOwned::String(key.to_string())));
137                let mut hasher = mapping.hasher().build_hasher();
138                needle.hash(&mut hasher);
139                let hash = hasher.finish();
140
141                match mapping
142                    .raw_entry_mut()
143                    .from_hash(hash, |candidate| *candidate == needle)
144                {
145                    Occupied(entry) => Some(entry.into_mut()),
146                    Vacant(_) => None,
147                }
148            }
149            _ => None,
150        }
151    }
152}
153
154impl<Node> IntoIterator for YamlDataOwned<Node>
155where
156    Node: core::hash::Hash + Eq + From<Self> + AnnotatedNodeOwned + PartialEq<Node::HashKey>,
157{
158    type Item = Node;
159    type IntoIter = AnnotatedYamlOwnedIter<Node>;
160
161    fn into_iter(self) -> Self::IntoIter {
162        Self::IntoIter {
163            yaml: self.into_vec().unwrap_or_default().into_iter(),
164        }
165    }
166}
167
168/// An iterator over a [`YamlDataOwned`] node.
169#[allow(clippy::module_name_repetitions)]
170pub struct AnnotatedYamlOwnedIter<Node>
171where
172    Node: core::hash::Hash + Eq + From<YamlDataOwned<Node>> + AnnotatedNodeOwned,
173{
174    yaml: alloc::vec::IntoIter<Node>,
175}
176
177impl<Node> Iterator for AnnotatedYamlOwnedIter<Node>
178where
179    Node: core::hash::Hash + Eq + From<YamlDataOwned<Node>> + AnnotatedNodeOwned,
180{
181    type Item = Node;
182
183    fn next(&mut self) -> Option<Node> {
184        self.yaml.next()
185    }
186}
187
188/// The type contained in the [`YamlDataOwned::Sequence`] variant. This corresponds to YAML sequences.
189#[allow(clippy::module_name_repetitions)]
190pub type AnnotatedSequenceOwned<Node> = Vec<Node>;
191/// The type contained in the [`YamlDataOwned::Mapping`] variant. This corresponds to YAML mappings.
192#[allow(clippy::module_name_repetitions)]
193pub type AnnotatedMappingOwned<Node> = LinkedHashMap<Node, Node>;