camel-bean-macros 0.5.1

Proc-macros for camel-bean crate
Documentation
# camel-bean-macros

Proc-macros for the [`camel-bean`](../camel-bean) crate that enable ergonomic bean definition and automatic `BeanProcessor` trait implementation.

## Overview

This crate provides three proc-macros:

1. **`#[bean_impl]`** - Main macro for generating `BeanProcessor` impl from impl blocks
2. **`#[handler]`** - Marker attribute for bean handler methods
3. **`#[derive(Bean)]`** - Placeholder derive macro (not recommended)

## Macros

### `#[bean_impl]` - Generate BeanProcessor Implementation

The primary macro that transforms an impl block into a `BeanProcessor` implementation with automatic parameter binding.

```rust
use camel_bean::{bean_impl, handler};

pub struct OrderService;

#[bean_impl]
impl OrderService {
    #[handler]
    pub async fn process(&self, body: Order) -> Result<ProcessedOrder, String> {
        // Business logic
        Ok(ProcessedOrder { /* ... */ })
    }

    #[handler]
    pub async fn validate(&self, body: Order, headers: Headers) -> Result<bool, String> {
        // Validation logic
        Ok(true)
    }
}
```

**What it generates:**

```rust,ignore
impl OrderService {
    // ... your methods unchanged ...
}

#[async_trait]
impl BeanProcessor for OrderService {
    async fn call(&self, method: &str, exchange: &mut Exchange) -> Result<(), CamelError> {
        match method {
            "process" => {
                // Extract body parameter
                let body: Order = exchange.input.body.try_into()?;
                // Call method
                let result = self.process(body).await;
                // Handle result (set response body or error)
                // ...
            }
            "validate" => {
                // Extract body and headers
                let body: Order = exchange.input.body.try_into()?;
                let headers = exchange.input.headers.clone();
                // Call and handle result
                // ...
            }
            _ => return Err(CamelError::from(format!("Unknown method: {}", method)))
        }
        Ok(())
    }

    fn methods(&self) -> Vec<&'static str> {
        vec!["process", "validate"]
    }
}
```

### `#[handler]` - Mark Handler Methods

Marker attribute that identifies which methods should be exposed as bean handlers.

```rust
#[bean_impl]
impl MyService {
    #[handler]  // This method is exposed
    pub async fn process(&self, body: Data) -> Result<Response, Error> {
        // ...
    }

    // This method is NOT exposed (no #[handler])
    fn helper(&self) -> String {
        "internal".to_string()
    }
}
```

**Rules:**
- Must be `async`
- First parameter must be `&self`
- Supported parameter types:
  - `body: T` - Extracted from exchange body (requires `TryFrom<Body>`)
  - `headers: Headers` - Cloned from exchange headers
  - `exchange: &mut Exchange` - Full exchange access
- Return type must be `Result<T, E>` where `E: Display`

### `#[derive(Bean)]` - Placeholder (Not Recommended)

Derive macro that generates a placeholder `BeanProcessor` implementation.

**⚠️ Warning:** This macro is incomplete and requires manual impl block with `#[handler]` methods. Use `#[bean_impl]` instead.

```rust,ignore
// NOT RECOMMENDED
#[derive(Bean)]
struct MyService;

// You still need to write impl block manually
impl MyService {
    #[handler]
    pub async fn process(&self, body: Data) -> Result<Response, Error> {
        // ...
    }
}
```

## Parameter Binding

The `#[bean_impl]` macro automatically extracts parameters by name:

```rust
#[bean_impl]
impl UserService {
    // Extract only body
    #[handler]
    pub async fn create(&self, body: CreateUser) -> Result<User, Error> {
        // ...
    }

    // Extract body + headers
    #[handler]
    pub async fn update(&self, body: UpdateUser, headers: Headers) -> Result<User, Error> {
        let user_id = headers.get("user-id").unwrap();
        // ...
    }

    // Full exchange access
    #[handler]
    pub async fn complex(&self, exchange: &mut Exchange) -> Result<(), Error> {
        // Read body, headers, properties, set response, etc.
        // ...
    }
}
```

**Parameter name inference:**
- `body` → Extracted from `exchange.input.body` via `TryFrom<Body>`
- `headers` → Cloned from `exchange.input.headers`
- `exchange` → Mutable reference to the full exchange

## Return Type Handling

Return types are automatically handled:

```rust
#[bean_impl]
impl MyService {
    // Success: body set to Result<T>
    // Error: exchange.set_error()
    #[handler]
    pub async fn process(&self, body: Input) -> Result<Output, String> {
        Ok(Output { /* ... */ })
    }
}
```

**Result handling:**
- `Ok(T)` → Body set to JSON-serialized `T` (if `T` is not `()`)
- `Err(E)``exchange.set_error()` called with error message

## Error Handling

The macro generates proper error handling:

```rust
#[bean_impl]
impl MyService {
    #[handler]
    pub async fn process(&self, body: Data) -> Result<Response, String> {
        if body.invalid() {
            return Err("Invalid data".to_string());
        }
        Ok(Response { /* ... */ })
    }
}
```

**Generated error handling:**
- Parameter extraction failures → `CamelError::ProcessorError`
- Method not found → `BeanError::MethodNotFound`
- Handler errors → Set on exchange via `set_error()`

## Integration with camel-bean

This crate is a dependency of `camel-bean` and is re-exported for convenience:

```rust
// Usually you don't depend on camel-bean-macros directly
use camel_bean::{bean_impl, handler, BeanRegistry};

// camel-bean re-exports the macros
```

## Examples

See the [`bean-demo` example](../../examples/bean-demo/) for a complete working example.

## Implementation Details

### Code Generation

The `#[bean_impl]` macro:

1. **Parses** the impl block to find `#[handler]` methods
2. **Validates** method signatures (async, &self, valid parameters)
3. **Generates** parameter extraction code for each handler
4. **Generates** method dispatch via `match` on method name
5. **Generates** result handling (set body or error)
6. **Preserves** original impl block unchanged

### Supported Types

**Parameter types:**
- `body: T` where `T: TryFrom<Body>`
- `headers: Headers` (cloned)
- `exchange: &mut Exchange`

**Return types:**
- `Result<T, E>` where `E: Display`
- `T` is serialized to JSON on success
- `()` is allowed (no body set on success)

## See Also

- [camel-bean]../camel-bean - Main bean registry and usage examples
- [examples/bean-demo]../../examples/bean-demo/ - Complete working example
- [Apache Camel Bean Binding]https://camel.apache.org/manual/bean-binding.html - Inspiration

## License

Apache-2.0