Skip to main content

camel_endpoint_macros/
lib.rs

1mod uri_config;
2
3use proc_macro::TokenStream;
4use syn::{DeriveInput, parse_macro_input};
5
6/// Derive macro for UriConfig trait implementation.
7///
8/// This macro generates the `from_uri()` implementation based on struct field attributes.
9///
10/// # Attributes
11///
12/// ## Struct-level attributes
13///
14/// - `#[uri_scheme = "xxx"]` - Required, defines the URI scheme
15/// - `#[uri_config(skip_impl)]` - Optional, generates only the parsing helper method
16///   instead of the full trait impl. Use this when you need custom `validate()` logic.
17///
18/// ## Field-level attributes
19///
20/// - `#[uri_param]` - Marks a field as a URI query parameter (uses field name as param name)
21/// - `#[uri_param(default = "value")]` - Provides a default value if param not present
22/// - `#[uri_param(name = "paramName")]` - Maps to a different query parameter name
23///
24/// # Example
25///
26/// ## Basic usage
27///
28/// ```ignore
29/// use camel_endpoint::UriConfig;
30///
31/// #[derive(Debug, Clone, UriConfig)]
32/// #[uri_scheme = "timer"]
33/// struct TimerConfig {
34///     // First field without #[uri_param] gets the path component
35///     name: String,
36///
37///     // Query parameters
38///     #[uri_param(default = "1000")]
39///     period: u64,
40///
41///     #[uri_param(default = "true")]
42///     repeat: bool,
43///
44///     #[uri_param(name = "cronExpr")]
45///     cron: Option<String>,
46/// }
47///
48/// // Generated impl allows:
49/// let config = TimerConfig::from_uri("timer:tick?period=5000").unwrap();
50/// assert_eq!(config.name, "tick");
51/// assert_eq!(config.period, 5000);
52/// assert!(config.repeat); // uses default
53/// assert!(config.cron.is_none()); // Option defaults to None
54/// ```
55///
56/// ## Custom validation with `skip_impl`
57///
58/// ```ignore
59/// use camel_endpoint::UriConfig;
60///
61/// #[derive(Debug, Clone, UriConfig)]
62/// #[uri_scheme = "file"]
63/// #[uri_config(skip_impl)]
64/// struct FileConfig {
65///     directory: String,
66///     #[uri_param(default = "false")]
67///     delete: bool,
68///     #[uri_param(name = "move")]
69///     move_to: Option<String>,
70/// }
71///
72/// // Implement the trait manually with custom validation
73/// impl UriConfig for FileConfig {
74///     fn scheme() -> &'static str { "file" }
75///     
76///     fn from_uri(uri: &str) -> Result<Self, CamelError> {
77///         let parts = parse_uri(uri)?;
78///         Self::from_components(parts)
79///     }
80///     
81///     fn from_components(parts: UriComponents) -> Result<Self, CamelError> {
82///         Self::parse_uri_components(parts)?.validate()
83///     }
84///     
85///     fn validate(self) -> Result<Self, CamelError> {
86///         // Custom validation: move_to is None if delete is true
87///         let move_to = if self.delete { None } else { self.move_to };
88///         Ok(Self { move_to, ..self })
89///     }
90/// }
91/// ```
92#[proc_macro_derive(UriConfig, attributes(uri_scheme, uri_param, uri_config))]
93pub fn derive_uri_config(input: TokenStream) -> TokenStream {
94    let input = parse_macro_input!(input as DeriveInput);
95    uri_config::impl_uri_config(&input).into()
96}