Repository navigation
Expand file tree
/
Copy pathnode.rs
More file actions
262 lines (234 loc) · 7.57 KB
/
Copy pathnode.rs
File metadata and controls
262 lines (234 loc) · 7.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
use crate::path::NodePath;
use core::fmt::{Display, Formatter, Write};
use hashlink::linked_hash_map::Entry;
use hashlink::{LinkedHashMap, LinkedHashSet};
/// An intermediate representation of a node tree, used to gather deletes and overrides.
/// Preserves the input ordering while allowing random access.
///
/// Behavior does not match `dtc` in all cases. Deleting a node and redefining it with
/// the same name will move it to the end, but `dtc` remembers the original ordering.
#[derive(Clone)]
pub struct Node<P> {
labels: LinkedHashSet<String>,
properties: LinkedHashMap<String, P>,
children: LinkedHashMap<String, Node<P>>,
}
impl<P> Node<P> {
pub fn walk<'a, 'b>(&'a self, path: impl IntoIterator<Item = &'b str>) -> Option<&'a Node<P>> {
let mut path = path.into_iter();
match path.next() {
None | Some("") => Some(self),
Some(segment) => self.children.get(segment)?.walk(path),
}
}
pub fn walk_mut<'a, 'b>(
&'a mut self,
path: impl IntoIterator<Item = &'b str>,
) -> Option<&'a mut Node<P>> {
let mut path = path.into_iter();
match path.next() {
None | Some("") => Some(self),
Some(segment) => self.children.get_mut(segment)?.walk_mut(path),
}
}
pub fn add_child(&mut self, name: &str) -> &mut Node<P> {
// Avoid `Entry::or_insert_with()` because on LinkedHashMap that reorders existing entries.
match self.children.entry(name.into()) {
Entry::Vacant(entry) => entry.insert(Default::default()),
Entry::Occupied(entry) => entry.into_mut(),
}
}
pub fn get_child_mut(&mut self, name: &str) -> Option<&mut Node<P>> {
self.children.get_mut(name)
}
pub fn get_child(&self, name: &str) -> Option<&Node<P>> {
self.children.get(name)
}
pub fn remove_child(&mut self, name: &str) {
self.children.remove(name);
}
pub fn get_property(&self, name: &str) -> Option<&P> {
self.properties.get(name)
}
pub fn set_property(&mut self, name: &str, value: P) -> Option<P> {
self.properties.replace(name.into(), value)
}
pub fn remove_property(&mut self, name: &str) -> Option<P> {
self.properties.remove(name)
}
pub fn children(&self) -> impl Iterator<Item = (&String, &Self)> {
self.children.iter()
}
pub fn children_mut(&mut self) -> impl Iterator<Item = (&String, &mut Self)> {
self.children.iter_mut()
}
pub fn properties(&self) -> impl Iterator<Item = (&String, &P)> {
self.properties.iter()
}
pub fn properties_mut(&mut self) -> impl Iterator<Item = (&String, &mut P)> {
self.properties.iter_mut()
}
pub fn add_label(&mut self, name: &str) {
self.labels.replace(name.into());
}
pub fn labels(&mut self) -> impl Iterator<Item = &String> {
self.labels.iter()
}
pub fn labels_as_display(&self) -> LabelsDisplay<'_> {
LabelsDisplay(&self.labels)
}
pub fn map_values<T>(self, f: &mut impl FnMut(P) -> T) -> Node<T> {
let Self {
labels,
properties,
children,
} = self;
let properties = properties
.into_iter()
.map(|(k, v)| (k, f(v)))
.collect::<LinkedHashMap<String, T>>();
let children = children
.into_iter()
.map(|(k, v)| (k, v.map_values(f)))
.collect::<LinkedHashMap<String, Node<T>>>();
Node::<T> {
labels,
properties,
children,
}
}
/// Like `map_values()`, but provide the callback with the node path.
pub fn map_located_values<T>(
self,
loc: &NodePath,
f: &mut impl FnMut(&NodePath, P) -> T,
) -> Node<T> {
let Self {
labels,
properties,
children,
} = self;
let properties = properties
.into_iter()
.map(|(k, v)| (k, f(loc, v)))
.collect::<LinkedHashMap<String, T>>();
let children = children
.into_iter()
.map(|(k, v)| (k.clone(), v.map_located_values(&loc.join(&k), f)))
.collect::<LinkedHashMap<String, Node<T>>>();
Node::<T> {
labels,
properties,
children,
}
}
/// Sort child nodes and properties by name, recursively.
pub fn sort(&mut self) {
let mut keys: Vec<String> = self.properties.keys().cloned().collect();
keys.sort_unstable();
for k in keys {
self.properties.to_back(&k);
}
let mut keys: Vec<String> = self.children.keys().cloned().collect();
keys.sort_unstable();
for k in keys {
self.children.to_back(&k).map(Self::sort);
}
}
}
impl<P> Default for Node<P> {
fn default() -> Self {
Self {
labels: Default::default(),
properties: Default::default(),
children: Default::default(),
}
}
}
pub trait OptionDisplay {
fn fmt_opt(&self) -> Option<String>;
}
impl OptionDisplay for &crate::parse::rules::Prop<'_> {
fn fmt_opt(&self) -> Option<String> {
use crate::parse::TypedRuleExt;
self.prop_value.map(|pv| pv.str().into())
}
}
impl OptionDisplay for Vec<u8> {
fn fmt_opt(&self) -> Option<String> {
if self.is_empty() {
return None;
}
let mut f = String::new();
// Formatting here is sloppy because we pass the output through the pretty-printer.
if let Some(s) = guess_c_strings(self) {
for (i, w) in s.split_terminator('\0').enumerate() {
if i > 0 {
_ = write!(f, ", ");
}
_ = write!(f, "\"{w}\"");
}
} else if self.len() % 4 == 0 {
_ = write!(f, "<");
for w in self.chunks_exact(4) {
// stabilization of slice::array_chunks would simplify this
let n = u32::from_be_bytes(w.try_into().unwrap());
_ = write!(f, " {n:#04x}");
}
_ = write!(f, ">")
} else {
_ = write!(f, "[");
for b in self {
_ = write!(f, " {b:02x}");
}
_ = write!(f, "]");
}
Some(f)
}
}
fn guess_c_strings(s: &[u8]) -> Option<&str> {
if s.last() != Some(&0) {
return None;
}
let mut nuls = 0;
for c in s {
match c {
0 => nuls += 1,
b' '..b'~' => (),
_ => return None,
}
}
if nuls == 1 || nuls <= s.len() / 2 {
Some(core::str::from_utf8(s).unwrap())
} else {
None
}
}
impl<P: OptionDisplay> Display for Node<P> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
writeln!(f, "{{")?;
for (name, value) in &self.properties {
if let Some(value) = value.fmt_opt() {
writeln!(f, "{name} = {value};")?;
} else {
writeln!(f, "{name};")?;
}
}
if !self.properties.is_empty() && !self.children.is_empty() {
writeln!(f)?;
}
for (name, node) in &self.children {
writeln!(f, "{}{name} {node};", node.labels_as_display())?;
}
write!(f, "}}")
}
}
pub struct LabelsDisplay<'a>(&'a LinkedHashSet<String>);
impl Display for LabelsDisplay<'_> {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
for label in self.0 {
write!(f, "{label}: ")?;
}
Ok(())
}
}