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