Skip to main content

yaml_edit/
mapping_view.rs

1//! Read-only mapping interface shared by [`Mapping`] and
2//! [`MergedMapping`](crate::anchor_resolution::MergedMapping).
3//!
4//! [`MappingView`] abstracts over "anything that looks like a YAML mapping
5//! for the purposes of reading it." It lets you write code that works
6//! generically against both the underlying CST [`Mapping`] and the
7//! alias/merge-key-resolving [`MergedMapping`](crate::anchor_resolution::MergedMapping)
8//! view.
9//!
10//! Iterators are returned as boxed trait objects so the trait stays
11//! object-safe and works on the crate's MSRV. Most uses pay one heap
12//! allocation per iteration - negligible for an editing library.
13//!
14//! # Example
15//!
16//! ```
17//! use yaml_edit::{Document, MappingView};
18//! use yaml_edit::anchor_resolution::{DocumentMergedExt, MappingMergedExt};
19//! use std::str::FromStr;
20//!
21//! fn count_keys(view: &impl MappingView) -> usize {
22//!     view.keys().count()
23//! }
24//!
25//! let yaml = "\
26//! defaults: &d
27//!   timeout: 30
28//! prod:
29//!   <<: *d
30//!   host: prod.example.com
31//! ";
32//! let doc = Document::from_str(yaml).unwrap();
33//! let root = doc.as_mapping().unwrap();
34//!
35//! // Works on a plain Mapping…
36//! assert_eq!(count_keys(&root), 2);
37//!
38//! // …and on a MergedMapping.
39//! let registry = doc.merged().unwrap().unwrap();
40//! let prod = registry.as_mapping().get_merged("prod").unwrap().unwrap();
41//! assert_eq!(count_keys(&prod), 2); // timeout + host
42//! ```
43//!
44//! The trait is intentionally read-only: mutations only make sense on the
45//! CST [`Mapping`], so [`set`](crate::yaml::Mapping::set),
46//! [`remove`](crate::yaml::Mapping::remove), etc. remain on that type.
47//!
48//! [`Mapping`]: crate::yaml::Mapping
49
50use crate::as_yaml::YamlNode;
51
52/// A read-only "view" of a YAML mapping, implemented by both the CST
53/// [`Mapping`](crate::yaml::Mapping) and the alias/merge-key-resolving
54/// [`MergedMapping`](crate::anchor_resolution::MergedMapping).
55///
56/// See the [module docs](self) for an overview and example.
57pub trait MappingView {
58    /// Look up the value associated with `key`.
59    ///
60    /// Key matching is semantic (quoting style is ignored), so `"foo"`,
61    /// `'foo'`, and `foo` all match the scalar `foo`.
62    fn get(&self, key: &dyn crate::AsYaml) -> Option<YamlNode>;
63
64    /// Returns `true` if [`get`](Self::get) would return `Some` for `key`.
65    fn contains_key(&self, key: &dyn crate::AsYaml) -> bool {
66        self.get(key).is_some()
67    }
68
69    /// Number of entries visible through this view.
70    fn len(&self) -> usize {
71        self.iter().count()
72    }
73
74    /// Returns `true` if the view has no entries.
75    fn is_empty(&self) -> bool {
76        self.iter().next().is_none()
77    }
78
79    /// Iterate over the keys.
80    fn keys<'a>(&'a self) -> Box<dyn Iterator<Item = YamlNode> + 'a> {
81        Box::new(self.iter().map(|(k, _)| k))
82    }
83
84    /// Iterate over the values.
85    fn values<'a>(&'a self) -> Box<dyn Iterator<Item = YamlNode> + 'a> {
86        Box::new(self.iter().map(|(_, v)| v))
87    }
88
89    /// Iterate over `(key, value)` pairs.
90    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (YamlNode, YamlNode)> + 'a>;
91}
92
93impl MappingView for crate::yaml::Mapping {
94    fn get(&self, key: &dyn crate::AsYaml) -> Option<YamlNode> {
95        crate::yaml::Mapping::get(self, key)
96    }
97
98    fn contains_key(&self, key: &dyn crate::AsYaml) -> bool {
99        crate::yaml::Mapping::contains_key(self, key)
100    }
101
102    fn len(&self) -> usize {
103        crate::yaml::Mapping::len(self)
104    }
105
106    fn is_empty(&self) -> bool {
107        crate::yaml::Mapping::is_empty(self)
108    }
109
110    fn keys<'a>(&'a self) -> Box<dyn Iterator<Item = YamlNode> + 'a> {
111        Box::new(crate::yaml::Mapping::keys(self))
112    }
113
114    fn values<'a>(&'a self) -> Box<dyn Iterator<Item = YamlNode> + 'a> {
115        Box::new(crate::yaml::Mapping::values(self))
116    }
117
118    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (YamlNode, YamlNode)> + 'a> {
119        Box::new(crate::yaml::Mapping::iter(self))
120    }
121}
122
123impl MappingView for crate::anchor_resolution::MergedMapping<'_> {
124    fn get(&self, key: &dyn crate::AsYaml) -> Option<YamlNode> {
125        crate::anchor_resolution::MergedMapping::get(self, key)
126    }
127
128    fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = (YamlNode, YamlNode)> + 'a> {
129        Box::new(crate::anchor_resolution::MergedMapping::iter(self))
130    }
131}