dioxus_provider/param_utils.rs
1//! Parameter normalization utilities for dioxus-provider
2
3use std::fmt::Debug;
4use std::hash::Hash;
5
6/// Trait for normalizing different parameter formats to work with providers
7///
8/// This trait allows the `use_provider` hook to accept parameters in different formats:
9/// - `()` for no parameters
10/// - `(param,)` for single parameter in tuple (e.g., `(42,)`)
11/// - Common primitive types directly (e.g., `42`, `"foo".to_string()`)
12/// - Custom types that implement the required bounds directly
13///
14/// # Custom Parameter Types
15///
16/// Custom types can be used directly as provider parameters by implementing the required bounds:
17/// - `Clone + PartialEq + Hash + Debug + Send + Sync + 'static`
18///
19/// Use the `provider_param!` macro for convenience:
20///
21/// ```rust
22/// use dioxus_provider::{prelude::*, provider_param};
23///
24/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
25/// struct UserId(u32);
26/// provider_param!(UserId); // Enable direct usage
27///
28/// #[provider]
29/// async fn fetch_user(user_id: UserId) -> Result<User, String> { ... }
30///
31/// // Now you can use it directly:
32/// let user = use_provider(fetch_user(), UserId(42));
33/// ```
34///
35/// # Usage and Ambiguity
36///
37/// - If your provider expects a single parameter, you can pass it directly or as a single-element tuple.
38/// - **Note:** Tuple syntax `(param,)` has priority over direct parameter syntax for types that implement both.
39/// - For string parameters, both `String` and `&str` are supported directly.
40///
41/// # Examples
42///
43/// ```rust
44/// use dioxus_provider::prelude::*;
45///
46/// #[provider]
47/// async fn fetch_user(user_id: u32) -> Result<User, String> { ... }
48///
49/// // All of these are valid:
50/// let user = use_provider(fetch_user(), 42); // direct primitive
51/// let user = use_provider(fetch_user(), (42,)); // single-element tuple
52/// let user = use_provider(fetch_user(), "foo".to_string()); // String
53/// ```
54pub trait IntoProviderParam {
55 /// The target parameter type after conversion
56 type Param: Clone + PartialEq + Hash + Debug + Send + Sync + 'static;
57
58 /// Convert the input into the parameter format expected by the provider
59 fn into_param(self) -> Self::Param;
60}
61
62/// Sealed trait to control which types can be used directly as provider parameters
63///
64/// This prevents conflicts between the blanket implementation and the tuple implementation.
65/// Types that implement this trait (along with the required bounds) can be used directly
66/// as provider parameters without requiring tuple wrappers.
67pub mod sealed {
68 pub trait DirectParam {}
69
70 // Implement for common primitive types
71 impl DirectParam for u8 {}
72 impl DirectParam for u16 {}
73 impl DirectParam for u32 {}
74 impl DirectParam for u64 {}
75 impl DirectParam for u128 {}
76 impl DirectParam for usize {}
77 impl DirectParam for i8 {}
78 impl DirectParam for i16 {}
79 impl DirectParam for i32 {}
80 impl DirectParam for i64 {}
81 impl DirectParam for i128 {}
82 impl DirectParam for isize {}
83 impl DirectParam for f32 {}
84 impl DirectParam for f64 {}
85 impl DirectParam for bool {}
86 impl DirectParam for char {}
87 impl DirectParam for String {}
88 impl DirectParam for &str {}
89}
90
91// Implementation for no parameters: () -> ()
92impl IntoProviderParam for () {
93 type Param = ();
94
95 fn into_param(self) -> Self::Param {
96 // nothing needed
97 }
98}
99
100// Implementation for tuple parameters: (Param,) -> Param
101// This has higher priority than the blanket implementation due to Rust's orphan rules
102impl<T> IntoProviderParam for (T,)
103where
104 T: Clone + PartialEq + Hash + Debug + Send + Sync + 'static,
105{
106 type Param = T;
107
108 fn into_param(self) -> Self::Param {
109 self.0
110 }
111}
112
113// Blanket implementation for direct parameters
114// This allows any type that implements the required bounds AND the sealed trait
115// to be used directly as a provider parameter
116impl<T> IntoProviderParam for T
117where
118 T: Clone + PartialEq + Hash + Debug + Send + Sync + 'static + sealed::DirectParam,
119{
120 type Param = T;
121
122 fn into_param(self) -> Self::Param {
123 self
124 }
125}
126
127/// Macro to enable a custom type to be used directly as a provider parameter
128///
129/// This macro implements the sealed `DirectParam` trait for your type, allowing it
130/// to be used with the blanket `IntoProviderParam` implementation.
131///
132/// # Requirements
133///
134/// Your type must implement:
135/// - `Clone + PartialEq + Hash + Debug + Send + Sync + 'static`
136///
137/// # Example
138///
139/// ```rust
140/// use dioxus_provider::{prelude::*, provider_param};
141///
142/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
143/// struct UserId(u32);
144///
145/// #[derive(Clone, PartialEq, Eq, Hash, Debug)]
146/// struct ProductId(String);
147///
148/// // Enable direct usage as provider parameters
149/// provider_param!(UserId);
150/// provider_param!(ProductId);
151///
152/// #[provider]
153/// async fn fetch_user(user_id: UserId) -> Result<User, String> { ... }
154///
155/// #[provider]
156/// async fn fetch_product(product_id: ProductId) -> Result<Product, String> { ... }
157///
158/// // Now you can use them directly:
159/// let user = use_provider(fetch_user(), UserId(42));
160/// let product = use_provider(fetch_product(), ProductId("abc".to_string()));
161/// ```
162#[macro_export]
163macro_rules! provider_param {
164 ($type:ty) => {
165 impl $crate::param_utils::sealed::DirectParam for $type {}
166 };
167}