inspect/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This module implements the inspect framework, which allows an object to
5//! expose its state for diagnostics purposes as a hierarchy. All such subtrees
6//! are linked together into a single tree for the process, and clients can
7//! query information from arbitrary portions of the tree by path.
8//!
9//! To participate in the tree, an object implements the [`Inspect`] trait and
10//! arranges for itself to be inspected, usually automatically via an existing
11//! parent-child relationship.
12//!
13//! For most cases, users should use the derive version of [`Inspect`](derive@Inspect)
14//! as this will automatically update the implementation as new fields are added.
15
16#![no_std]
17#![forbid(unsafe_code)]
18
19extern crate alloc;
20
21// Enable `std` for features that depend on it.
22//
23// Don't just have these features depend on the "std" feature, because they
24// might be able to remove their `std` dependency in the future. (Most of them
25// are blocked on `core::error::Error` not being stable yet.)
26#[cfg(any(feature = "std", feature = "initiate", feature = "defer",))]
27extern crate std;
28
29#[cfg(feature = "defer")]
30mod defer;
31#[cfg(feature = "initiate")]
32mod initiate;
33
34#[cfg(all(test, feature = "derive", feature = "initiate"))]
35extern crate self as inspect;
36
37#[cfg(feature = "defer")]
38pub use defer::*;
39#[cfg(feature = "initiate")]
40pub use initiate::*;
41
42/// Derives the [`Inspect`] trait for a struct or enum.
43///
44/// [`InspectMut`](derive@InspectMut) can also be derived using the same
45/// attributes.
46///
47/// # Structs
48///
49/// By default, the macro implements [`Inspect`] or [`InspectMut`] by calling
50/// [`Request::respond`] and then calling [`Response::field`] on each field by
51/// reference. You can use attributes to control this behavior.
52///
53/// Attributes are comma separated and nested inside the `inspect` attribute,
54/// e.g. `#[inspect(hex, rename = "foo")]`.
55///
56/// If you derive `Inspect` on a struct with the `bitfield` attribute, then it
57/// is assumed to be from the `bitfield-struct` crate. The derived
58/// implementation will have a field for each bitfield field, plus one called
59/// `raw` with the raw value in hexadecimal. Be sure to put the `derive`
60/// attribute above the `bitfield` attribute for this to work.
61///
62/// ## Struct attributes
63///
64/// ### `transparent(attrs)`
65///
66/// Forward the request to a single field of the struct.
67///
68/// `(attrs)` is optional. If provided, these are attributes to apply to the
69/// field. This is useful when the field is generated by another macro (such as
70/// `bitflags!`) and you cannot put attributes on it.
71///
72/// This attribute requires that there be exactly one non-skipped field. Fields
73/// of type [`PhantomData`](core::marker::PhantomData) are automatically skipped.
74///
75/// Note that it is not sufficient to mark any extraneous fields' _types_ with
76/// `skip`--you must mark the individual fields with the `skip` attribute.
77///
78/// ### `skip`
79///
80/// Skip this type when inspected so that they do not appear in inspect output.
81/// Calls [`Request::ignore`].
82///
83/// ### `with`
84///
85/// Wraps the type with `expr`, so that the inspect implementation is deferred
86/// to `expr(&field)`.
87///
88/// `expr` is often a type constructor such as [`AsDisplay`] or [`AsDebug`] (but
89/// note that there are shorthand attributes `display` and `debug` for these
90/// types).
91///
92/// ### `display`
93///
94/// Inspect the type by formatting it as a string, using the type's [`ToString`]
95/// implementation.
96///
97/// Usually implementing [`Inspect`] for the type should be preferred to this in
98/// order to preserve structured data. However, this may be useful if the
99/// display string is the canonical way to view a field's data.
100///
101/// This is equivalent to `with = "inspect::AsDisplay"`. See [`AsDisplay`].
102///
103/// ### `debug`
104///
105/// Inspect the type by formatting it as a string, using the type's [`Debug`]
106/// implementation.
107///
108/// Usually implementing [`Inspect`] for the type should be preferred to this in
109/// order to preserve structured data. However, this may be useful if the debug
110/// string is the canonical way to view a field's data, as with `bitfields!` or
111/// `open_enum!`.
112///
113/// This is equivalent to `with = "inspect::AsDebug"`. See [`AsDebug`].
114///
115/// ### `extra = "expr"`
116///
117/// In addition to inspecting each field as normal, call `expr(self, resp)`.
118/// This allows you to add synthetic fields to the struct without manually
119/// implementing inspect for all fields.
120///
121/// ```no_run
122/// # use inspect::Inspect;
123/// #[derive(Inspect)]
124/// #[inspect(extra = "Foo::inspect_extra")]
125/// struct Foo {
126///     x: u32,
127///     y: u32,
128/// }
129///
130/// impl Foo {
131///     fn inspect_extra(&self, resp: &mut inspect::Response<'_>) {
132///         resp.field("sum", self.x + self.y);
133///     }
134/// }
135/// ```
136///
137/// ### `hex`
138///
139/// Use hexadecimal formatting by default for all fields, recursively.
140///
141/// ## Field attributes
142///
143/// ### `rename = "custom_name"`
144///
145/// Set the name of the field to "custom_name". By default, fields have the same
146/// name as their Rust identifier.
147///
148/// ### `field`
149///
150/// Inspect the field using its [`Inspect`] implementation, by calling
151/// [`Response::field`] with `&field`.
152///
153/// This is the default.
154///
155/// ### `with = "expr"`
156///
157/// Wraps the field with `expr`, so that `expr(&field)` is the inspected value.
158/// This is useful when the field cannot implement `Inspect`, but you can wrap
159/// it in an object that can.
160///
161/// `expr` is often a type constructor such as [`AsDisplay`] or [`AsDebug`] (but
162/// note that there are shorthand attributes `display` and `debug` for these
163/// types).
164///
165/// This can also be used to implement helper functions that implement
166/// [`Inspect`] to allow complex types to use the the derive macro.
167///
168/// ### `send = "expr"`
169///
170/// Sends a deferred request message for the field so that it can be handled by
171/// some remote asynchronous task (potentially on another thread or in another
172/// process). The field must be a `mesh::Sender<T>`, and `expr` must be a
173/// function that maps from a `Deferred` to `T` (the request type of the
174/// sender).
175///
176/// This is shorthand for `with = "|x| inspect::send(x, expr)"`, which in turn
177/// is roughly the same as `with = "|x| inspect::adhoc(|req|
178/// x.send(expr(req.defer())))"`.
179///
180/// #### Examples
181/// The following structure has a field that is not normally inspectable, but we
182/// can use the derive macro with a helper pattern of making a new helper
183/// function, along with the `with` attribute.
184///
185///
186/// ```no_run
187/// # use inspect::Inspect;
188/// struct NotInspectable {
189///     complex_field: u64 // use your imaginatation to pretend this is a complex field
190/// }
191///
192/// #[derive(Inspect)]
193/// struct Foo {
194///     #[inspect(with = "inspect_awesome_data")]
195///     awesome_data: NotInspectable,
196///     data: String,
197/// }
198///
199/// pub fn inspect_awesome_data(id: &NotInspectable) -> impl Inspect {
200///     // Do some complex field transformation to a string here...
201///     format!("{}, {:x}", (id.complex_field * 42), id.complex_field)
202///     // Since String via str has an implementation for Inspect, we can return the value directly.
203/// }
204///
205/// // In general, inspect helper functions would be defined as the following in another sub module and used
206/// // as `[inspect(with = "inspect_helpers::awesome_data")]` but rustdoc limitations prevent this.
207/// // mod inspect_helpers {
208/// //     use super::*;
209/// //
210/// //     pub(super) fn awesome_data(id: &NotInspectable) -> impl Inspect { ... }
211/// // }
212/// ```
213///
214/// ### `display`
215///
216/// Inspect the field by formatting it as a string, using the field's
217/// [`ToString`] implementation.
218///
219/// This is equivalent to `with = "inspect::AsDisplay"`. See [`AsDisplay`].
220///
221/// ### `debug`
222///
223/// Inspect the field by formatting it as a string, using the field's
224/// [`core::fmt::Debug`] implementation.
225///
226/// In general, implementing [`Inspect`] for the field should be preferred to
227/// this in order to preserve structured data.
228///
229/// This is equivalent to `with = "inspect::AsDebug"`. See [`AsDebug`].
230///
231/// ### `format = "format"`
232///
233/// Inspect the field by formatting it as a string, using the provided format.
234///
235/// For example, `format = "id{:02x}"` might be useful on a field that
236/// represents a 2-digit hex identifier.
237///
238/// ### `hex`
239///
240/// Inspect the field, including all children, recursively, with hexadecimal
241/// formatting.
242///
243/// ### `binary`
244///
245/// Inspect the field, including all children, recursively, with binary
246/// formatting.
247///
248/// ### `bytes`
249///
250/// Inspect the field as a list of bytes. The field must be iterable with an
251/// item type of `u8` or `&u8`.
252///
253/// ### `flatten`
254///
255/// Merge the contents of the field into this inspection node, without an
256/// intermediate child node. Calls [`Response::merge`] with `&field`.
257///
258/// This is useful when your struct is split into an outer and inner type that
259/// are logically the same type from a diagnostics perspective. In this case,
260/// you probably would not want to expose the implementation detail of the inner
261/// type.
262///
263/// ### `iter_by_key`
264///
265/// Inspects a list of items, iterating them by calling
266/// [`into_iter()`](IntoIterator::into_iter) on the field. Each item must have
267/// type `(K, V)`, where `K: Display` and `V: Inspect`. `K` is used as the field
268/// name.
269///
270/// ### `iter_by_index`
271///
272/// Inspects a list of items, enumerating them by calling
273/// [`into_iter()`](IntoIterator::into_iter) on the field. Each item must have
274/// type `V: Inspect`. The field name is the index in the enumeration, starting
275/// with `0`.
276///
277/// #### Example
278///
279/// ```no_run
280/// # use inspect::Inspect;
281/// # use std::sync::Arc;
282/// #[derive(Inspect)]
283/// struct Foo {
284///     id: u32,
285///     #[inspect(flatten)]
286///     inner: Arc<Inner>,
287/// }
288///
289/// #[derive(Inspect)]
290/// struct Inner {
291///     state: String,
292/// }
293/// ```
294///
295/// ### `skip`
296///
297/// Skip inspecting this field.
298///
299/// ### `mut`
300///
301/// Pass the field as a mutable reference to the appropriate method so that its
302/// [`InspectMut`] implementation is called instead of its [`Inspect`]
303/// implementation.
304///
305/// This is only valid in uses of the [`InspectMut`](derive@InspectMut) derive
306/// macro.
307///
308/// This can be used in conjunction with other attributes:
309///
310/// * `field`: calls [`Response::field_mut`] with `&mut field`.
311/// * `flatten`: calls [`Response::merge`] with `&mut field`.
312/// * `transparent` (struct attribute): calls [`InspectMut::inspect_mut`] on the
313///   sole unskipped field.
314/// ## Example
315///
316/// ```no_run
317/// # use inspect::{Inspect, InspectMut};
318/// # use std::sync::Arc;
319/// # use std::path::PathBuf;
320/// #[derive(InspectMut)]
321/// struct Outer {
322///     #[inspect(hex)]
323///     id: u32,                // will be displayed as hex
324///     count: usize,
325///     #[inspect(mut)]
326///     max_buffers: usize,     // can be changed via `update()`
327///     #[inspect(skip)]
328///     signal: Box<dyn Send>,  // won't be present in inspect output
329///     #[inspect(flatten)]
330///     inner: Arc<Inner>,      // contents will be merged in
331/// }
332///
333/// #[derive(Inspect)]
334/// struct Inner {
335///     #[inspect(format = "{:016x}")]
336///     uuid: u128,             // will be displayed as a 0-padded hex string
337/// }
338/// ```
339///
340/// # Unit-only enums
341///
342/// The macro supports enums, with multiple different output formats:
343///
344/// By default, deriving `Inspect` or `InspectMut` on an enum only works if the
345/// enum variants are all unit variants. In this case, the inspect output is a
346/// string containing the name of the enum variant converted from the standard
347/// `PascalCase` to `snake_case`.
348///
349///  For example:
350///
351/// ```no_run
352/// # use inspect::Inspect;
353/// #[derive(Inspect)]
354/// enum State {
355///     NotRunning, // will be displayed as "not_running"
356///     Running,    // will be displayed as "running"
357/// }
358/// ```
359///
360/// For `InspectMut`, the enum variant name is parsed for update requests.
361///
362/// # Enums with fields
363///
364/// To derive `Inspect` for an enum whose variants might contain fields, you
365/// must select an output format.
366///
367/// ## Output formats
368///
369/// In each of the formats below, when the variant name is used in the output,
370/// it is converted from `PascalCase` to `snake_case`. You can specify an
371/// alternate name with the `rename` attribute, e.g. `#[inspect(rename =
372/// "paused")]`.
373///
374/// ### `tag = "tag_name"`
375///
376/// Includes the variant name as a field called `tag_name`, and flattens all the
377/// variant's fields into the object.
378///
379/// ```no_run
380/// # use inspect::Inspect;
381/// #[derive(Inspect)]
382/// #[inspect(tag = "state")]
383/// enum State {
384///     Stopped,
385///     Running { with_optimizations: bool },
386/// }
387/// ```
388///
389/// Example output for `State::Running { with_optimizations: true }`:
390///
391/// ```text
392/// {
393///     state: "running"
394///     with_optimizations: true
395/// }
396/// ```
397///
398/// ### `external_tag`
399///
400/// Writes a single field named for the variant, with the variant's fields as
401/// children.
402///
403/// ```no_run
404/// # use inspect::Inspect;
405/// #[derive(Inspect)]
406/// #[inspect(external_tag)]
407/// enum State {
408///     Stopped,
409///     Running { with_optimizations: bool },
410/// }
411/// ```
412///
413/// Example output for `State::Running { with_optimizations: true }`:
414///
415/// ```text
416/// {
417///     running: {
418///         with_optimizations: true
419///     }
420/// }
421/// ```
422///
423/// ### `untagged`
424///
425/// Flattens the variant's fields as in `tag`, but does not include the variant
426/// name anywhere.
427///
428/// ```no_run
429/// # use inspect::Inspect;
430/// #[derive(Inspect)]
431/// #[inspect(untagged)]
432/// enum DiskBacking {
433///     File { file_path: String },
434///     Memory { ramdisk_size: u64 },
435/// }
436/// ```
437///
438/// Example output for `State::File { file_path: "file.img" }`:
439///
440/// ```text
441/// {
442///     file_path: "file.img"
443/// }
444/// ```
445///
446/// ## Additional attributes
447///
448/// On enums, as with structs, you can put the [`skip`](#skip), [`with`](#with),
449/// [`display`](#display), [`debug`](#debug), or [`extra`](#extra--expr)
450/// attributes to specify alternate inspect behavior.
451///
452/// On each enum variant, you can use [`transparent`](#transparentattrs) as you
453/// would with a struct.
454///
455/// On enum variant fields, you can use any attribute that you would use with a
456/// struct field.
457#[cfg(feature = "derive")]
458pub use inspect_derive::Inspect;
459/// Derives the [`InspectMut`] trait for a struct or enum.
460///
461/// See the [`Inspect`](derive@Inspect) derive macro for more details.
462#[cfg(feature = "derive")]
463pub use inspect_derive::InspectMut;
464
465use alloc::borrow::Cow;
466use alloc::borrow::ToOwned;
467use alloc::boxed::Box;
468use alloc::format;
469use alloc::rc::Rc;
470use alloc::string::String;
471use alloc::string::ToString;
472use alloc::sync::Arc;
473use alloc::vec::Vec;
474use core::fmt;
475use core::fmt::Arguments;
476use core::fmt::Debug;
477use core::fmt::Display;
478use core::mem::ManuallyDrop;
479use core::num::Wrapping;
480use core::ops::Deref;
481
482/// An inspection request.
483pub struct Request<'a> {
484    params: RequestParams<'a>,
485    node: &'a mut InternalNode,
486}
487
488struct RootParams<'a> {
489    full_path: &'a str,
490    value: Option<&'a str>,
491    sensitivity: SensitivityLevel,
492}
493
494#[derive(Copy, Clone)]
495struct RequestParams<'a> {
496    root: &'a RootParams<'a>,
497    path_start: usize,
498    depth: usize,
499    number_format: NumberFormat,
500    parent_sensitivity: SensitivityLevel,
501}
502
503impl<'a> RequestParams<'a> {
504    fn path(&self) -> &'a str {
505        &self.root.full_path[self.path_start..]
506    }
507
508    fn is_leaf(&self) -> bool {
509        self.path_start >= self.root.full_path.len()
510    }
511}
512
513#[cfg_attr(
514    any(feature = "defer", feature = "initiate"),
515    derive(mesh::MeshPayload)
516)]
517#[derive(Debug, Default, Copy, Clone)]
518enum NumberFormat {
519    #[default]
520    Decimal,
521    Hex,
522    Binary,
523    Counter,
524}
525
526/// The sensitivity level for an inspection node or request.
527/// Requests will only return nodes at or below their sensitivity level.
528/// For example, a request set to `SensitivityLevel::Safe` will not return nodes
529/// with `SensitivityLevel::Sensitive` or `SensitivityLevel::Unknown`.
530#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Default)]
531#[cfg_attr(
532    any(feature = "defer", feature = "initiate"),
533    derive(mesh::MeshPayload)
534)]
535#[cfg_attr(
536    any(feature = "defer", feature = "initiate"),
537    mesh(package = "inspect")
538)]
539#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
540pub enum SensitivityLevel {
541    /// The node doesn't contain sensitive information and is always safe to expose.
542    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
543    Safe,
544    /// The node might contain sensitive information and should only be exposed
545    /// in a secure context.
546    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
547    #[default]
548    Unspecified,
549    /// The node contains sensitive information and should only be exposed in a
550    /// secure context.
551    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(3))]
552    Sensitive,
553}
554
555/// A type used to build an inspection response.
556pub struct Response<'a> {
557    params: RequestParams<'a>,
558    /// Remaining path without leading '/'s.
559    path_without_slashes: &'a str,
560    /// The list of inspected children.
561    ///
562    /// This is `None` when the depth is exhaused (in which case all children
563    /// are ignored--the response object was just created to report that this
564    /// node in the inspect tree is a directory and not a value).
565    children: Option<&'a mut Vec<InternalEntry>>,
566}
567
568#[derive(Debug)]
569enum Action<'a> {
570    Process {
571        name: &'a str,
572        sensitivity: SensitivityLevel,
573        new_path_start: usize,
574        new_depth: usize,
575    },
576    Skip,
577    DepthExhausted {
578        name: &'a str,
579        sensitivity: SensitivityLevel,
580    },
581}
582
583impl<'a> Action<'a> {
584    // Determine which action to take for the child field `child`, given the
585    // remaining `path` and the remaining `depth`.
586    fn eval(child: &Child<'a>, params: &RequestParams<'_>, path: &str) -> Self {
587        if params.depth == 0 {
588            // Don't return any subfields if the depth is exhausted, since depth
589            // exhausted will be reported for the current node.
590            return Self::Skip;
591        }
592        if params.root.sensitivity < child.sensitivity {
593            // Don't return any subfields if the request's sensitivity level is too low.
594            return Self::Skip;
595        }
596        if let Some(rest) = path.strip_prefix(child.name) {
597            if rest.is_empty() || rest.as_bytes()[0] == b'/' {
598                // Exact or prefix match, e.g. name and path are both "foo", or
599                // name is "foo", path is "foo/bar".
600                Self::Process {
601                    name: child.name,
602                    sensitivity: child.sensitivity,
603                    new_path_start: params.root.full_path.len() - rest.len(),
604                    new_depth: params.depth,
605                }
606            } else {
607                // Mismatch, e.g. name is "foo", path is "foobar", or this is a
608                // prefix match but this node is a value.
609                Self::Skip
610            }
611        } else if child.name.starts_with(path) {
612            let remaining_name = if path.is_empty() {
613                // Path is empty, so allow all names.
614                child.name
615            } else {
616                if child.name.as_bytes()[path.len()] != b'/' {
617                    // Mismatch, e.g. name is "foobar", path is "foo".
618                    return Self::Skip;
619                }
620                // Prefix match, e.g. name is "foo/bar", path is "foo".
621                &child.name[path.len() + 1..]
622            };
623            // Ensure there is enough depth for the name.
624            match remaining_name.match_indices('/').nth(params.depth - 1) {
625                None => Self::Process {
626                    name: child.name,
627                    sensitivity: child.sensitivity,
628                    new_path_start: params.root.full_path.len(),
629                    new_depth: params.depth - 1,
630                },
631                Some((n, _)) => Self::DepthExhausted {
632                    name: &child.name[..child.name.len() - remaining_name.len() + n],
633                    sensitivity: child.sensitivity,
634                },
635            }
636        } else {
637            Self::Skip
638        }
639    }
640}
641
642struct Child<'a> {
643    name: &'a str,
644    sensitivity: SensitivityLevel,
645}
646
647impl Response<'_> {
648    /// Adds a field to the response.
649    ///
650    /// ```no_run
651    /// # use inspect::{Request, Response};
652    /// # use std::path::Path;
653    /// fn inspect_data(a: u32, b: &str, c: bool, maybe: Option<u32>, req: Request) {
654    ///     req.respond()
655    ///        .field("a", a)
656    ///        .field("b", b)
657    ///        .field("c", c)
658    ///        .field("maybe", maybe)
659    ///        .field("computed", format_args!("{a}-{b}"));
660    /// }
661    /// ```
662    pub fn field<T>(&mut self, name: &str, value: T) -> &mut Self
663    where
664        T: Inspect,
665    {
666        self.inspect_field(name, &value)
667    }
668
669    /// Adds a field to the response with a [`SensitivityLevel`].
670    pub fn sensitivity_field<T>(
671        &mut self,
672        name: &str,
673        sensitivity: SensitivityLevel,
674        value: T,
675    ) -> &mut Self
676    where
677        T: Inspect,
678    {
679        self.sensitivity_inspect_field(name, sensitivity, &value)
680    }
681
682    /// Adds a hexadecimal field to the response.
683    pub fn hex<T>(&mut self, name: &str, value: T) -> &mut Self
684    where
685        T: Inspect,
686    {
687        self.field(name, AsHex(value))
688    }
689
690    /// Adds a counter field to the response.
691    pub fn counter<T>(&mut self, name: &str, value: T) -> &mut Self
692    where
693        T: Inspect,
694    {
695        self.field(name, AsCounter(value))
696    }
697
698    /// Adds a counter field to the response.
699    ///
700    /// This is a convenience method for `sensitivity_field(name, sensitivity, Value::counter(value))`.
701    pub fn sensitivity_counter<T>(
702        &mut self,
703        name: &str,
704        sensitivity: SensitivityLevel,
705        value: T,
706    ) -> &mut Self
707    where
708        T: Inspect,
709    {
710        self.sensitivity_field(name, sensitivity, AsCounter(value))
711    }
712
713    /// Adds a binary field to the response.
714    ///
715    /// This is a convenience method for `field(name, Value::binary(value))`.
716    pub fn binary<T>(&mut self, name: &str, value: T) -> &mut Self
717    where
718        T: Inspect,
719    {
720        self.field(name, AsBinary(value))
721    }
722
723    /// Adds a string field that implements [`core::fmt::Display`].
724    ///
725    /// This is a convenience method for `field_with(name, ||
726    /// value.to_string())`. It lazily allocates the string only when the
727    /// inspector requests it, so it is more efficient than using `format!` or
728    /// `to_string()`.
729    ///
730    /// ```no_run
731    /// # use inspect::{Request, Response};
732    /// # use std::path::Path;
733    /// fn inspect_data(path: &Path, id: u32, req: Request) {
734    ///     req.respond().display("path", &path.display());
735    /// }
736    /// ```
737    pub fn display(&mut self, name: &str, value: &impl Display) -> &mut Self {
738        self.field_with(name, || value.to_string())
739    }
740
741    /// Adds a string field that implements [`core::fmt::Debug`].
742    ///
743    /// This is a convenience method for `field(name, format_args!("{value:?}"))`.
744    ///
745    /// Take care not to overuse this. This is best used for fields that have a
746    /// short or natural `Debug` implementation, such as dataless enums. If your
747    /// field is an aggregate type whose `Debug` implementation has a
748    /// complicated structure, consider implementing `Inspect` on the type and
749    /// calling [`field`](`Self::field`).
750    pub fn display_debug(&mut self, name: &str, value: &impl Debug) -> &mut Self {
751        self.field(name, format_args!("{value:?}"))
752    }
753
754    /// Adds a field to the response, calling `f` to get its value.
755    pub fn field_with<F, V>(&mut self, name: &str, f: F) -> &mut Self
756    where
757        F: FnOnce() -> V,
758        V: Inspect,
759    {
760        self.sensitivity_field_with(name, SensitivityLevel::Unspecified, f)
761    }
762
763    /// Adds a field to the response with a [`SensitivityLevel`], calling `f` to get its value.
764    pub fn sensitivity_field_with<F, V>(
765        &mut self,
766        name: &str,
767        sensitivity: SensitivityLevel,
768        f: F,
769    ) -> &mut Self
770    where
771        F: FnOnce() -> V,
772        V: Inspect,
773    {
774        if let Some(req) = self.child_request(Child { name, sensitivity }) {
775            (f)().inspect(req)
776        }
777        self
778    }
779
780    /// Adds a mutable field to the response.
781    pub fn field_mut<T>(&mut self, name: &str, value: &mut T) -> &mut Self
782    where
783        T: InspectMut + ?Sized,
784    {
785        self.inspect_field(name, value)
786    }
787
788    /// Adds a mutable field to the response with a [`SensitivityLevel`].
789    pub fn sensitivity_field_mut<T>(
790        &mut self,
791        name: &str,
792        sensitivity: SensitivityLevel,
793        value: &mut T,
794    ) -> &mut Self
795    where
796        T: InspectMut + ?Sized,
797    {
798        self.sensitivity_inspect_field(name, sensitivity, value)
799    }
800
801    /// Adds a mutable field with custom get/update function.
802    pub fn field_mut_with<F, V, E>(&mut self, name: &str, f: F) -> &mut Self
803    where
804        F: FnOnce(Option<&str>) -> Result<V, E>,
805        V: Into<ValueKind>,
806        E: Into<Box<dyn core::error::Error + Send + Sync>>,
807    {
808        self.child(name, |req| match req.update() {
809            Ok(req) => match (f)(Some(req.new_value())) {
810                Ok(v) => req.succeed(v),
811                Err(err) => req.fail(err),
812            },
813            Err(req) => req.value((f)(None).ok().unwrap()),
814        })
815    }
816
817    fn child_request(&mut self, child: Child<'_>) -> Option<Request<'_>> {
818        let children = &mut **self.children.as_mut()?;
819        let action = Action::eval(&child, &self.params, self.path_without_slashes);
820        match action {
821            Action::Process {
822                name,
823                sensitivity,
824                new_path_start,
825                new_depth,
826            } => {
827                children.push(InternalEntry {
828                    name: name.to_owned(),
829                    node: InternalNode::Unevaluated,
830                    sensitivity,
831                });
832                let entry = children.last_mut().unwrap();
833                Some(
834                    RequestParams {
835                        path_start: new_path_start,
836                        depth: new_depth,
837                        root: self.params.root,
838                        number_format: self.params.number_format,
839                        parent_sensitivity: child.sensitivity,
840                    }
841                    .request(&mut entry.node),
842                )
843            }
844            Action::Skip => None,
845            Action::DepthExhausted { name, sensitivity } => {
846                children.push(InternalEntry {
847                    name: name.to_owned(),
848                    node: InternalNode::DepthExhausted,
849                    sensitivity,
850                });
851                None
852            }
853        }
854    }
855
856    /// Inspects a child object.
857    fn inspect_field(&mut self, name: &str, child: impl InspectMut) -> &mut Self {
858        self.sensitivity_inspect_field(name, SensitivityLevel::Unspecified, child)
859    }
860
861    /// Inspects a child object with a [`SensitivityLevel`].
862    #[inline(never)] // Testing shows a significant code size reduction with this.
863    fn sensitivity_inspect_field(
864        &mut self,
865        name: &str,
866        sensitivity: SensitivityLevel,
867        mut child: impl InspectMut,
868    ) -> &mut Self {
869        if let Some(req) = self.child_request(Child { name, sensitivity }) {
870            child.inspect_mut(req);
871        }
872        self
873    }
874
875    /// Inspects a list of children objects as children of the node `name`.
876    ///
877    /// Each element of `children` should be a tuple (`child_name`, `child`).
878    /// `child_name` must be convertible to a string.
879    #[cfg_attr(
880        feature = "initiate",
881        doc = r##"
882```rust
883# use inspect::{inspect, Inspect, Request};
884# use core::time::Duration;
885
886struct Child(u32);
887impl Inspect for Child {
888    fn inspect(&self, req: Request) {
889        req.respond().field("x", self.0);
890    }
891}
892
893struct Parent(Vec<Child>);
894impl Inspect for Parent {
895    fn inspect(&self, req: Request) {
896        req.respond().fields("children", self.0.iter().enumerate());
897    }
898}
899
900assert_eq!(
901    inspect(
902        "",
903        &Parent(vec![Child(5), Child(12)]),
904    )
905    .results()
906    .to_string(),
907    r#"{children: {0: {x: 5}, 1: {x: 12}}}"#
908);
909```
910"##
911    )]
912    pub fn fields<I, N, C>(&mut self, name: &str, children: I) -> &mut Self
913    where
914        I: Iterator<Item = (N, C)>,
915        N: ToString,
916        C: Inspect,
917    {
918        self.child(name, |req| {
919            let mut resp = req.respond();
920            for (name, child) in children {
921                resp.inspect_field(&name.to_string(), &child);
922            }
923        })
924    }
925
926    /// Inspects a list of children objects as children of the node `name`.
927    ///
928    /// Each element of `children` should be a tuple (`child_name`, `child`).
929    /// `child_name` must be convertible to a string.
930    #[cfg_attr(
931        feature = "initiate",
932        doc = r##"
933```rust
934# use inspect::{inspect, InspectMut, Request};
935# use std::time::Duration;
936struct Child(u32);
937impl InspectMut for Child {
938    fn inspect_mut(&mut self, req: Request) {
939        req.respond().field("x", self.0);
940    }
941}
942
943struct Parent(Vec<Child>);
944impl InspectMut for Parent {
945    fn inspect_mut(&mut self, req: Request) {
946        req.respond().fields_mut("children", self.0.iter_mut().enumerate());
947    }
948}
949
950assert_eq!(
951    inspect(
952        "",
953        &mut Parent(vec![Child(5), Child(12)]),
954    )
955    .results()
956    .to_string(),
957    r#"{children: {0: {x: 5}, 1: {x: 12}}}"#
958);
959```
960"##
961    )]
962    pub fn fields_mut<I, N, C>(&mut self, name: &str, children: I) -> &mut Self
963    where
964        I: Iterator<Item = (N, C)>,
965        N: ToString,
966        C: InspectMut,
967    {
968        self.child(name, |req| {
969            let mut resp = req.respond();
970            for (name, child) in children {
971                resp.inspect_field(&name.to_string(), child);
972            }
973        })
974    }
975
976    /// Adds a child node to the inspection response.
977    ///
978    /// If `name` is empty, then the results of the inspection are merged into
979    /// this node.
980    pub fn child<F: FnOnce(Request<'_>)>(&mut self, name: &str, f: F) -> &mut Self {
981        self.sensitivity_child(name, SensitivityLevel::Unspecified, f)
982    }
983
984    /// Adds a child node to the inspection response with a [`SensitivityLevel`].
985    ///
986    /// If `name` is empty, then the results of the inspection are merged into
987    /// this node.
988    pub fn sensitivity_child<F: FnOnce(Request<'_>)>(
989        &mut self,
990        name: &str,
991        sensitivity: SensitivityLevel,
992        f: F,
993    ) -> &mut Self {
994        if let Some(req) = self.child_request(Child { name, sensitivity }) {
995            f(req);
996        }
997        self
998    }
999
1000    /// Inspects an object and merges its responses into this node without
1001    /// creating a child node.
1002    pub fn merge(&mut self, mut child: impl InspectMut) -> &mut Self {
1003        if let Some(req) = self.request() {
1004            child.inspect_mut(req);
1005        }
1006        self
1007    }
1008
1009    /// Gets another request for this response. The response to that request
1010    /// will be merged into this response.
1011    ///
1012    /// Returns `None` if the depth is already exhausted, in which case there is
1013    /// no need (or ability--requests must have nodes) to propagate the request.
1014    fn request(&mut self) -> Option<Request<'_>> {
1015        let children = &mut **self.children.as_mut()?;
1016        children.push(InternalEntry {
1017            name: String::new(),
1018            node: InternalNode::Unevaluated,
1019            sensitivity: SensitivityLevel::Unspecified,
1020        });
1021        let entry = children.last_mut().unwrap();
1022        Some(self.params.request(&mut entry.node))
1023    }
1024
1025    /// Gets the sensitivity level of the parent node of this request. This is
1026    /// useful for wrappers around fields that wish to inherit the sensitivity
1027    /// level of their parent node.
1028    pub fn parent_sensitivity(&self) -> SensitivityLevel {
1029        self.params.parent_sensitivity
1030    }
1031}
1032
1033impl<'a> RequestParams<'a> {
1034    #[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
1035    fn inspect(self, mut obj: impl InspectMut) -> InternalNode {
1036        self.with(|req| {
1037            obj.inspect_mut(req);
1038        })
1039    }
1040
1041    fn with(self, f: impl FnOnce(Request<'_>)) -> InternalNode {
1042        let mut node = InternalNode::Unevaluated;
1043        f(self.request(&mut node));
1044        node
1045    }
1046
1047    fn request(self, node: &'a mut InternalNode) -> Request<'a> {
1048        Request { params: self, node }
1049    }
1050}
1051
1052impl<'a> Request<'a> {
1053    /// Sets numeric values to be displayed in decimal format.
1054    #[must_use]
1055    pub fn with_decimal_format(mut self) -> Self {
1056        self.params.number_format = NumberFormat::Decimal;
1057        self
1058    }
1059
1060    /// Sets numeric values to be displayed in hexadecimal format.
1061    #[must_use]
1062    pub fn with_hex_format(mut self) -> Self {
1063        self.params.number_format = NumberFormat::Hex;
1064        self
1065    }
1066
1067    /// Sets numeric values to be displayed in binary format.
1068    #[must_use]
1069    pub fn with_binary_format(mut self) -> Self {
1070        self.params.number_format = NumberFormat::Binary;
1071        self
1072    }
1073
1074    /// Sets numeric values to be displayed as counters.
1075    #[must_use]
1076    pub fn with_counter_format(mut self) -> Self {
1077        self.params.number_format = NumberFormat::Counter;
1078        self
1079    }
1080
1081    /// Responds to the request with a value.
1082    pub fn value(self, value: impl Into<ValueKind>) {
1083        self.value_(value.into());
1084    }
1085    fn value_(self, value: ValueKind) {
1086        let node = if self.params.is_leaf() {
1087            if self.params.root.value.is_none() {
1088                InternalNode::Value(value.with_format(self.params.number_format))
1089            } else {
1090                InternalNode::Failed(InternalError::Immutable)
1091            }
1092        } else {
1093            InternalNode::Failed(InternalError::NotADirectory)
1094        };
1095        *self.node = node;
1096    }
1097
1098    /// Returns an object used for handling an update request.
1099    ///
1100    /// If this is not an update request, returns `Err(self)`.
1101    pub fn update(self) -> Result<UpdateRequest<'a>, Self> {
1102        if let Some(value) = self.params.root.value {
1103            if !self.params.is_leaf() {
1104                return Err(self);
1105            }
1106            Ok(UpdateRequest {
1107                node: self.node,
1108                value,
1109                number_format: self.params.number_format,
1110            })
1111        } else {
1112            Err(self)
1113        }
1114    }
1115
1116    /// Responds to the inspection request with a directory node.
1117    ///
1118    /// Returns an object that can be used to provide the inspection results.
1119    pub fn respond(self) -> Response<'a> {
1120        let children = if self.params.depth > 0 {
1121            *self.node = InternalNode::Dir(Vec::new());
1122            let InternalNode::Dir(children) = self.node else {
1123                unreachable!()
1124            };
1125            Some(children)
1126        } else {
1127            // No children will be collected, but this node had to be inspected
1128            // in case it was a value and not a directory node.
1129            *self.node = InternalNode::DepthExhausted;
1130            None
1131        };
1132        Response {
1133            params: self.params,
1134            path_without_slashes: self.params.path().trim_start_matches('/'),
1135            children,
1136        }
1137    }
1138
1139    /// Removes this node from the inspection output.
1140    pub fn ignore(self) {
1141        *self.node = InternalNode::Ignored;
1142    }
1143
1144    /// If true, this is an update request.
1145    pub fn is_update(&self) -> bool {
1146        self.params.root.value.is_some()
1147    }
1148
1149    /// Gets the sensitivity level for this request.
1150    pub fn sensitivity(&self) -> SensitivityLevel {
1151        self.params.root.sensitivity
1152    }
1153}
1154
1155/// An update request, used for updating a value.
1156pub struct UpdateRequest<'a> {
1157    node: &'a mut InternalNode,
1158    value: &'a str,
1159    number_format: NumberFormat,
1160}
1161
1162impl UpdateRequest<'_> {
1163    /// Gets the requested new value.
1164    pub fn new_value(&self) -> &str {
1165        self.value
1166    }
1167
1168    /// Report that the update succeeded, with a new value of `value`.
1169    pub fn succeed(self, value: impl Into<ValueKind>) {
1170        self.succeed_(value.into());
1171    }
1172    fn succeed_(self, value: ValueKind) {
1173        *self.node = InternalNode::Value(value.with_format(self.number_format));
1174    }
1175
1176    /// Report that the update failed, with the reason in `err`.
1177    pub fn fail<E: Into<Box<dyn core::error::Error + Send + Sync>>>(self, err: E) {
1178        *self.node = InternalNode::failed(err.into());
1179    }
1180}
1181
1182/// A child inspection value. This is not constructed directly but exists to
1183/// provide `From` implementations from the allowed value types.
1184#[derive(Debug, Clone, PartialEq)]
1185#[cfg_attr(
1186    any(feature = "defer", feature = "initiate"),
1187    derive(mesh::MeshPayload),
1188    mesh(package = "inspect")
1189)]
1190#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1191pub struct Value {
1192    /// The kind and associated value.
1193    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
1194    pub kind: ValueKind,
1195    /// Flags affecting how the value is displayed.
1196    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
1197    pub flags: ValueFlags,
1198}
1199
1200impl Value {
1201    /// Creates a new value from `kind`.
1202    pub fn new(kind: impl Into<ValueKind>) -> Self {
1203        Self {
1204            kind: kind.into(),
1205            flags: Default::default(),
1206        }
1207    }
1208
1209    /// Creates a new hexadecimal value from `kind`.
1210    pub fn hex(kind: impl Into<ValueKind>) -> Self {
1211        Self::new(kind).into_hex()
1212    }
1213
1214    /// Creates a new counter value from `kind`.
1215    pub fn counter(kind: impl Into<ValueKind>) -> Self {
1216        Self::new(kind).into_counter()
1217    }
1218
1219    /// Creates a new binary value from `kind`.
1220    pub fn binary(kind: impl Into<ValueKind>) -> Self {
1221        Self::new(kind).into_binary()
1222    }
1223
1224    /// Returns this value as a hexadecimal.
1225    pub fn into_hex(self) -> Self {
1226        Self {
1227            kind: self.kind,
1228            flags: self.flags.with_hex(true),
1229        }
1230    }
1231
1232    /// Returns this value as a counter.
1233    pub fn into_counter(self) -> Self {
1234        Self {
1235            kind: self.kind,
1236            flags: self.flags.with_count(true),
1237        }
1238    }
1239
1240    /// Returns this value as a binary.
1241    pub fn into_binary(self) -> Self {
1242        Self {
1243            kind: self.kind,
1244            flags: self.flags.with_binary(true),
1245        }
1246    }
1247}
1248
1249/// The different kinds of values that can be emitted.
1250#[derive(Debug, Clone, PartialEq)]
1251#[cfg_attr(
1252    any(feature = "defer", feature = "initiate"),
1253    derive(mesh::MeshPayload),
1254    mesh(package = "inspect")
1255)]
1256#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1257pub enum ValueKind {
1258    /// A signed integer.
1259    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
1260    Signed(i64),
1261    /// An unsigned integer.
1262    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
1263    Unsigned(u64),
1264    /// A 32-bit floating point.
1265    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(3))]
1266    Float(f32),
1267    /// A 64-bit floating point.
1268    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(4))]
1269    Double(f64),
1270    /// A Boolean value.
1271    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(5))]
1272    Bool(bool),
1273    /// A string.
1274    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(6))]
1275    String(String),
1276    /// Opaque binary data.
1277    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(7))]
1278    Bytes(Vec<u8>),
1279}
1280
1281impl ValueKind {
1282    fn with_format(self, format: NumberFormat) -> Value {
1283        match self {
1284            Self::Signed(_) | Self::Unsigned(_) => match format {
1285                NumberFormat::Decimal => Value::new(self),
1286                NumberFormat::Hex => Value::hex(self).into_hex(),
1287                NumberFormat::Binary => Value::binary(self).into_binary(),
1288                NumberFormat::Counter => Value::counter(self).into_counter(),
1289            },
1290            v => v.into(),
1291        }
1292    }
1293}
1294
1295/// Flags specifying additional metadata on values.
1296#[bitfield_struct::bitfield(u64)]
1297#[derive(PartialEq)]
1298#[cfg_attr(
1299    any(feature = "defer", feature = "initiate"),
1300    derive(mesh::MeshPayload),
1301    mesh(package = "inspect")
1302)]
1303#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1304pub struct ValueFlags {
1305    /// This value should be displayed as hexadecimal.
1306    pub hex: bool,
1307    /// This value is a count and should be displayed as a rate.
1308    pub count: bool,
1309    /// This value should be displayed as binary.
1310    pub binary: bool,
1311
1312    #[bits(61)]
1313    _reserved: u64,
1314}
1315
1316impl Display for Value {
1317    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1318        match &self.kind {
1319            ValueKind::Signed(n) => {
1320                if self.flags.hex() {
1321                    write!(f, "{:#x}", &n)
1322                } else if self.flags.binary() {
1323                    write!(f, "{:#b}", &n)
1324                } else {
1325                    Display::fmt(&n, f)
1326                }
1327            }
1328            ValueKind::Unsigned(n) => {
1329                if self.flags.hex() {
1330                    write!(f, "{:#x}", &n)
1331                } else if self.flags.binary() {
1332                    write!(f, "{:#b}", &n)
1333                } else {
1334                    Display::fmt(&n, f)
1335                }
1336            }
1337            ValueKind::Float(n) => Display::fmt(&n, f),
1338            ValueKind::Double(n) => Display::fmt(&n, f),
1339            ValueKind::Bool(b) => Display::fmt(&b, f),
1340            ValueKind::String(s) => Debug::fmt(&s, f),
1341            ValueKind::Bytes(b) => {
1342                f.write_str("<")?;
1343                for &b in b {
1344                    write!(f, "{:02x}", b)?;
1345                }
1346                f.write_str(">")
1347            }
1348        }
1349    }
1350}
1351
1352impl<T> From<T> for Value
1353where
1354    ValueKind: From<T>,
1355{
1356    fn from(v: T) -> Self {
1357        Value {
1358            kind: v.into(),
1359            flags: Default::default(),
1360        }
1361    }
1362}
1363
1364macro_rules! from_as {
1365    ($x:ident, $to:ty, $($ty:ty),* $(,)?) => {
1366        $(
1367            impl From<$ty> for ValueKind {
1368                fn from(v: $ty) -> Self {
1369                    Self::$x(v as $to)
1370                }
1371            }
1372        )*
1373    };
1374}
1375
1376from_as!(Unsigned, u64, u8, u16, u32, u64, usize);
1377from_as!(Signed, i64, i8, i16, i32, i64, isize);
1378
1379macro_rules! from_get {
1380    ($x:ident, $to:ty, $($ty:ty),* $(,)?) => {
1381        $(
1382            impl From<$ty> for ValueKind {
1383                fn from(v: $ty) -> Self {
1384                    Self::$x(v.get() as $to)
1385                }
1386            }
1387        )*
1388    };
1389}
1390
1391from_get!(
1392    Unsigned,
1393    u64,
1394    core::num::NonZeroU8,
1395    core::num::NonZeroU16,
1396    core::num::NonZeroU32,
1397    core::num::NonZeroU64,
1398    core::num::NonZeroUsize
1399);
1400from_get!(
1401    Signed,
1402    i64,
1403    core::num::NonZeroI8,
1404    core::num::NonZeroI16,
1405    core::num::NonZeroI32,
1406    core::num::NonZeroI64,
1407    core::num::NonZeroIsize
1408);
1409
1410impl From<f32> for ValueKind {
1411    fn from(v: f32) -> Self {
1412        Self::Float(v)
1413    }
1414}
1415
1416impl From<f64> for ValueKind {
1417    fn from(v: f64) -> Self {
1418        Self::Double(v)
1419    }
1420}
1421
1422impl From<bool> for ValueKind {
1423    fn from(v: bool) -> Self {
1424        Self::Bool(v)
1425    }
1426}
1427
1428impl From<&'_ str> for ValueKind {
1429    fn from(v: &str) -> Self {
1430        Self::String(v.to_owned())
1431    }
1432}
1433
1434impl From<String> for ValueKind {
1435    fn from(v: String) -> Self {
1436        Self::String(v)
1437    }
1438}
1439
1440#[cfg(feature = "std")]
1441impl From<&'_ std::ffi::CStr> for ValueKind {
1442    fn from(v: &std::ffi::CStr) -> Self {
1443        Self::String(v.to_string_lossy().to_string())
1444    }
1445}
1446
1447#[cfg(feature = "std")]
1448impl From<std::ffi::CString> for ValueKind {
1449    fn from(v: std::ffi::CString) -> Self {
1450        Self::String(v.to_string_lossy().to_string())
1451    }
1452}
1453
1454impl From<&'_ [u8]> for ValueKind {
1455    fn from(v: &[u8]) -> Self {
1456        Self::Bytes(v.to_vec())
1457    }
1458}
1459
1460impl<const N: usize> From<[u8; N]> for ValueKind {
1461    fn from(v: [u8; N]) -> Self {
1462        Self::Bytes(v.to_vec())
1463    }
1464}
1465
1466impl From<Vec<u8>> for ValueKind {
1467    fn from(v: Vec<u8>) -> Self {
1468        Self::Bytes(v)
1469    }
1470}
1471
1472impl From<Arguments<'_>> for ValueKind {
1473    fn from(v: Arguments<'_>) -> Self {
1474        Self::String(v.to_string())
1475    }
1476}
1477
1478impl<T: Into<ValueKind> + Clone> From<&'_ T> for ValueKind {
1479    fn from(v: &T) -> Self {
1480        v.clone().into()
1481    }
1482}
1483
1484impl Inspect for () {
1485    fn inspect(&self, req: Request<'_>) {
1486        req.respond();
1487    }
1488}
1489
1490impl InspectMut for () {
1491    fn inspect_mut(&mut self, req: Request<'_>) {
1492        req.respond();
1493    }
1494}
1495
1496macro_rules! inspect_value_immut {
1497    ($($(#[$attr:meta])* $ty:ty),* $(,)?) => {
1498        $(
1499        $(#[$attr])*
1500        impl Inspect for $ty {
1501            fn inspect(&self, req: Request<'_>) {
1502                req.value(self)
1503            }
1504        }
1505        )*
1506    };
1507}
1508
1509macro_rules! inspect_value {
1510    ($($(#[$attr:meta])* $ty:ty),* $(,)?) => {
1511        inspect_value_immut!($($ty,)*);
1512        $(
1513        $(#[$attr])*
1514        impl InspectMut for $ty {
1515            fn inspect_mut(&mut self, req: Request<'_>) {
1516                match req.update() {
1517                    Ok(req) => match req.new_value().parse::<Self>() {
1518                        Ok(v) => {
1519                            *self = v.clone();
1520                            req.succeed(v);
1521                        }
1522                        Err(err) => req.fail(err),
1523                    }
1524                    Err(req) => req.value(&*self),
1525                }
1526            }
1527        }
1528        )*
1529    };
1530}
1531
1532inspect_value_immut! {
1533    str,
1534    #[cfg(feature = "std")]
1535    std::ffi::CStr,
1536    #[cfg(feature = "std")]
1537    std::ffi::CString,
1538    [u8],
1539    Vec<u8>,
1540    Arguments<'_>,
1541}
1542
1543inspect_value! {
1544    u8,
1545    u16,
1546    u32,
1547    u64,
1548    usize,
1549    i8,
1550    i16,
1551    i32,
1552    i64,
1553    isize,
1554    core::num::NonZeroU8,
1555    core::num::NonZeroU16,
1556    core::num::NonZeroU32,
1557    core::num::NonZeroU64,
1558    core::num::NonZeroUsize,
1559    core::num::NonZeroI8,
1560    core::num::NonZeroI16,
1561    core::num::NonZeroI32,
1562    core::num::NonZeroI64,
1563    core::num::NonZeroIsize,
1564    f32,
1565    f64,
1566    bool,
1567    String,
1568}
1569
1570impl<const N: usize> Inspect for [u8; N] {
1571    fn inspect(&self, req: Request<'_>) {
1572        self.as_slice().inspect(req)
1573    }
1574}
1575
1576/// Inspect wrapper for an atomic value (e.g.,
1577/// [`AtomicBool`](core::sync::atomic::AtomicBool), allowing updates.
1578pub struct AtomicMut<T>(pub T);
1579
1580macro_rules! inspect_atomic_value {
1581    ($(($ty:ident, $backing:ty)),* $(,)?) => {
1582        $(
1583
1584        impl Inspect for core::sync::atomic::$ty {
1585            fn inspect(&self, req: Request<'_>) {
1586                req.value(self.load(core::sync::atomic::Ordering::Relaxed))
1587            }
1588        }
1589
1590        // It doesn't make much sense to impl InspectMut, since if you have a &mut
1591        // reference to the Atomic, it's most likely that type didn't need to be
1592        // atomic in the first place.
1593
1594        impl Inspect for AtomicMut<&core::sync::atomic::$ty> {
1595            fn inspect(&self, req: Request<'_>) {
1596                let mut value = self.0.load(core::sync::atomic::Ordering::Relaxed);
1597                let is_update = req.is_update();
1598                value.inspect_mut(req);
1599                if is_update {
1600                    self.0.store(value, core::sync::atomic::Ordering::Relaxed);
1601                }
1602            }
1603        }
1604
1605        )*
1606    };
1607}
1608
1609inspect_atomic_value! {
1610    (AtomicBool,  bool),
1611    (AtomicU8,    u8),
1612    (AtomicU16,   u16),
1613    (AtomicU32,   u32),
1614    (AtomicU64,   u64),
1615    (AtomicUsize, usize),
1616    (AtomicI8,    i8),
1617    (AtomicI16,   i16),
1618    (AtomicI32,   i32),
1619    (AtomicI64,   i64),
1620    (AtomicIsize, isize),
1621}
1622
1623impl<T: Inspect> Inspect for Wrapping<T> {
1624    fn inspect(&self, req: Request<'_>) {
1625        self.0.inspect(req)
1626    }
1627}
1628
1629impl<T: InspectMut> InspectMut for Wrapping<T> {
1630    fn inspect_mut(&mut self, req: Request<'_>) {
1631        self.0.inspect_mut(req)
1632    }
1633}
1634
1635#[cfg(feature = "filepath")]
1636impl Inspect for std::fs::File {
1637    fn inspect(&self, req: Request<'_>) {
1638        use filepath::FilePath;
1639        if let Ok(path) = self.path() {
1640            req.value(path.display().to_string());
1641        } else {
1642            req.ignore();
1643        }
1644    }
1645}
1646
1647impl Inspect for core::time::Duration {
1648    fn inspect(&self, req: Request<'_>) {
1649        req.value(format!("{}.{:09}s", self.as_secs(), self.subsec_nanos()));
1650    }
1651}
1652
1653/// Wrapper around `T` that implements [`Inspect`] by calling
1654/// [`ToString::to_string()`].
1655pub struct AsDisplay<T>(pub T);
1656
1657impl<T: Display> Inspect for AsDisplay<T> {
1658    fn inspect(&self, req: Request<'_>) {
1659        req.value(self.0.to_string())
1660    }
1661}
1662
1663/// Wrapper around `T` that implements [`Inspect`] by formatting with the
1664/// [`Debug`] trait.
1665pub struct AsDebug<T>(pub T);
1666
1667impl<T: Debug> InspectMut for AsDebug<T> {
1668    fn inspect_mut(&mut self, req: Request<'_>) {
1669        req.value(format!("{:?}", self.0))
1670    }
1671}
1672
1673impl<T: Debug> Inspect for AsDebug<T> {
1674    fn inspect(&self, req: Request<'_>) {
1675        req.value(format!("{:?}", self.0))
1676    }
1677}
1678
1679macro_rules! hexbincount {
1680    ($tr:ident, $fmt:expr) => {
1681        impl<T: Inspect> Inspect for $tr<T> {
1682            fn inspect(&self, mut req: Request<'_>) {
1683                req.params.number_format = $fmt;
1684                self.0.inspect(req);
1685            }
1686        }
1687
1688        impl<T: InspectMut> InspectMut for $tr<T> {
1689            fn inspect_mut(&mut self, mut req: Request<'_>) {
1690                req.params.number_format = $fmt;
1691                self.0.inspect_mut(req);
1692            }
1693        }
1694    };
1695}
1696
1697/// Wrapper around `T` that implements [`Inspect`] by writing a value with
1698/// [`ValueFlags::hex`].
1699#[derive(Clone, Copy)]
1700pub struct AsHex<T>(pub T);
1701
1702hexbincount!(AsHex, NumberFormat::Hex);
1703
1704/// Wrapper around `T` that implements [`Inspect`] by writing a value with
1705/// [`ValueFlags::binary`].
1706pub struct AsBinary<T>(pub T);
1707
1708hexbincount!(AsBinary, NumberFormat::Binary);
1709
1710/// Wrapper around `T` that implements [`Inspect`] by writing a value with
1711/// [`ValueFlags::count`].
1712pub struct AsCounter<T>(pub T);
1713
1714hexbincount!(AsCounter, NumberFormat::Counter);
1715
1716/// Wrapper around `T` that implements [`Inspect`] by writing a
1717/// [`ValueKind::Bytes`] value.
1718///
1719/// `T` must be iterable with `u8` or `&u8`.
1720pub struct AsBytes<T>(pub T);
1721
1722impl<T> Inspect for AsBytes<T>
1723where
1724    T: Clone + IntoIterator,
1725    Vec<u8>: Extend<T::Item>,
1726{
1727    fn inspect(&self, req: Request<'_>) {
1728        let mut v = Vec::new();
1729        v.extend(self.0.clone());
1730        req.value(ValueKind::Bytes(v))
1731    }
1732}
1733
1734#[doc(hidden)]
1735pub mod derive_helpers {
1736    /// Helps with type inference in inspect_derive output.
1737    pub fn call<T, F, R>(t: T, f: F) -> R
1738    where
1739        F: Fn(T) -> R,
1740    {
1741        f(t)
1742    }
1743}
1744
1745/// Implementation of [`Inspect`] a type that implements [`Iterator`] with an
1746/// item type `(K, V)`.
1747///
1748/// Inspecting this type will respond with a field for each entry in the
1749/// iterator, with `K` for the field's name and `V` for the field's value.
1750///
1751/// Construct with [`iter_by_key`] or [`iter_by_index`].
1752#[derive(Default)]
1753pub struct Iterated<I>(I);
1754
1755/// Wraps an iterator for inspection.
1756///
1757/// # Example
1758///
1759/// ```rust
1760/// # use std::collections::BTreeMap;
1761/// fn inspect(req: inspect::Request<'_>) {
1762///     let v = BTreeMap::from([("foo", 1), ("bar", 2)]);
1763///     // Responds with { foo: 1, bar: 2 }.
1764///     req.respond().field("v", inspect::iter_by_key(&v));
1765/// }
1766/// ```
1767pub fn iter_by_key<I, K, V>(iter: impl IntoIterator<IntoIter = I>) -> Iterated<I>
1768where
1769    I: Clone + Iterator<Item = (K, V)>,
1770{
1771    Iterated(iter.into_iter())
1772}
1773
1774/// Wraps an iterator for inspection, using the index in the enumeration as
1775/// the field name.
1776///
1777/// # Example
1778///
1779/// ```rust
1780/// fn inspect(req: inspect::Request<'_>) {
1781///     let v = vec!["foo", "bar", "baz"];
1782///     // Responds with { 0: "foo", 1: "bar", 2: "baz" }.
1783///     req.respond().field("v", inspect::iter_by_index(&v));
1784/// }
1785/// ```
1786pub fn iter_by_index<I, V>(
1787    iter: impl IntoIterator<IntoIter = I>,
1788) -> Iterated<core::iter::Enumerate<I>>
1789where
1790    I: Clone + Iterator<Item = V>,
1791{
1792    iter_by_key(iter.into_iter().enumerate())
1793}
1794
1795impl<I, K, V> Iterated<I>
1796where
1797    I: Clone + Iterator<Item = (K, V)>,
1798{
1799    /// Maps the key of the iterator with `f`.
1800    ///
1801    /// # Example
1802    ///
1803    /// ```rust
1804    /// fn inspect(req: inspect::Request<'_>) {
1805    ///     let v = vec!["foo", "bar", "baz"];
1806    ///     // Responds with { 1: "foo", 2: "bar", 3: "baz" }.
1807    ///     req.respond().field("v", inspect::iter_by_index(&v).map_key(|k| k + 1));
1808    /// }
1809    /// ```
1810    pub fn map_key<F, K2>(self, mut f: F) -> Iterated<impl Clone + Iterator<Item = (K2, V)>>
1811    where
1812        F: Clone + FnMut(K) -> K2,
1813    {
1814        iter_by_key(self.0.map(move |(k, v)| (f(k), v)))
1815    }
1816
1817    /// Maps the value of the iterator with `f`.
1818    ///
1819    /// # Example
1820    ///
1821    /// ```rust
1822    /// fn inspect(req: inspect::Request<'_>) {
1823    ///     let v = vec![10, 20, 30];
1824    ///     // Responds with { 0: 20, 1: 40, 2: 60 }.
1825    ///     req.respond().field("v", inspect::iter_by_index(&v).map_value(|v| v * 2));
1826    /// }
1827    /// ```
1828    pub fn map_value<F, V2>(self, mut f: F) -> Iterated<impl Clone + Iterator<Item = (K, V2)>>
1829    where
1830        F: Clone + FnMut(V) -> V2,
1831    {
1832        iter_by_key(self.0.map(move |(k, v)| (k, f(v))))
1833    }
1834
1835    /// Prefixes each key with the string `prefix`.
1836    ///
1837    /// # Example
1838    ///
1839    /// ```rust
1840    /// fn inspect(req: inspect::Request<'_>) {
1841    ///     let v = vec![10, 20, 30];
1842    ///     // Responds with { n0: 10, n1: 20, n2: 30 }.
1843    ///     req.respond().field("v", inspect::iter_by_index(&v).prefix("n"));
1844    /// }
1845    /// ```
1846    pub fn prefix<'a>(
1847        self,
1848        prefix: &'a str,
1849    ) -> Iterated<impl 'a + Clone + Iterator<Item = (String, V)>>
1850    where
1851        K: Display,
1852        I: 'a,
1853        K: 'a,
1854        V: 'a,
1855    {
1856        self.map_key(move |k| format!("{}{}", prefix, k))
1857    }
1858}
1859
1860impl<I, K, V> Inspect for Iterated<I>
1861where
1862    I: Clone + Iterator<Item = (K, V)>,
1863    K: ToString,
1864    V: Inspect,
1865{
1866    fn inspect(&self, req: Request<'_>) {
1867        let mut resp = req.respond();
1868        for (name, value) in self.0.clone() {
1869            resp.sensitivity_field(&name.to_string(), resp.parent_sensitivity(), value);
1870        }
1871    }
1872}
1873
1874#[derive(Debug)]
1875#[cfg_attr(
1876    any(feature = "defer", feature = "initiate"),
1877    derive(mesh::MeshPayload)
1878)]
1879#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
1880enum InternalNode {
1881    Unevaluated,
1882    Failed(InternalError),
1883    DepthExhausted,
1884    Value(Value),
1885    Dir(Vec<InternalEntry>),
1886    Ignored,
1887    #[cfg(any(feature = "defer", feature = "initiate"))]
1888    Deferred(mesh::OneshotReceiver<InternalNode>),
1889    #[cfg(any(feature = "defer", feature = "initiate"))]
1890    DirResolved(Vec<InternalEntry>),
1891}
1892
1893#[derive(Debug)]
1894#[cfg_attr(
1895    any(feature = "defer", feature = "initiate"),
1896    derive(mesh::MeshPayload)
1897)]
1898// Without the initiate feature we never read fields of the InternalEntry
1899// to produce a user-visible Entry, but we still need those fields.
1900#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
1901struct InternalEntry {
1902    name: String,
1903    node: InternalNode,
1904    sensitivity: SensitivityLevel,
1905}
1906
1907#[derive(Debug)]
1908#[cfg_attr(
1909    any(feature = "defer", feature = "initiate"),
1910    derive(mesh::MeshPayload)
1911)]
1912#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
1913enum InternalError {
1914    Immutable,
1915    Update(String),
1916    NotADirectory,
1917    Unresolved,
1918    Mesh(String),
1919}
1920
1921impl InternalNode {
1922    fn failed(err: Box<dyn core::error::Error + Send + Sync>) -> Self {
1923        use core::fmt::Write;
1924
1925        let mut s = err.to_string();
1926        let mut src = err.source();
1927        while let Some(err) = src {
1928            src = err.source();
1929            let _ = write!(&mut s, ": {err}");
1930        }
1931        InternalNode::Failed(InternalError::Update(s))
1932    }
1933}
1934
1935/// Trait implemented by objects whose state can be inspected and mutated. Most users should not implement this trait
1936/// directly, but instead derive [`InspectMut`](derive@InspectMut).
1937///
1938/// See the [`Inspect`] trait for more information on implementation strategies.
1939pub trait InspectMut {
1940    /// Inspects the object.
1941    fn inspect_mut(&mut self, req: Request<'_>);
1942}
1943
1944impl<T: Inspect + ?Sized> InspectMut for &'_ T {
1945    fn inspect_mut(&mut self, req: Request<'_>) {
1946        self.inspect(req);
1947    }
1948}
1949
1950impl<T: InspectMut + ?Sized> InspectMut for &'_ mut T {
1951    fn inspect_mut(&mut self, req: Request<'_>) {
1952        T::inspect_mut(*self, req)
1953    }
1954}
1955
1956impl<T: InspectMut + ?Sized> InspectMut for Box<T> {
1957    fn inspect_mut(&mut self, req: Request<'_>) {
1958        T::inspect_mut(self.as_mut(), req)
1959    }
1960}
1961
1962impl<T: InspectMut> InspectMut for Option<T> {
1963    fn inspect_mut(&mut self, req: Request<'_>) {
1964        if let Some(val) = self {
1965            val.inspect_mut(req);
1966        } else {
1967            req.ignore();
1968        }
1969    }
1970}
1971
1972/// Trait implemented by objects whose state can be inspected but not mutated.
1973///
1974/// For most cases, users should use the derive version of [`Inspect`](derive@Inspect)
1975/// as this will automatically update the implementation as new fields are added.
1976///
1977/// However there are cases where a user may want to manually implement this trait, such as when
1978/// an inspection result may require multiple struct fields for a single inspection field.
1979/// Users should take advantage of Rust's struct destructuring to introduce compiler errors to
1980/// keep manual `Inspect` trait implementations up to date as the struct changes.
1981///
1982/// # Example
1983/// The following code compiles.
1984/// ```no_run
1985/// # use inspect::Inspect;
1986/// struct Foo {
1987///     id: u32,
1988///     more_stuff: usize,
1989///     super_string: String,
1990/// }
1991///
1992/// impl Inspect for Foo {
1993///     fn inspect(&self, req: inspect::Request<'_>) {
1994///         let Foo {
1995///             id,
1996///             more_stuff,
1997///             super_string,
1998///         } = self;
1999///
2000///         let mut resp = req.respond();
2001///         resp.hex("id", id);
2002///         resp.field("more_stuff", more_stuff);
2003///
2004///         // Only provide the super_string field if id is 2.
2005///         if *id == 2 {
2006///             resp.field("super_string", super_string);
2007///         }
2008///     }
2009/// }
2010/// ```
2011///
2012/// However, someone adding a new field to `Foo` without updating the inspect implementation will no longer compile.
2013/// ```compile_fail
2014/// # use inspect::Inspect;
2015/// struct Foo {
2016///     id: u32,
2017///     more_stuff: usize,
2018///     super_string: String,
2019///     awesome_new_field: String,
2020/// }
2021///
2022/// impl Inspect for Foo {
2023///     fn inspect(&self, req: inspect::Request<'_>) {
2024///         let Foo {
2025///             id,
2026///             more_stuff,
2027///             super_string,
2028///             // Missing awesome_new_field!
2029///         } = self;
2030///
2031///         let mut resp = respond();
2032///         resp.hex("id", id);
2033///         resp.field("more_stuff", more_stuff);
2034///
2035///         // Only provide the super_string field if id is 2.
2036///         if id == 2 {
2037///             resp.field("super_string", super_string);
2038///         }
2039///     }
2040/// }
2041/// ```
2042pub trait Inspect {
2043    /// Inspects the object.
2044    fn inspect(&self, req: Request<'_>);
2045}
2046
2047impl<T: Inspect + ?Sized> Inspect for &'_ T {
2048    fn inspect(&self, req: Request<'_>) {
2049        T::inspect(*self, req)
2050    }
2051}
2052
2053impl<T: Inspect + ?Sized> Inspect for &'_ mut T {
2054    fn inspect(&self, req: Request<'_>) {
2055        T::inspect(*self, req)
2056    }
2057}
2058
2059impl<T: Inspect + ?Sized> Inspect for Box<T> {
2060    fn inspect(&self, req: Request<'_>) {
2061        T::inspect(self.as_ref(), req)
2062    }
2063}
2064
2065impl<T: Inspect + ?Sized> Inspect for Rc<T> {
2066    fn inspect(&self, req: Request<'_>) {
2067        T::inspect(self.as_ref(), req)
2068    }
2069}
2070
2071impl<T: Inspect + ?Sized> Inspect for Arc<T> {
2072    fn inspect(&self, req: Request<'_>) {
2073        T::inspect(self.as_ref(), req)
2074    }
2075}
2076
2077impl<T: Inspect + ?Sized> Inspect for parking_lot::Mutex<T> {
2078    fn inspect(&self, req: Request<'_>) {
2079        T::inspect(&*self.lock(), req)
2080    }
2081}
2082
2083impl<T: Inspect + ?Sized> Inspect for parking_lot::RwLock<T> {
2084    fn inspect(&self, req: Request<'_>) {
2085        T::inspect(&*self.read(), req)
2086    }
2087}
2088
2089#[cfg(feature = "std")]
2090impl<T: Inspect> Inspect for std::sync::OnceLock<T> {
2091    fn inspect(&self, req: Request<'_>) {
2092        <_ as Inspect>::inspect(&self.get(), req)
2093    }
2094}
2095
2096impl<T: Inspect> Inspect for Option<T> {
2097    fn inspect(&self, req: Request<'_>) {
2098        if let Some(val) = self {
2099            val.inspect(req);
2100        } else {
2101            req.ignore();
2102        }
2103    }
2104}
2105
2106impl<T: ?Sized + Inspect + ToOwned> Inspect for Cow<'_, T> {
2107    fn inspect(&self, req: Request<'_>) {
2108        self.as_ref().inspect(req)
2109    }
2110}
2111
2112impl<T> Inspect for *mut T {
2113    fn inspect(&self, req: Request<'_>) {
2114        req.with_hex_format().value(*self as usize)
2115    }
2116}
2117
2118impl<T> Inspect for *const T {
2119    fn inspect(&self, req: Request<'_>) {
2120        req.with_hex_format().value(*self as usize)
2121    }
2122}
2123
2124impl Inspect for ValueKind {
2125    fn inspect(&self, req: Request<'_>) {
2126        req.value(self.clone());
2127    }
2128}
2129
2130impl Inspect for Value {
2131    fn inspect(&self, req: Request<'_>) {
2132        let req = if self.flags.count() {
2133            req.with_counter_format()
2134        } else if self.flags.hex() {
2135            req.with_hex_format()
2136        } else if self.flags.binary() {
2137            req.with_binary_format()
2138        } else {
2139            req
2140        };
2141        req.value(self.kind.clone());
2142    }
2143}
2144impl<T: Inspect + ?Sized> Inspect for ManuallyDrop<T> {
2145    fn inspect(&self, req: Request<'_>) {
2146        self.deref().inspect(req);
2147    }
2148}
2149
2150/// Returned by [`adhoc`] or [`adhoc_mut`].
2151pub struct Adhoc<F>(F);
2152
2153impl<F> InspectMut for Adhoc<F>
2154where
2155    F: FnMut(Request<'_>),
2156{
2157    fn inspect_mut(&mut self, req: Request<'_>) {
2158        (self.0)(req)
2159    }
2160}
2161
2162impl<F> Inspect for Adhoc<F>
2163where
2164    F: Fn(Request<'_>),
2165{
2166    fn inspect(&self, req: Request<'_>) {
2167        (self.0)(req)
2168    }
2169}
2170
2171/// Returns an object that implements `Inspect` by calling `f` with the
2172/// inspection request.
2173pub fn adhoc<F>(f: F) -> Adhoc<F>
2174where
2175    F: Fn(Request<'_>),
2176{
2177    Adhoc(f)
2178}
2179
2180/// Returns an object that implements `InspectMut` by calling `f` with the
2181/// inspection request.
2182pub fn adhoc_mut<F>(f: F) -> Adhoc<F>
2183where
2184    F: FnMut(Request<'_>),
2185{
2186    Adhoc(f)
2187}
2188
2189#[cfg(all(test, feature = "derive", feature = "initiate"))]
2190mod tests {
2191    use crate::AsBytes;
2192    use crate::AtomicMut;
2193    use crate::Error;
2194    use crate::Inspect;
2195    use crate::InspectMut;
2196    use crate::InspectionBuilder;
2197    use crate::Node;
2198    use crate::Request;
2199    use crate::SensitivityLevel;
2200    use crate::ValueKind;
2201    use crate::adhoc;
2202    use crate::adhoc_mut;
2203    use crate::inspect;
2204    use crate::update;
2205    use alloc::boxed::Box;
2206    use alloc::string::String;
2207    use alloc::string::ToString;
2208    use alloc::vec;
2209    use alloc::vec::Vec;
2210    use core::time::Duration;
2211    use expect_test::Expect;
2212    use expect_test::expect;
2213    use futures::FutureExt;
2214    use pal_async::DefaultDriver;
2215    use pal_async::async_test;
2216    use pal_async::timer::Instant;
2217    use pal_async::timer::PolledTimer;
2218
2219    fn expected_node(node: Node, expect: Expect) -> Node {
2220        expect.assert_eq(&std::format!("{node:#}|{}", node.json()));
2221        node
2222    }
2223
2224    async fn inspect_async(
2225        driver: &DefaultDriver,
2226        path: &str,
2227        depth: Option<usize>,
2228        timeout: Duration,
2229        obj: impl InspectMut,
2230    ) -> Node {
2231        let deadline = Instant::now() + timeout;
2232        let mut result = InspectionBuilder::new(path).depth(depth).inspect(obj);
2233        let mut timer = PolledTimer::new(driver);
2234        futures::select! { // race semantics
2235            _ = result.resolve().fuse() => {}
2236            _ = timer.sleep_until(deadline).fuse() => {}
2237        };
2238        result.results()
2239    }
2240
2241    async fn inspect_async_expect(
2242        driver: &DefaultDriver,
2243        path: &str,
2244        depth: Option<usize>,
2245        timeout: Duration,
2246        obj: impl InspectMut,
2247        expect: Expect,
2248    ) -> Node {
2249        expected_node(
2250            inspect_async(driver, path, depth, timeout, obj).await,
2251            expect,
2252        )
2253    }
2254
2255    fn inspect_sync(path: &str, depth: Option<usize>, obj: impl InspectMut) -> Node {
2256        let mut result = InspectionBuilder::new(path).depth(depth).inspect(obj);
2257        result.resolve().now_or_never();
2258        result.results()
2259    }
2260
2261    fn inspect_sync_expect(
2262        path: &str,
2263        depth: Option<usize>,
2264        obj: impl InspectMut,
2265        expect: Expect,
2266    ) -> Node {
2267        expected_node(inspect_sync(path, depth, obj), expect)
2268    }
2269
2270    #[derive(Default)]
2271    struct Foo {
2272        xx: u32,
2273        xy: bool,
2274        xz: Vec<Foo>,
2275    }
2276
2277    impl Inspect for Foo {
2278        fn inspect(&self, req: Request<'_>) {
2279            let mut resp = req.respond();
2280            resp.field("xx", self.xx)
2281                .field("xy", self.xy)
2282                .fields("", self.xz.iter().enumerate());
2283        }
2284    }
2285
2286    #[test]
2287    fn test() {
2288        let f = Foo {
2289            xx: 1,
2290            xy: true,
2291            xz: vec![
2292                Foo {
2293                    xx: 3,
2294                    xy: false,
2295                    xz: vec![],
2296                },
2297                Foo {
2298                    xx: 5,
2299                    xy: true,
2300                    xz: vec![],
2301                },
2302            ],
2303        };
2304        inspect_sync_expect(
2305            "",
2306            None,
2307            &f,
2308            expect!([r#"
2309                {
2310                    0: {
2311                        xx: 3,
2312                        xy: false,
2313                    },
2314                    1: {
2315                        xx: 5,
2316                        xy: true,
2317                    },
2318                    xx: 1,
2319                    xy: true,
2320                }|{"0":{"xx":3,"xy":false},"1":{"xx":5,"xy":true},"xx":1,"xy":true}"#]),
2321        );
2322    }
2323
2324    #[async_test]
2325    async fn test_deferred(driver: DefaultDriver) {
2326        inspect_async_expect(
2327            &driver,
2328            "",
2329            None,
2330            Duration::from_secs(1),
2331            adhoc(|req| {
2332                let foo = req.defer();
2333                std::thread::spawn(|| foo.inspect(&Foo::default()));
2334            }),
2335            expect!([r#"
2336                {
2337                    xx: 0,
2338                    xy: false,
2339                }|{"xx":0,"xy":false}"#]),
2340        )
2341        .await;
2342    }
2343
2344    #[async_test]
2345    async fn test_dropped(driver: DefaultDriver) {
2346        inspect_async_expect(
2347            &driver,
2348            "",
2349            None,
2350            Duration::from_secs(86400),
2351            adhoc(|req| {
2352                drop(req.defer());
2353            }),
2354            expect!([r#"error (unresolved)|{"$error":"unresolved"}"#]),
2355        )
2356        .await;
2357    }
2358
2359    #[test]
2360    fn test_path() {
2361        let mut obj = adhoc(|req| {
2362            req.respond().field("a", 1).child("b", |req| {
2363                req.respond().field("c", 2).field("d", 2).child("e", |req| {
2364                    req.respond();
2365                });
2366            });
2367        });
2368        inspect_sync_expect("a", None, &mut obj, expect!("1|1"));
2369        inspect_sync_expect("///a", None, &mut obj, expect!("1|1"));
2370        inspect_sync_expect(
2371            "b",
2372            None,
2373            &mut obj,
2374            expect!([r#"
2375                {
2376                    c: 2,
2377                    d: 2,
2378                    e: {},
2379                }|{"c":2,"d":2,"e":{}}"#]),
2380        );
2381        inspect_sync_expect("b/c", None, &mut obj, expect!("2|2"));
2382        inspect_sync_expect("b////c", None, &mut obj, expect!("2|2"));
2383        inspect_sync_expect(
2384            "b/c/",
2385            None,
2386            &mut obj,
2387            expect!([r#"error (not a directory)|{"$error":"not a directory"}"#]),
2388        );
2389        inspect_sync_expect(
2390            "b/c/x",
2391            None,
2392            &mut obj,
2393            expect!([r#"error (not a directory)|{"$error":"not a directory"}"#]),
2394        );
2395        inspect_sync_expect("b/e", None, &mut obj, expect!("{}|{}"));
2396        inspect_sync_expect("b/e/", None, &mut obj, expect!("{}|{}"));
2397        inspect_sync_expect("b/e///", None, &mut obj, expect!("{}|{}"));
2398        inspect_sync_expect(
2399            "b/f",
2400            None,
2401            &mut obj,
2402            expect!([r#"error (not found)|{"$error":"not found"}"#]),
2403        );
2404    }
2405
2406    #[async_test]
2407    async fn test_timeout(driver: DefaultDriver) {
2408        inspect_async_expect(
2409            &driver,
2410            "",
2411            None,
2412            Duration::from_millis(10),
2413            adhoc(|req| {
2414                let foo = req.defer();
2415                std::thread::spawn(|| {
2416                    std::thread::sleep(Duration::from_millis(250));
2417                    foo.inspect(&Foo::default())
2418                });
2419            }),
2420            expect!([r#"error (unresolved)|{"$error":"unresolved"}"#]),
2421        )
2422        .await;
2423    }
2424
2425    #[test]
2426    fn test_merge() {
2427        let mut obj = adhoc(|req| {
2428            req.respond().field("a", 1).merge(adhoc(|req| {
2429                req.respond().field("b", 2);
2430            }));
2431        });
2432
2433        inspect_sync_expect(
2434            "",
2435            None,
2436            &mut obj,
2437            expect!([r#"
2438                {
2439                    a: 1,
2440                    b: 2,
2441                }|{"a":1,"b":2}"#]),
2442        );
2443        inspect_sync_expect("a", None, &mut obj, expect!("1|1"));
2444        inspect_sync_expect("b", None, &mut obj, expect!("2|2"));
2445        inspect_sync_expect(
2446            "c",
2447            None,
2448            &mut obj,
2449            expect!([r#"error (not found)|{"$error":"not found"}"#]),
2450        );
2451    }
2452
2453    #[test]
2454    fn test_named_merge() {
2455        let mut obj = adhoc(|req| {
2456            req.respond()
2457                .field("a", 1)
2458                .field("a", 2)
2459                .child("x", |req| {
2460                    req.respond().field("b", 3).child("c", |req| {
2461                        req.respond().field("y", 4);
2462                    });
2463                })
2464                .child("x", |req| {
2465                    req.respond().field("b", 4).child("d", |req| {
2466                        req.respond().field("y", 5);
2467                    });
2468                });
2469        });
2470
2471        inspect_sync_expect(
2472            "",
2473            None,
2474            &mut obj,
2475            expect!([r#"
2476                {
2477                    a: 2,
2478                    x: {
2479                        b: 4,
2480                        c: {
2481                            y: 4,
2482                        },
2483                        d: {
2484                            y: 5,
2485                        },
2486                    },
2487                }|{"a":2,"x":{"b":4,"c":{"y":4},"d":{"y":5}}}"#]),
2488        );
2489        inspect_sync_expect(
2490            "x",
2491            None,
2492            &mut obj,
2493            expect!([r#"
2494                {
2495                    b: 4,
2496                    c: {
2497                        y: 4,
2498                    },
2499                    d: {
2500                        y: 5,
2501                    },
2502                }|{"b":4,"c":{"y":4},"d":{"y":5}}"#]),
2503        );
2504    }
2505
2506    #[test]
2507    fn test_update() {
2508        struct Foo {
2509            immut: u32,
2510            mut_: u32,
2511            child: Option<Box<Foo>>,
2512        }
2513
2514        impl InspectMut for Foo {
2515            fn inspect_mut(&mut self, req: Request<'_>) {
2516                let mut resp = req.respond();
2517                resp.field("immut", self.immut)
2518                    .field_mut("mut", &mut self.mut_)
2519                    .field_mut("child", &mut self.child);
2520            }
2521        }
2522
2523        let mut foo = Foo {
2524            immut: 1,
2525            mut_: 2,
2526            child: Some(Box::new(Foo {
2527                immut: 101,
2528                mut_: 102,
2529                child: None,
2530            })),
2531        };
2532        assert_eq!(
2533            update("immut", "12", &mut foo)
2534                .now_or_never()
2535                .unwrap()
2536                .unwrap_err(),
2537            Error::Immutable
2538        );
2539        assert_eq!(
2540            update("mut/", "4", &mut foo)
2541                .now_or_never()
2542                .unwrap()
2543                .unwrap_err(),
2544            Error::NotADirectory
2545        );
2546        assert_eq!(
2547            update("mut", "3", &mut foo)
2548                .now_or_never()
2549                .unwrap()
2550                .unwrap()
2551                .kind,
2552            ValueKind::Unsigned(3)
2553        );
2554        assert_eq!(
2555            update("//child/mut", "103", &mut foo)
2556                .now_or_never()
2557                .unwrap()
2558                .unwrap()
2559                .kind,
2560            ValueKind::Unsigned(103)
2561        );
2562        assert_eq!(foo.mut_, 3);
2563        assert_eq!(foo.child.as_ref().unwrap().mut_, 103);
2564    }
2565
2566    #[test]
2567    fn test_nest() {
2568        let mut obj = adhoc(|req| {
2569            req.respond().field("x/a/b", 1).field("x/a/c", 2);
2570        });
2571
2572        inspect_sync_expect(
2573            "",
2574            None,
2575            &mut obj,
2576            expect!([r#"
2577                {
2578                    x: {
2579                        a: {
2580                            b: 1,
2581                            c: 2,
2582                        },
2583                    },
2584                }|{"x":{"a":{"b":1,"c":2}}}"#]),
2585        );
2586        inspect_sync_expect(
2587            "x/a",
2588            None,
2589            &mut obj,
2590            expect!([r#"
2591                {
2592                    b: 1,
2593                    c: 2,
2594                }|{"b":1,"c":2}"#]),
2595        );
2596        inspect_sync_expect(
2597            "x",
2598            Some(0),
2599            &mut obj,
2600            expect!([r#"
2601                {
2602                    a: _,
2603                }|{"a":null}"#]),
2604        );
2605        inspect_sync_expect(
2606            "x",
2607            Some(2),
2608            &mut obj,
2609            expect!([r#"
2610                {
2611                    a: {
2612                        b: 1,
2613                        c: 2,
2614                    },
2615                }|{"a":{"b":1,"c":2}}"#]),
2616        );
2617    }
2618
2619    #[test]
2620    fn test_depth() {
2621        let mut obj = adhoc(|req| {
2622            req.respond()
2623                .field("1a", 0)
2624                .field("1b", 0)
2625                .field("1c", 0)
2626                .child("1d", |req| {
2627                    req.respond().field("2a", 0).child("2b", |req| {
2628                        req.respond().child("3a", |req| {
2629                            req.respond().field_with("xxx", || -> u32 { panic!() });
2630                        });
2631                    });
2632                })
2633                .field("1d/2b/3b", 0);
2634        });
2635
2636        inspect_sync_expect(
2637            "1d",
2638            Some(0),
2639            &mut obj,
2640            expect!([r#"
2641                {
2642                    2a: 0,
2643                    2b: _,
2644                }|{"2a":0,"2b":null}"#]),
2645        );
2646        inspect_sync_expect(
2647            "",
2648            Some(0),
2649            &mut obj,
2650            expect!([r#"
2651                {
2652                    1a: 0,
2653                    1b: 0,
2654                    1c: 0,
2655                    1d: _,
2656                }|{"1a":0,"1b":0,"1c":0,"1d":null}"#]),
2657        );
2658        inspect_sync_expect(
2659            "",
2660            Some(1),
2661            &mut obj,
2662            expect!([r#"
2663                {
2664                    1a: 0,
2665                    1b: 0,
2666                    1c: 0,
2667                    1d: {
2668                        2a: 0,
2669                        2b: _,
2670                    },
2671                }|{"1a":0,"1b":0,"1c":0,"1d":{"2a":0,"2b":null}}"#]),
2672        );
2673    }
2674
2675    #[test]
2676    fn test_hex() {
2677        let mut obj = adhoc(|req| {
2678            req.respond().hex("a", 0x1234i32).hex("b", 0x5678u32);
2679        });
2680        inspect_sync_expect(
2681            "",
2682            Some(0),
2683            &mut obj,
2684            expect!([r#"
2685                {
2686                    a: 0x1234,
2687                    b: 0x5678,
2688                }|{"a":"0x1234","b":"0x5678"}"#]),
2689        );
2690    }
2691
2692    #[test]
2693    fn test_binary() {
2694        let mut obj = adhoc(|req| {
2695            req.respond()
2696                .binary("a", 0b1001000110100i32)
2697                .binary("b", 0b1101010101111000u32);
2698        });
2699        inspect_sync_expect(
2700            "",
2701            Some(0),
2702            &mut obj,
2703            expect!([r#"
2704                {
2705                    a: 0b1001000110100,
2706                    b: 0b1101010101111000,
2707                }|{"a":"0b1001000110100","b":"0b1101010101111000"}"#]),
2708        );
2709    }
2710
2711    #[test]
2712    fn test_since() {
2713        let mut n = 500_u32;
2714        let mut b = false;
2715        let mut obj = adhoc_mut(|req| {
2716            req.respond()
2717                .counter("c", n)
2718                .field("f", n)
2719                .child("d", |req| {
2720                    let mut resp = req.respond();
2721                    if !b {
2722                        resp.field("1_a", true).field("1_b", true);
2723                    } else {
2724                        resp.field("1_c", true);
2725                    }
2726                    resp.field("2", true).counter("3", n);
2727                    if !b {
2728                        resp.field("4_a", true);
2729                    } else {
2730                        resp.field("4_b", true).field("4_c", true);
2731                    }
2732                });
2733            n += 100;
2734            b = true;
2735        });
2736        let old = inspect_sync("", Some(1), &mut obj);
2737        let new = inspect_sync("", Some(1), &mut obj);
2738
2739        let diff = new.since(&old, Duration::from_secs(2));
2740
2741        expected_node(
2742            diff,
2743            expect!([r#"
2744                {
2745                    c: 50,
2746                    d: {
2747                        1_c: true,
2748                        2: true,
2749                        3: 50,
2750                        4_b: true,
2751                        4_c: true,
2752                    },
2753                    f: 600,
2754                }|{"c":50,"d":{"1_c":true,"2":true,"3":50,"4_b":true,"4_c":true},"f":600}"#]),
2755        );
2756    }
2757
2758    #[test]
2759    fn test_bytes() {
2760        inspect_sync_expect(
2761            "",
2762            Some(1),
2763            &AsBytes([0xab, 0xcd, 0xef]),
2764            expect!([r#"<abcdef>|"q83v""#]),
2765        );
2766    }
2767
2768    #[test]
2769    fn test_sensitivity() {
2770        let mut obj = adhoc(|req| {
2771            req.respond()
2772                .sensitivity_field("1a", SensitivityLevel::Safe, 0)
2773                .sensitivity_field("1b", SensitivityLevel::Unspecified, 0)
2774                .sensitivity_field("1c", SensitivityLevel::Sensitive, 0)
2775                .sensitivity_child("1d", SensitivityLevel::Safe, |req| {
2776                    req.respond()
2777                        .sensitivity_field("2a", SensitivityLevel::Sensitive, 0)
2778                        .sensitivity_child("2b", SensitivityLevel::Safe, |req| {
2779                            req.respond().sensitivity_child(
2780                                "3a",
2781                                SensitivityLevel::Sensitive,
2782                                |req| {
2783                                    req.respond().sensitivity_field(
2784                                        "4a",
2785                                        SensitivityLevel::Safe,
2786                                        0,
2787                                    );
2788                                },
2789                            );
2790                        });
2791                })
2792                .sensitivity_field("1d/2b/3b", SensitivityLevel::Unspecified, 0)
2793                .sensitivity_child("", SensitivityLevel::Sensitive, |req| {
2794                    req.respond()
2795                        .sensitivity_field("1e", SensitivityLevel::Safe, 0);
2796                });
2797        });
2798
2799        fn inspect_sync(
2800            path: &str,
2801            sensitivity: Option<SensitivityLevel>,
2802            obj: impl InspectMut,
2803        ) -> Node {
2804            let mut result = InspectionBuilder::new(path)
2805                .sensitivity(sensitivity)
2806                .inspect(obj);
2807            result.resolve().now_or_never();
2808            result.results()
2809        }
2810
2811        expected_node(
2812            inspect_sync("", Some(SensitivityLevel::Safe), &mut obj),
2813            expect!([r#"
2814                {
2815                    1a: 0,
2816                    1d: {
2817                        2b: {},
2818                    },
2819                }|{"1a":0,"1d":{"2b":{}}}"#]),
2820        );
2821        expected_node(
2822            inspect_sync("", Some(SensitivityLevel::Unspecified), &mut obj),
2823            expect!([r#"
2824                {
2825                    1a: 0,
2826                    1b: 0,
2827                    1d: {
2828                        2b: {
2829                            3b: 0,
2830                        },
2831                    },
2832                }|{"1a":0,"1b":0,"1d":{"2b":{"3b":0}}}"#]),
2833        );
2834        expected_node(
2835            inspect_sync("", Some(SensitivityLevel::Sensitive), &mut obj),
2836            expect!([r#"
2837                {
2838                    1a: 0,
2839                    1b: 0,
2840                    1c: 0,
2841                    1d: {
2842                        2a: 0,
2843                        2b: {
2844                            3a: {
2845                                4a: 0,
2846                            },
2847                            3b: 0,
2848                        },
2849                    },
2850                    1e: 0,
2851                }|{"1a":0,"1b":0,"1c":0,"1d":{"2a":0,"2b":{"3a":{"4a":0},"3b":0}},"1e":0}"#]),
2852        );
2853        expected_node(
2854            inspect_sync("", None, &mut obj),
2855            expect!([r#"
2856                {
2857                    1a: 0,
2858                    1b: 0,
2859                    1c: 0,
2860                    1d: {
2861                        2a: 0,
2862                        2b: {
2863                            3a: {
2864                                4a: 0,
2865                            },
2866                            3b: 0,
2867                        },
2868                    },
2869                    1e: 0,
2870                }|{"1a":0,"1b":0,"1c":0,"1d":{"2a":0,"2b":{"3a":{"4a":0},"3b":0}},"1e":0}"#]),
2871        );
2872        expected_node(
2873            inspect_sync("", Some(SensitivityLevel::Sensitive), &mut obj),
2874            expect!([r#"
2875                {
2876                    1a: 0,
2877                    1b: 0,
2878                    1c: 0,
2879                    1d: {
2880                        2a: 0,
2881                        2b: {
2882                            3a: {
2883                                4a: 0,
2884                            },
2885                            3b: 0,
2886                        },
2887                    },
2888                    1e: 0,
2889                }|{"1a":0,"1b":0,"1c":0,"1d":{"2a":0,"2b":{"3a":{"4a":0},"3b":0}},"1e":0}"#]),
2890        );
2891    }
2892
2893    #[test]
2894    fn test_derive() {
2895        use std::marker::PhantomData;
2896
2897        #[derive(InspectMut)]
2898        struct Derived {
2899            dec: u32,
2900            #[inspect(hex, rename = "hex")]
2901            val2: u64,
2902            #[inspect(binary)]
2903            bin: u8,
2904            inner: Inner,
2905            #[inspect(mut)]
2906            inner_mut: InnerMut,
2907            #[inspect(flatten)]
2908            flattened: Inner,
2909            #[inspect(skip)]
2910            _skip: bool,
2911            t: Transparent,
2912            t2: Newtype,
2913            #[inspect(format = "{:02x}")]
2914            minute: u8,
2915            #[inspect(debug)]
2916            debug: (),
2917            #[inspect(display)]
2918            display: u8,
2919            var: Enum,
2920            ignored: Ignored,
2921            tr1: Tr1,
2922            tr2: Tr2,
2923            unnamed: Unnamed,
2924            #[inspect(iter_by_index, hex)]
2925            hex_array: [u8; 4],
2926            hex_inner: HexInner,
2927            #[inspect(hex)]
2928            inner_as_hex: Inner,
2929        }
2930
2931        #[derive(Clone, Inspect)]
2932        struct Inner {
2933            val: u32,
2934        }
2935
2936        #[derive(InspectMut)]
2937        struct InnerMut {
2938            val: String,
2939        }
2940
2941        #[derive(Inspect)]
2942        #[inspect(hex)]
2943        struct HexInner {
2944            val: u32,
2945        }
2946
2947        #[derive(Inspect)]
2948        #[inspect(transparent)]
2949        struct Transparent {
2950            inner: Inner,
2951        }
2952
2953        #[derive(Inspect)]
2954        struct Unnamed(u8, i32);
2955
2956        #[derive(Inspect)]
2957        #[inspect(transparent)]
2958        struct Newtype(Inner, PhantomData<()>);
2959
2960        #[derive(Inspect)]
2961        #[inspect(transparent(hex))]
2962        struct Tr1(u32, PhantomData<()>);
2963
2964        #[derive(Inspect)]
2965        #[inspect(transparent)]
2966        struct Tr2(#[inspect(debug)] ());
2967
2968        #[derive(Inspect)]
2969        #[expect(dead_code)]
2970        enum Enum {
2971            Foo,
2972            BarBaz,
2973            #[inspect(rename = "brother")]
2974            Other,
2975        }
2976
2977        #[derive(Inspect)]
2978        #[inspect(skip)]
2979        struct Ignored {
2980            _x: fn(),
2981        }
2982
2983        let mut obj = Derived {
2984            dec: 5,
2985            val2: 4,
2986            bin: 3,
2987            inner: Inner { val: 3 },
2988            inner_mut: InnerMut {
2989                val: "hi".to_string(),
2990            },
2991            _skip: true,
2992            flattened: Inner { val: 8 },
2993            t: Transparent {
2994                inner: Inner { val: 1 },
2995            },
2996            t2: Newtype(Inner { val: 2 }, PhantomData),
2997            debug: (),
2998            display: 10,
2999            minute: 7,
3000            var: Enum::BarBaz,
3001            ignored: Ignored { _x: || () },
3002            tr1: Tr1(10, PhantomData),
3003            tr2: Tr2(()),
3004            unnamed: Unnamed(5, -83),
3005            hex_array: [100, 101, 102, 103],
3006            hex_inner: HexInner { val: 100 },
3007            inner_as_hex: Inner { val: 100 },
3008        };
3009
3010        inspect_sync_expect(
3011            "",
3012            None,
3013            &mut obj,
3014            expect!([r#"
3015                {
3016                    bin: 0b11,
3017                    debug: "()",
3018                    dec: 5,
3019                    display: "10",
3020                    hex: 0x4,
3021                    hex_array: {
3022                        0: 0x64,
3023                        1: 0x65,
3024                        2: 0x66,
3025                        3: 0x67,
3026                    },
3027                    hex_inner: {
3028                        val: 0x64,
3029                    },
3030                    inner: {
3031                        val: 3,
3032                    },
3033                    inner_as_hex: {
3034                        val: 0x64,
3035                    },
3036                    inner_mut: {
3037                        val: "hi",
3038                    },
3039                    minute: "07",
3040                    t: {
3041                        val: 1,
3042                    },
3043                    t2: {
3044                        val: 2,
3045                    },
3046                    tr1: 0xa,
3047                    tr2: "()",
3048                    unnamed: {
3049                        0: 5,
3050                        1: -83,
3051                    },
3052                    val: 8,
3053                    var: "bar_baz",
3054                }|{"bin":"0b11","debug":"()","dec":5,"display":"10","hex":"0x4","hex_array":{"0":"0x64","1":"0x65","2":"0x66","3":"0x67"},"hex_inner":{"val":"0x64"},"inner":{"val":3},"inner_as_hex":{"val":"0x64"},"inner_mut":{"val":"hi"},"minute":"07","t":{"val":1},"t2":{"val":2},"tr1":"0xa","tr2":"()","unnamed":{"0":5,"1":-83},"val":8,"var":"bar_baz"}"#]),
3055        );
3056    }
3057
3058    #[test]
3059    fn test_derive_enum() {
3060        #[expect(dead_code)]
3061        #[derive(Inspect)]
3062        enum EmptyUnitEmum {}
3063
3064        #[expect(dead_code)]
3065        #[derive(Inspect)]
3066        #[inspect(untagged)]
3067        enum EmptyUntaggedEmum {}
3068
3069        #[expect(dead_code)]
3070        #[derive(Inspect)]
3071        enum UnitEnum {
3072            A,
3073            B,
3074            C,
3075        }
3076
3077        inspect_sync_expect("", None, &UnitEnum::B, expect!([r#""b"|"b""#]));
3078
3079        #[expect(dead_code)]
3080        #[derive(Inspect)]
3081        #[inspect(tag = "tag")]
3082        enum TaggedEnum {
3083            A { x: u32 },
3084            B(#[inspect(rename = "y")] bool),
3085        }
3086
3087        inspect_sync_expect(
3088            "",
3089            None,
3090            &TaggedEnum::B(true),
3091            expect!([r#"
3092                {
3093                    tag: "b",
3094                    y: true,
3095                }|{"tag":"b","y":true}"#]),
3096        );
3097
3098        #[expect(dead_code)]
3099        #[derive(Inspect)]
3100        #[inspect(external_tag)]
3101        enum ExternallyTaggedEnum {
3102            A {
3103                x: u32,
3104            },
3105            B(#[inspect(rename = "y")] bool),
3106            #[inspect(transparent)]
3107            C(u32),
3108        }
3109
3110        inspect_sync_expect(
3111            "",
3112            None,
3113            &ExternallyTaggedEnum::B(true),
3114            expect!([r#"
3115                {
3116                    b: {
3117                        y: true,
3118                    },
3119                }|{"b":{"y":true}}"#]),
3120        );
3121
3122        inspect_sync_expect(
3123            "",
3124            None,
3125            &ExternallyTaggedEnum::C(5),
3126            expect!([r#"
3127                {
3128                    c: 5,
3129                }|{"c":5}"#]),
3130        );
3131
3132        #[expect(dead_code)]
3133        #[derive(Inspect)]
3134        #[inspect(untagged)]
3135        enum UntaggedEnum {
3136            A { x: u32 },
3137            B(#[inspect(rename = "y")] bool),
3138        }
3139
3140        inspect_sync_expect(
3141            "",
3142            None,
3143            &UntaggedEnum::B(true),
3144            expect!([r#"
3145                {
3146                    y: true,
3147                }|{"y":true}"#]),
3148        );
3149    }
3150
3151    #[test]
3152    fn test_derive_extra() {
3153        #[derive(Inspect)]
3154        #[inspect(extra = "Foo::inspect_extra")]
3155        struct Foo {
3156            x: u32,
3157            y: u32,
3158        }
3159
3160        impl Foo {
3161            fn inspect_extra(&self, resp: &mut inspect::Response<'_>) {
3162                resp.field("sum", self.x + self.y);
3163            }
3164        }
3165
3166        inspect_sync_expect(
3167            "",
3168            None,
3169            &Foo { x: 2, y: 5 },
3170            expect!([r#"
3171                {
3172                    sum: 7,
3173                    x: 2,
3174                    y: 5,
3175                }|{"sum":7,"x":2,"y":5}"#]),
3176        );
3177    }
3178
3179    #[test]
3180    fn test_derive_sensitivity() {
3181        #[derive(Inspect)]
3182        struct Foo {
3183            #[inspect(safe)]
3184            a: u32,
3185            b: u32,
3186            #[inspect(sensitive)]
3187            c: u32,
3188            #[inspect(safe)]
3189            d: Bar,
3190        }
3191        #[derive(Inspect)]
3192        struct Bar {
3193            #[inspect(sensitive)]
3194            a: u32,
3195            #[inspect(safe)]
3196            b: Baz,
3197        }
3198        #[derive(Inspect)]
3199        struct Baz {
3200            #[inspect(sensitive)]
3201            a: Qux,
3202            b: u32,
3203        }
3204        #[derive(Inspect)]
3205        struct Qux {
3206            #[inspect(safe)]
3207            a: u32,
3208        }
3209
3210        fn inspect_sync(
3211            path: &str,
3212            sensitivity: Option<SensitivityLevel>,
3213            obj: impl Inspect,
3214        ) -> Node {
3215            let mut result = InspectionBuilder::new(path)
3216                .sensitivity(sensitivity)
3217                .inspect(&obj);
3218            result.resolve().now_or_never();
3219            result.results()
3220        }
3221
3222        let obj = Foo {
3223            a: 0,
3224            b: 0,
3225            c: 0,
3226            d: Bar {
3227                a: 0,
3228                b: Baz {
3229                    a: Qux { a: 0 },
3230                    b: 0,
3231                },
3232            },
3233        };
3234
3235        expected_node(
3236            inspect_sync("", Some(SensitivityLevel::Safe), &obj),
3237            expect!([r#"
3238                {
3239                    a: 0,
3240                    d: {
3241                        b: {},
3242                    },
3243                }|{"a":0,"d":{"b":{}}}"#]),
3244        );
3245        expected_node(
3246            inspect_sync("", Some(SensitivityLevel::Unspecified), &obj),
3247            expect!([r#"
3248                {
3249                    a: 0,
3250                    b: 0,
3251                    d: {
3252                        b: {
3253                            b: 0,
3254                        },
3255                    },
3256                }|{"a":0,"b":0,"d":{"b":{"b":0}}}"#]),
3257        );
3258        let node = expected_node(
3259            inspect_sync("", Some(SensitivityLevel::Sensitive), &obj),
3260            expect!([r#"
3261                {
3262                    a: 0,
3263                    b: 0,
3264                    c: 0,
3265                    d: {
3266                        a: 0,
3267                        b: {
3268                            a: {
3269                                a: 0,
3270                            },
3271                            b: 0,
3272                        },
3273                    },
3274                }|{"a":0,"b":0,"c":0,"d":{"a":0,"b":{"a":{"a":0},"b":0}}}"#]),
3275        );
3276        assert_eq!(
3277            node,
3278            inspect_sync("", Some(SensitivityLevel::Sensitive), &obj)
3279        );
3280    }
3281
3282    #[test]
3283    fn test_inherit_sensitivity() {
3284        fn inspect_sync(
3285            path: &str,
3286            sensitivity: Option<SensitivityLevel>,
3287            obj: impl Inspect,
3288        ) -> Node {
3289            let mut result = InspectionBuilder::new(path)
3290                .sensitivity(sensitivity)
3291                .inspect(&obj);
3292            result.resolve().now_or_never();
3293            result.results()
3294        }
3295
3296        #[derive(Inspect)]
3297        struct Indexed {
3298            #[inspect(safe, iter_by_index)]
3299            a: Vec<Baz>,
3300        }
3301
3302        #[derive(Inspect)]
3303        struct Baz {
3304            #[inspect(safe)]
3305            b: u32,
3306            #[inspect(safe)]
3307            c: Qux,
3308        }
3309
3310        #[derive(Inspect)]
3311        struct Qux {
3312            #[inspect(sensitive)]
3313            d: u32,
3314            e: u32,
3315            #[inspect(safe)]
3316            f: u32,
3317        }
3318
3319        let obj = Indexed {
3320            a: vec![
3321                Baz {
3322                    b: 0,
3323                    c: Qux { d: 0, e: 0, f: 0 },
3324                },
3325                Baz {
3326                    b: 0,
3327                    c: Qux { d: 0, e: 0, f: 0 },
3328                },
3329            ],
3330        };
3331
3332        expected_node(
3333            inspect_sync("", Some(SensitivityLevel::Safe), &obj),
3334            expect!([r#"
3335                {
3336                    a: {
3337                        0: {
3338                            b: 0,
3339                            c: {
3340                                f: 0,
3341                            },
3342                        },
3343                        1: {
3344                            b: 0,
3345                            c: {
3346                                f: 0,
3347                            },
3348                        },
3349                    },
3350                }|{"a":{"0":{"b":0,"c":{"f":0}},"1":{"b":0,"c":{"f":0}}}}"#]),
3351        );
3352    }
3353
3354    /// Test that you can update via AtomicMut.
3355    #[test]
3356    fn test_atomic_mut() {
3357        let mut v = core::sync::atomic::AtomicBool::new(false);
3358        let obj = AtomicMut(&v);
3359        update("", "true", &obj).now_or_never().unwrap().unwrap();
3360        assert!(*v.get_mut());
3361    }
3362}