dyn_context/
lib.rs

1#![deny(warnings)]
2#![doc(test(attr(deny(warnings))))]
3#![doc(test(attr(allow(dead_code))))]
4#![doc(test(attr(allow(unused_variables))))]
5#![allow(unknown_lints)]
6#![allow(clippy::mixed_attributes_style)]
7#![allow(clippy::needless_doctest_main)]
8
9#![no_std]
10
11#[doc=include_str!("../README.md")]
12type _DocTestReadme = ();
13
14#[doc(hidden)]
15pub use core::concat as std_concat;
16#[doc(hidden)]
17pub use core::ops::FnOnce as std_ops_FnOnce;
18#[doc(hidden)]
19pub use core::stringify as std_stringify;
20#[doc(hidden)]
21pub use generics::concat as generics_concat;
22#[doc(hidden)]
23pub use generics::parse as generics_parse;
24#[doc(hidden)]
25pub use indoc::indoc as indoc_indoc;
26#[doc(hidden)]
27pub use paste::paste as paste_paste;
28
29/// Creates structure, allowing to pack several references into
30/// a one reference to a `'static` type.
31///
32/// Accepts input in the following form:
33///
34/// ```ignore
35/// $(#[$attr:meta])*
36/// $vis:vis struct $name:ident $(<$generics> $(where $where_clause)?)? {
37///     $($(
38///         $field:ident : $($lt:lifetime ref | $lt:lifetime mut | const) $ty:ty
39///     ),+ $(,)?)?
40/// }
41/// ```
42///
43/// In Rust, lifetimes are intrusive, and sometimes it can lead to
44/// an inadequately complex code. Moreover, in some cases it can lead to an _impossible code_,
45/// means code so complex, so it can not make to compiles, even it is logically meaningful.
46/// (Such situations could occur because Rust does not support existential types
47/// with infinite parameters list.)
48///
49/// The `free_lifetimes!` macro allows to "compress" several lifetimes into a one.
50///
51/// For example, you can pack together two `str` references and use them with
52/// a code, requiring a `'static` type:
53///
54/// ```rust
55/// # use dyn_context::{free_lifetimes};
56/// #
57/// free_lifetimes! {
58///     struct DoubleStr {
59///         str_1: 'str_1 ref str,
60///         str_2: 'str_2 ref str
61///     }
62/// }
63///
64/// fn call_back<T: 'static, R>(t: &T, callback: impl FnOnce(&T) -> R) -> R {
65///     callback(t)
66/// }
67///
68/// # fn main() {
69/// let s_1 = String::from("str1");
70/// let s_2 = String::from("str2");
71/// let r = DoubleStrBuilder {
72///     str_1: &s_1[1..],
73///     str_2: &s_2[2..]
74/// }.build_and_then(|double_str| call_back(double_str, |double_str| {
75///     format!("{}{}", double_str.str_1(), double_str.str_2())
76/// }));
77/// assert_eq!(r, "tr1r2");
78/// # }
79/// ```
80#[macro_export]
81macro_rules! free_lifetimes {
82    (
83        $(#[$attr:meta])*
84        $vis:vis struct $name:ident $($token:tt)*
85    ) => {
86        $crate::generics_parse! {
87            $crate::free_lifetimes_impl {
88                @struct [$([$attr])*] [$vis] [$name]
89            }
90            $($token)*
91        }
92    };
93}
94
95#[doc(hidden)]
96#[macro_export]
97macro_rules! free_lifetimes_impl {
98    (
99        @struct [$([$attr:meta])*] [$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
100        {
101            $($(
102                $field:ident : $($field_lt:lifetime)? $field_mod:ident $field_ty:ty
103            ),+ $(,)?)?
104        }
105    ) => {
106        $crate::free_lifetimes_impl! {
107            @impl struct
108            [$name] [$([$attr])*] [$vis] [ty] [this] [builder]
109            [$($g)*] [$($r)*] [$($w)*]
110            [] [] [] [] []
111            [$($([$field : $($field_lt)? $field_mod $field_ty])+)?]
112        }
113    };
114    (
115        @struct [$([$attr:meta])*] [$vis:vis] [$name:ident] [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
116        $($body:tt)*
117    ) => {
118        $crate::std_compile_error!($crate::indoc_indoc!("
119            invalid free lifetimes struct definition, allowed form is
120
121            $(#[$attr:meta])*
122            $vis:vis struct $name:ident $(<$generics> $(where $where_clause)?)? {
123                $($(
124                    $field:ident : $($lt:lifetime ref | $lt:lifetime mut | const) $ty:ty
125                ),+ $(,)?)?
126            }
127
128        "));
129    };
130    (
131        @impl struct
132        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
133        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
134        [$($builder_lts:tt)*]
135        [$($builder_fields:tt)*]
136        [$($struct_fields:tt)*]
137        [$($ctor_assignments:tt)*]
138        [$($struct_methods:tt)*]
139        [[$field:ident : $field_lt:lifetime ref $field_ty:ty] $($other_fields:tt)*]
140    ) => {
141        $crate::free_lifetimes_impl! {
142            @impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
143            [
144                $($builder_lts)*
145                [$field_lt]
146            ]
147            [
148                $($builder_fields)*
149                pub $field : & $field_lt $field_ty,
150            ]
151            [
152                $($struct_fields)*
153                $field : &'static $field_ty,
154            ]
155            [
156                $($ctor_assignments)*
157                $field : unsafe { &*($builder . $field as *const $field_ty) },
158            ]
159            [
160                $($struct_methods)*
161                $vis fn $field (&self) -> &$field_ty { self.$field }
162            ]
163            [$($other_fields)*]
164        }
165    };
166    (
167        @impl struct
168        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
169        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
170        [$($builder_lts:tt)*]
171        [$($builder_fields:tt)*]
172        [$($struct_fields:tt)*]
173        [$($ctor_assignments:tt)*]
174        [$($struct_methods:tt)*]
175        [[$field:ident : $field_lt:lifetime mut $field_ty:ty] $($other_fields:tt)*]
176    ) => {
177        $crate::free_lifetimes_impl! {
178            @impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
179            [
180                $($builder_lts)*
181                [$field_lt]
182            ]
183            [
184                $($builder_fields)*
185                pub $field : & $field_lt mut $field_ty,
186            ]
187            [
188                $($struct_fields)*
189                $field : &'static mut $field_ty,
190            ]
191            [
192                $($ctor_assignments)*
193                $field : unsafe { &mut *($builder . $field as *mut $field_ty) },
194            ]
195            [
196                $($struct_methods)*
197
198                #[allow(dead_code)]
199                $vis fn $field (&self) -> &$field_ty { self.$field }
200
201                #[allow(dead_code)]
202                $vis fn [< $field _mut >] (&mut self) -> &mut $field_ty { self.$field }
203            ]
204            [$($other_fields)*]
205        }
206    };
207    (
208        @impl struct
209        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
210        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
211        [$($builder_lts:tt)*]
212        [$($builder_fields:tt)*]
213        [$($struct_fields:tt)*]
214        [$($ctor_assignments:tt)*]
215        [$($struct_methods:tt)*]
216        [[$field:ident : const $field_ty:ty] $($other_fields:tt)*]
217    ) => {
218        $crate::free_lifetimes_impl! {
219            @impl struct [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
220            [$($builder_lts)*]
221            [
222                $($builder_fields)*
223                pub $field : $field_ty,
224            ]
225            [
226                $($struct_fields)*
227                $field : $field_ty,
228            ]
229            [
230                $($ctor_assignments)*
231                $field: $builder . $field,
232            ]
233            [
234                $($struct_methods)*
235                $vis fn $field (&self) -> $field_ty { self.$field }
236            ]
237            [$($other_fields)*]
238        }
239    };
240    (
241        @impl struct
242        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
243        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
244        [$($builder_lts:tt)*]
245        [$($builder_fields:tt)*]
246        [$($struct_fields:tt)*]
247        [$($ctor_assignments:tt)*]
248        [$($struct_methods:tt)*]
249        [[$field:ident : $($field_lt:lifetime)? $field_mod:ident $field_ty:ty] $($other_fields:tt)*]
250    ) => {
251        $crate::std_compile_error!($crate::std_concat!(
252            "invalid free lifetimes struct field '",
253            $crate::std_stringify!($field : $($field_lt)? $field_mod $field_ty),
254            "\
255                ', allowed form is '\
256                $field:ident : $($lt:lifetime ref | $lt:lifetime mut | const) $ty:ty\
257                '\
258            ",
259        ));
260    };
261    (
262        @impl struct
263        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
264        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
265        [$($([$builder_lts:tt])+)?]
266        [$($builder_fields:tt)*]
267        [$($struct_fields:tt)*]
268        [$($ctor_assignments:tt)*]
269        [$($struct_methods:tt)*]
270        []
271    ) => {    
272        $crate::generics_concat! {
273            $crate::free_lifetimes_impl {
274                @impl [$name] [$([$attr])*] [$vis] [$ty] [$this] [$builder] [$($g)*] [$($r)*] [$($w)*]
275                [$($builder_fields)*]
276                [$($struct_fields)*]
277                [$($ctor_assignments)*]
278                [$($struct_methods)*]
279            }
280            [$( < $($builder_lts),+ > )?] [$( < $($builder_lts),+ > )?] [],
281            [$($g)*] [$($r)*] [$($w)*]
282        }
283    };
284    (
285        @impl
286        [$name:ident] [$([$attr:meta])*] [$vis:vis] [$ty:ident] [$this:ident] [$builder:ident]
287        [$($g:tt)*] [$($r:tt)*] [$($w:tt)*]
288        [$($builder_fields:tt)*]
289        [$($struct_fields:tt)*]
290        [$($ctor_assignments:tt)*]
291        [$($struct_methods:tt)*]
292        [$($builder_g:tt)*] [$($builder_r:tt)*] [$($builder_w:tt)*]
293    ) => {
294        $crate::paste_paste! {
295            $vis struct [< $name Builder >] $($builder_g)* $($w)* {
296                $($builder_fields)*
297            }
298
299            impl $($builder_g)* [< $name Builder >] $($builder_r)* $($builder_w)* {
300                /// Converts regular structure into a special structure with "erased" field lifetimes
301                /// and passes result to provided function.
302                $vis fn build_and_then<FreeLifetimesStructBuildReturnType>(
303                    &mut self,
304                    f: impl $crate::std_ops_FnOnce(&mut $name $($r)*) -> FreeLifetimesStructBuildReturnType
305                ) -> FreeLifetimesStructBuildReturnType {
306                    let $builder = self;
307                    let mut free_lifetimes = $name {
308                        $($ctor_assignments)*
309                    };
310                    f(&mut free_lifetimes)
311                }
312            }
313                        
314            $(#[$attr])*
315            $vis struct $name $($g)* $($w)* {
316                $($struct_fields)*
317            }
318
319            impl $($g)* $name $($r)* $($w)* {
320                $($struct_methods)*
321            }
322        }
323    };
324}
325
326#[cfg(docsrs)]
327pub mod example {
328    //! [`free_lifetimes`] macro expansion example.
329    //!
330    //! ```ignore
331    //! free_lifetimes! {
332    //!     pub struct FreeLifetimesStruct {
333    //!         data: 'data mut Data,
334    //!         str_data: 'str_data ref str,
335    //!         id: const usize,
336    //!     }
337    //! }
338    //!
339    //! ```
340
341    pub struct Data {
342        pub x: i16,
343        pub y: i16
344    }
345
346    free_lifetimes! {
347        pub struct FreeLifetimesStruct {
348            data: 'data mut Data,
349            str_data: 'str_data ref str,
350            id: const usize,
351        }
352    }
353}
354
355#[cfg(test)]
356mod test {
357    use core::mem::replace;
358    use core::ops::Deref;
359
360    free_lifetimes! {
361        struct State1 {
362            a: const u8,
363            b: 'b ref u16,
364            c: 'c mut u32,
365        }
366    }
367    
368    #[test]
369    fn test_state_1() {
370        let mut x = 3;
371        let res = State1Builder {
372            a: 1,
373            b: &2,
374            c: &mut x
375        }.build_and_then(|state| {
376            assert_eq!(state.a(), 1u8);
377            assert_eq!(state.b(), &2u16);
378            assert_eq!(replace(state.c_mut(), 12), 3u32);
379            "res"
380        });
381        assert_eq!(res, "res");
382        assert_eq!(x, 12);
383    }
384
385    #[test]
386    fn test_state_1_const() {
387        let mut x = 3;
388        let res = State1Builder {
389            a: 1,
390            b: &2,
391            c: &mut x
392        }.build_and_then(|state| {
393            assert_eq!(state.a(), 1u8);
394            assert_eq!(state.b(), &2u16);
395            assert_eq!(state.c(), &3u32);
396            "res"
397        });
398        assert_eq!(res, "res");
399        assert_eq!(x, 3);
400    }
401
402    #[allow(dead_code)]
403    #[derive(Debug, Clone, Copy)]
404    struct PrivStr;
405
406    free_lifetimes! {
407        #[derive(Debug)]
408        pub struct Items<ItemType: 'static> {
409            items: 'items ref [ItemType],
410        }
411    }
412
413    impl<ItemType> Deref for Items<ItemType> {
414        type Target = [ItemType];
415
416        fn deref(&self) -> &Self::Target {
417            self.items()
418        }
419    }
420
421    #[test]
422    fn free_lifetimes_with_generics() {
423        let items = &[1, 2, 3];
424        let sum: u8 = ItemsBuilder {
425            items
426        }.build_and_then(|items| {
427            items.iter().sum()
428        });
429        assert_eq!(sum, 6);
430    }
431}