# Hub-Macro: Caller-Wraps Streaming Protocol

This document describes the `hub-macro` procedural macro crate and its implementation of the caller-wraps streaming architecture.

## Overview

`hub-macro` provides attribute macros that generate activation boilerplate:
- RPC trait and server implementation (jsonrpsee)
- Activation trait implementation
- Method enum with JSON schemas

```
┌──────────────────────────────────────────────────────────────┐
│                     #[hub_methods]                           │
│                                                              │
│  Input:                          Output:                     │
│  ┌────────────────────┐         ┌────────────────────────┐  │
│  │ impl MyActivation  │         │ MyActivationRpc trait  │  │
│  │   async fn foo()   │  ───►   │ MyActivationRpcServer  │  │
│  │   async fn bar()   │         │ impl Activation        │  │
│  └────────────────────┘         │ MyActivationMethod enum│  │
│                                 └────────────────────────┘  │
└──────────────────────────────────────────────────────────────┘
```

## Usage

### Activation Definition

```rust
use async_stream::stream;
use futures::Stream;

#[derive(Clone)]
pub struct Echo;

#[hub_macro::hub_methods(
    namespace = "echo",
    version = "1.0.0",
    description = "Echo messages back"
)]
impl Echo {
    #[hub_macro::hub_method(
        description = "Echo a message",
        params(message = "The message to echo")
    )]
    async fn echo(&self, message: String) -> impl Stream<Item = EchoEvent> + Send + 'static {
        stream! {
            yield EchoEvent::Echo { message };
        }
    }
}
```

### Event Types

Event types are plain domain types with standard derives:

```rust
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "event", rename_all = "snake_case")]
pub enum EchoEvent {
    Echo { message: String },
}
```

No special traits required. The caller (generated code) handles wrapping.

## Generated Code

### RPC Server Methods

For each `#[hub_method]`, the macro generates:

```rust
async fn echo(
    &self,
    pending: PendingSubscriptionSink,
    message: String,
) -> SubscriptionResult {
    use futures::StreamExt;

    let sink = pending.accept().await?;
    let stream = Echo::echo(self, message).await;
    let wrapped = wrap_stream(stream, "echo.echo", vec!["echo".into()]);

    tokio::spawn(async move {
        let mut stream = wrapped;
        while let Some(item) = stream.next().await {
            if let Ok(raw_value) = serde_json::value::to_raw_value(&item) {
                if sink.send(raw_value).await.is_err() {
                    break;
                }
            }
        }
        // Append Done event
        let done = PlexusStreamItem::Done {
            metadata: StreamMetadata::new(vec!["echo".into()], PlexusContext::hash()),
        };
        if let Ok(raw_value) = serde_json::value::to_raw_value(&done) {
            let _ = sink.send(raw_value).await;
        }
    });

    Ok(())
}
```

### Activation::call() Dispatch

```rust
async fn call(&self, method: &str, params: Value) -> Result<PlexusStream, PlexusError> {
    match method {
        "echo" => {
            let message = /* extract from params */;
            let stream = self.echo(message).await;
            Ok(wrap_stream(stream, "echo.echo", vec!["echo".into()]))
        }
        _ => Err(PlexusError::MethodNotFound { ... }),
    }
}
```

## Key Design Decisions

### 1. Caller-Wraps Pattern

The activation method returns plain domain events. The generated code wraps them:

```
Activation method          Generated RPC/call()
     │                            │
     ▼                            ▼
Stream<EchoEvent>    ───►    wrap_stream()    ───►    PlexusStream
                                                           │
                                                           ▼
                                               PlexusStreamItem::Data {
                                                   content_type: "echo.echo",
                                                   provenance: ["echo"],
                                                   payload: { ... }
                                               }
```

### 2. Content Type Format

Content type follows `"{namespace}.{method}"` pattern:
- `echo.echo` for the `echo` method in `echo` namespace
- `health.check` for the `check` method in `health` namespace

### 3. No StreamEvent Derive

The `#[derive(StreamEvent)]` macro is **deprecated**. Event types no longer need to implement `ActivationStreamItem`. Just use:

```rust
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
```

### 4. Automatic Done Event

The RPC forwarding task automatically appends a `PlexusStreamItem::Done` when the stream completes. Activations don't need to yield terminal events.

## Attribute Reference

### `#[hub_methods]`

Applied to impl block:

| Attribute | Required | Description |
|-----------|----------|-------------|
| `namespace` | Yes | RPC namespace (e.g., "echo") |
| `version` | Yes | Semantic version (e.g., "1.0.0") |
| `description` | No | Human-readable description |
| `resolve_handle` | No | Generate resolve_handle method |

### `#[hub_method]`

Applied to individual methods:

| Attribute | Required | Description |
|-----------|----------|-------------|
| `description` | No | Method description for schema |
| `params(...)` | No | Parameter descriptions: `params(name = "desc")` |
| `name` | No | Override method name in RPC |

## Integration

Register macro-generated activations in the builder:

```rust
pub fn build_plexus() -> Arc<Plexus> {
    Arc::new(
        Plexus::new()
            .register(Health::new())  // Manual implementation
            .register(Echo::new()),   // Macro-generated
    )
}
```

Both manual and macro-generated activations work identically.

## Comparison: Manual vs Macro

| Aspect | Manual (Health) | Macro (Echo) |
|--------|-----------------|--------------|
| RPC trait | Write by hand | Generated |
| Activation impl | Write by hand | Generated |
| Method enum | Write by hand | Generated |
| Event types | Plain types | Plain types |
| wrap_stream() | Called manually | Generated |
| Lines of code | ~150 | ~40 |

Use the macro for standard activations. Use manual implementation when you need custom RPC behavior or complex routing.
