Derive Macro inspect::Inspect

#[derive(Inspect)]
{
    // Attributes available to this derive:
    #[inspect]
}
Expand description

Derives the Inspect trait for a struct or enum.

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 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.

#[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);
    }
}

§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.

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 std::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 as a hex-formatted numeric value. Calls Response::hex with &field.

§binary

Inspect the field as a binary-formatted numeric value. Calls Response::hex with &field.

§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() 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() on the field. Each item must have type V: Inspect. The field name is the index in the enumeration, starting with 0.

§Example
#[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 macro.

This can be used in conjunction with other attributes:

§Example

#[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:

#[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.

#[derive(Inspect)]
#[inspect(tag = "state")]
enum State {
    Stopped,
    Running { with_optimizations: bool },
}

Example output for State::Running { with_optimizations: true }:

{
    state: "running"
    with_optimizations: true
}

§external_tag

Writes a single field named for the variant, with the variant’s fields as children.

#[derive(Inspect)]
#[inspect(external_tag)]
enum State {
    Stopped,
    Running { with_optimizations: bool },
}

Example output for State::Running { with_optimizations: true }:

{
    running: {
        with_optimizations: true
    }
}

§untagged

Flattens the variant’s fields as in tag, but does not include the variant name anywhere.

#[derive(Inspect)]
#[inspect(untagged)]
enum DiskBacking {
    File { file_path: String },
    Memory { ramdisk_size: u64 },
}

Example output for State::File { file_path: "file.img" }:

{
    file_path: "file.img"
}

§Additional attributes

On enums, as with structs, you can put the skip, with, display, debug, or extra attributes to specify alternate inspect behavior.

On each enum variant, you can use transparent as you would with a struct.

On enum variant fields, you can use any attribute that you would use with a struct field.