1mod natural_sort;
7
8use super::InspectMut;
9use super::InternalError;
10use super::InternalNode;
11use super::SensitivityLevel;
12use super::Value;
13use super::ValueKind;
14use crate::NumberFormat;
15use crate::RequestParams;
16use crate::RootParams;
17use alloc::borrow::ToOwned;
18use alloc::string::String;
19use alloc::string::ToString;
20use alloc::vec;
21use alloc::vec::Vec;
22use base64::display::Base64Display;
23use core::cmp::Ordering;
24use core::fmt;
25use core::fmt::Write;
26use core::future::Future;
27use core::future::poll_fn;
28use core::pin::Pin;
29use core::task::Context;
30use core::task::Poll;
31use core::time::Duration;
32use mesh::MeshPayload;
33use thiserror::Error;
34
35#[derive(Debug, Clone, PartialEq, MeshPayload)]
37#[mesh(package = "inspect")]
38#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
39pub enum Node {
40 #[mesh(1)]
42 Unevaluated,
43 #[mesh(2)]
45 Failed(Error),
46 #[mesh(3)]
48 Value(Value),
49 #[mesh(4)]
51 Dir(Vec<Entry>),
52}
53
54#[derive(Debug, Clone, PartialEq, MeshPayload)]
55#[mesh(package = "inspect")]
56#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
57pub struct Entry {
59 #[mesh(1)]
61 pub name: String,
62 #[mesh(2)]
64 pub node: Node,
65 #[mesh(3)]
67 pub sensitivity: SensitivityLevel,
68}
69
70#[derive(Debug, Clone, PartialEq, Eq, Error, MeshPayload)]
72#[mesh(package = "inspect")]
73#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
74pub enum Error {
75 #[error("unresolved")]
77 #[mesh(1)]
78 Unresolved,
79 #[error("channel error: {0}")]
81 #[mesh(2)]
82 Mesh(String),
83 #[error("immutable node")]
85 #[mesh(3)]
86 Immutable,
87 #[error("update error: {0}")]
89 #[mesh(4)]
90 Update(String),
91 #[error("not a directory")]
93 #[mesh(5)]
94 NotADirectory,
95 #[error("not found")]
97 #[mesh(6)]
98 NotFound,
99 #[error("internal error")]
101 #[mesh(7)]
102 Internal,
103}
104
105impl From<InternalError> for Error {
106 fn from(value: InternalError) -> Self {
107 match value {
108 InternalError::Immutable => Self::Immutable,
109 InternalError::Update(v) => Self::Update(v),
110 InternalError::NotADirectory => Self::NotADirectory,
111 InternalError::Unresolved => Self::Unresolved,
112 InternalError::Mesh(v) => Self::Mesh(v),
113 }
114 }
115}
116
117struct DebugFromDisplay<T>(T);
119
120impl<T: fmt::Display> fmt::Debug for DebugFromDisplay<T> {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 self.0.fmt(f)
123 }
124}
125
126impl fmt::Display for Node {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match self {
129 Node::Unevaluated => f.pad("_"),
130 Node::Failed(err) => write!(f, "error ({err})"),
131 Node::Value(v) => fmt::Display::fmt(v, f),
132 Node::Dir(children) => {
133 let mut map = f.debug_map();
134 for entry in children {
135 map.entry(
136 &DebugFromDisplay(&entry.name),
137 &DebugFromDisplay(&entry.node),
138 );
139 }
140 map.finish()
141 }
142 }
143 }
144}
145
146impl Node {
147 fn merge_list(children: &mut Vec<Entry>) {
148 children.sort_by(|a, b| natural_sort::compare(&a.name, &b.name));
150
151 {
153 let mut last: Option<&mut Entry> = None;
154 for entry in children.iter_mut() {
155 if entry.name.is_empty() {
156 continue;
157 }
158 match &mut last {
159 Some(last_entry) if *last_entry.name == entry.name => {
160 last_entry
161 .node
162 .merge(core::mem::replace(&mut entry.node, Node::Unevaluated));
163 entry.name.clear();
164 }
165 _ => {
166 last = Some(entry);
167 }
168 }
169 }
170 }
171
172 children.retain(|entry| !entry.name.is_empty());
174 }
175
176 fn merge(&mut self, other: Node) {
177 if matches!(other, Node::Unevaluated) {
178 return;
179 }
180 match self {
181 Node::Unevaluated | Node::Failed(_) | Node::Value(_) => {
182 *self = other;
184 }
185
186 Node::Dir(children) => match other {
187 Node::Unevaluated | Node::Failed(_) | Node::Value(_) => {
188 }
190 Node::Dir(mut other_children) => {
191 children.append(&mut other_children);
192 Self::merge_list(children);
193 }
194 },
195 }
196 }
197
198 fn skip(mut self, mut n: usize) -> Node {
199 while n > 0 {
200 self = match self {
201 Node::Dir(d) => {
202 if d.len() == 1 {
203 d.into_iter().next().unwrap().node
204 } else if d.is_empty() {
205 return Node::Failed(Error::NotFound);
206 } else {
207 return Node::Failed(Error::Internal);
210 }
211 }
212 Node::Failed(_) | Node::Unevaluated => return self,
213 Node::Value(_) => {
214 return Node::Failed(Error::Internal);
216 }
217 };
218 n -= 1;
219 }
220 self
221 }
222
223 fn compute_since(&self, last: &Node, t: f64) -> Node {
224 match (self, last) {
225 (Node::Value(value), Node::Value(last)) if value.flags.count() => {
226 let kind = match (&value.kind, &last.kind) {
227 (ValueKind::Unsigned(x), ValueKind::Unsigned(y)) => {
228 ValueKind::Double((x - y) as f64 / t)
229 }
230 (ValueKind::Signed(x), ValueKind::Signed(y)) => {
231 ValueKind::Double((x - y) as f64 / t)
232 }
233 (ValueKind::Float(x), ValueKind::Float(y)) => {
234 ValueKind::Double((x - y) as f64 / t)
235 }
236 (ValueKind::Double(x), ValueKind::Double(y)) => ValueKind::Double((x - y) / t),
237 (kind, _) => kind.clone(),
238 };
239 Node::Value(Value {
240 kind,
241 flags: value.flags,
242 })
243 }
244 (Node::Dir(this), Node::Dir(last)) => {
245 let mut children = Vec::new();
246 let mut this = this.iter().peekable();
247 let mut last = last.iter().peekable();
248 while let (Some(&this_entry), Some(&last_entry)) = (this.peek(), last.peek()) {
249 match this_entry.name.cmp(&last_entry.name) {
250 Ordering::Less => {
251 children.push(this_entry.clone());
252 this.next();
253 }
254 Ordering::Equal => {
255 children.push(Entry {
256 node: this_entry.node.compute_since(&last_entry.node, t),
257 ..this_entry.clone()
258 });
259 this.next();
260 last.next();
261 }
262 Ordering::Greater => {
263 last.next();
264 }
265 }
266 }
267 children.extend(this.cloned());
268 Node::Dir(children)
269 }
270 (node, _) => node.clone(),
271 }
272 }
273
274 pub fn since(&self, last: &Node, duration: Duration) -> Self {
284 self.compute_since(last, duration.as_secs_f64())
285 }
286
287 pub fn json(&self) -> impl '_ + fmt::Display {
289 JsonDisplay(self)
290 }
291}
292
293struct JsonDisplay<'a>(&'a Node);
294
295impl fmt::Display for JsonDisplay<'_> {
296 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297 match self.0 {
298 Node::Unevaluated | Node::Failed(_) => f.write_str("null"),
299 Node::Value(value) => match &value.kind {
300 ValueKind::Signed(v) => write!(f, "{}", v),
301 ValueKind::Unsigned(v) => write!(f, "{}", v),
302 ValueKind::Float(v) => write!(f, "{}", v),
303 ValueKind::Double(v) => write!(f, "{}", v),
304 ValueKind::Bool(v) => write!(f, "{}", v),
305 ValueKind::String(v) => write!(f, "{:?}", v),
306 ValueKind::Bytes(b) => {
307 write!(
309 f,
310 r#""{}""#,
311 Base64Display::new(b, &base64::engine::general_purpose::STANDARD_NO_PAD)
312 )
313 }
314 },
315 Node::Dir(children) => {
316 f.write_char('{')?;
317 let mut comma = "";
318 for entry in children {
319 let child = JsonDisplay(&entry.node);
320 let name = &entry.name;
321 write!(f, "{comma}{name:?}:{child}")?;
322 comma = ",";
323 }
324 f.write_char('}')?;
325 Ok(())
326 }
327 }
328 }
329}
330
331fn path_node_count(path: &str) -> usize {
332 path.split('/').filter(|x| !x.is_empty()).count()
333}
334
335pub struct InspectionBuilder<'a> {
337 path: &'a str,
338 depth: Option<usize>,
339 sensitivity: Option<SensitivityLevel>,
340}
341
342impl<'a> InspectionBuilder<'a> {
343 pub fn new(path: &'a str) -> Self {
345 Self {
346 path,
347 depth: None,
348 sensitivity: None,
349 }
350 }
351
352 pub fn depth(mut self, depth: Option<usize>) -> Self {
354 self.depth = depth;
355 self
356 }
357
358 pub fn sensitivity(mut self, sensitivity: Option<SensitivityLevel>) -> Self {
360 self.sensitivity = sensitivity;
361 self
362 }
363
364 pub fn inspect(self, obj: impl InspectMut) -> Inspection {
366 let root = self.root();
367 let (params, skip) = self.build(&root);
368 Inspection {
369 node: params.inspect(obj),
370 skip,
371 }
372 }
373
374 pub fn update(self, value: &'a str, obj: impl InspectMut) -> Update {
376 let mut root = self.root();
377 root.value = Some(value);
378 let (params, skip) = self.build(&root);
379 Update {
380 node: params.inspect(obj),
381 skip,
382 }
383 }
384
385 fn root(&self) -> RootParams<'_> {
386 RootParams {
387 full_path: self.path,
388 value: None,
389 sensitivity: self.sensitivity.unwrap_or(SensitivityLevel::Sensitive),
390 }
391 }
392
393 fn build<'b>(&self, root: &'b RootParams<'_>) -> (RequestParams<'b>, usize) {
394 const MAX_INSPECT_DEPTH: usize = 4096;
398 let depth_with_root = if let Some(depth) = self.depth {
399 depth.saturating_add(1).min(MAX_INSPECT_DEPTH)
400 } else {
401 MAX_INSPECT_DEPTH
402 };
403 let params = RequestParams {
404 root,
405 path_start: 0,
406 depth: depth_with_root,
407 number_format: NumberFormat::default(),
408 };
409 (params, path_node_count(root.full_path))
410 }
411}
412
413pub fn inspect(path: &str, obj: impl InspectMut) -> Inspection {
431 InspectionBuilder::new(path).inspect(obj)
432}
433
434#[derive(Debug)]
436pub struct Inspection {
437 node: InternalNode,
438 skip: usize,
439}
440
441impl Inspection {
442 pub async fn resolve(&mut self) {
449 self.node.resolve().await
450 }
451
452 pub fn results(self) -> Node {
457 self.node.into_node().skip(self.skip)
458 }
459}
460
461pub fn update(path: &str, value: &str, obj: impl InspectMut) -> Update {
463 InspectionBuilder::new(path).update(value, obj)
464}
465
466pub struct Update {
468 node: InternalNode,
469 skip: usize,
470}
471
472impl Future for Update {
473 type Output = Result<Value, Error>;
474
475 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
476 let this = self.get_mut();
477 core::task::ready!(this.node.poll_resolve(cx));
478 Poll::Ready(
479 match std::mem::replace(&mut this.node, InternalNode::Unevaluated)
480 .into_node()
481 .skip(this.skip)
482 {
483 Node::Unevaluated => Err(Error::Unresolved),
484 Node::Failed(err) => Err(err),
485 Node::Value(v) => Ok(v),
486 Node::Dir(_) => Err(Error::Unresolved),
487 },
488 )
489 }
490}
491
492impl InternalNode {
493 fn poll_resolve(&mut self, cx: &mut Context<'_>) -> Poll<()> {
494 loop {
495 match self {
496 InternalNode::Dir(children) => {
497 if !children
501 .iter_mut()
502 .all(|entry| entry.node.poll_resolve(cx).is_ready())
503 {
504 break Poll::Pending;
505 }
506 *self = InternalNode::DirResolved(core::mem::take(children));
509 }
510 InternalNode::Deferred(recv) => match Pin::new(recv).poll(cx) {
511 Poll::Ready(node) => {
512 *self = match node {
513 Ok(node) => {
514 node
517 }
518 Err(err) => InternalNode::Failed(match err {
519 mesh::RecvError::Closed => InternalError::Unresolved,
520 mesh::RecvError::Error(err) => InternalError::Mesh(err.to_string()),
521 }),
522 };
523 }
524 _ => break Poll::Pending,
525 },
526 _ => break Poll::Ready(()),
527 }
528 }
529 }
530
531 async fn resolve(&mut self) {
532 poll_fn(|cx| self.poll_resolve(cx)).await
533 }
534
535 fn into_node(self) -> Node {
536 match self {
537 InternalNode::Dir(children) | InternalNode::DirResolved(children) => {
538 let mut child_nodes = Vec::new();
540 for entry in children {
541 if matches!(entry.node, InternalNode::Ignored) {
542 continue;
543 }
544 let mut child_node = entry.node.into_node();
545
546 if entry.name.is_empty() {
547 if let Node::Dir(grandchildren) = child_node {
548 child_nodes.extend(grandchildren);
550 }
551 } else {
552 let mut name = entry.name;
553 let root_len = {
554 let mut names = name.split('/');
556 let root_name = names.next().unwrap();
557 for interior_name in names.rev() {
558 child_node = Node::Dir(vec![Entry {
559 name: interior_name.to_owned(),
560 node: child_node,
561 sensitivity: entry.sensitivity,
562 }]);
563 }
564 root_name.len()
565 };
566 name.truncate(root_len);
567 child_nodes.push(Entry {
568 name,
569 node: child_node,
570 sensitivity: entry.sensitivity,
571 });
572 }
573 }
574
575 Node::merge_list(&mut child_nodes);
576 Node::Dir(child_nodes)
577 }
578 InternalNode::Value(v) => Node::Value(v),
579 InternalNode::Failed(err) => Node::Failed(err.into()),
580 InternalNode::Deferred(_) => Node::Failed(Error::Unresolved),
581 InternalNode::DepthExhausted | InternalNode::Unevaluated | InternalNode::Ignored => {
582 Node::Unevaluated
583 }
584 }
585 }
586}