Crate mirror_mirror
source ·Expand description
General purpose reflection library for Rust.
🚨 Warning 🚨
This library is still experimental and should not be used for anything serious, yet. Many things are still undocumented and breaking changes are to be expected, though we do adhere to semver.
Examples
Access a field by its string name and mutate it
use mirror_mirror::{Reflect, Struct};
#[derive(Reflect, Clone, Debug)]
struct Foo {
x: i32,
}
let mut foo = Foo { x: 42 };
// Get a `Struct` trait object for `Foo`.
//
// The `Struct` trait has methods available for all structs such as accessing
// fields by name and iterating over the fields.
let struct_obj: &mut dyn Struct = foo.as_struct_mut()?;
// Mutably borrow the `x` field. We can access fields using string names.
let x: &mut dyn Reflect = struct_obj.field_mut("x")?;
// Downcast `x` into a mutable `i32`
let x: &mut i32 = x.downcast_mut::<i32>()?;
// Change the value of `x`
*x += 1;
// The value of `x` in `foo` has now changed.
assert_eq!(foo.x, 43);
Iterate over all fields
use mirror_mirror::{Reflect, Struct, ReflectMut, ScalarMut, enum_::VariantFieldMut};
// A function that iterates over the fields in an enum and mutates them.
fn change_enum_fields(value: &mut dyn Reflect) -> Option<()> {
let enum_ = value.as_enum_mut()?;
for field in enum_.fields_mut() {
match field {
VariantFieldMut::Struct(_, value) | VariantFieldMut::Tuple(value) => {
match value.reflect_mut() {
ReflectMut::Scalar(ScalarMut::i32(n)) => {
*n *= 2;
}
ReflectMut::Scalar(ScalarMut::String(s)) => {
*s = format!("{s}bar");
}
// Ignore other types
_ => {}
}
}
}
}
Some(())
}
#[derive(Reflect, Clone, Debug)]
enum Bar {
X { x: i32 },
Y(String),
}
let mut bar = Bar::X { x: 42 };
change_enum_fields(bar.as_reflect_mut())?;
assert!(matches!(bar, Bar::X { x: 84 }));
let mut bar = Bar::Y("foo".to_owned());
change_enum_fields(bar.as_reflect_mut())?;
assert!(matches!(bar, Bar::Y(s) if s == "foobar"));
Query value and type information using key paths
use mirror_mirror::{
Reflect,
key_path,
key_path::{GetPath, GetTypePath, field},
type_info::{DescribeType, ScalarType},
};
// Some complex nested data type.
#[derive(Reflect, Clone, Debug)]
struct User {
employer: Option<Company>,
}
#[derive(Reflect, Clone, Debug)]
struct Company {
countries: Vec<Country>,
}
#[derive(Reflect, Clone, Debug)]
struct Country {
name: String
}
let user = User {
employer: Some(Company {
countries: vec![Country {
name: "Denmark".to_owned(),
}],
}),
};
// Build a key path that represents accessing `.employer::Some.0.countries[0].name`.
//
// `::Some` means to access the `Some` variant of `Option<Company>`.
let path = field("employer").variant("Some").field(0).field("countries").get(0).field("name");
// Get the value at the key path.
assert_eq!(user.get_at::<String>(&path).unwrap(), "Denmark");
// Key paths can also be constructed using the `key_path!` macro.
// This invocation expands the same code we have above.
let path = key_path!(.employer::Some.0.countries[0].name);
// Use the same key path to query type information. You don't need a value
// of the type to access its type information.
let user_type = <User as DescribeType>::type_descriptor();
assert!(matches!(
user_type.type_at(&path).unwrap().as_scalar().unwrap(),
ScalarType::String,
));
Using opaque Value
types
use mirror_mirror::{Reflect, Value, FromReflect};
#[derive(Reflect, Clone, Debug)]
struct Foo(Vec<i32>);
let foo = Foo(vec![1, 2, 3]);
// Convert `foo` into general "value" type.
let mut value: Value = foo.to_value();
// `Value` also implements `Reflect` so it can be mutated in the
// same way we've seen before. So these mutations can be made
// by another crate that doesn't know about the `Foo` type.
//
// `Value` is also serializable with `speedy`, for binary serialization,
// or `serde`, for everything else.
value
.as_tuple_struct_mut()?
.field_at_mut(0)?
.as_list_mut()?
.push(&4);
// Convert the `value` back into a `Foo`.
let new_foo = Foo::from_reflect(&value)?;
// Our changes were applied.
assert_eq!(new_foo.0, vec![1, 2, 3, 4]);
Inspiration
The design of this library is heavily inspired by bevy_reflect
but with a few key
differences:
speedy
integration which is useful for marshalling data perhaps to send it across FFI.- A
Value
type that can be serialized and deserialized without using trait objects. - More type information captured.
- Add meta data to types which becomes part of the type information.
- Key paths for querying value and type information.
- No dependencies on
bevy
specific crates. #![no_std]
support.
Feature flags
mirror-mirror uses a set of [feature flags] to optionally reduce the number of dependencies.
The following optional features are available:
Modules
Reflected array types.
Reflected enum types.
Helper traits for accessing fields on reflected values.
Iterator types.
Key paths for querying value and type information.
Reflected list types.
Reflected map types.
Reflected struct types.
Reflected tuple types.
Reflected tuple struct types.
Type information.
Type erased value types.
Macros
Structs
The root of a type.
Enums
A mutable reflected value.
An owned reflected value.
An immutable reflected value.
An mutable reflected scalar value.
An owned reflected scalar type.
An immutable reflected scalar value.
A type erased value type.
Traits
A reflected array type.
Trait for accessing type information.
A reflected enum type.
A trait for types which can be constructed from a reflected type.
Helper trait for accessing and downcasting fields on reflected values.
Helper trait for mutably accessing and downcasting fields on reflected values.
A reflected list type.
A reflected map type.
A reflected type.
A reflected struct type.
A reflected tuple type.
A reflected tuple struct type.
Functions
Debug formatter for any reflection value.
Derive Macros
Derive an implementation of
Reflect
and other appropriate traits.