#![cfg_attr(feature="nightly", feature(external_doc))]
#![cfg_attr(feature="nightly", feature(never_type))]
#![deny(warnings)]
#![no_std]
#[doc(include="../README.md")]
#[cfg(feature="nightly")]
type _DocTestReadme = ();
use core::any::{TypeId, Any, type_name};
#[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 core::compile_error as std_compile_error;
#[doc(hidden)]
pub use core::concat as std_concat;
#[doc(hidden)]
pub use core::ops::FnOnce as std_ops_FnOnce;
#[doc(hidden)]
pub use core::option::Option as std_option_Option;
#[doc(hidden)]
pub use core::stringify as std_stringify;
#[doc(hidden)]
pub use generics::concat as generics_concat;
#[doc(hidden)]
pub use generics::parse as generics_parse;
#[doc(hidden)]
pub use paste::paste as paste_paste;
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 { }
free_lifetimes! {
struct ContextSum {
a: 'a ref dyn Context,
b: 'b ref dyn Context,
}
}
impl Context for ContextSum {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
if let Some(r) = self.a().get_raw(ty) {
Some(r)
} else if let Some(r) = self.b().get_raw(ty) {
Some(r)
} else {
None
}
}
fn get_mut_raw(&mut self, _ty: TypeId) -> Option<&mut dyn Any> {
unreachable!()
}
}
free_lifetimes! {
struct ContextSumMut {
a: 'a mut dyn Context,
b: 'b mut dyn Context,
}
}
impl Context for ContextSumMut {
fn get_raw(&self, ty: TypeId) -> Option<&dyn Any> {
if let Some(r) = self.a().get_raw(ty) {
Some(r)
} else if let Some(r) = self.b().get_raw(ty) {
Some(r)
} else {
None
}
}
fn get_mut_raw(&mut self, ty: TypeId) -> Option<&mut dyn Any> {
let r = if let Some(r) = self.a_mut().get_mut_raw(ty) {
Some(r as *mut _)
} else if let Some(r) = self.b_mut().get_mut_raw(ty) {
Some(r as *mut _)
} else {
None
};
r.map(|x| unsafe { &mut *x })
}
}
pub trait ContextRef {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn Context) -> T, other: &dyn Context) -> T;
}
impl<C: Context> ContextRef for &C {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn Context) -> T, other: &dyn Context) -> T {
ContextSumBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
impl ContextRef for &dyn Context {
fn merge_and_then<T>(self, f: impl FnOnce(&dyn Context) -> T, other: &dyn Context) -> T {
ContextSumBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
pub trait ContextRefMut {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn Context) -> T, other: &mut dyn Context) -> T;
}
impl<C: Context> ContextRefMut for &mut C {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn Context) -> T, other: &mut dyn Context) -> T {
ContextSumMutBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
impl ContextRefMut for &mut dyn Context {
fn merge_mut_and_then<T>(self, f: impl FnOnce(&mut dyn Context) -> T, other: &mut dyn Context) -> T {
ContextSumBuilder {
a: self,
b: other,
}.build_and_then(|x| f(x))
}
}
#[macro_export]
macro_rules! Context {
(
()
$vis:vis struct $name:ident $($body:tt)*
) => {
$crate::generics_parse! {
$crate::Context { @impl [$name] } $($body)*
}
};
(
()
$vis:vis enum $name:ident $($body:tt)*
) => {
$crate::generics_parse! {
$crate::Context { @impl [$name] } $($body)*
}
};
(
@impl [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*] $($body:tt)*
) => {
impl $($g)* $crate::Context for $name $($r)* $($w)* {
fn get_raw(
&self,
ty: $crate::std_any_TypeId
) -> $crate::std_option_Option<&dyn $crate::std_any_Any> {
if ty == $crate::std_any_TypeId::of::<Self>() {
$crate::std_option_Option::Some(self)
} else {
$crate::std_option_Option::None
}
}
fn get_mut_raw(
&mut self,
ty: $crate::std_any_TypeId
) -> $crate::std_option_Option<&mut dyn $crate::std_any_Any> {
if ty == $crate::std_any_TypeId::of::<Self>() {
$crate::std_option_Option::Some(self)
} else {
$crate::std_option_Option::None
}
}
}
};
}
#[macro_export]
macro_rules! free_lifetimes {
(
$(#[$attr:meta])*
$vis:vis struct $name:ident $($body:tt)*
) => {
$crate::generics_parse! {
$crate::free_lifetimes_impl {
@struct [$([$attr])*] [$vis] [$name]
}
$($body)*
}
};
}
#[doc(hidden)]
#[macro_export]
macro_rules! free_lifetimes_impl {
(
@struct [$([$attr:meta])*] [$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
{
$($(
$field:ident : $($field_lt:lifetime)? $field_mod:ident $field_ty:ty
),+ $(,)?)?
}
) => {
$crate::free_lifetimes_impl! {
@impl struct
[$name] [$([$attr])*] [$vis] [ty] [this] [builder]
[$($g)*] [$($r)*] [$($w)*]
[] [] [] [] []
[$($([$field : $($field_lt)? $field_mod $field_ty])+)?]
}
};
(
@struct [$([$attr:meta])*] [$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
$($body:tt)*
) => {
$crate::std_compile_error!("\
invalid free lifetimes struct definition, allowed form is\n\
\n\
$(#[attr])* $vis struct $name {\n\
$field_1_name: $('field_1_lt ref | 'field_1_lt mut | const) $field_1_type,\n\
$field_2_name: $('field_2_lt ref | 'field_2_lt mut | const) $field_2_type,\n\
...\n\
}\n\
\n\
");
};
(
@impl struct
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($builder_lts:tt)*]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[[$field:ident : $field_lt:lifetime ref $field_ty:ty] $($other_fields:tt)*]
) => {
$crate::free_lifetimes_impl! {
@impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
[
$($builder_lts)*
[$field_lt]
]
[
$($builder_fields)*
pub $field : & $field_lt $field_ty,
]
[
$($struct_fields)*
$field : *const $field_ty,
]
[
$($ctor_assignments)*
$field : $builder . $field as *const $field_ty,
]
[
$($struct_methods)*
$vis fn $field (&self) -> &$field_ty { unsafe { &*self.$field } }
]
[$($other_fields)*]
}
};
(
@impl struct
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($builder_lts:tt)*]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[[$field:ident : $field_lt:lifetime mut $field_ty:ty] $($other_fields:tt)*]
) => {
$crate::free_lifetimes_impl! {
@impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
[
$($builder_lts)*
[$field_lt]
]
[
$($builder_fields)*
pub $field : & $field_lt mut $field_ty,
]
[
$($struct_fields)*
$field : *mut $field_ty,
]
[
$($ctor_assignments)*
$field : $builder . $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
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($builder_lts:tt)*]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[[$field:ident : const $field_ty:ty] $($other_fields:tt)*]
) => {
$crate::free_lifetimes_impl! {
@impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
[$($builder_lts)*]
[
$($builder_fields)*
pub $field : $field_ty,
]
[
$($struct_fields)*
$field : $field_ty,
]
[
$($ctor_assignments)*
$field: $builder . $field,
]
[
$($struct_methods)*
$vis fn $field (&self) -> $field_ty { self.$field }
]
[$($other_fields)*]
}
};
(
@impl struct
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($builder_lts:tt)*]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[[$field:ident : $($field_lt:lifetime)? $field_mod:ident $field_ty:ty] $($other_fields:tt)*]
) => {
$crate::std_compile_error!($crate::std_concat!(
"invalid free lifetimes struct field '",
$crate::std_stringify!($field : $($field_lt)? $field_mod $field_ty),
"', allowed form is '$name: $('lt ref | 'lt mut | const) $type'",
));
};
(
@impl struct
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($([$builder_lts:tt])+)?]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[]
) => {
$crate::generics_concat! {
$crate::free_lifetimes_impl {
@impl [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
[$($builder_fields)*]
[$($struct_fields)*]
[$($ctor_assignments)*]
[$($struct_methods)*]
}
[$( < $($builder_lts),+ > )?] [$( < $($builder_lts),+ > )?] [],
[$($g)*] [$($r)*] [$($w)*]
}
};
(
@impl
[$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
[$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
[$($builder_fields:tt)*]
[$($struct_fields:tt)*]
[$($ctor_assignments:tt)*]
[$($struct_methods:tt)*]
[$($builder_g:tt)*] [$($builder_r:tt)*] [$($builder_w:tt)*]
) => {
$crate::paste_paste! {
$vis struct [< $name Builder >] $($builder_g)* $($w)* {
$($builder_fields)*
}
impl $($builder_g)* [< $name Builder >] $($builder_r)* $($builder_w)* {
$vis fn build_and_then<FreeLifetimesStructBuildReturnType>(
&mut self,
f: impl $crate::std_ops_FnOnce(&mut $name) -> FreeLifetimesStructBuildReturnType
) -> FreeLifetimesStructBuildReturnType {
let $builder = self;
let mut free_lifetimes = $name {
$($ctor_assignments)*
};
f(&mut free_lifetimes)
}
}
$(#[$attr])*
$vis struct $name $($g)* $($w)* {
$($struct_fields)*
}
impl $($g)* $name $($r)* $($w)* {
$($struct_methods)*
}
unsafe impl $($g)* Send for $name $($r)* $($w)* { }
unsafe impl $($g)* Sync for $name $($r)* $($w)* { }
}
};
}
#[cfg(docsrs)]
pub mod example {
pub struct Data {
pub x: i16,
pub y: i16
}
free_lifetimes! {
pub struct FreeLifetimesStruct {
data: 'data mut Data,
str_data: 'str_data ref str,
id: const usize,
}
}
}
#[cfg(test)]
mod test {
use crate::{ContextExt, ContextRefMut};
use core::mem::replace;
use macro_attr_2018::macro_attr;
free_lifetimes! {
struct Context1 {
a: const u8,
b: 'b ref u16,
c: 'c mut u32,
}
}
Context!(() struct Context1 { .. });
#[test]
fn test_context_1() {
let mut x = 3;
let res = Context1Builder {
a: 1,
b: &2,
c: &mut x
}.build_and_then(|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);
}
#[test]
fn test_context_1_const() {
let mut x = 3;
let res = Context1Builder {
a: 1,
b: &2,
c: &mut x
}.build_and_then(|context| {
assert_eq!(context.a(), 1u8);
assert_eq!(context.b(), &2u16);
assert_eq!(context.c(), &3u32);
"res"
});
assert_eq!(res, "res");
assert_eq!(x, 3);
}
macro_attr! {
#[derive(Debug, Clone, Copy, Context!)]
struct PrivStr;
}
#[test]
fn test_context_4() {
let mut x = 3;
let res = Context1Builder {
a: 1,
b: &2,
c: &mut x
}.build_and_then(|context| {
context.merge_mut_and_then(|context| {
assert_eq!(context.get::<Context1>().a(), 1u8);
assert_eq!(context.get::<Context1>().b(), &2u16);
assert_eq!(replace(context.get_mut::<Context1>().c_mut(), 12), 3u32);
"res"
}, &mut PrivStr)
});
assert_eq!(res, "res");
assert_eq!(x, 12);
}
}