#[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:
field
: callsResponse::field_mut
with&mut field
.flatten
: callsResponse::merge
with&mut field
.transparent
(struct attribute): callsInspectMut::inspect_mut
on the sole unskipped field.
§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.