facet-reflect 0.46.5

Build and manipulate values of arbitrary Facet types at runtime while respecting invariants - safe runtime reflection
Documentation
use facet::Facet;
use facet_reflect::{Poke, ReflectErrorKind};

#[derive(Debug, Facet, PartialEq)]
#[facet(pod)]
#[repr(u8)]
enum SimpleEnum {
    Unit,
    #[allow(dead_code)]
    Tuple(u32),
    #[allow(dead_code)]
    Struct {
        a: u8,
        b: String,
    },
}

#[test]
fn poke_enum_into_enum() {
    let mut value = SimpleEnum::Unit;
    let poke = Poke::new(&mut value);

    let poke_enum = poke.into_enum().expect("SimpleEnum is an enum");
    assert_eq!(poke_enum.variant_name_active().unwrap(), "Unit");
}

#[test]
fn poke_enum_variant_info() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let poke_enum = poke.into_enum().unwrap();

    assert_eq!(poke_enum.variant_count(), 3);
    assert_eq!(poke_enum.variant_name(0), Some("Unit"));
    assert_eq!(poke_enum.variant_name(1), Some("Tuple"));
    assert_eq!(poke_enum.variant_name(2), Some("Struct"));
}

#[test]
fn poke_enum_field_access() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    // Get field
    let field = poke_enum.field(0).unwrap().unwrap();
    assert_eq!(*field.get::<u32>().unwrap(), 42);
}

#[test]
fn poke_enum_set_field_pod() {
    let mut value = SimpleEnum::Tuple(42);
    {
        let poke = Poke::new(&mut value);
        let mut poke_enum = poke.into_enum().unwrap();
        poke_enum.set_field(0, 100u32).unwrap();
    }

    match value {
        SimpleEnum::Tuple(v) => assert_eq!(v, 100),
        _ => panic!("Expected Tuple variant"),
    }
}

#[test]
fn poke_enum_set_field_by_name() {
    let mut value = SimpleEnum::Struct {
        a: 1,
        b: "hello".to_string(),
    };
    {
        let poke = Poke::new(&mut value);
        let mut poke_enum = poke.into_enum().unwrap();
        poke_enum.set_field_by_name("a", 99u8).unwrap();
        poke_enum
            .set_field_by_name("b", "world".to_string())
            .unwrap();
    }

    match value {
        SimpleEnum::Struct { a, b } => {
            assert_eq!(a, 99);
            assert_eq!(b, "world");
        }
        _ => panic!("Expected Struct variant"),
    }
}

#[test]
fn poke_enum_non_pod_fails() {
    #[derive(Debug, Facet, PartialEq)]
    #[repr(u8)]
    enum NotPod {
        #[allow(dead_code)]
        Value(u32),
    }

    let mut value = NotPod::Value(42);
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    // Setting field on non-POD should fail
    let result = poke_enum.set_field(0, 100u32);
    assert!(matches!(result, Err(ref err) if matches!(err.kind, ReflectErrorKind::NotPod { .. })));
}

#[test]
fn poke_enum_wrong_field_type() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    // Try to set u32 field with i32
    let result = poke_enum.set_field(0, 100i32);
    assert!(
        matches!(result, Err(ref err) if matches!(err.kind, ReflectErrorKind::WrongShape { .. }))
    );
}

#[test]
fn poke_enum_field_index_out_of_bounds() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    // Tuple variant only has one field (index 0)
    let result = poke_enum.set_field(99, 100u32);
    assert!(
        matches!(result, Err(ref err) if matches!(err.kind, ReflectErrorKind::FieldError { .. }))
    );
}

#[test]
fn poke_enum_no_such_field() {
    let mut value = SimpleEnum::Struct {
        a: 1,
        b: "hello".to_string(),
    };
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    let result = poke_enum.set_field_by_name("nonexistent", 100u32);
    assert!(
        matches!(result, Err(ref err) if matches!(err.kind, ReflectErrorKind::FieldError { .. }))
    );
}

#[test]
fn poke_enum_peek_field() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let poke_enum = poke.into_enum().unwrap();

    let peek = poke_enum.peek_field(0).unwrap().unwrap();
    assert_eq!(*peek.get::<u32>().unwrap(), 42);
}

#[test]
fn poke_enum_as_peek_enum() {
    let mut value = SimpleEnum::Unit;
    let poke = Poke::new(&mut value);
    let poke_enum = poke.into_enum().unwrap();

    let peek_enum = poke_enum.as_peek_enum();
    assert_eq!(peek_enum.variant_count(), 3);
    assert_eq!(peek_enum.variant_name_active().unwrap(), "Unit");
}

#[test]
fn poke_enum_into_inner() {
    let mut value = SimpleEnum::Unit;
    let poke = Poke::new(&mut value);
    let poke_enum = poke.into_enum().unwrap();

    let poke_back = poke_enum.into_inner();
    assert_eq!(poke_back.shape(), SimpleEnum::SHAPE);
}

#[test]
fn poke_not_enum_fails() {
    #[derive(Debug, Facet)]
    struct NotAnEnum {
        x: i32,
    }

    let mut value = NotAnEnum { x: 42 };
    let poke = Poke::new(&mut value);

    let result = poke.into_enum();
    assert!(matches!(result, Err(ref err) if matches!(err.kind, ReflectErrorKind::WasNotA { .. })));
}

#[test]
fn poke_enum_set_field_from_heap() {
    let mut value = SimpleEnum::Tuple(42);
    {
        let poke = Poke::new(&mut value);
        let mut poke_enum = poke.into_enum().unwrap();

        let hv = facet_reflect::Partial::alloc::<u32>()
            .unwrap()
            .set(100u32)
            .unwrap()
            .build()
            .unwrap();
        poke_enum.set_field_from_heap(0, hv).unwrap();
    }

    match value {
        SimpleEnum::Tuple(v) => assert_eq!(v, 100),
        _ => panic!("Expected Tuple variant"),
    }
}

#[test]
fn poke_enum_set_field_by_name_from_heap() {
    let mut value = SimpleEnum::Struct {
        a: 1,
        b: "hello".to_string(),
    };
    {
        let poke = Poke::new(&mut value);
        let mut poke_enum = poke.into_enum().unwrap();

        let a_val = facet_reflect::Partial::alloc::<u8>()
            .unwrap()
            .set(99u8)
            .unwrap()
            .build()
            .unwrap();
        let b_val = facet_reflect::Partial::alloc::<String>()
            .unwrap()
            .set("world".to_string())
            .unwrap()
            .build()
            .unwrap();

        poke_enum.set_field_by_name_from_heap("a", a_val).unwrap();
        poke_enum.set_field_by_name_from_heap("b", b_val).unwrap();
    }

    match value {
        SimpleEnum::Struct { a, b } => {
            assert_eq!(a, 99);
            assert_eq!(b, "world");
        }
        _ => panic!("Expected Struct variant"),
    }
}

#[test]
fn poke_enum_set_field_from_heap_wrong_shape_fails() {
    let mut value = SimpleEnum::Tuple(42);
    let poke = Poke::new(&mut value);
    let mut poke_enum = poke.into_enum().unwrap();

    let hv = facet_reflect::Partial::alloc::<i32>()
        .unwrap()
        .set(100i32)
        .unwrap()
        .build()
        .unwrap();
    let res = poke_enum.set_field_from_heap(0, hv);
    assert!(matches!(res, Err(ref err) if matches!(err.kind, ReflectErrorKind::WrongShape { .. })));
}