streamweave 0.10.1

Composable, async, stream-first computation in pure Rust
Documentation
# Graph Macro Migration Guide

This guide helps you migrate from the traditional Graph API to the new `graph!` macro for declarative graph construction.

## Overview

The `graph!` macro provides a concise, declarative syntax that reduces boilerplate by 80-90% while maintaining the same functionality as the traditional Graph API.

## Before and After Comparison

### Traditional Graph API

```rust
use streamweave::edge::Edge;
use streamweave::graph::Graph;

let mut graph = Graph::new("my_graph".to_string());

// Add nodes
graph.add_node("producer".to_string(), Box::new(producer_node))?;
graph.add_node("transform".to_string(), Box::new(transform_node))?;
graph.add_node("sink".to_string(), Box::new(sink_node))?;

// Connect nodes
graph.add_edge(Edge {
    source_node: "producer".to_string(),
    source_port: "out".to_string(),
    target_node: "transform".to_string(),
    target_port: "in".to_string(),
})?;
graph.add_edge(Edge {
    source_node: "transform".to_string(),
    source_port: "out".to_string(),
    target_node: "sink".to_string(),
    target_port: "in".to_string(),
})?;

// Expose graph I/O
graph.expose_input_port("producer", "in", "input")?;
graph.expose_output_port("sink", "out", "output")?;
```

### Using `graph!` Macro

```rust
use streamweave::graph;

let mut graph: Graph = graph! {
    producer: producer_node,
    transform: transform_node,
    sink: sink_node,
    ; producer.out => transform.in,
      transform.out => sink.in,
      graph.input => producer.in,
      sink.out => graph.output
};
```

**Benefits:**
- **80-90% less code** - Connections declared declaratively
- **Visual clarity** - Graph structure immediately visible
- **Type safety** - Compile-time validation
- **Less error-prone** - Fewer string conversions and manual edge construction

## Migration Steps

### Step 1: Add the Macro Import

```rust
use streamweave::graph;  // Import the macro
```

### Step 2: Convert Node Addition

**Before:**
```rust
graph.add_node("producer".to_string(), Box::new(producer_node))?;
```

**After:**
```rust
graph! {
    producer: producer_node,
    // ...
}
```

**Key Changes:**
- Node name becomes an identifier (no quotes)
- Node instance is passed directly (no `Box::new()` needed)
- Multiple nodes separated by commas

### Step 3: Convert Edge Connections

**Before:**
```rust
graph.add_edge(Edge {
    source_node: "producer".to_string(),
    source_port: "out".to_string(),
    target_node: "transform".to_string(),
    target_port: "in".to_string(),
})?;
```

**After:**
```rust
graph! {
    // ... nodes ...
    ; producer.out => transform.in
}
```

**Key Changes:**
- Connections follow a semicolon after node definitions
- Syntax: `source_node.source_port => target_node.target_port`
- All ports must be explicitly named (no defaults)
- Multiple connections separated by commas

### Step 4: Convert Graph I/O

#### Graph Inputs

**Before:**
```rust
graph.expose_input_port("producer", "in", "input")?;
// Then connect channel at runtime
graph.connect_input_channel("input", input_rx)?;
```

**After (without initial value):**
```rust
graph! {
    producer: producer_node,
    ; graph.input => producer.in
}
```

**After (with initial value):**
```rust
graph! {
    producer: producer_node,
    ; graph.config: 42i32 => producer.in
}
```

#### Graph Outputs

**Before:**
```rust
graph.expose_output_port("sink", "out", "output")?;
graph.connect_output_channel("output", output_tx)?;
```

**After:**
```rust
graph! {
    sink: sink_node,
    ; sink.out => graph.output
}
```

## Common Patterns

### Linear Pipeline

**Traditional:**
```rust
let mut graph = Graph::new("pipeline".to_string());
graph.add_node("source".to_string(), Box::new(source))?;
graph.add_node("transform".to_string(), Box::new(transform))?;
graph.add_node("sink".to_string(), Box::new(sink))?;
graph.add_edge(Edge {
    source_node: "source".to_string(),
    source_port: "out".to_string(),
    target_node: "transform".to_string(),
    target_port: "in".to_string(),
})?;
graph.add_edge(Edge {
    source_node: "transform".to_string(),
    source_port: "out".to_string(),
    target_node: "sink".to_string(),
    target_port: "in".to_string(),
})?;
```

**With Macro:**
```rust
let mut graph: Graph = graph! {
    source: source,
    transform: transform,
    sink: sink,
    ; source.out => transform.in,
      transform.out => sink.in
};
```

### Graph I/O with Values

**Traditional:**
```rust
graph.expose_input_port("node", "in", "config")?;
let (tx, rx) = mpsc::channel(1);
graph.connect_input_channel("config", rx)?;
tx.send(Arc::new(42i32) as Arc<dyn Any + Send + Sync>).await?;
```

**With Macro:**
```rust
let mut graph: Graph = graph! {
    node: node,
    ; graph.config: 42i32 => node.in
};
// Value is automatically sent when graph is built
```

### Fan-In Pattern

**Traditional:**
```rust
graph.add_edge(Edge {
    source_node: "source1".to_string(),
    source_port: "out".to_string(),
    target_node: "merge".to_string(),
    target_port: "in1".to_string(),
})?;
graph.add_edge(Edge {
    source_node: "source2".to_string(),
    source_port: "out".to_string(),
    target_node: "merge".to_string(),
    target_port: "in2".to_string(),
})?;
```

**With Macro:**
```rust
let mut graph: Graph = graph! {
    source1: source1,
    source2: source2,
    merge: merge,
    ; source1.out => merge.in1,
      source2.out => merge.in2
};
```

**Note:** Fan-in (multiple sources → one target) is supported. Fan-out (one source → multiple targets) is **not supported**.

## Restrictions and Limitations

### 1. Fan-Out Not Supported

**Cannot do:**
```rust
// This will panic at build time
graph! {
    source: source,
    target1: target1,
    target2: target2,
    ; source.out => target1.in,
      source.out => target2.in  // ERROR: Fan-out not supported
};
```

**Workaround:** Use intermediate nodes or restructure your graph.

### 2. Reserved Node Name

**Cannot do:**
```rust
// This will cause a compile error
graph! {
    graph: some_node  // ERROR: "graph" is reserved
};
```

**Reason:** `graph` is reserved for graph I/O namespace (`graph.input_name`, `graph.output_name`).

### 3. Explicit Ports Required

**Cannot do:**
```rust
// No default ports or sequential shortcuts
graph! {
    source: source,
    sink: sink,
    ; source => sink  // ERROR: Ports must be explicit
};
```

**Must do:**
```rust
graph! {
    source: source,
    sink: sink,
    ; source.out => sink.in  // Ports explicitly named
};
```

### 4. Multiple Connections in Macro

Currently, the macro supports multiple connections, but they must be separated by commas:

```rust
graph! {
    a: node_a,
    b: node_b,
    c: node_c,
    ; a.out => b.in, b.out => c.in  // Multiple connections
};
```

## What Stays the Same

The following operations remain unchanged and work the same way:

- **Graph execution**: `Graph::execute(&mut graph).await?`
- **External channel connections**: `graph.connect_input_channel()` and `graph.connect_output_channel()`
- **Graph lifecycle**: `start()`, `pause()`, `resume()`, `stop()`, `wait_for_completion()`
- **Node implementations**: All existing nodes work without modification
- **Runtime behavior**: Graphs built with the macro behave identically to traditionally built graphs

## Migration Checklist

- [ ] Import `graph!` macro: `use streamweave::graph;`
- [ ] Convert node additions to macro syntax
- [ ] Convert edge connections to macro syntax
- [ ] Convert graph I/O to macro syntax
- [ ] Remove manual `Edge` construction
- [ ] Remove string conversions for node/port names
- [ ] Test graph execution (should work identically)
- [ ] Update any documentation/comments

## Troubleshooting

### Compile Error: "Node name 'graph' is reserved"

**Problem:** You're trying to name a node `graph`.

**Solution:** Use a different name. `graph` is reserved for graph I/O namespace.

### Runtime Panic: "Fan-out not supported"

**Problem:** You're trying to connect the same source port to multiple targets.

**Solution:** Restructure your graph or use intermediate nodes. Fan-out requires stream duplication which isn't supported yet.

### Compile Error: "no rules expected `.`"

**Problem:** Macro parsing issue, often due to incorrect syntax.

**Solution:** Ensure:
- Connections follow a semicolon after nodes
- Port names are explicitly specified
- Syntax is: `node.port => node.port`

## Examples

See the following examples for complete migration patterns:

- `examples/graph_macro_simple.rs` - Basic linear pipeline
- `examples/graph_macro_fan_patterns.rs` - Fan-in patterns
- `examples/graph_macro_io.rs` - Graph I/O patterns

## Further Reading

- [Graph API Guide]GRAPH.md - Advanced graph patterns and routing
- [API Documentation]https://docs.rs/streamweave - Full API reference
- [Macro Documentation]../src/graph_macros.rs - Macro implementation details