incrstruct 0.1.2

Build self-referencing structs using two-phase initialization
Documentation
use core::cell::{Ref, RefCell};
use core::ptr::drop_in_place;

#[cfg(test)]
mod simple {
    use super::*;

    #[derive(incrstruct::IncrStruct)]
    struct AStruct<'a> {
        #[borrows(b)]
        pub c: &'a i32,

        #[borrows(head1, head2)]
        pub b: Ref<'a, i32>,

        pub head1: RefCell<i32>,
        pub head2: i64,

        #[header]
        hdr: incrstruct::Header,
    }

    impl<'a> AStructInit<'a> for AStruct<'a> {
        fn init_field_c(b: &'a Ref<'a, i32>) -> &'a i32 {
            b
        }

        fn init_field_b(_head2: &'a i64, head1: &'a RefCell<i32>) -> Ref<'a, i32> {
            head1.borrow()
        }
    }

    #[test]
    fn new_uninit_works() {
        let mut a = unsafe { AStruct::new_uninit(4711, RefCell::new(42)) };
        let aref = AStruct::ensure_init(&mut a);

        assert_eq!(*aref.head1.borrow(), 42);
        assert_eq!(*aref.b, 42);
        assert_eq!(*aref.c, 42);

        unsafe { drop_in_place(aref) };
    }

    #[test]
    fn drop_uninit_in_place_works() {
        let a = unsafe { AStruct::new_uninit(4711, RefCell::new(42)) };

        assert_eq!(*(unsafe { &*a.as_ptr() }).head1.borrow(), 42);

        unsafe { AStruct::drop_uninit(a) };
    }

    #[test]
    fn new_box_works() {
        let a = AStruct::new_box(4711, RefCell::new(42));

        assert_eq!(*a.head1.borrow(), 42);
        assert_eq!(*a.b, 42);
        assert_eq!(*a.c, 42);
    }

    #[test]
    fn new_rc_works() {
        let a = AStruct::new_rc(4711, RefCell::new(42));

        assert_eq!(*a.head1.borrow(), 42);
        assert_eq!(*a.b, 42);
        assert_eq!(*a.c, 42);
    }

    #[test]
    fn force_init_works() {
        let a = AStruct::new_box(4711, RefCell::new(42));
        let mut b = *a;

        AStruct::force_init(&mut b);

        assert_eq!(*b.head1.borrow(), 42);
        assert_eq!(*b.b, 42);
        assert_eq!(*b.c, 42);
    }
}

#[cfg(test)]
mod init_err {
    use super::*;

    #[derive(Debug, Eq, PartialEq)]
    enum Error {
        Failed,
    }

    #[derive(Debug, incrstruct::IncrStruct)]
    #[init_err(Error)]
    struct AStruct<'a> {
        #[borrows(b, head2)]
        pub c: &'a i32,

        #[borrows(head1)]
        pub b: Ref<'a, i32>,

        pub head1: RefCell<i32>,
        pub head2: i64,

        #[header]
        hdr: incrstruct::Header,
    }

    const HEAD2_FAIL: i64 = 18;

    impl<'a> AStructInit<'a> for AStruct<'a> {
        fn init_field_c(head2: &'a i64, b: &'a Ref<'a, i32>) -> Result<&'a i32, Error> {
            if *head2 == HEAD2_FAIL {
                Err(Error::Failed)
            } else {
                Ok(b)
            }
        }

        fn init_field_b(head1: &'a RefCell<i32>) -> Result<Ref<'a, i32>, Error> {
            Ok(head1.borrow())
        }
    }

    #[test]
    fn new_uninit_works() {
        let mut a = unsafe { AStruct::new_uninit(4711, RefCell::new(42)) };
        let aref = AStruct::ensure_init(&mut a).unwrap();

        assert_eq!(*aref.head1.borrow(), 42);
        assert_eq!(*aref.b, 42);
        assert_eq!(*aref.c, 42);

        unsafe { drop_in_place(aref) };
    }

    #[test]
    fn drop_uninit_in_place_works() {
        let a = unsafe { AStruct::new_uninit(4711, RefCell::new(42)) };

        assert_eq!(*(unsafe { &*a.as_ptr() }).head1.borrow(), 42);

        unsafe { AStruct::drop_uninit(a) };
    }

    #[test]
    fn new_box_works() {
        let a = AStruct::new_box(4711, RefCell::new(42)).unwrap();

        assert_eq!(*a.head1.borrow(), 42);
        assert_eq!(*a.b, 42);
        assert_eq!(*a.c, 42);
    }

    #[test]
    fn new_rc_works() {
        let a = AStruct::new_rc(4711, RefCell::new(42)).unwrap();

        assert_eq!(*a.head1.borrow(), 42);
        assert_eq!(*a.b, 42);
        assert_eq!(*a.c, 42);
    }

    #[test]
    fn force_init_works() {
        let a = AStruct::new_box(4711, RefCell::new(42)).unwrap();
        let mut b = *a;

        AStruct::force_init(&mut b).unwrap();

        assert_eq!(*b.head1.borrow(), 42);
        assert_eq!(*b.b, 42);
        assert_eq!(*b.c, 42);
    }

    #[test]
    fn new_box_fails_gracefully() {
        assert_eq!(
            AStruct::new_box(HEAD2_FAIL, RefCell::new(42)).unwrap_err(),
            Error::Failed
        );
    }

    #[test]
    fn second_force_init_fails_gracefully() {
        let a = AStruct::new_box(4711, RefCell::new(42)).unwrap();
        let mut b = *a;

        AStruct::force_init(&mut b).unwrap();

        b.head2 = HEAD2_FAIL;

        assert_eq!(AStruct::force_init(&mut b).unwrap_err(), Error::Failed);
    }
}