devela/code/util/
const.rs

1// devela::code::util::const
2//
3//! Defines the CONST! macro.
4//
5// TODO
6// - allow to pass different attributes for the macro `$vis use` statement,
7//   in order to be able to use attribute `macro_export` when $vis is pub.
8//   (maybe there's a cleaner solution with compile macros?) (same($vis, pub))
9
10/// A helper for constructing macro constants.
11///
12/// It accepts either a series of expressions or a series of functions.
13///
14/// # Examples
15/// ```
16/// # use devela::CONST;
17/// CONST!{ /* Supports empty declarations */ }
18///
19/// // Supports any expresion
20/// CONST!{EXPR = 2 * 15 / 3}
21/// assert_eq![EXPR![], 10u8];
22/// assert_eq![EXPR![], 10i8];
23///
24/// CONST!{
25///     /// Supports docs, attributes, and visibility.
26///     #[macro_export] // attribute needed if public
27///     pub ARRAY = [1, 2, 3]
28/// }
29/// assert_eq![ARRAY![], [1u16, 2, 3]];
30/// assert_eq![ARRAY![], [1i32, 2, 3]];
31///
32/// // Supports multiple definitions of constant expressions, ended with ;
33/// CONST! {
34///     DOC_1 = "Document expression." ;
35///     DOC_2 = "Document expression." ;
36/// }
37/// assert_eq![DOC_1![], "Document expression."];
38///
39/// // A good use-case is for repeated documentation
40/// /// Function 1, version a.
41/// #[doc = DOC_1!()]
42/// pub fn version_1a() {}
43/// /// Function 1, version b.
44/// #[doc = DOC_1!()]
45/// pub fn version_1b() {}
46///
47/// // Supports multiple definitions of constants functions, ended with ;
48/// CONST! {
49///     /// Supports *const* functions.
50///     FN_1 =
51///     /// Returns `n × 5`.
52///     #[inline] #[must_use]
53///     pub const fn fn_1(n: i32) -> i64 { (n * 5) as i64 };
54///
55///     /// You can repeat functions.
56///     pub(crate) FN_2 = pub const fn fn_2(c: char) { };
57///
58///     /// Supports optional *unsafe*.
59///     FN_3 = pub const unsafe fn fn_3() {};
60///
61///     // NOTE: It's not possible to mix expressions and functions in the same invocation.
62///     // EXPR_ERR = "Compile fails if this line is uncommented";
63/// }
64/// pub struct Fns;
65/// impl Fns {
66///     FN_1!{}
67///     FN_2!{}
68///     FN_3!{}
69/// }
70///
71/// assert_eq![Fns::fn_1(0i32), 0i64];
72/// assert_eq![Fns::fn_1(5), 25];
73/// let _: () = Fns::fn_2('a');
74/// unsafe { Fns::fn_3(); }
75///
76/// // Supports giving a shared visibility for all defined constants
77/// CONST! { pub(crate),
78///     E1 = 1 + 1;
79///     E2 = 2 + 2;
80///     // pub E3 = 3 + 3; // shared visibility can't be overriden
81/// }
82/// CONST! { pub(crate),
83///     F1 = pub const fn f1(a: i32) -> i32 { a + 1 };
84///     F2 = pub const fn f2(a: i32) -> i32 { a + 2 };
85///     // pub F3 = pub const fn f3(a: i32) -> i32 { a + 3 };
86/// }
87/// ```
88// Related links
89// - https://doc.rust-lang.org/reference/items/external-blocks.html#functions
90#[doc(hidden)]
91#[macro_export]
92macro_rules! _CONST {
93    (
94    // Either multiple `const fn`
95    $(
96        $(#[$CONST_ATTRS:meta])*
97        $item_vis:vis $CONST_NAME:ident$(!)? =
98            $(#[$fn_attrs:meta])*
99            $fn_vis:vis const
100            $(async$($_a:block)?)? $(safe$($_s:block)?)? $(unsafe$($_u:block)?)?
101            fn $fn:ident($($param:ident: $param_ty:ty),* $(,)?)
102        $(-> $fn_return:ty)?
103        $fn_body:block
104    );* $(;)?) => {
105        $(
106            $(#[$CONST_ATTRS])* // specially for #[macro_export]
107            #[allow(unused_macros)]
108            macro_rules! $CONST_NAME {
109                () => {
110                    $(#[$fn_attrs])*
111                    $fn_vis const $(async$($_a)?)? $(safe$($_s)?)? $(unsafe$($_u)?)?
112                    fn $fn($($param: $param_ty),*) $(-> $fn_return)? $fn_body
113                }
114            }
115            // NOTE: shouldn't duplicate the macro attributes it, instead
116            // should allow receiving separate attributes, like allow(unused).
117            // $(#[$CONST_ATTRS])*
118            $item_vis use $CONST_NAME;
119        )*
120    };
121    (
122    $shared_vis:vis, // (shared visibility alternative)
123    $(
124        $(#[$CONST_ATTRS:meta])*
125        $CONST_NAME:ident =
126            $(#[$fn_attrs:meta])*
127            $fn_vis:vis const
128            $(async$($_a:block)?)? $(safe$($_s:block)?)? $(unsafe$($_u:block)?)?
129            fn $fn:ident($($param:ident: $param_ty:ty),* $(,)?)
130        $(-> $fn_return:ty)?
131        $fn_body:block
132    );* $(;)?) => {
133        $(
134            $(#[$CONST_ATTRS])*
135            #[allow(unused_macros)]
136            macro_rules! $CONST_NAME {
137                () => {
138                    $(#[$fn_attrs])*
139                    $shared_vis const $(async$($_a)?)? $(safe$($_s)?)? $(unsafe$($_u)?)?
140                    fn $fn($($param: $param_ty),*) $(-> $fn_return)? $fn_body
141                }
142            }
143            // $(#[$CONST_ATTRS])*
144            $shared_vis use $CONST_NAME;
145        )*
146    };
147    (
148
149    // Either multiple expressions
150    $(
151        $(#[$CONST_ATTRS:meta])*
152        $item_vis:vis $CONST_NAME:ident $(!)? = $expr:expr
153     );* $(;)?) => {
154        $(
155            $(#[$CONST_ATTRS])*
156            #[allow(unused_macro)]
157            macro_rules! $CONST_NAME { () => { $expr } }
158            // $(#[$CONST_ATTRS])*
159            $item_vis use $CONST_NAME;
160        )*
161    };
162    (
163    $shared_vis:vis, // (shared visibility alternative)
164    $(
165        $(#[$CONST_ATTRS:meta])*
166        $CONST_NAME:ident = $expr:expr
167     );* $(;)?) => {
168        $(
169            $(#[$CONST_ATTRS])*
170            #[allow(unused_macro)]
171            macro_rules! $CONST_NAME { () => { $expr } }
172            // $(#[$CONST_ATTRS])*
173            $shared_vis use $CONST_NAME;
174        )*
175    };
176}
177#[doc(inline)]
178pub use _CONST as CONST;