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