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