Skip to main content

inspect/
lib.rs

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