Skip to main content

waterui_core/
macros.rs

1/// Implements a basic `Debug` trait for types using their type name.
2///
3/// This macro generates a `Debug` implementation that simply prints the type name,
4/// useful for types where the internal structure doesn't need to be exposed.
5#[macro_export]
6macro_rules! impl_debug {
7    ($ty:ty) => {
8        impl core::fmt::Debug for $ty {
9            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
10                f.write_str(core::any::type_name::<Self>())
11            }
12        }
13    };
14}
15
16/// Implements a native view that is handled by the platform backend.
17///
18/// This macro implements both `NativeView` and `View` traits for a type.
19/// The `View::body()` returns `Native(self)` to delegate to the native backend.
20///
21/// # Usage
22///
23/// ```
24/// use waterui_core::{layout::StretchAxis, raw_view};
25///
26/// // Default stretch axis (None): the view is sized by its content.
27/// struct Badge;
28/// raw_view!(Badge);
29///
30/// // With an explicit stretch axis.
31/// struct Backdrop;
32/// raw_view!(Backdrop, StretchAxis::Both);
33///
34/// struct Divider;
35/// raw_view!(Divider, StretchAxis::Horizontal);
36/// ```
37#[macro_export]
38macro_rules! raw_view {
39    // With explicit stretch axis
40    ($ty:ty, $axis:expr) => {
41        impl $crate::NativeView for $ty {
42            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
43                $axis
44            }
45        }
46
47        impl $crate::View for $ty {
48            fn body(self, _env: &$crate::Environment) -> impl $crate::View {
49                $crate::Native::new(self)
50            }
51
52            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
53                $axis
54            }
55        }
56    };
57
58    // Default stretch axis (None)
59    ($ty:ty) => {
60        impl $crate::NativeView for $ty {}
61
62        impl $crate::View for $ty {
63            fn body(self, _env: &$crate::Environment) -> impl $crate::View {
64                $crate::Native::new(self)
65            }
66
67            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
68                $crate::layout::StretchAxis::None
69            }
70        }
71    };
72}
73
74/// Creates a configurable view with builder pattern methods.
75///
76/// This macro generates a wrapper struct and builder methods for configuring views,
77/// following the builder pattern commonly used in UI frameworks.
78///
79/// # Usage
80///
81/// ```
82/// use waterui_core::{configurable, layout::StretchAxis};
83///
84/// // Default stretch axis (None) - for content-sized views.
85/// #[derive(Debug, Default)]
86/// pub struct BadgeConfig {
87///     pub count: u32,
88/// }
89/// configurable!(Badge, BadgeConfig);
90///
91/// // With an explicit stretch axis - for views that expand.
92/// #[derive(Debug, Default)]
93/// pub struct SliderConfig {
94///     pub value: f64,
95/// }
96/// configurable!(Slider, SliderConfig, StretchAxis::Horizontal);
97///
98/// // With a dynamic stretch axis - for runtime-dependent behaviour.
99/// #[derive(Debug, Default)]
100/// pub struct ProgressConfig {
101///     pub circular: bool,
102/// }
103/// configurable!(Progress, ProgressConfig, |config| if config.circular {
104///     StretchAxis::None
105/// } else {
106///     StretchAxis::Horizontal
107/// });
108/// ```
109#[macro_export]
110macro_rules! configurable {
111    // Internal implementation with stretch axis
112    (@impl $(#[$meta:meta])*; $view:ident, $config:ty, $axis:expr) => {
113        $crate::configurable!(
114            @impl $(#[$meta])*;
115            $view,
116            $config,
117            $axis,
118            |config: $config, _env: &$crate::Environment| config
119        );
120    };
121
122    // Internal implementation with stretch axis and a native payload resolver.
123    (@impl $(#[$meta:meta])*; $view:ident, $config:ty, $axis:expr, $resolve_native:expr) => {
124        $(#[$meta])*
125        pub struct $view($config);
126
127        impl $crate::NativeView for $config {
128            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
129                $axis
130            }
131        }
132
133        impl $crate::view::ConfigurableView for $view {
134            type Config = $config;
135            #[inline] fn config(self) -> Self::Config { self.0 }
136        }
137
138        impl $crate::view::ViewConfiguration for $config {
139            type View = $view;
140            #[inline] fn render(self) -> Self::View { $view(self) }
141        }
142
143        impl From<$config> for $view {
144            #[inline] fn from(value: $config) -> Self { Self(value) }
145        }
146
147        impl $crate::view::View for $view {
148            fn body(self, env: &$crate::Environment) -> impl $crate::View {
149                use $crate::view::ConfigurableView;
150                let config = self.config();
151                if let Some(hook) = env.get::<$crate::view::Hook<$config>>() {
152                    $crate::AnyView::new(hook.apply(env, config))
153                } else {
154                    $crate::AnyView::new($crate::Native::new(($resolve_native)(config, env)))
155                }
156            }
157
158            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
159                $crate::NativeView::stretch_axis(&self.0)
160            }
161        }
162    };
163
164    // Dynamic stretch axis with closure/function
165    // Internal implementation that generates NativeView with the provided function
166    (@impl_dynamic $(#[$meta:meta])*; $view:ident, $config:ty, $stretch_fn:expr) => {
167        $crate::configurable!(
168            @impl_dynamic $(#[$meta])*;
169            $view,
170            $config,
171            $stretch_fn,
172            |config: $config, _env: &$crate::Environment| config
173        );
174    };
175
176    // Dynamic stretch axis with closure/function and a native payload resolver.
177    (@impl_dynamic $(#[$meta:meta])*; $view:ident, $config:ty, $stretch_fn:expr, $resolve_native:expr) => {
178        $(#[$meta])*
179        #[derive(Debug)]
180        pub struct $view($config);
181
182        impl $crate::NativeView for $config {
183            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
184                ($stretch_fn)(self)
185            }
186        }
187
188        impl $crate::view::ConfigurableView for $view {
189            type Config = $config;
190            #[inline] fn config(self) -> Self::Config { self.0 }
191        }
192
193        impl $crate::view::ViewConfiguration for $config {
194            type View = $view;
195            #[inline] fn render(self) -> Self::View { $view(self) }
196        }
197
198        impl From<$config> for $view {
199            #[inline] fn from(value: $config) -> Self { Self(value) }
200        }
201
202        impl $crate::view::View for $view {
203            fn body(self, env: &$crate::Environment) -> impl $crate::View {
204                use $crate::view::ConfigurableView;
205                let config = self.config();
206                if let Some(hook) = env.get::<$crate::view::Hook<$config>>() {
207                    $crate::AnyView::new(hook.apply(env, config))
208                } else {
209                    $crate::AnyView::new($crate::Native::new(($resolve_native)(config, env)))
210                }
211            }
212
213            fn stretch_axis(&self) -> $crate::layout::StretchAxis {
214                $crate::NativeView::stretch_axis(&self.0)
215            }
216        }
217    };
218
219    // Dynamic stretch axis with a native payload resolver.
220    ($(#[$meta:meta])* $view:ident, $config:ty, |$param:ident| $body:expr, resolve |$config_param:ident, $env_param:ident| $resolve_body:expr) => {
221        $crate::configurable!(
222            @impl_dynamic $(#[$meta])*;
223            $view,
224            $config,
225            |$param: &$config| $body,
226            |$config_param: $config, $env_param: &$crate::Environment| $resolve_body
227        );
228    };
229
230    // Public variant for dynamic stretch_axis with closure: |config| -> StretchAxis
231    // IMPORTANT: This must come BEFORE the $axis:expr variant (closure pattern)
232    ($(#[$meta:meta])* $view:ident, $config:ty, |$param:ident| $body:expr) => {
233        $crate::configurable!(@impl_dynamic $(#[$meta])*; $view, $config, |$param: &$config| $body);
234    };
235
236    // Explicit stretch axis with a native payload resolver.
237    ($(#[$meta:meta])* $view:ident, $config:ty, $axis:expr, resolve |$config_param:ident, $env_param:ident| $body:expr) => {
238        $crate::configurable!(
239            @impl $(#[$meta])*;
240            $view,
241            $config,
242            $axis,
243            |$config_param: $config, $env_param: &$crate::Environment| $body
244        );
245    };
246
247    // With explicit stretch axis
248    ($(#[$meta:meta])* $view:ident, $config:ty, $axis:expr) => {
249        $crate::configurable!(@impl $(#[$meta])*; $view, $config, $axis);
250    };
251
252    // Default stretch axis with a native payload resolver.
253    ($(#[$meta:meta])* $view:ident, $config:ty, resolve |$config_param:ident, $env_param:ident| $body:expr) => {
254        $crate::configurable!(
255            @impl $(#[$meta])*;
256            $view,
257            $config,
258            $crate::layout::StretchAxis::None,
259            |$config_param: $config, $env_param: &$crate::Environment| $body
260        );
261    };
262
263    // Default stretch axis (None)
264    ($(#[$meta:meta])* $view:ident, $config:ty) => {
265        $crate::configurable!(@impl $(#[$meta])*; $view, $config, $crate::layout::StretchAxis::None);
266    };
267}
268macro_rules! tuples {
269    ($macro:ident) => {
270        $macro!();
271        $macro!(T0);
272        $macro!(T0, T1);
273        $macro!(T0, T1, T2);
274        $macro!(T0, T1, T2, T3);
275        $macro!(T0, T1, T2, T3, T4);
276        $macro!(T0, T1, T2, T3, T4, T5);
277        $macro!(T0, T1, T2, T3, T4, T5, T6);
278        $macro!(T0, T1, T2, T3, T4, T5, T6, T7);
279        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
280        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
281        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
282        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
283        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
284        $macro!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
285        $macro!(
286            T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14
287        );
288    };
289}
290
291/// Implements the `Extractor` trait for a type.
292///
293/// This macro generates an implementation that extracts values from the environment
294/// using the `Use<T>` wrapper, commonly used for dependency injection.
295#[macro_export]
296macro_rules! impl_extractor {
297    ($ty:ty) => {
298        impl $crate::extract::Extractor for $ty {
299            fn extract(env: &$crate::Environment) -> core::result::Result<Self, $crate::Error> {
300                $crate::extract::Extractor::extract(env)
301                    .map(|value: $crate::extract::Use<$ty>| value.0)
302            }
303        }
304    };
305}
306
307/// Implements the `Deref` trait for transparent access to an inner type.
308///
309/// This macro generates a `Deref` implementation that allows transparent
310/// access to the inner value of wrapper types.
311#[macro_export]
312macro_rules! impl_deref {
313    ($ty:ty,$target:ty) => {
314        impl core::ops::Deref for $ty {
315            type Target = $target;
316            fn deref(&self) -> &Self::Target {
317                &self.0
318            }
319        }
320
321        impl core::ops::DerefMut for $ty {
322            fn deref_mut(&mut self) -> &mut Self::Target {
323                &mut self.0
324            }
325        }
326    };
327}