#[derive(UriConfig)]
{
// Attributes available to this derive:
#[uri_scheme]
#[uri_param]
#[uri_config]
}
Expand description
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 customvalidate()logic.#[uri_config(crate = "path")]- Optional, overrides the generated code’s crate path. Defaults tocamel_endpoint. Component crates using thecamel-component-apire-exports set this tocamel_component_api.#[uri_config(metadata(scheme = "..", description = "..", producer, consumer, polling_consumer, streaming))]- Optional, opts in to generating an inherentfn metadata() -> ComponentMetadataon the config struct. The group mixes bare capability flags (producer,consumer,polling_consumer,streaming) withkey = "value"pairs (scheme,description). Whenschemeis omitted it falls back to the#[uri_scheme]value.
§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#[uri_param(desc = "text")]- Human-readable description for the generatedUriOption.#[uri_param(required)]- Marks the option required (bare flag; also acceptsrequired = true). Without it,Option<T>fields are not required, and non-Optionfields without adefaultare required.#[uri_param(secret)]- Marks the option as secret (bare flag; also acceptssecret = true). Combiningsecretwithdefaultis a compile error.#[uri_param(deprecated = "reason")]- Deprecation notice.#[uri_param(aliases = ["a", "b"])]- Alias parameter names.#[uri_param(kind = "duration|bool|int|float|string|enum:A,B")]- Overrides the inferredOptionKind. Inference never producesEnum; the only way to get anEnumoption is an explicitkind = "enum:...". An unrecognized kind string is a spanned compile error.
§Generated helper functions
In addition to the URI parsing impl, the derive always generates:
pub fn uri_options() -> Vec<UriOption>- one entry per#[uri_param]field (the path field is excluded).OptionKindis inferred from the Rust type after unwrappingOption<T>.
And, when #[uri_config(metadata(..))] is present:
pub fn metadata() -> ComponentMetadata- built from the metadata attribute and the deriveduri_options(). Component structs delegate theirComponent::metadataoverride to this (e.g.fn metadata(&self) -> ComponentMetadata { Config::metadata() }).
§Example
§Basic usage
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
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 })
}
}§OptionKind type inference
The OptionKind for each #[uri_param] field is inferred from its Rust
type after unwrapping Option<T>:
| Rust type | Inferred OptionKind |
|---|---|
std::time::Duration | Duration |
bool | Bool |
u8, u16, u32, u64, usize, i8, i16, i32, i64, isize | Int |
f32, f64 | Float |
String, &str | String |
Vec<T> | List(Box::new(inner_kind_of_T)) |
| anything else (enums, custom types, …) | String |
Inference never produces OptionKind::Enum. The only way to get an
Enum option is an explicit kind = "enum:A,B,C" override.
§Guardrail: secret + default is a compile error
#[uri_param(secret, default = "x")] produces a compile-time error:
"#[uri_param] cannot have both secret and default; a secret must
never carry a default value." This prevents sensitive values from being embedded
in generated code or discovery output.
§Delegation convention
The macro generates uri_options() and (when opted in) metadata() as
inherent methods on the config struct. The component struct
implements Component, whose metadata() default returns
ComponentMetadata::minimal(scheme) with empty uri_options. Every
component MUST override metadata() to delegate to its config struct:
impl Component for MyComponent {
fn scheme(&self) -> &str { "my-scheme" }
fn metadata(&self) -> ComponentMetadata {
MyConfig::metadata()
// Or, without the metadata(..) opt-in:
// ComponentMetadata::minimal(self.scheme())
// .with_uri_options(MyConfig::uri_options())
}
}Without this delegation step, the catalog returns empty uri_options.
§Worked example: component with metadata
use camel_endpoint::UriConfig;
#[derive(Debug, Clone, UriConfig)]
#[uri_scheme = "sql"]
#[uri_config(
metadata(
scheme = "sql",
description = "Execute SQL against a configured datasource",
producer,
consumer,
)
)]
struct SqlConfig {
query: String,
#[uri_param(secret, desc = "Database connection URL")]
db_url: String,
#[uri_param(
name = "outputType",
desc = "Output type for query results",
kind = "enum:SelectList,SelectOne,StreamList",
default = "SelectList"
)]
output_type: SqlOutputType,
#[uri_param(
name = "maxConnections",
desc = "Maximum connections in the pool",
default = "5"
)]
max_connections: u32,
}
// Generated:
// - SqlConfig::uri_options() returns 3 UriOption entries
// - SqlConfig::metadata() returns ComponentMetadata with scheme "sql",
// producer + consumer capabilities, and the derived uri_options
// Component delegation (hand-written):
impl Component for SqlComponent {
fn scheme(&self) -> &str { "sql" }
fn metadata(&self) -> ComponentMetadata { SqlConfig::metadata() }
}