1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#[macro_export]
macro_rules! context {
    (mod $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ $(,)?>)? $name:ident  {
        $($field:ident $(($field_mut:ident))? : $ref_mut:tt $type_:ty ),*
        $(,)?
    }) => {
        mod $name {
            #[allow(unused_imports)]
            use super::*;

            context! { @impl Context [ $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)?] [ $(< $( $lt ),+ >)?]
                {} {} {} {} { $($field $(($field_mut))? : $ref_mut $type_),* } }
        }
    };
    (@impl $c:ident [$($i:tt)*] [$($r:tt)*]
        {$({$($f:tt)*})*} {$({$($p:tt)*})*} {$({$($a:tt)*})*} {$({$($b:tt)*})*} {}) => {
        
        pub struct $c $($i)* {
            $($($f)*),*
        }

        impl $($i)* $c $($r)* {
            pub fn call<ContextCallReturnType>(
                $($($p)*),*,
                f: impl $crate::std_ops_FnOnce(&mut Self) -> ContextCallReturnType 
            ) -> ContextCallReturnType {
                let mut context = Self {
                    $($($a)*),*
                };
                f(&mut context)
            }

            $($($b)*)*
        }

        unsafe impl $($i)* Send for $c $($r)* { }
        unsafe impl $($i)* Sync for $c $($r)* { }
    };
    (@impl $c:ident [$($i:tt)*] [$($r:tt)*]
        {$({$($f:tt)*})*} {$({$($p:tt)*})*} {$({$($a:tt)*})*} {$({$($b:tt)*})*}
        {$field:ident : ref $type_:ty $(, $ft:ident $(($fm:ident))? : $rt:tt $t:ty)*}) => {

        context! { @impl $c [$($i)*] [$($r)*]
            {$({$($f)*})* {$field : *const $type_}}
            {$({$($p)*})* {$field : &$type_}}
            {$({$($a)*})* {$field : $field as *const $type_}}
            {$({$($b)*})* {
                pub fn $field (&self) -> &$type_ { unsafe { &*self.$field } }
            }}
            {$($ft $(($fm))? : $rt $t),*}
        }
    };
    (@impl $c:ident [$($i:tt)*] [$($r:tt)*]
        {$({$($f:tt)*})*} {$({$($p:tt)*})*} {$({$($a:tt)*})*} {$({$($b:tt)*})*}
        {$field:ident ($field_mut:ident) : mut $type_:ty $(, $ft:ident $(($fm:ident))? : $rt:tt $t:ty)*}) => {

        context! { @impl $c [$($i)*] [$($r)*]
            {$({$($f)*})* {$field : *mut $type_}}
            {$({$($p)*})* {$field : &mut $type_}}
            {$({$($a)*})* {$field : $field as *mut $type_}}
            {$({$($b)*})* {
                #[allow(dead_code)]
                pub fn $field (&self) -> &$type_ { unsafe { &*self.$field } }
                #[allow(dead_code)]
                pub fn $field_mut (&mut self) -> &mut $type_ { unsafe { &mut *self.$field } }
            }}
            {$($ft $(($fm))? : $rt $t),*}
        }
    };
    (@impl $c:ident [$($i:tt)*] [$($r:tt)*]
        {$({$($f:tt)*})*} {$({$($p:tt)*})*} {$({$($a:tt)*})*} {$({$($b:tt)*})*}
        {$field:ident : const $type_:ty $(, $ft:ident $(($fm:ident))? : $rt:tt $t:ty)*}) => {

        context! { @impl $c [$($i)*] [$($r)*]
            {$({$($f)*})* {$field : $type_}}
            {$({$($p)*})* {$field : $type_}}
            {$({$($a)*})* {$field}}
            {$({$($b)*})* {
                pub fn $field (&self) -> $type_ { self.$field }
            }}
            {$($ft $(($fm))? : $rt $t),*}
        }
    };
}

#[cfg(test)]
mod test {
    use core::mem::replace;

    context! {
        mod context_1 {
            a: const u8,
            b: ref u16,
            c (c_mut): mut u32,
        }
    }

    type Context1 = context_1::Context;

    #[test]
    fn test_context_1() {
        let mut x = 3;
        let res = Context1::call(1, &2, &mut x, |context| {
            assert_eq!(context.a(), 1u8);
            assert_eq!(context.b(), &2u16);
            assert_eq!(replace(context.c_mut(), 12), 3u32);
            "res"
        });
        assert_eq!(res, "res");
        assert_eq!(x, 12);
    }

    context! {
        mod context_2 {
            a: const u8,
            b: ref u16,
            c (c_mut): mut u32,
        }
    }

    pub type Context2 = context_2::Context;

    #[test]
    fn test_context_2() {
        let mut x = 3;
        let res = Context2::call(1, &2, &mut x, |context| {
            assert_eq!(context.a(), 1u8);
            assert_eq!(context.b(), &2u16);
            assert_eq!(replace(context.c_mut(), 12), 3u32);
            "res"
        });
        assert_eq!(res, "res");
        assert_eq!(x, 12);
    }
}