// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! This module implements the inspect framework, which allows an object to
//! expose its state for diagnostics purposes as a hierarchy. All such subtrees
//! are linked together into a single tree for the process, and clients can
//! query information from arbitrary portions of the tree by path.
//!
//! To participate in the tree, an object implements the [`Inspect`] trait and
//! arranges for itself to be inspected, usually automatically via an existing
//! parent-child relationship.
//!
//! For most cases, users should use the derive version of [`Inspect`](derive@Inspect)
//! as this will automatically update the implementation as new fields are added.

#![no_std]
#![forbid(unsafe_code)]

extern crate alloc;

// Enable `std` for features that depend on it.
//
// Don't just have these features depend on the "std" feature, because they
// might be able to remove their `std` dependency in the future. (Most of them
// are blocked on `core::error::Error` not being stable yet.)
#[cfg(any(feature = "std", feature = "initiate", feature = "defer",))]
extern crate std;

#[cfg(feature = "defer")]
mod defer;
#[cfg(feature = "initiate")]
mod initiate;

#[cfg(all(test, feature = "derive", feature = "initiate"))]
extern crate self as inspect;

#[cfg(feature = "defer")]
pub use defer::*;
#[cfg(feature = "initiate")]
pub use initiate::*;

/// Derives the [`Inspect`] trait for a struct or enum.
///
/// [`InspectMut`](derive@InspectMut) can also be derived using the same
/// attributes.
///
/// # Structs
///
/// By default, the macro implements [`Inspect`] or [`InspectMut`] by calling
/// [`Request::respond`] and then calling [`Response::field`] on each field by
/// reference. You can use attributes to control this behavior.
///
/// Attributes are comma separated and nested inside the `inspect` attribute,
/// e.g. `#[inspect(hex, rename = "foo")]`.
///
/// If you derive `Inspect` on a struct with the `bitfield` attribute, then it
/// is assumed to be from the `bitfield-struct` crate. The derived
/// implementation will have a field for each bitfield field, plus one called
/// `raw` with the raw value in hexadecimal. Be sure to put the `derive`
/// attribute above the `bitfield` attribute for this to work.
///
/// ## Struct attributes
///
/// ### `transparent(attrs)`
///
/// Forward the request to a single field of the struct.
///
/// `(attrs)` is optional. If provided, these are attributes to apply to the
/// field. This is useful when the field is generated by another macro (such as
/// `bitflags!`) and you cannot put attributes on it.
///
/// This attribute requires that there be exactly one non-skipped field. Fields
/// of type [`PhantomData`](core::marker::PhantomData) are automatically skipped.
///
/// Note that it is not sufficient to mark any extraneous fields' _types_ with
/// `skip`--you must mark the individual fields with the `skip` attribute.
///
/// ### `skip`
///
/// Skip this type when inspected so that they do not appear in inspect output.
/// Calls [`Request::ignore`].
///
/// ### `with`
///
/// Wraps the type with `expr`, so that the inspect implementation is deferred
/// to `expr(&field)`.
///
/// `expr` is often a type constructor such as [`AsDisplay`] or [`AsDebug`] (but
/// note that there are shorthand attributes `display` and `debug` for these
/// types).
///
/// ### `display`
///
/// Inspect the type by formatting it as a string, using the type's [`ToString`]
/// implementation.
///
/// Usually implementing [`Inspect`] for the type should be preferred to this in
/// order to preserve structured data. However, this may be useful if the
/// display string is the canonical way to view a field's data.
///
/// This is equivalent to `with = "inspect::AsDisplay"`. See [`AsDisplay`].
///
/// ### `debug`
///
/// Inspect the type by formatting it as a string, using the type's [`Debug`]
/// implementation.
///
/// Usually implementing [`Inspect`] for the type should be preferred to this in
/// order to preserve structured data. However, this may be useful if the debug
/// string is the canonical way to view a field's data, as with `bitfields!` or
/// `open_enum!`.
///
/// This is equivalent to `with = "inspect::AsDebug"`. See [`AsDebug`].
///
/// ### `extra = "expr"`
///
/// In addition to inspecting each field as normal, call `expr(self, resp)`.
/// This allows you to add synthetic fields to the struct without manually
/// implementing inspect for all fields.
///
/// ```no_run
/// # use inspect::Inspect;
/// #[derive(Inspect)]
/// #[inspect(extra = "Foo::inspect_extra")]
/// struct Foo {
///     x: u32,
///     y: u32,
/// }
///
/// impl Foo {
///     fn inspect_extra(&self, resp: &mut inspect::Response<'_>) {
///         resp.field("sum", self.x + self.y);
///     }
/// }
/// ```
///
/// ### `hex`
///
/// Use hexadecimal formatting by default for all fields, recursively.
///
/// ## Field attributes
///
/// ### `rename = "custom_name"`
///
/// Set the name of the field to "custom_name". By default, fields have the same
/// name as their Rust identifier.
///
/// ### `field`
///
/// Inspect the field using its [`Inspect`] implementation, by calling
/// [`Response::field`] with `&field`.
///
/// This is the default.
///
/// ### `with = "expr"`
///
/// Wraps the field with `expr`, so that `expr(&field)` is the inspected value.
/// This is useful when the field cannot implement `Inspect`, but you can wrap
/// it in an object that can.
///
/// `expr` is often a type constructor such as [`AsDisplay`] or [`AsDebug`] (but
/// note that there are shorthand attributes `display` and `debug` for these
/// types).
///
/// This can also be used to implement helper functions that implement
/// [`Inspect`] to allow complex types to use the the derive macro.
///
/// #### Examples
/// The following structure has a field that is not normally inspectable, but we
/// can use the derive macro with a helper pattern of making a new helper
/// function, along with the `with` attribute.
///
///
/// ```no_run
/// # use inspect::Inspect;
/// struct NotInspectable {
///     complex_field: u64 // use your imaginatation to pretend this is a complex field
/// }
///
/// #[derive(Inspect)]
/// struct Foo {
///     #[inspect(with = "inspect_awesome_data")]
///     awesome_data: NotInspectable,
///     data: String,
/// }
///
/// pub fn inspect_awesome_data(id: &NotInspectable) -> impl Inspect {
///     // Do some complex field transformation to a string here...
///     format!("{}, {:x}", (id.complex_field * 42), id.complex_field)
///     // Since String via str has an implementation for Inspect, we can return the value directly.
/// }
///
/// // In general, inspect helper functions would be defined as the following in another sub module and used
/// // as `[inspect(with = "inspect_helpers::awesome_data")]` but rustdoc limitations prevent this.
/// // mod inspect_helpers {
/// //     use super::*;
/// //
/// //     pub(super) fn awesome_data(id: &NotInspectable) -> impl Inspect { ... }
/// // }
/// ```
///
/// ### `display`
///
/// Inspect the field by formatting it as a string, using the field's
/// [`ToString`] implementation.
///
/// This is equivalent to `with = "inspect::AsDisplay"`. See [`AsDisplay`].
///
/// ### `debug`
///
/// Inspect the field by formatting it as a string, using the field's
/// [`core::fmt::Debug`] implementation.
///
/// In general, implementing [`Inspect`] for the field should be preferred to
/// this in order to preserve structured data.
///
/// This is equivalent to `with = "inspect::AsDebug"`. See [`AsDebug`].
///
/// ### `format = "format"`
///
/// Inspect the field by formatting it as a string, using the provided format.
///
/// For example, `format = "id{:02x}"` might be useful on a field that
/// represents a 2-digit hex identifier.
///
/// ### `hex`
///
/// Inspect the field, including all children, recursively, with hexadecimal
/// formatting.
///
/// ### `binary`
///
/// Inspect the field, including all children, recursively, with binary
/// formatting.
///
/// ### `bytes`
///
/// Inspect the field as a list of bytes. The field must be iterable with an
/// item type of `u8` or `&u8`.
///
/// ### `flatten`
///
/// Merge the contents of the field into this inspection node, without an
/// intermediate child node. Calls [`Response::merge`] with `&field`.
///
/// This is useful when your struct is split into an outer and inner type that
/// are logically the same type from a diagnostics perspective. In this case,
/// you probably would not want to expose the implementation detail of the inner
/// type.
///
/// ### `iter_by_key`
///
/// Inspects a list of items, iterating them by calling
/// [`into_iter()`](IntoIterator::into_iter) on the field. Each item must have
/// type `(K, V)`, where `K: Display` and `V: Inspect`. `K` is used as the field
/// name.
///
/// ### `iter_by_index`
///
/// Inspects a list of items, enumerating them by calling
/// [`into_iter()`](IntoIterator::into_iter) on the field. Each item must have
/// type `V: Inspect`. The field name is the index in the enumeration, starting
/// with `0`.
///
/// #### Example
///
/// ```no_run
/// # use inspect::Inspect;
/// # use std::sync::Arc;
/// #[derive(Inspect)]
/// struct Foo {
///     id: u32,
///     #[inspect(flatten)]
///     inner: Arc<Inner>,
/// }
///
/// #[derive(Inspect)]
/// struct Inner {
///     state: String,
/// }
/// ```
///
/// ### `skip`
///
/// Skip inspecting this field.
///
/// ### `mut`
///
/// Pass the field as a mutable reference to the appropriate method so that its
/// [`InspectMut`] implementation is called instead of its [`Inspect`]
/// implementation.
///
/// This is only valid in uses of the [`InspectMut`](derive@InspectMut) derive
/// macro.
///
/// This can be used in conjunction with other attributes:
///
/// * `field`: calls [`Response::field_mut`] with `&mut field`.
/// * `flatten`: calls [`Response::merge`] with `&mut field`.
/// * `transparent` (struct attribute): calls [`InspectMut::inspect_mut`] on the
///   sole unskipped field.
/// ## Example
///
/// ```no_run
/// # use inspect::{Inspect, InspectMut};
/// # use std::sync::Arc;
/// # use std::path::PathBuf;
/// #[derive(InspectMut)]
/// struct Outer {
///     #[inspect(hex)]
///     id: u32,                // will be displayed as hex
///     count: usize,
///     #[inspect(mut)]
///     max_buffers: usize,     // can be changed via `update()`
///     #[inspect(skip)]
///     signal: Box<dyn Send>,  // won't be present in inspect output
///     #[inspect(flatten)]
///     inner: Arc<Inner>,      // contents will be merged in
/// }
///
/// #[derive(Inspect)]
/// struct Inner {
///     #[inspect(format = "{:016x}")]
///     uuid: u128,             // will be displayed as a 0-padded hex string
/// }
/// ```
///
/// # Unit-only enums
///
/// The macro supports enums, with multiple different output formats:
///
/// By default, deriving `Inspect` or `InspectMut` on an enum only works if the
/// enum variants are all unit variants. In this case, the inspect output is a
/// string containing the name of the enum variant converted from the standard
/// `PascalCase` to `snake_case`.
///
///  For example:
///
/// ```no_run
/// # use inspect::Inspect;
/// #[derive(Inspect)]
/// enum State {
///     NotRunning, // will be displayed as "not_running"
///     Running,    // will be displayed as "running"
/// }
/// ```
///
/// For `InspectMut`, the enum variant name is parsed for update requests.
///
/// # Enums with fields
///
/// To derive `Inspect` for an enum whose variants might contain fields, you
/// must select an output format.
///
/// ## Output formats
///
/// In each of the formats below, when the variant name is used in the output,
/// it is converted from `PascalCase` to `snake_case`. You can specify an
/// alternate name with the `rename` attribute, e.g. `#[inspect(rename =
/// "paused")]`.
///
/// ### `tag = "tag_name"`
///
/// Includes the variant name as a field called `tag_name`, and flattens all the
/// variant's fields into the object.
///
/// ```no_run
/// # use inspect::Inspect;
/// #[derive(Inspect)]
/// #[inspect(tag = "state")]
/// enum State {
///     Stopped,
///     Running { with_optimizations: bool },
/// }
/// ```
///
/// Example output for `State::Running { with_optimizations: true }`:
///
/// ```text
/// {
///     state: "running"
///     with_optimizations: true
/// }
/// ```
///
/// ### `external_tag`
///
/// Writes a single field named for the variant, with the variant's fields as
/// children.
///
/// ```no_run
/// # use inspect::Inspect;
/// #[derive(Inspect)]
/// #[inspect(external_tag)]
/// enum State {
///     Stopped,
///     Running { with_optimizations: bool },
/// }
/// ```
///
/// Example output for `State::Running { with_optimizations: true }`:
///
/// ```text
/// {
///     running: {
///         with_optimizations: true
///     }
/// }
/// ```
///
/// ### `untagged`
///
/// Flattens the variant's fields as in `tag`, but does not include the variant
/// name anywhere.
///
/// ```no_run
/// # use inspect::Inspect;
/// #[derive(Inspect)]
/// #[inspect(untagged)]
/// enum DiskBacking {
///     File { file_path: String },
///     Memory { ramdisk_size: u64 },
/// }
/// ```
///
/// Example output for `State::File { file_path: "file.img" }`:
///
/// ```text
/// {
///     file_path: "file.img"
/// }
/// ```
///
/// ## Additional attributes
///
/// On enums, as with structs, you can put the [`skip`](#skip), [`with`](#with),
/// [`display`](#display), [`debug`](#debug), or [`extra`](#extra--expr)
/// attributes to specify alternate inspect behavior.
///
/// On each enum variant, you can use [`transparent`](#transparentattrs) as you
/// would with a struct.
///
/// On enum variant fields, you can use any attribute that you would use with a
/// struct field.
#[cfg(feature = "derive")]
pub use inspect_derive::Inspect;
/// Derives the [`InspectMut`] trait for a struct or enum.
///
/// See the [`Inspect`](derive@Inspect) derive macro for more details.
#[cfg(feature = "derive")]
pub use inspect_derive::InspectMut;

use alloc::borrow::Cow;
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::string::ToString;
use alloc::sync::Arc;
use alloc::vec::Vec;
use core::fmt;
use core::fmt::Arguments;
use core::fmt::Debug;
use core::fmt::Display;
use core::num::Wrapping;

/// An inspection request.
pub struct Request<'a> {
    params: RequestParams<'a>,
    node: &'a mut InternalNode,
}

struct RootParams<'a> {
    full_path: &'a str,
    value: Option<&'a str>,
    sensitivity: SensitivityLevel,
}

#[derive(Copy, Clone)]
struct RequestParams<'a> {
    root: &'a RootParams<'a>,
    path_start: usize,
    depth: usize,
    number_format: NumberFormat,
}

impl<'a> RequestParams<'a> {
    fn path(&self) -> &'a str {
        &self.root.full_path[self.path_start..]
    }

    fn is_leaf(&self) -> bool {
        self.path_start >= self.root.full_path.len()
    }
}

#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload)
)]
#[derive(Debug, Default, Copy, Clone)]
enum NumberFormat {
    #[default]
    Decimal,
    Hex,
    Binary,
    Counter,
}

/// The sensitivity level for an inspection node or request.
/// Requests will only return nodes at or below their sensitivity level.
/// For example, a request set to `SensitivityLevel::Safe` will not return nodes
/// with `SensitivityLevel::Sensitive` or `SensitivityLevel::Unknown`.
#[derive(Debug, Copy, Clone, PartialOrd, Ord, PartialEq, Eq, Default)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload)
)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    mesh(package = "inspect")
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum SensitivityLevel {
    /// The node doesn't contain sensitive information and is always safe to expose.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
    Safe,
    /// The node might contain sensitive information and should only be exposed
    /// in a secure context.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
    #[default]
    Unspecified,
    /// The node contains sensitive information and should only be exposed in a
    /// secure context.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(3))]
    Sensitive,
}

/// A type used to build an inspection response.
pub struct Response<'a> {
    params: RequestParams<'a>,
    /// Remaining path without leading '/'s.
    path_without_slashes: &'a str,
    /// The list of inspected children.
    ///
    /// This is `None` when the depth is exhaused (in which case all children
    /// are ignored--the response object was just created to report that this
    /// node in the inspect tree is a directory and not a value).
    children: Option<&'a mut Vec<InternalEntry>>,
}

#[derive(Debug)]
enum Action<'a> {
    Process {
        name: &'a str,
        sensitivity: SensitivityLevel,
        new_path_start: usize,
        new_depth: usize,
    },
    Skip,
    DepthExhausted {
        name: &'a str,
        sensitivity: SensitivityLevel,
    },
}

impl<'a> Action<'a> {
    // Determine which action to take for the field with `name`, given the
    // remaining `path` and the remaining `depth`.
    fn eval(child: Child<'a>, params: &RequestParams<'_>, path: &str) -> Self {
        if params.depth == 0 {
            // Don't return any subfields if the depth is exhausted, since depth
            // exhausted will be reported for the current node.
            return Self::Skip;
        }
        if params.root.sensitivity < child.sensitivity {
            // Don't return any subfields if the request's sensitivity level is too low.
            return Self::Skip;
        }
        if let Some(rest) = path.strip_prefix(child.name) {
            if rest.is_empty() || rest.as_bytes()[0] == b'/' {
                // Exact or prefix match, e.g. name and path are both "foo", or
                // name is "foo", path is "foo/bar".
                Self::Process {
                    name: child.name,
                    sensitivity: child.sensitivity,
                    new_path_start: params.root.full_path.len() - rest.len(),
                    new_depth: params.depth,
                }
            } else {
                // Mismatch, e.g. name is "foo", path is "foobar", or this is a
                // prefix match but this node is a value.
                Self::Skip
            }
        } else if child.name.starts_with(path) {
            let remaining_name = if path.is_empty() {
                // Path is empty, so allow all names.
                child.name
            } else {
                if child.name.as_bytes()[path.len()] != b'/' {
                    // Mismatch, e.g. name is "foobar", path is "foo".
                    return Self::Skip;
                }
                // Prefix match, e.g. name is "foo/bar", path is "foo".
                &child.name[path.len() + 1..]
            };
            // Ensure there is enough depth for the name.
            match remaining_name.match_indices('/').nth(params.depth - 1) {
                None => Self::Process {
                    name: child.name,
                    sensitivity: child.sensitivity,
                    new_path_start: params.root.full_path.len(),
                    new_depth: params.depth - 1,
                },
                Some((n, _)) => Self::DepthExhausted {
                    name: &child.name[..child.name.len() - remaining_name.len() + n],
                    sensitivity: child.sensitivity,
                },
            }
        } else {
            Self::Skip
        }
    }
}

struct Child<'a> {
    name: &'a str,
    sensitivity: SensitivityLevel,
}

impl Response<'_> {
    /// Adds a field to the response.
    ///
    /// ```no_run
    /// # use inspect::{Request, Response};
    /// # use std::path::Path;
    /// fn inspect_data(a: u32, b: &str, c: bool, maybe: Option<u32>, req: Request) {
    ///     req.respond()
    ///        .field("a", a)
    ///        .field("b", b)
    ///        .field("c", c)
    ///        .field("maybe", maybe)
    ///        .field("computed", format_args!("{a}-{b}"));
    /// }
    /// ```
    pub fn field<T>(&mut self, name: &str, value: T) -> &mut Self
    where
        T: Inspect,
    {
        self.inspect_field(name, &value)
    }

    /// Adds a field to the response with a [`SensitivityLevel`].
    pub fn sensitivity_field<T>(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        value: T,
    ) -> &mut Self
    where
        T: Inspect,
    {
        self.sensitivity_inspect_field(name, sensitivity, &value)
    }

    /// Adds a hexadecimal field to the response.
    pub fn hex<T>(&mut self, name: &str, value: T) -> &mut Self
    where
        T: Inspect,
    {
        self.field(name, AsHex(value))
    }

    /// Adds a counter field to the response.
    pub fn counter<T>(&mut self, name: &str, value: T) -> &mut Self
    where
        T: Inspect,
    {
        self.field(name, AsCounter(value))
    }

    /// Adds a counter field to the response.
    ///
    /// This is a convenience method for `sensitivity_field(name, sensitivity, Value::counter(value))`.
    pub fn sensitivity_counter<T>(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        value: T,
    ) -> &mut Self
    where
        T: Inspect,
    {
        self.sensitivity_field(name, sensitivity, AsCounter(value))
    }

    /// Adds a binary field to the response.
    ///
    /// This is a convenience method for `field(name, Value::binary(value))`.
    pub fn binary<T>(&mut self, name: &str, value: T) -> &mut Self
    where
        T: Inspect,
    {
        self.field(name, AsBinary(value))
    }

    /// Adds a string field that implements [`core::fmt::Display`].
    ///
    /// This is a convenience method for `field_with(name, ||
    /// value.to_string())`. It lazily allocates the string only when the
    /// inspector requests it, so it is more efficient than using `format!` or
    /// `to_string()`.
    ///
    /// ```no_run
    /// # use inspect::{Request, Response};
    /// # use std::path::Path;
    /// fn inspect_data(path: &Path, id: u32, req: Request) {
    ///     req.respond().display("path", &path.display());
    /// }
    /// ```
    pub fn display(&mut self, name: &str, value: &impl Display) -> &mut Self {
        self.field_with(name, || value.to_string())
    }

    /// Adds a string field that implements [`core::fmt::Debug`].
    ///
    /// This is a convenience method for `field(name, format_args!("{value:?}"))`.
    ///
    /// Take care not to overuse this. This is best used for fields that have a
    /// short or natural `Debug` implementation, such as dataless enums. If your
    /// field is an aggregate type whose `Debug` implementation has a
    /// complicated structure, consider implementing `Inspect` on the type and
    /// calling [`field`](`Self::field`).
    pub fn display_debug(&mut self, name: &str, value: &impl Debug) -> &mut Self {
        self.field(name, format_args!("{value:?}"))
    }

    /// Adds a field to the response, calling `f` to get its value.
    pub fn field_with<F, V>(&mut self, name: &str, f: F) -> &mut Self
    where
        F: FnOnce() -> V,
        V: Inspect,
    {
        self.sensitivity_field_with(name, SensitivityLevel::Unspecified, f)
    }

    /// Adds a field to the response with a [`SensitivityLevel`], calling `f` to get its value.
    pub fn sensitivity_field_with<F, V>(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        f: F,
    ) -> &mut Self
    where
        F: FnOnce() -> V,
        V: Inspect,
    {
        if let Some(req) = self.child_request(Child { name, sensitivity }) {
            (f)().inspect(req)
        }
        self
    }

    /// Adds a mutable field to the response.
    pub fn field_mut<T>(&mut self, name: &str, value: &mut T) -> &mut Self
    where
        T: InspectMut + ?Sized,
    {
        self.inspect_field(name, value)
    }

    /// Adds a mutable field to the response with a [`SensitivityLevel`].
    pub fn sensitivity_field_mut<T>(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        value: &mut T,
    ) -> &mut Self
    where
        T: InspectMut + ?Sized,
    {
        self.sensitivity_inspect_field(name, sensitivity, value)
    }

    /// Adds a mutable field with custom get/update function.
    pub fn field_mut_with<F, V, E>(&mut self, name: &str, f: F) -> &mut Self
    where
        F: FnOnce(Option<&str>) -> Result<V, E>,
        V: Into<ValueKind>,
        E: Into<Box<dyn core::error::Error + Send + Sync>>,
    {
        self.child(name, |req| match req.update() {
            Ok(req) => match (f)(Some(req.new_value())) {
                Ok(v) => req.succeed(v),
                Err(err) => req.fail(err),
            },
            Err(req) => req.value((f)(None).ok().unwrap()),
        })
    }

    fn child_request(&mut self, child: Child<'_>) -> Option<Request<'_>> {
        let children = &mut **self.children.as_mut()?;
        let action = Action::eval(child, &self.params, self.path_without_slashes);
        match action {
            Action::Process {
                name,
                sensitivity,
                new_path_start,
                new_depth,
            } => {
                children.push(InternalEntry {
                    name: name.to_owned(),
                    node: InternalNode::Unevaluated,
                    sensitivity,
                });
                let entry = children.last_mut().unwrap();
                Some(
                    RequestParams {
                        path_start: new_path_start,
                        depth: new_depth,
                        ..self.params
                    }
                    .request(&mut entry.node),
                )
            }
            Action::Skip => None,
            Action::DepthExhausted { name, sensitivity } => {
                children.push(InternalEntry {
                    name: name.to_owned(),
                    node: InternalNode::DepthExhausted,
                    sensitivity,
                });
                None
            }
        }
    }

    /// Inspects a child object.
    fn inspect_field(&mut self, name: &str, child: impl InspectMut) -> &mut Self {
        self.sensitivity_inspect_field(name, SensitivityLevel::Unspecified, child)
    }

    /// Inspects a child object with a [`SensitivityLevel`].
    #[inline(never)] // Testing shows a significant code size reduction with this.
    fn sensitivity_inspect_field(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        mut child: impl InspectMut,
    ) -> &mut Self {
        if let Some(req) = self.child_request(Child { name, sensitivity }) {
            child.inspect_mut(req);
        }
        self
    }

    /// Inspects a list of children objects as children of the node `name`.
    ///
    /// Each element of `children` should be a tuple (`child_name`, `child`).
    /// `child_name` must be convertible to a string.
    #[cfg_attr(
        feature = "initiate",
        doc = r##"
```rust
# use inspect::{inspect, Inspect, Request};
# use core::time::Duration;

struct Child(u32);
impl Inspect for Child {
    fn inspect(&self, req: Request) {
        req.respond().field("x", self.0);
    }
}

struct Parent(Vec<Child>);
impl Inspect for Parent {
    fn inspect(&self, req: Request) {
        req.respond().fields("children", self.0.iter().enumerate());
    }
}

assert_eq!(
    inspect(
        "",
        &Parent(vec![Child(5), Child(12)]),
    )
    .results()
    .to_string(),
    r#"{children: {0: {x: 5}, 1: {x: 12}}}"#
);
```
"##
    )]
    pub fn fields<I, N, C>(&mut self, name: &str, children: I) -> &mut Self
    where
        I: Iterator<Item = (N, C)>,
        N: ToString,
        C: Inspect,
    {
        self.child(name, |req| {
            let mut resp = req.respond();
            for (name, child) in children {
                resp.inspect_field(&name.to_string(), &child);
            }
        })
    }

    /// Inspects a list of children objects as children of the node `name`.
    ///
    /// Each element of `children` should be a tuple (`child_name`, `child`).
    /// `child_name` must be convertible to a string.
    #[cfg_attr(
        feature = "initiate",
        doc = r##"
```rust
# use inspect::{inspect, InspectMut, Request};
# use std::time::Duration;
struct Child(u32);
impl InspectMut for Child {
    fn inspect_mut(&mut self, req: Request) {
        req.respond().field("x", self.0);
    }
}

struct Parent(Vec<Child>);
impl InspectMut for Parent {
    fn inspect_mut(&mut self, req: Request) {
        req.respond().fields_mut("children", self.0.iter_mut().enumerate());
    }
}

assert_eq!(
    inspect(
        "",
        &mut Parent(vec![Child(5), Child(12)]),
    )
    .results()
    .to_string(),
    r#"{children: {0: {x: 5}, 1: {x: 12}}}"#
);
```
"##
    )]
    pub fn fields_mut<I, N, C>(&mut self, name: &str, children: I) -> &mut Self
    where
        I: Iterator<Item = (N, C)>,
        N: ToString,
        C: InspectMut,
    {
        self.child(name, |req| {
            let mut resp = req.respond();
            for (name, child) in children {
                resp.inspect_field(&name.to_string(), child);
            }
        })
    }

    /// Adds a child node to the inspection response.
    ///
    /// If `name` is empty, then the results of the inspection are merged into
    /// this node.
    pub fn child<F: FnOnce(Request<'_>)>(&mut self, name: &str, f: F) -> &mut Self {
        self.sensitivity_child(name, SensitivityLevel::Unspecified, f)
    }

    /// Adds a child node to the inspection response with a [`SensitivityLevel`].
    ///
    /// If `name` is empty, then the results of the inspection are merged into
    /// this node.
    pub fn sensitivity_child<F: FnOnce(Request<'_>)>(
        &mut self,
        name: &str,
        sensitivity: SensitivityLevel,
        f: F,
    ) -> &mut Self {
        if let Some(req) = self.child_request(Child { name, sensitivity }) {
            f(req);
        }
        self
    }

    /// Inspects an object and merges its responses into this node without
    /// creating a child node.
    pub fn merge(&mut self, mut child: impl InspectMut) -> &mut Self {
        if let Some(req) = self.request() {
            child.inspect_mut(req);
        }
        self
    }

    /// Gets another request for this response. The response to that request
    /// will be merged into this response.
    ///
    /// Returns `None` if the depth is already exhausted, in which case there is
    /// no need (or ability--requests must have nodes) to propagate the request.
    fn request(&mut self) -> Option<Request<'_>> {
        let children = &mut **self.children.as_mut()?;
        children.push(InternalEntry {
            name: String::new(),
            node: InternalNode::Unevaluated,
            sensitivity: SensitivityLevel::Unspecified,
        });
        let entry = children.last_mut().unwrap();
        Some(self.params.request(&mut entry.node))
    }
}

impl<'a> RequestParams<'a> {
    #[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
    fn inspect(self, mut obj: impl InspectMut) -> InternalNode {
        self.with(|req| {
            obj.inspect_mut(req);
        })
    }

    fn with(self, f: impl FnOnce(Request<'_>)) -> InternalNode {
        let mut node = InternalNode::Unevaluated;
        f(self.request(&mut node));
        node
    }

    fn request(self, node: &'a mut InternalNode) -> Request<'a> {
        Request { params: self, node }
    }
}

impl<'a> Request<'a> {
    /// Sets numeric values to be displayed in decimal format.
    #[must_use]
    pub fn with_decimal_format(mut self) -> Self {
        self.params.number_format = NumberFormat::Decimal;
        self
    }

    /// Sets numeric values to be displayed in hexadecimal format.
    #[must_use]
    pub fn with_hex_format(mut self) -> Self {
        self.params.number_format = NumberFormat::Hex;
        self
    }

    /// Sets numeric values to be displayed in binary format.
    #[must_use]
    pub fn with_binary_format(mut self) -> Self {
        self.params.number_format = NumberFormat::Binary;
        self
    }

    /// Sets numeric values to be displayed as counters.
    #[must_use]
    pub fn with_counter_format(mut self) -> Self {
        self.params.number_format = NumberFormat::Counter;
        self
    }

    /// Responds to the request with a value.
    pub fn value(self, value: impl Into<ValueKind>) {
        self.value_(value.into());
    }
    fn value_(self, value: ValueKind) {
        let node = if self.params.is_leaf() {
            if self.params.root.value.is_none() {
                InternalNode::Value(value.with_format(self.params.number_format))
            } else {
                InternalNode::Failed(InternalError::Immutable)
            }
        } else {
            InternalNode::Failed(InternalError::NotADirectory)
        };
        *self.node = node;
    }

    /// Returns an object used for handling an update request.
    ///
    /// If this is not an update request, returns `Err(self)`.
    pub fn update(self) -> Result<UpdateRequest<'a>, Self> {
        if let Some(value) = self.params.root.value {
            if !self.params.is_leaf() {
                return Err(self);
            }
            Ok(UpdateRequest {
                node: self.node,
                value,
                number_format: self.params.number_format,
            })
        } else {
            Err(self)
        }
    }

    /// Responds to the inspection request with a directory node.
    ///
    /// Returns an object that can be used to provide the inspection results.
    pub fn respond(self) -> Response<'a> {
        let children = if self.params.depth > 0 {
            *self.node = InternalNode::Dir(Vec::new());
            let InternalNode::Dir(children) = self.node else {
                unreachable!()
            };
            Some(children)
        } else {
            // No children will be collected, but this node had to be inspected
            // in case it was a value and not a directory node.
            *self.node = InternalNode::DepthExhausted;
            None
        };
        Response {
            params: self.params,
            path_without_slashes: self.params.path().trim_start_matches('/'),
            children,
        }
    }

    /// Removes this node from the inspection output.
    pub fn ignore(self) {
        *self.node = InternalNode::Ignored;
    }

    /// If true, this is an update request.
    pub fn is_update(&self) -> bool {
        self.params.root.value.is_some()
    }

    /// Gets the sensitivity level for this request.
    pub fn sensitivity(&self) -> SensitivityLevel {
        self.params.root.sensitivity
    }
}

/// An update request, used for updating a value.
pub struct UpdateRequest<'a> {
    node: &'a mut InternalNode,
    value: &'a str,
    number_format: NumberFormat,
}

impl UpdateRequest<'_> {
    /// Gets the requested new value.
    pub fn new_value(&self) -> &str {
        self.value
    }

    /// Report that the update succeeded, with a new value of `value`.
    pub fn succeed(self, value: impl Into<ValueKind>) {
        self.succeed_(value.into());
    }
    fn succeed_(self, value: ValueKind) {
        *self.node = InternalNode::Value(value.with_format(self.number_format));
    }

    /// Report that the update failed, with the reason in `err`.
    pub fn fail<E: Into<Box<dyn core::error::Error + Send + Sync>>>(self, err: E) {
        *self.node = InternalNode::failed(err.into());
    }
}

/// A child inspection value. This is not constructed directly but exists to
/// provide `From` implementations from the allowed value types.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload),
    mesh(package = "inspect")
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct Value {
    /// The kind and associated value.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
    pub kind: ValueKind,
    /// Flags affecting how the value is displayed.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
    pub flags: ValueFlags,
}

impl Value {
    /// Creates a new value from `kind`.
    pub fn new(kind: impl Into<ValueKind>) -> Self {
        Self {
            kind: kind.into(),
            flags: Default::default(),
        }
    }

    /// Creates a new hexadecimal value from `kind`.
    pub fn hex(kind: impl Into<ValueKind>) -> Self {
        Self::new(kind).into_hex()
    }

    /// Creates a new counter value from `kind`.
    pub fn counter(kind: impl Into<ValueKind>) -> Self {
        Self::new(kind).into_counter()
    }

    /// Creates a new binary value from `kind`.
    pub fn binary(kind: impl Into<ValueKind>) -> Self {
        Self::new(kind).into_binary()
    }

    /// Returns this value as a hexadecimal.
    pub fn into_hex(self) -> Self {
        Self {
            kind: self.kind,
            flags: self.flags.with_hex(true),
        }
    }

    /// Returns this value as a counter.
    pub fn into_counter(self) -> Self {
        Self {
            kind: self.kind,
            flags: self.flags.with_count(true),
        }
    }

    /// Returns this value as a binary.
    pub fn into_binary(self) -> Self {
        Self {
            kind: self.kind,
            flags: self.flags.with_binary(true),
        }
    }
}

/// The different kinds of values that can be emitted.
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload),
    mesh(package = "inspect")
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub enum ValueKind {
    /// A signed integer.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(1))]
    Signed(i64),
    /// An unsigned integer.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(2))]
    Unsigned(u64),
    /// A 32-bit floating point.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(3))]
    Float(f32),
    /// A 64-bit floating point.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(4))]
    Double(f64),
    /// A Boolean value.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(5))]
    Bool(bool),
    /// A string.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(6))]
    String(String),
    /// Opaque binary data.
    #[cfg_attr(any(feature = "defer", feature = "initiate"), mesh(7))]
    Bytes(Vec<u8>),
}

impl ValueKind {
    fn with_format(self, format: NumberFormat) -> Value {
        match self {
            Self::Signed(_) | Self::Unsigned(_) => match format {
                NumberFormat::Decimal => Value::new(self),
                NumberFormat::Hex => Value::hex(self).into_hex(),
                NumberFormat::Binary => Value::binary(self).into_binary(),
                NumberFormat::Counter => Value::counter(self).into_counter(),
            },
            v => v.into(),
        }
    }
}

/// Flags specifying additional metadata on values.
#[bitfield_struct::bitfield(u64)]
#[derive(PartialEq)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload),
    mesh(package = "inspect")
)]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
pub struct ValueFlags {
    /// This value should be displayed as hexadecimal.
    pub hex: bool,
    /// This value is a count and should be displayed as a rate.
    pub count: bool,
    /// This value should be displayed as binary.
    pub binary: bool,

    #[bits(61)]
    _reserved: u64,
}

impl Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match &self.kind {
            ValueKind::Signed(n) => {
                if self.flags.hex() {
                    write!(f, "{:#x}", &n)
                } else if self.flags.binary() {
                    write!(f, "{:#b}", &n)
                } else {
                    Display::fmt(&n, f)
                }
            }
            ValueKind::Unsigned(n) => {
                if self.flags.hex() {
                    write!(f, "{:#x}", &n)
                } else if self.flags.binary() {
                    write!(f, "{:#b}", &n)
                } else {
                    Display::fmt(&n, f)
                }
            }
            ValueKind::Float(n) => Display::fmt(&n, f),
            ValueKind::Double(n) => Display::fmt(&n, f),
            ValueKind::Bool(b) => Display::fmt(&b, f),
            ValueKind::String(s) => Debug::fmt(&s, f),
            ValueKind::Bytes(b) => {
                f.write_str("<")?;
                for &b in b {
                    write!(f, "{:02x}", b)?;
                }
                f.write_str(">")
            }
        }
    }
}

impl<T> From<T> for Value
where
    ValueKind: From<T>,
{
    fn from(v: T) -> Self {
        Value {
            kind: v.into(),
            flags: Default::default(),
        }
    }
}

macro_rules! from_as {
    ($x:ident, $to:ty, $($ty:ty),* $(,)?) => {
        $(
            impl From<$ty> for ValueKind {
                fn from(v: $ty) -> Self {
                    Self::$x(v as $to)
                }
            }
        )*
    };
}

from_as!(Unsigned, u64, u8, u16, u32, u64, usize);
from_as!(Signed, i64, i8, i16, i32, i64, isize);

macro_rules! from_get {
    ($x:ident, $to:ty, $($ty:ty),* $(,)?) => {
        $(
            impl From<$ty> for ValueKind {
                fn from(v: $ty) -> Self {
                    Self::$x(v.get() as $to)
                }
            }
        )*
    };
}

from_get!(
    Unsigned,
    u64,
    core::num::NonZeroU8,
    core::num::NonZeroU16,
    core::num::NonZeroU32,
    core::num::NonZeroU64,
    core::num::NonZeroUsize
);
from_get!(
    Signed,
    i64,
    core::num::NonZeroI8,
    core::num::NonZeroI16,
    core::num::NonZeroI32,
    core::num::NonZeroI64,
    core::num::NonZeroIsize
);

impl From<f32> for ValueKind {
    fn from(v: f32) -> Self {
        Self::Float(v)
    }
}

impl From<f64> for ValueKind {
    fn from(v: f64) -> Self {
        Self::Double(v)
    }
}

impl From<bool> for ValueKind {
    fn from(v: bool) -> Self {
        Self::Bool(v)
    }
}

impl From<&'_ str> for ValueKind {
    fn from(v: &str) -> Self {
        Self::String(v.to_owned())
    }
}

impl From<String> for ValueKind {
    fn from(v: String) -> Self {
        Self::String(v)
    }
}

#[cfg(feature = "std")]
impl From<&'_ std::ffi::CStr> for ValueKind {
    fn from(v: &std::ffi::CStr) -> Self {
        Self::String(v.to_string_lossy().to_string())
    }
}

#[cfg(feature = "std")]
impl From<std::ffi::CString> for ValueKind {
    fn from(v: std::ffi::CString) -> Self {
        Self::String(v.to_string_lossy().to_string())
    }
}

impl From<&'_ [u8]> for ValueKind {
    fn from(v: &[u8]) -> Self {
        Self::Bytes(v.to_vec())
    }
}

impl<const N: usize> From<[u8; N]> for ValueKind {
    fn from(v: [u8; N]) -> Self {
        Self::Bytes(v.to_vec())
    }
}

impl From<Vec<u8>> for ValueKind {
    fn from(v: Vec<u8>) -> Self {
        Self::Bytes(v)
    }
}

impl From<Arguments<'_>> for ValueKind {
    fn from(v: Arguments<'_>) -> Self {
        Self::String(v.to_string())
    }
}

impl<T: Into<ValueKind> + Clone> From<&'_ T> for ValueKind {
    fn from(v: &T) -> Self {
        v.clone().into()
    }
}

impl Inspect for () {
    fn inspect(&self, req: Request<'_>) {
        req.respond();
    }
}

impl InspectMut for () {
    fn inspect_mut(&mut self, req: Request<'_>) {
        req.respond();
    }
}

macro_rules! inspect_value_immut {
    ($($(#[$attr:meta])* $ty:ty),* $(,)?) => {
        $(
        $(#[$attr])*
        impl Inspect for $ty {
            fn inspect(&self, req: Request<'_>) {
                req.value(self)
            }
        }
        )*
    };
}

macro_rules! inspect_value {
    ($($(#[$attr:meta])* $ty:ty),* $(,)?) => {
        inspect_value_immut!($($ty,)*);
        $(
        $(#[$attr])*
        impl InspectMut for $ty {
            fn inspect_mut(&mut self, req: Request<'_>) {
                match req.update() {
                    Ok(req) => match req.new_value().parse::<Self>() {
                        Ok(v) => {
                            *self = v.clone();
                            req.succeed(v);
                        }
                        Err(err) => req.fail(err),
                    }
                    Err(req) => req.value(&*self),
                }
            }
        }
        )*
    };
}

inspect_value_immut! {
    str,
    #[cfg(feature = "std")]
    std::ffi::CStr,
    #[cfg(feature = "std")]
    std::ffi::CString,
    [u8],
    Vec<u8>,
    Arguments<'_>,
}

inspect_value! {
    u8,
    u16,
    u32,
    u64,
    usize,
    i8,
    i16,
    i32,
    i64,
    isize,
    core::num::NonZeroU8,
    core::num::NonZeroU16,
    core::num::NonZeroU32,
    core::num::NonZeroU64,
    core::num::NonZeroUsize,
    core::num::NonZeroI8,
    core::num::NonZeroI16,
    core::num::NonZeroI32,
    core::num::NonZeroI64,
    core::num::NonZeroIsize,
    f32,
    f64,
    bool,
    String,
}

impl<const N: usize> Inspect for [u8; N] {
    fn inspect(&self, req: Request<'_>) {
        self.as_slice().inspect(req)
    }
}

/// Inspect wrapper for an atomic value (e.g.,
/// [`AtomicBool`](core::sync::atomic::AtomicBool), allowing updates.
pub struct AtomicMut<T>(pub T);

macro_rules! inspect_atomic_value {
    ($(($ty:ident, $backing:ty)),* $(,)?) => {
        $(

        impl Inspect for core::sync::atomic::$ty {
            fn inspect(&self, req: Request<'_>) {
                req.value(self.load(core::sync::atomic::Ordering::Relaxed))
            }
        }

        // It doesn't make much sense to impl InspectMut, since if you have a &mut
        // reference to the Atomic, it's most likely that type didn't need to be
        // atomic in the first place.

        impl Inspect for AtomicMut<&core::sync::atomic::$ty> {
            fn inspect(&self, req: Request<'_>) {
                let mut value = self.0.load(core::sync::atomic::Ordering::Relaxed);
                let is_update = req.is_update();
                value.inspect_mut(req);
                if is_update {
                    self.0.store(value, core::sync::atomic::Ordering::Relaxed);
                }
            }
        }

        )*
    };
}

inspect_atomic_value! {
    (AtomicBool,  bool),
    (AtomicU8,    u8),
    (AtomicU16,   u16),
    (AtomicU32,   u32),
    (AtomicU64,   u64),
    (AtomicUsize, usize),
    (AtomicI8,    i8),
    (AtomicI16,   i16),
    (AtomicI32,   i32),
    (AtomicI64,   i64),
    (AtomicIsize, isize),
}

impl<T: Inspect> Inspect for Wrapping<T> {
    fn inspect(&self, req: Request<'_>) {
        self.0.inspect(req)
    }
}

impl<T: InspectMut> InspectMut for Wrapping<T> {
    fn inspect_mut(&mut self, req: Request<'_>) {
        self.0.inspect_mut(req)
    }
}

#[cfg(feature = "filepath")]
impl Inspect for std::fs::File {
    fn inspect(&self, req: Request<'_>) {
        use filepath::FilePath;
        if let Ok(path) = self.path() {
            req.value(path.display().to_string());
        } else {
            req.ignore();
        }
    }
}

/// Wrapper around `T` that implements [`Inspect`] by calling
/// [`ToString::to_string()`].
pub struct AsDisplay<T>(pub T);

impl<T: Display> Inspect for AsDisplay<T> {
    fn inspect(&self, req: Request<'_>) {
        req.value(self.0.to_string())
    }
}

/// Wrapper around `T` that implements [`Inspect`] by formatting with the
/// [`Debug`] trait.
pub struct AsDebug<T>(pub T);

impl<T: Debug> InspectMut for AsDebug<T> {
    fn inspect_mut(&mut self, req: Request<'_>) {
        req.value(format!("{:?}", self.0))
    }
}

impl<T: Debug> Inspect for AsDebug<T> {
    fn inspect(&self, req: Request<'_>) {
        req.value(format!("{:?}", self.0))
    }
}

macro_rules! hexbincount {
    ($tr:ident, $fmt:expr) => {
        impl<T: Inspect> Inspect for $tr<T> {
            fn inspect(&self, mut req: Request<'_>) {
                req.params.number_format = $fmt;
                self.0.inspect(req);
            }
        }

        impl<T: InspectMut> InspectMut for $tr<T> {
            fn inspect_mut(&mut self, mut req: Request<'_>) {
                req.params.number_format = $fmt;
                self.0.inspect_mut(req);
            }
        }
    };
}

/// Wrapper around `T` that implements [`Inspect`] by writing a value with
/// [`ValueFlags::hex`].
#[derive(Clone, Copy)]
pub struct AsHex<T>(pub T);

hexbincount!(AsHex, NumberFormat::Hex);

/// Wrapper around `T` that implements [`Inspect`] by writing a value with
/// [`ValueFlags::binary`].
pub struct AsBinary<T>(pub T);

hexbincount!(AsBinary, NumberFormat::Binary);

/// Wrapper around `T` that implements [`Inspect`] by writing a value with
/// [`ValueFlags::count`].
pub struct AsCounter<T>(pub T);

hexbincount!(AsCounter, NumberFormat::Counter);

/// Wrapper around `T` that implements [`Inspect`] by writing a
/// [`ValueKind::Bytes`] value.
///
/// `T` must be iterable with `u8` or `&u8`.
pub struct AsBytes<T>(pub T);

impl<T> Inspect for AsBytes<T>
where
    T: Clone + IntoIterator,
    Vec<u8>: Extend<T::Item>,
{
    fn inspect(&self, req: Request<'_>) {
        let mut v = Vec::new();
        v.extend(self.0.clone());
        req.value(ValueKind::Bytes(v))
    }
}

#[doc(hidden)]
pub mod derive_helpers {
    /// Helps with type inference in inspect_derive output.
    pub fn call<T, F, R>(t: T, f: F) -> R
    where
        F: Fn(T) -> R,
    {
        f(t)
    }
}

/// Implementation of [`Inspect`] a type that implements [`Iterator`] with an
/// item type `(K, V)`.
///
/// Inspecting this type will respond with a field for each entry in the
/// iterator, with `K` for the field's name and `V` for the field's value.
///
/// Construct with [`iter_by_key`] or [`iter_by_index`].
#[derive(Default)]
pub struct Iterated<I>(I);

/// Wraps an iterator for inspection.
///
/// # Example
///
/// ```rust
/// # use std::collections::BTreeMap;
/// fn inspect(req: inspect::Request<'_>) {
///     let v = BTreeMap::from([("foo", 1), ("bar", 2)]);
///     // Responds with { foo: 1, bar: 2 }.
///     req.respond().field("v", inspect::iter_by_key(&v));
/// }
/// ```
pub fn iter_by_key<I, K, V>(iter: impl IntoIterator<IntoIter = I>) -> Iterated<I>
where
    I: Clone + Iterator<Item = (K, V)>,
{
    Iterated(iter.into_iter())
}

/// Wraps an iterator for inspection, using the index in the enumeration as
/// the field name.
///
/// # Example
///
/// ```rust
/// fn inspect(req: inspect::Request<'_>) {
///     let v = vec!["foo", "bar", "baz"];
///     // Responds with { 0: "foo", 1: "bar", 2: "baz" }.
///     req.respond().field("v", inspect::iter_by_index(&v));
/// }
/// ```
pub fn iter_by_index<I, V>(
    iter: impl IntoIterator<IntoIter = I>,
) -> Iterated<core::iter::Enumerate<I>>
where
    I: Clone + Iterator<Item = V>,
{
    iter_by_key(iter.into_iter().enumerate())
}

impl<I, K, V> Iterated<I>
where
    I: Clone + Iterator<Item = (K, V)>,
{
    /// Maps the key of the iterator with `f`.
    ///
    /// # Example
    ///
    /// ```rust
    /// fn inspect(req: inspect::Request<'_>) {
    ///     let v = vec!["foo", "bar", "baz"];
    ///     // Responds with { 1: "foo", 2: "bar", 3: "baz" }.
    ///     req.respond().field("v", inspect::iter_by_index(&v).map_key(|k| k + 1));
    /// }
    /// ```
    pub fn map_key<F, K2>(self, mut f: F) -> Iterated<impl Clone + Iterator<Item = (K2, V)>>
    where
        F: Clone + FnMut(K) -> K2,
    {
        iter_by_key(self.0.map(move |(k, v)| (f(k), v)))
    }

    /// Maps the value of the iterator with `f`.
    ///
    /// # Example
    ///
    /// ```rust
    /// fn inspect(req: inspect::Request<'_>) {
    ///     let v = vec![10, 20, 30];
    ///     // Responds with { 0: 20, 1: 40, 2: 60 }.
    ///     req.respond().field("v", inspect::iter_by_index(&v).map_value(|v| v * 2));
    /// }
    /// ```
    pub fn map_value<F, V2>(self, mut f: F) -> Iterated<impl Clone + Iterator<Item = (K, V2)>>
    where
        F: Clone + FnMut(V) -> V2,
    {
        iter_by_key(self.0.map(move |(k, v)| (k, f(v))))
    }

    /// Prefixes each key with the string `prefix`.
    ///
    /// # Example
    ///
    /// ```rust
    /// fn inspect(req: inspect::Request<'_>) {
    ///     let v = vec![10, 20, 30];
    ///     // Responds with { n0: 10, n1: 20, n2: 30 }.
    ///     req.respond().field("v", inspect::iter_by_index(&v).prefix("n"));
    /// }
    /// ```
    pub fn prefix<'a>(
        self,
        prefix: &'a str,
    ) -> Iterated<impl 'a + Clone + Iterator<Item = (String, V)>>
    where
        K: Display,
        I: 'a,
        K: 'a,
        V: 'a,
    {
        self.map_key(move |k| format!("{}{}", prefix, k))
    }
}

impl<I, K, V> Inspect for Iterated<I>
where
    I: Clone + Iterator<Item = (K, V)>,
    K: ToString,
    V: Inspect,
{
    fn inspect(&self, req: Request<'_>) {
        let mut resp = req.respond();
        for (name, value) in self.0.clone() {
            resp.field(&name.to_string(), value);
        }
    }
}

#[derive(Debug)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload)
)]
#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
enum InternalNode {
    Unevaluated,
    Failed(InternalError),
    DepthExhausted,
    Value(Value),
    Dir(Vec<InternalEntry>),
    Ignored,
    #[cfg(any(feature = "defer", feature = "initiate"))]
    Deferred(mesh::OneshotReceiver<InternalNode>),
    #[cfg(any(feature = "defer", feature = "initiate"))]
    DirResolved(Vec<InternalEntry>),
}

#[derive(Debug)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload)
)]
// Without the initiate feature we never read fields of the InternalEntry
// to produce a user-visible Entry, but we still need those fields.
#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
struct InternalEntry {
    name: String,
    node: InternalNode,
    sensitivity: SensitivityLevel,
}

#[derive(Debug)]
#[cfg_attr(
    any(feature = "defer", feature = "initiate"),
    derive(mesh::MeshPayload)
)]
#[cfg_attr(not(any(feature = "defer", feature = "initiate")), expect(dead_code))]
enum InternalError {
    Immutable,
    Update(String),
    NotADirectory,
    Unresolved,
    Mesh(String),
}

impl InternalNode {
    fn failed(err: Box<dyn core::error::Error + Send + Sync>) -> Self {
        use core::fmt::Write;

        let mut s = err.to_string();
        let mut src = err.source();
        while let Some(err) = src {
            src = err.source();
            let _ = write!(&mut s, ": {err}");
        }
        InternalNode::Failed(InternalError::Update(s))
    }
}

/// Trait implemented by objects whose state can be inspected and mutated. Most users should not implement this trait
/// directly, but instead derive [`InspectMut`](derive@InspectMut).
///
/// See the [`Inspect`] trait for more information on implementation strategies.
pub trait InspectMut {
    /// Inspects the object.
    fn inspect_mut(&mut self, req: Request<'_>);
}

impl<T: Inspect + ?Sized> InspectMut for &'_ T {
    fn inspect_mut(&mut self, req: Request<'_>) {
        self.inspect(req);
    }
}

impl<T: InspectMut + ?Sized> InspectMut for &'_ mut T {
    fn inspect_mut(&mut self, req: Request<'_>) {
        T::inspect_mut(*self, req)
    }
}

impl<T: InspectMut + ?Sized> InspectMut for Box<T> {
    fn inspect_mut(&mut self, req: Request<'_>) {
        T::inspect_mut(self.as_mut(), req)
    }
}

impl<T: InspectMut> InspectMut for Option<T> {
    fn inspect_mut(&mut self, req: Request<'_>) {
        if let Some(val) = self {
            val.inspect_mut(req);
        } else {
            req.ignore();
        }
    }
}

/// Trait implemented by objects whose state can be inspected but not mutated.
///
/// For most cases, users should use the derive version of [`Inspect`](derive@Inspect)
/// as this will automatically update the implementation as new fields are added.
///
/// However there are cases where a user may want to manually implement this trait, such as when
/// an inspection result may require multiple struct fields for a single inspection field.
/// Users should take advantage of Rust's struct destructuring to introduce compiler errors to
/// keep manual `Inspect` trait implementations up to date as the struct changes.
///
/// # Example
/// The following code compiles.
/// ```no_run
/// # use inspect::Inspect;
/// struct Foo {
///     id: u32,
///     more_stuff: usize,
///     super_string: String,
/// }
///
/// impl Inspect for Foo {
///     fn inspect(&self, req: inspect::Request<'_>) {
///         let Foo {
///             id,
///             more_stuff,
///             super_string,
///         } = self;
///
///         let mut resp = req.respond();
///         resp.hex("id", id);
///         resp.field("more_stuff", more_stuff);
///
///         // Only provide the super_string field if id is 2.
///         if *id == 2 {
///             resp.field("super_string", super_string);
///         }
///     }
/// }
/// ```
///
/// However, someone adding a new field to `Foo` without updating the inspect implementation will no longer compile.
/// ```compile_fail
/// # use inspect::Inspect;
/// struct Foo {
///     id: u32,
///     more_stuff: usize,
///     super_string: String,
///     awesome_new_field: String,
/// }
///
/// impl Inspect for Foo {
///     fn inspect(&self, req: inspect::Request<'_>) {
///         let Foo {
///             id,
///             more_stuff,
///             super_string,
///             // Missing awesome_new_field!
///         } = self;
///
///         let mut resp = respond();
///         resp.hex("id", id);
///         resp.field("more_stuff", more_stuff);
///
///         // Only provide the super_string field if id is 2.
///         if id == 2 {
///             resp.field("super_string", super_string);
///         }
///     }
/// }
/// ```
pub trait Inspect {
    /// Inspects the object.
    fn inspect(&self, req: Request<'_>);
}

impl<T: Inspect + ?Sized> Inspect for &'_ T {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(*self, req)
    }
}

impl<T: Inspect + ?Sized> Inspect for &'_ mut T {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(*self, req)
    }
}

impl<T: Inspect + ?Sized> Inspect for Box<T> {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(self.as_ref(), req)
    }
}

impl<T: Inspect + ?Sized> Inspect for Rc<T> {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(self.as_ref(), req)
    }
}

impl<T: Inspect + ?Sized> Inspect for Arc<T> {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(self.as_ref(), req)
    }
}

impl<T: Inspect + ?Sized> Inspect for parking_lot::Mutex<T> {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(&*self.lock(), req)
    }
}

impl<T: Inspect + ?Sized> Inspect for parking_lot::RwLock<T> {
    fn inspect(&self, req: Request<'_>) {
        T::inspect(&*self.read(), req)
    }
}

#[cfg(feature = "std")]
impl<T: Inspect> Inspect for std::sync::OnceLock<T> {
    fn inspect(&self, req: Request<'_>) {
        <_ as Inspect>::inspect(&self.get(), req)
    }
}

impl<T: Inspect> Inspect for Option<T> {
    fn inspect(&self, req: Request<'_>) {
        if let Some(val) = self {
            val.inspect(req);
        } else {
            req.ignore();
        }
    }
}

impl<T: ?Sized + Inspect + ToOwned> Inspect for Cow<'_, T> {
    fn inspect(&self, req: Request<'_>) {
        self.as_ref().inspect(req)
    }
}

impl<T> Inspect for *mut T {
    fn inspect(&self, req: Request<'_>) {
        req.with_hex_format().value(*self as usize)
    }
}

impl<T> Inspect for *const T {
    fn inspect(&self, req: Request<'_>) {
        req.with_hex_format().value(*self as usize)
    }
}

impl Inspect for ValueKind {
    fn inspect(&self, req: Request<'_>) {
        req.value(self.clone());
    }
}

impl Inspect for Value {
    fn inspect(&self, req: Request<'_>) {
        let req = if self.flags.count() {
            req.with_counter_format()
        } else if self.flags.hex() {
            req.with_hex_format()
        } else if self.flags.binary() {
            req.with_binary_format()
        } else {
            req
        };
        req.value(self.kind.clone());
    }
}

/// Returned by [`adhoc`] or [`adhoc_mut`].
pub struct Adhoc<F>(F);

impl<F> InspectMut for Adhoc<F>
where
    F: FnMut(Request<'_>),
{
    fn inspect_mut(&mut self, req: Request<'_>) {
        (self.0)(req)
    }
}

impl<F> Inspect for Adhoc<F>
where
    F: Fn(Request<'_>),
{
    fn inspect(&self, req: Request<'_>) {
        (self.0)(req)
    }
}

/// Returns an object that implements `Inspect` by calling `f` with the
/// inspection request.
pub fn adhoc<F>(f: F) -> Adhoc<F>
where
    F: Fn(Request<'_>),
{
    Adhoc(f)
}

/// Returns an object that implements `InspectMut` by calling `f` with the
/// inspection request.
pub fn adhoc_mut<F>(f: F) -> Adhoc<F>
where
    F: FnMut(Request<'_>),
{
    Adhoc(f)
}

#[cfg(all(test, feature = "derive", feature = "initiate"))]
mod tests {
    use crate::AsBytes;
    use crate::AtomicMut;
    use crate::Error;
    use crate::Inspect;
    use crate::InspectMut;
    use crate::InspectionBuilder;
    use crate::Node;
    use crate::Request;
    use crate::SensitivityLevel;
    use crate::ValueKind;
    use crate::adhoc;
    use crate::adhoc_mut;
    use crate::inspect;
    use crate::update;
    use alloc::boxed::Box;
    use alloc::string::String;
    use alloc::string::ToString;
    use alloc::vec;
    use alloc::vec::Vec;
    use core::time::Duration;
    use expect_test::Expect;
    use expect_test::expect;
    use futures::FutureExt;
    use pal_async::DefaultDriver;
    use pal_async::async_test;
    use pal_async::timer::Instant;
    use pal_async::timer::PolledTimer;

    fn expected_node(node: Node, expect: Expect) -> Node {
        expect.assert_eq(&std::format!("{node:#}"));
        node
    }

    async fn inspect_async(
        driver: &DefaultDriver,
        path: &str,
        depth: Option<usize>,
        timeout: Duration,
        obj: impl InspectMut,
    ) -> Node {
        let deadline = Instant::now() + timeout;
        let mut result = InspectionBuilder::new(path).depth(depth).inspect(obj);
        let mut timer = PolledTimer::new(driver);
        futures::select! { // race semantics
            _ = result.resolve().fuse() => {}
            _ = timer.sleep_until(deadline).fuse() => {}
        };
        result.results()
    }

    async fn inspect_async_expect(
        driver: &DefaultDriver,
        path: &str,
        depth: Option<usize>,
        timeout: Duration,
        obj: impl InspectMut,
        expect: Expect,
    ) -> Node {
        expected_node(
            inspect_async(driver, path, depth, timeout, obj).await,
            expect,
        )
    }

    fn inspect_sync(path: &str, depth: Option<usize>, obj: impl InspectMut) -> Node {
        let mut result = InspectionBuilder::new(path).depth(depth).inspect(obj);
        result.resolve().now_or_never();
        result.results()
    }

    fn inspect_sync_expect(
        path: &str,
        depth: Option<usize>,
        obj: impl InspectMut,
        expect: Expect,
    ) -> Node {
        expected_node(inspect_sync(path, depth, obj), expect)
    }

    #[derive(Default)]
    struct Foo {
        xx: u32,
        xy: bool,
        xz: Vec<Foo>,
    }

    impl Inspect for Foo {
        fn inspect(&self, req: Request<'_>) {
            let mut resp = req.respond();
            resp.field("xx", self.xx)
                .field("xy", self.xy)
                .fields("", self.xz.iter().enumerate());
        }
    }

    #[test]
    fn test() {
        let f = Foo {
            xx: 1,
            xy: true,
            xz: vec![
                Foo {
                    xx: 3,
                    xy: false,
                    xz: vec![],
                },
                Foo {
                    xx: 5,
                    xy: true,
                    xz: vec![],
                },
            ],
        };
        let node = inspect_sync_expect(
            "",
            None,
            &f,
            expect!([r#"
                {
                    0: {
                        xx: 3,
                        xy: false,
                    },
                    1: {
                        xx: 5,
                        xy: true,
                    },
                    xx: 1,
                    xy: true,
                }"#]),
        );
        let expected_json =
            expect!([r#"{"0":{"xx":3,"xy":false},"1":{"xx":5,"xy":true},"xx":1,"xy":true}"#]);
        expected_json.assert_eq(&node.json().to_string());
    }

    #[async_test]
    async fn test_deferred(driver: DefaultDriver) {
        inspect_async_expect(
            &driver,
            "",
            None,
            Duration::from_secs(1),
            adhoc(|req| {
                let foo = req.defer();
                std::thread::spawn(|| foo.inspect(&Foo::default()));
            }),
            expect!([r#"
                {
                    xx: 0,
                    xy: false,
                }"#]),
        )
        .await;
    }

    #[async_test]
    async fn test_dropped(driver: DefaultDriver) {
        inspect_async_expect(
            &driver,
            "",
            None,
            Duration::from_secs(86400),
            adhoc(|req| {
                drop(req.defer());
            }),
            expect!("error (unresolved)"),
        )
        .await;
    }

    #[test]
    fn test_path() {
        let mut obj = adhoc(|req| {
            req.respond().field("a", 1).child("b", |req| {
                req.respond().field("c", 2).field("d", 2).child("e", |req| {
                    req.respond();
                });
            });
        });
        inspect_sync_expect("a", None, &mut obj, expect!("1"));
        inspect_sync_expect("///a", None, &mut obj, expect!("1"));
        inspect_sync_expect(
            "b",
            None,
            &mut obj,
            expect!([r#"
            {
                c: 2,
                d: 2,
                e: {},
            }"#]),
        );
        inspect_sync_expect("b/c", None, &mut obj, expect!("2"));
        inspect_sync_expect("b////c", None, &mut obj, expect!("2"));
        inspect_sync_expect("b/c/", None, &mut obj, expect!("error (not a directory)"));
        inspect_sync_expect("b/c/x", None, &mut obj, expect!("error (not a directory)"));
        inspect_sync_expect("b/e", None, &mut obj, expect!("{}"));
        inspect_sync_expect("b/e/", None, &mut obj, expect!("{}"));
        inspect_sync_expect("b/e///", None, &mut obj, expect!("{}"));
        inspect_sync_expect("b/f", None, &mut obj, expect!("error (not found)"));
    }

    #[async_test]
    async fn test_timeout(driver: DefaultDriver) {
        inspect_async_expect(
            &driver,
            "",
            None,
            Duration::from_millis(10),
            adhoc(|req| {
                let foo = req.defer();
                std::thread::spawn(|| {
                    std::thread::sleep(Duration::from_millis(250));
                    foo.inspect(&Foo::default())
                });
            }),
            expect!("error (unresolved)"),
        )
        .await;
    }

    #[test]
    fn test_merge() {
        let mut obj = adhoc(|req| {
            req.respond().field("a", 1).merge(adhoc(|req| {
                req.respond().field("b", 2);
            }));
        });

        inspect_sync_expect(
            "",
            None,
            &mut obj,
            expect!([r#"
            {
                a: 1,
                b: 2,
            }"#]),
        );
        inspect_sync_expect("a", None, &mut obj, expect!("1"));
        inspect_sync_expect("b", None, &mut obj, expect!("2"));
        inspect_sync_expect("c", None, &mut obj, expect!("error (not found)"));
    }

    #[test]
    fn test_named_merge() {
        let mut obj = adhoc(|req| {
            req.respond()
                .field("a", 1)
                .field("a", 2)
                .child("x", |req| {
                    req.respond().field("b", 3).child("c", |req| {
                        req.respond().field("y", 4);
                    });
                })
                .child("x", |req| {
                    req.respond().field("b", 4).child("d", |req| {
                        req.respond().field("y", 5);
                    });
                });
        });

        inspect_sync_expect(
            "",
            None,
            &mut obj,
            expect!([r#"
                {
                    a: 2,
                    x: {
                        b: 4,
                        c: {
                            y: 4,
                        },
                        d: {
                            y: 5,
                        },
                    },
                }"#]),
        );
        inspect_sync_expect(
            "x",
            None,
            &mut obj,
            expect!([r#"
            {
                b: 4,
                c: {
                    y: 4,
                },
                d: {
                    y: 5,
                },
            }"#]),
        );
    }

    #[test]
    fn test_update() {
        struct Foo {
            immut: u32,
            mut_: u32,
            child: Option<Box<Foo>>,
        }

        impl InspectMut for Foo {
            fn inspect_mut(&mut self, req: Request<'_>) {
                let mut resp = req.respond();
                resp.field("immut", self.immut)
                    .field_mut("mut", &mut self.mut_)
                    .field_mut("child", &mut self.child);
            }
        }

        let mut foo = Foo {
            immut: 1,
            mut_: 2,
            child: Some(Box::new(Foo {
                immut: 101,
                mut_: 102,
                child: None,
            })),
        };
        assert_eq!(
            update("immut", "12", &mut foo)
                .now_or_never()
                .unwrap()
                .unwrap_err(),
            Error::Immutable
        );
        assert_eq!(
            update("mut/", "4", &mut foo)
                .now_or_never()
                .unwrap()
                .unwrap_err(),
            Error::NotADirectory
        );
        assert_eq!(
            update("mut", "3", &mut foo)
                .now_or_never()
                .unwrap()
                .unwrap()
                .kind,
            ValueKind::Unsigned(3)
        );
        assert_eq!(
            update("//child/mut", "103", &mut foo)
                .now_or_never()
                .unwrap()
                .unwrap()
                .kind,
            ValueKind::Unsigned(103)
        );
        assert_eq!(foo.mut_, 3);
        assert_eq!(foo.child.as_ref().unwrap().mut_, 103);
    }

    #[test]
    fn test_nest() {
        let mut obj = adhoc(|req| {
            req.respond().field("x/a/b", 1).field("x/a/c", 2);
        });

        inspect_sync_expect(
            "",
            None,
            &mut obj,
            expect!([r#"
            {
                x: {
                    a: {
                        b: 1,
                        c: 2,
                    },
                },
            }"#]),
        );
        inspect_sync_expect(
            "x/a",
            None,
            &mut obj,
            expect!([r#"
            {
                b: 1,
                c: 2,
            }"#]),
        );
        inspect_sync_expect(
            "x",
            Some(0),
            &mut obj,
            expect!([r#"
            {
                a: _,
            }"#]),
        );
        inspect_sync_expect(
            "x",
            Some(2),
            &mut obj,
            expect!([r#"
            {
                a: {
                    b: 1,
                    c: 2,
                },
            }"#]),
        );
    }

    #[test]
    fn test_depth() {
        let mut obj = adhoc(|req| {
            req.respond()
                .field("1a", 0)
                .field("1b", 0)
                .field("1c", 0)
                .child("1d", |req| {
                    req.respond().field("2a", 0).child("2b", |req| {
                        req.respond().child("3a", |req| {
                            req.respond().field_with("xxx", || -> u32 { panic!() });
                        });
                    });
                })
                .field("1d/2b/3b", 0);
        });

        inspect_sync_expect(
            "1d",
            Some(0),
            &mut obj,
            expect!([r#"
            {
                2a: 0,
                2b: _,
            }"#]),
        );
        inspect_sync_expect(
            "",
            Some(0),
            &mut obj,
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1c: 0,
                    1d: _,
                }"#]),
        );
        inspect_sync_expect(
            "",
            Some(1),
            &mut obj,
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1c: 0,
                    1d: {
                        2a: 0,
                        2b: _,
                    },
                }"#]),
        );
    }

    #[test]
    fn test_hex() {
        let mut obj = adhoc(|req| {
            req.respond().hex("a", 0x1234);
        });
        inspect_sync_expect(
            "",
            Some(0),
            &mut obj,
            expect!([r#"
            {
                a: 0x1234,
            }"#]),
        );
    }

    #[test]
    fn test_binary() {
        let mut obj = adhoc(|req| {
            req.respond().binary("a", 0b1001000110100);
        });
        inspect_sync_expect(
            "",
            Some(0),
            &mut obj,
            expect!([r#"
            {
                a: 0b1001000110100,
            }"#]),
        );
    }

    #[test]
    fn test_since() {
        let mut n = 500_u32;
        let mut b = false;
        let mut obj = adhoc_mut(|req| {
            req.respond()
                .counter("c", n)
                .field("f", n)
                .child("d", |req| {
                    let mut resp = req.respond();
                    if !b {
                        resp.field("1_a", true).field("1_b", true);
                    } else {
                        resp.field("1_c", true);
                    }
                    resp.field("2", true).counter("3", n);
                    if !b {
                        resp.field("4_a", true);
                    } else {
                        resp.field("4_b", true).field("4_c", true);
                    }
                });
            n += 100;
            b = true;
        });
        let old = inspect_sync("", Some(1), &mut obj);
        let new = inspect_sync("", Some(1), &mut obj);

        let diff = new.since(&old, Duration::from_secs(2));

        expected_node(
            diff,
            expect!([r#"
                {
                    c: 50,
                    d: {
                        1_c: true,
                        2: true,
                        3: 50,
                        4_b: true,
                        4_c: true,
                    },
                    f: 600,
                }"#]),
        );
    }

    #[test]
    fn test_bytes() {
        inspect_sync_expect(
            "",
            Some(1),
            &AsBytes([0xab, 0xcd, 0xef]),
            expect!("<abcdef>"),
        );
    }

    #[test]
    fn test_sensitivity() {
        let mut obj = adhoc(|req| {
            req.respond()
                .sensitivity_field("1a", SensitivityLevel::Safe, 0)
                .sensitivity_field("1b", SensitivityLevel::Unspecified, 0)
                .sensitivity_field("1c", SensitivityLevel::Sensitive, 0)
                .sensitivity_child("1d", SensitivityLevel::Safe, |req| {
                    req.respond()
                        .sensitivity_field("2a", SensitivityLevel::Sensitive, 0)
                        .sensitivity_child("2b", SensitivityLevel::Safe, |req| {
                            req.respond().sensitivity_child(
                                "3a",
                                SensitivityLevel::Sensitive,
                                |req| {
                                    req.respond().sensitivity_field(
                                        "4a",
                                        SensitivityLevel::Safe,
                                        0,
                                    );
                                },
                            );
                        });
                })
                .sensitivity_field("1d/2b/3b", SensitivityLevel::Unspecified, 0)
                .sensitivity_child("", SensitivityLevel::Sensitive, |req| {
                    req.respond()
                        .sensitivity_field("1e", SensitivityLevel::Safe, 0);
                });
        });

        fn inspect_sync(
            path: &str,
            sensitivity: Option<SensitivityLevel>,
            obj: impl InspectMut,
        ) -> Node {
            let mut result = InspectionBuilder::new(path)
                .sensitivity(sensitivity)
                .inspect(obj);
            result.resolve().now_or_never();
            result.results()
        }

        expected_node(
            inspect_sync("", Some(SensitivityLevel::Safe), &mut obj),
            expect!([r#"
                {
                    1a: 0,
                    1d: {
                        2b: {},
                    },
                }"#]),
        );
        expected_node(
            inspect_sync("", Some(SensitivityLevel::Unspecified), &mut obj),
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1d: {
                        2b: {
                            3b: 0,
                        },
                    },
                }"#]),
        );
        expected_node(
            inspect_sync("", Some(SensitivityLevel::Sensitive), &mut obj),
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1c: 0,
                    1d: {
                        2a: 0,
                        2b: {
                            3a: {
                                4a: 0,
                            },
                            3b: 0,
                        },
                    },
                    1e: 0,
                }"#]),
        );
        expected_node(
            inspect_sync("", None, &mut obj),
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1c: 0,
                    1d: {
                        2a: 0,
                        2b: {
                            3a: {
                                4a: 0,
                            },
                            3b: 0,
                        },
                    },
                    1e: 0,
                }"#]),
        );
        expected_node(
            inspect_sync("", Some(SensitivityLevel::Sensitive), &mut obj),
            expect!([r#"
                {
                    1a: 0,
                    1b: 0,
                    1c: 0,
                    1d: {
                        2a: 0,
                        2b: {
                            3a: {
                                4a: 0,
                            },
                            3b: 0,
                        },
                    },
                    1e: 0,
                }"#]),
        );
    }

    #[test]
    fn test_derive() {
        use std::marker::PhantomData;

        #[derive(InspectMut)]
        struct Derived {
            dec: u32,
            #[inspect(hex, rename = "hex")]
            val2: u64,
            #[inspect(binary)]
            bin: u8,
            inner: Inner,
            #[inspect(mut)]
            inner_mut: InnerMut,
            #[inspect(flatten)]
            flattened: Inner,
            #[inspect(skip)]
            _skip: bool,
            t: Transparent,
            t2: Newtype,
            #[inspect(format = "{:02x}")]
            minute: u8,
            #[inspect(debug)]
            debug: (),
            #[inspect(display)]
            display: u8,
            var: Enum,
            ignored: Ignored,
            tr1: Tr1,
            tr2: Tr2,
            #[inspect(iter_by_index, hex)]
            hex_array: [u8; 4],
            hex_inner: HexInner,
            #[inspect(hex)]
            inner_as_hex: Inner,
        }

        #[derive(Clone, Inspect)]
        struct Inner {
            val: u32,
        }

        #[derive(InspectMut)]
        struct InnerMut {
            val: String,
        }

        #[derive(Inspect)]
        #[inspect(hex)]
        struct HexInner {
            val: u32,
        }

        #[derive(Inspect)]
        #[inspect(transparent)]
        struct Transparent {
            inner: Inner,
        }

        #[derive(Inspect)]
        #[inspect(transparent)]
        struct Newtype(Inner, PhantomData<()>);

        #[derive(Inspect)]
        #[inspect(transparent(hex))]
        struct Tr1(u32, PhantomData<()>);

        #[derive(Inspect)]
        #[inspect(transparent)]
        struct Tr2(#[inspect(debug)] ());

        #[derive(Inspect)]
        #[expect(dead_code)]
        enum Enum {
            Foo,
            BarBaz,
            #[inspect(rename = "brother")]
            Other,
        }

        #[derive(Inspect)]
        #[inspect(skip)]
        struct Ignored {
            _x: fn(),
        }

        let mut obj = Derived {
            dec: 5,
            val2: 4,
            bin: 3,
            inner: Inner { val: 3 },
            inner_mut: InnerMut {
                val: "hi".to_string(),
            },
            _skip: true,
            flattened: Inner { val: 8 },
            t: Transparent {
                inner: Inner { val: 1 },
            },
            t2: Newtype(Inner { val: 2 }, PhantomData),
            debug: (),
            display: 10,
            minute: 7,
            var: Enum::BarBaz,
            ignored: Ignored { _x: || () },
            tr1: Tr1(10, PhantomData),
            tr2: Tr2(()),
            hex_array: [100, 101, 102, 103],
            hex_inner: HexInner { val: 100 },
            inner_as_hex: Inner { val: 100 },
        };

        inspect_sync_expect(
            "",
            None,
            &mut obj,
            expect!([r#"
                {
                    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: "()",
                    val: 8,
                    var: "bar_baz",
                }"#]),
        );
    }

    #[test]
    fn test_derive_enum() {
        #[expect(dead_code)]
        #[derive(Inspect)]
        enum EmptyUnitEmum {}

        #[expect(dead_code)]
        #[derive(Inspect)]
        #[inspect(untagged)]
        enum EmptyUntaggedEmum {}

        #[expect(dead_code)]
        #[derive(Inspect)]
        enum UnitEnum {
            A,
            B,
            C,
        }

        inspect_sync_expect("", None, &UnitEnum::B, expect!([r#""b""#]));

        #[expect(dead_code)]
        #[derive(Inspect)]
        #[inspect(tag = "tag")]
        enum TaggedEnum {
            A { x: u32 },
            B(#[inspect(rename = "y")] bool),
        }

        inspect_sync_expect(
            "",
            None,
            &TaggedEnum::B(true),
            expect!([r#"
                {
                    tag: "b",
                    y: true,
                }"#]),
        );

        #[expect(dead_code)]
        #[derive(Inspect)]
        #[inspect(external_tag)]
        enum ExternallyTaggedEnum {
            A {
                x: u32,
            },
            B(#[inspect(rename = "y")] bool),
            #[inspect(transparent)]
            C(u32),
        }

        inspect_sync_expect(
            "",
            None,
            &ExternallyTaggedEnum::B(true),
            expect!([r#"
                {
                    b: {
                        y: true,
                    },
                }"#]),
        );

        inspect_sync_expect(
            "",
            None,
            &ExternallyTaggedEnum::C(5),
            expect!([r#"
            {
                c: 5,
            }"#]),
        );

        #[expect(dead_code)]
        #[derive(Inspect)]
        #[inspect(untagged)]
        enum UntaggedEnum {
            A { x: u32 },
            B(#[inspect(rename = "y")] bool),
        }

        inspect_sync_expect(
            "",
            None,
            &UntaggedEnum::B(true),
            expect!([r#"
            {
                y: true,
            }"#]),
        );
    }

    #[test]
    fn test_derive_extra() {
        #[derive(Inspect)]
        #[inspect(extra = "Foo::inspect_extra")]
        struct Foo {
            x: u32,
            y: u32,
        }

        impl Foo {
            fn inspect_extra(&self, resp: &mut inspect::Response<'_>) {
                resp.field("sum", self.x + self.y);
            }
        }

        inspect_sync_expect(
            "",
            None,
            &Foo { x: 2, y: 5 },
            expect!([r#"
                {
                    sum: 7,
                    x: 2,
                    y: 5,
                }"#]),
        );
    }

    #[test]
    fn test_derive_sensitivity() {
        #[derive(Inspect)]
        struct Foo {
            #[inspect(safe)]
            a: u32,
            b: u32,
            #[inspect(sensitive)]
            c: u32,
            #[inspect(safe)]
            d: Bar,
        }
        #[derive(Inspect)]
        struct Bar {
            #[inspect(sensitive)]
            a: u32,
            #[inspect(safe)]
            b: Baz,
        }
        #[derive(Inspect)]
        struct Baz {
            #[inspect(sensitive)]
            a: Qux,
            b: u32,
        }
        #[derive(Inspect)]
        struct Qux {
            #[inspect(safe)]
            a: u32,
        }

        fn inspect_sync(
            path: &str,
            sensitivity: Option<SensitivityLevel>,
            obj: impl Inspect,
        ) -> Node {
            let mut result = InspectionBuilder::new(path)
                .sensitivity(sensitivity)
                .inspect(&obj);
            result.resolve().now_or_never();
            result.results()
        }

        let obj = Foo {
            a: 0,
            b: 0,
            c: 0,
            d: Bar {
                a: 0,
                b: Baz {
                    a: Qux { a: 0 },
                    b: 0,
                },
            },
        };

        expected_node(
            inspect_sync("", Some(SensitivityLevel::Safe), &obj),
            expect!([r#"
                {
                    a: 0,
                    d: {
                        b: {},
                    },
                }"#]),
        );
        expected_node(
            inspect_sync("", Some(SensitivityLevel::Unspecified), &obj),
            expect!([r#"
                {
                    a: 0,
                    b: 0,
                    d: {
                        b: {
                            b: 0,
                        },
                    },
                }"#]),
        );
        let node = expected_node(
            inspect_sync("", Some(SensitivityLevel::Sensitive), &obj),
            expect!([r#"
                {
                    a: 0,
                    b: 0,
                    c: 0,
                    d: {
                        a: 0,
                        b: {
                            a: {
                                a: 0,
                            },
                            b: 0,
                        },
                    },
                }"#]),
        );
        assert_eq!(
            node,
            inspect_sync("", Some(SensitivityLevel::Sensitive), &obj)
        );
    }

    /// Test that you can update via AtomicMut.
    #[test]
    fn test_atomic_mut() {
        let mut v = core::sync::atomic::AtomicBool::new(false);
        let obj = AtomicMut(&v);
        update("", "true", &obj).now_or_never().unwrap().unwrap();
        assert!(*v.get_mut());
    }
}
