#[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(docsrs)]
pub mod example {
use core::fmt::Display;
pub struct Data {
pub x: i16,
pub y: i16
}
context! {
mod example_context {
data (data_mut): mut Data,
display: ref dyn Display,
id: const usize,
}
}
pub use example_context::Context as ExampleContext;
}
#[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);
}
}