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}