#![cfg_attr(feature="nightly", feature(never_type))]
#![deny(warnings)]
#![no_std]
use core::any::{TypeId, Any, type_name};
#[doc(hidden)]
pub use core::ops::FnOnce as std_ops_FnOnce;
#[doc(hidden)]
pub use core::any::Any as std_any_Any;
#[doc(hidden)]
pub use core::any::TypeId as std_any_TypeId;
#[doc(hidden)]
pub use paste::paste as paste_paste;
#[doc(hidden)]
pub use core::stringify as std_stringify;
#[doc(hidden)]
pub use core::concat as std_concat;
#[doc(hidden)]
pub use core::compile_error as std_compile_error;
pub trait Context: 'static {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any>;
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any>;
}
#[cfg(feature="nightly")]
impl Context for ! {
fn get_raw(&self, _ty: TypeId) -> Option<&dyn Any> { Some(self) }
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> { Some(self) }
}
impl Context for () {
fn get_raw(&self, _ty: TypeId) -> Option<&dyn Any> { None }
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> { None }
}
pub trait ContextExt: Context {
fn get<T: 'static>(&self) -> &T {
self.get_raw(TypeId::of::<T>())
.unwrap_or_else(|| panic!("{} required", type_name::<T>()))
.downcast_ref::<T>().expect("invalid cast")
}
fn get_mut<T: 'static>(&mut self) -> &mut T {
self.get_mut_raw(TypeId::of::<T>())
.unwrap_or_else(|| panic!("{} required", type_name::<T>()))
.downcast_mut::<T>().expect("invalid cast")
}
}
impl<T: Context + ?Sized> ContextExt for T { }
pub trait TrivialContext: 'static { }
impl<T: TrivialContext> Context for T {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
if ty == TypeId::of::<Self>() {
Some(self)
} else {
None
}
}
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any> {
if ty == TypeId::of::<Self>() {
Some(self)
} else {
None
}
}
}
#[macro_export]
macro_rules! context {
(
$vis:vis struct $name:ident
$(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ $(,)?>)?
{
$($(
$field_1:ident $($field_2:ident)? : $field_mod:ident $field_ty:ty
),+ $(,)?)?
}
) => {
$crate::context! {
@impl struct [static]
[$name] [$vis] [ty] [this]
[ $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? ]
[ $(< $( $lt ),+ >)? ]
[] [] [] []
[ $($($field_1 $($field_2)? : $field_mod $field_ty),+)? ]
}
};
(
$vis:vis dyn struct $name:ident
$(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ $(,)?>)?
{
$($(
$field_1:ident $($field_2:ident)? : $field_mod:ident $field_ty:ty
),+ $(,)?)?
}
) => {
$crate::context! {
@impl struct [dyn]
[$name] [$vis] [ty] [this]
[ $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? ]
[ $(< $( $lt ),+ >)? ]
[] [] [] []
[ $($($field_1 $($field_2)? : $field_mod $field_ty),+)? ]
}
$crate::context! {
@impl trait
[$name] [$vis] [ty] [this]
[ $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? ]
[ $(< $( $lt ),+ >)? ]
[] []
[ $($($field_1 $($field_2)? : $field_mod $field_ty),+)? ]
}
};
(
@impl struct [static]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[dyn $($field_2:ident)? : $field_mod:ident $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::std_compile_error!($crate::std_concat!(
"dynamic fields in non-dynamic context are not allowed, ",
"consider changing 'struct' to 'dyn struct' or remove 'dyn' from field definition: '",
$crate::std_stringify!(dyn $($field_2)? : $field_mod $field_ty),
"'"
));
};
(
@impl struct [$channel:ident]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$field:ident : ref $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [$channel] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : *const $field_ty,
]
[
$($ctor_args)*
$field : &$field_ty,
]
[
$($ctor_assignments)*
$field : $field as *const $field_ty,
]
[
$($struct_methods)*
$vis fn $field (&self) -> &$field_ty { unsafe { &*self.$field } }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [dyn]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[dyn $field:ident : ref $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [dyn] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : *const $field_ty,
]
[
$($ctor_args)*
$field : &$field_ty,
]
[
$($ctor_assignments)*
$field : $field as *const $field_ty,
]
[
$($struct_methods)*
$vis fn $field (&self) -> &$field_ty { unsafe { &*self.$field } }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [$channel:ident]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$field:ident : mut $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [$channel] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : *mut $field_ty,
]
[
$($ctor_args)*
$field : &mut $field_ty,
]
[
$($ctor_assignments)*
$field : $field as *mut $field_ty,
]
[
$($struct_methods)*
#[allow(dead_code)]
$vis fn $field (&self) -> &$field_ty { unsafe { &*self.$field } }
#[allow(dead_code)]
$vis fn [< $field _mut >] (&mut self) -> &mut $field_ty { unsafe { &mut *self.$field } }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [dyn]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[dyn $field:ident : mut $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [dyn] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : *mut $field_ty,
]
[
$($ctor_args)*
$field : &mut $field_ty,
]
[
$($ctor_assignments)*
$field : $field as *mut $field_ty,
]
[
$($struct_methods)*
#[allow(dead_code)]
$vis fn $field (&self) -> &$field_ty { unsafe { &*self.$field } }
#[allow(dead_code)]
$vis fn [< $field _mut >] (&mut self) -> &mut $field_ty { unsafe { &mut *self.$field } }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [$channel:ident]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$field:ident : const $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [$channel] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : $field_ty,
]
[
$($ctor_args)*
$field : $field_ty,
]
[
$($ctor_assignments)*
$field,
]
[
$($struct_methods)*
$vis fn $field (&self) -> $field_ty { self.$field }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [dyn]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$field:ident : const $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl struct [dyn] [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($struct_fields)*
$field : $field_ty,
]
[
$($ctor_args)*
$field : $field_ty,
]
[
$($ctor_assignments)*
$field,
]
[
$($struct_methods)*
$vis fn $field (&self) -> $field_ty { self.$field }
]
[$($($other_fields)+)?]
}
};
(
@impl struct [$channel:ident]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$field_1:ident $($field_2:ident)? : $field_mod:ident $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::std_compile_error!($crate::std_concat!(
"invalid context field '",
$crate::std_stringify!($field_1 $($field_2)? : $field_mod $field_ty),
"', allowed form is '[dyn] $name: (const | ref | mut) $type'",
));
};
(
@impl struct [$channel:ident]
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($struct_fields:tt)*]
[$($ctor_args:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[]
) => {
$crate::paste_paste! {
$vis struct $name $($g)* {
$($struct_fields)*
}
impl $($g)* $name $($r)* {
$vis fn call<ContextCallReturnType>(
$($ctor_args)*
f: impl $crate::std_ops_FnOnce(&mut Self) -> ContextCallReturnType
) -> ContextCallReturnType {
let mut context = Self {
$($ctor_assignments)*
};
f(&mut context)
}
$($struct_methods)*
}
unsafe impl $($g)* Send for $name $($r)* { }
unsafe impl $($g)* Sync for $name $($r)* { }
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[$field:ident : ref $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if $ty == $crate::std_any_TypeId::of::<$field_ty>() {
Some($this.$field())
} else
]
[
$($trait_impl_mut)*
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[dyn $field:ident : ref $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if let Some(res) = $crate::Context::get_raw($this.$field(), $ty) {
Some(res)
} else
]
[
$($trait_impl_mut)*
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[$field:ident : mut $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if $ty == $crate::std_any_TypeId::of::<$field_ty>() {
Some($this.$field())
} else
]
[
$($trait_impl_mut)*
if $ty == $crate::std_any_TypeId::of::<$field_ty>() {
Some($this. [< $field _mut >] ())
} else
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[dyn $field:ident : mut $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if let Some(res) = $crate::Context::get_raw($this.$field(), $ty) {
Some(res)
} else
]
[
$($trait_impl_mut)*
if let Some(res) = $crate::Context::get_mut_raw($this. [< $field _mut >] (), $ty) {
Some(res)
} else
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[$field:ident : const $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if $ty == $crate::std_any_TypeId::of::<$field_ty>() {
Some(&$this.$field)
} else
]
[
$($trait_impl_mut)*
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[dyn $field:ident : const $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
$crate::context! {
@impl trait [$name] [$vis] [$ty] [$this] [$($g)*] [$($r)*]
[
$($trait_impl_ref)*
if let Some(res) = $crate::Context::get_raw(&$this.$field, $ty) {
Some(res)
} else
]
[
$($trait_impl_mut)*
]
[$($($other_fields)+)?]
}
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[$field_1:ident $($field_2:ident)? : $field_mod:ident $field_ty:ty $(, $($other_fields:tt)+)?]
) => {
};
(
@impl trait
[$name:ident] [$vis:vis] [$ty:ident] [$this:ident] [$($g:tt)*] [$($r:tt)*]
[$($trait_impl_ref:tt)*]
[$($trait_impl_mut:tt)*]
[]
) => {
$crate::paste_paste! {
impl $($g)* $crate::Context for $name $($r)* {
fn get_raw(&self, $ty: $crate::std_any_TypeId) -> Option<&dyn $crate::std_any_Any> {
let $this = self;
$($trait_impl_ref)*
{ None }
}
fn get_mut_raw(&mut self, $ty: $crate::std_any_TypeId) -> Option<&mut dyn $crate::std_any_Any> {
let $this = self;
$($trait_impl_mut)*
{ None }
}
}
}
};
}
#[cfg(docsrs)]
pub mod example {
pub struct Data {
pub x: i16,
pub y: i16
}
context! {
pub struct StrData {
r: ref str,
}
}
context! {
pub dyn struct ExampleContext {
data: ref Data,
str_data: mut StrData,
id: const usize,
}
}
}
#[cfg(test)]
mod test {
use crate::ContextExt;
use core::mem::replace;
context! {
struct Context1 {
a: const u8,
b: ref u16,
c: mut u32,
}
}
#[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! {
dyn struct Context2 {
a: const u8,
b: ref u16,
c: mut u32,
}
}
#[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);
assert_eq!(context.get::<u32>(), &12);
"res"
});
assert_eq!(res, "res");
assert_eq!(x, 12);
}
#[derive(Debug, Clone, Copy)]
struct PrivStr;
context! {
dyn struct Context3 {
a: const PrivStr,
b: ref u16,
c: mut u32,
}
}
#[test]
fn test_context_3() {
let mut x = 3;
let res = Context3::call(PrivStr, &2, &mut x, |context| {
let _ = context.a();
assert_eq!(context.b(), &2u16);
assert_eq!(replace(context.c_mut(), 12), 3u32);
assert_eq!(context.get::<u32>(), &12);
assert_eq!(context.get::<u16>(), &2);
"res"
});
assert_eq!(res, "res");
assert_eq!(x, 12);
}
context! {
dyn struct Context4 {
dyn c_3: mut Context3,
b: const u8
}
}
#[test]
fn test_context_4() {
let mut x = 3;
let res = Context3::call(PrivStr, &2, &mut x, |context| {
Context4::call(context, 7, |context| {
assert_eq!(context.b(), 7);
assert_eq!(replace(context.get_mut::<u32>(), 9), 3);
assert_eq!(context.get::<u8>(), &7);
"res"
})
});
assert_eq!(res, "res");
assert_eq!(x, 9);
}
}