mirror-mirror 0.1.0

Reflection library for Rust
Documentation

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);
# Some(())
# })().unwrap();

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"));
# Some(())
# })().unwrap();

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]);
# Some(())
# })().unwrap();

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][type_info] captured.
  • Add meta data to types which becomes part of the type information.
  • [Key paths][mod@key_path] 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:

Name Description Default?
std Enables using the standard library (core and alloc are always required) Yes
speedy Enables speedy support for most types Yes
serde Enables serde support for most types Yes