# Migration Guide: `#[derive(Node)]` Macro

This guide shows how to migrate existing nodes from manual `run()` functions or legacy macros to the new `#[derive(Node)]` macro.

## Migration Paths

1. [From manual `run()` function](#from-manual-run-function)
2. [From `run_node_simple!` macro](#from-run_node_simple-macro)
3. [From `run_node_with_instance!` macro](#from-run_node_with_instance-macro)

---

## From Manual `run()` Function

### Before

```rust
use mecha10::prelude::*;

pub struct CameraNode {
    config: CameraConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CameraConfig {
    pub fps: f32,
}

#[async_trait]
impl NodeImpl for CameraNode {
    type Config = CameraConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}

// Manual run() function
pub async fn run() -> Result<()> {
    init_logging();
    info!("Starting camera node");

    let config = load_config_with_fallback(
        "CAMERA_CONFIG",
        "config/camera.yaml",
        "CAMERA",
        Some(CameraConfig::default())
    )?;

    let ctx = Context::new("camera").await?;

    run_node::<CameraNode>(
        config,
        ctx,
        HealthReportingConfig::default()
    ).await
}
```

### After

```rust
use mecha10::prelude::*;

#[derive(Debug, Node)]  // Add derive macro
#[node(
    name = "camera",
    config_env = "CAMERA_CONFIG",
    config_path = "config/camera.yaml",
    env_prefix = "CAMERA"
)]
pub struct CameraNode {
    config: CameraConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CameraConfig {
    pub fps: f32,
}

#[async_trait]
impl NodeImpl for CameraNode {
    type Config = CameraConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}

// run() function is auto-generated!
```

### Changes

1. ✅ Add `#[derive(Node)]` to the struct
2. ✅ Add `#[node(...)]` attributes with configuration
3. ❌ Delete the manual `run()` function

---

## From `run_node_simple!` Macro

### Before

```rust
use mecha10::prelude::*;

pub struct LidarNode {
    config: LidarConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LidarConfig {
    pub scan_rate: f32,
}

#[async_trait]
impl NodeImpl for LidarNode {
    type Config = LidarConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}

// Legacy macro
run_node_simple!(
    LidarNode,
    "lidar",
    "LIDAR_CONFIG",
    "config/lidar.yaml",
    "LIDAR"
);
```

### After

```rust
use mecha10::prelude::*;

#[derive(Debug, Node)]
#[node(
    name = "lidar",
    config_env = "LIDAR_CONFIG",
    config_path = "config/lidar.yaml",
    env_prefix = "LIDAR"
)]
pub struct LidarNode {
    config: LidarConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct LidarConfig {
    pub scan_rate: f32,
}

#[async_trait]
impl NodeImpl for LidarNode {
    type Config = LidarConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}

// Macro invocation is replaced with derive!
```

### Migration Mapping

| `run_node_simple!` Argument | `#[node(...)]` Attribute |
|----------------------------|--------------------------|
| 1st: `LidarNode` (type)    | Applied to struct        |
| 2nd: `"lidar"` (name)      | `name = "lidar"`         |
| 3rd: `"LIDAR_CONFIG"`      | `config_env = "LIDAR_CONFIG"` |
| 4th: `"config/lidar.yaml"` | `config_path = "config/lidar.yaml"` |
| 5th: `"LIDAR"`             | `env_prefix = "LIDAR"`   |

---

## From `run_node_with_instance!` Macro

### Before

```rust
use mecha10::prelude::*;

pub struct CameraNode {
    config: CameraConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CameraConfig {
    pub fps: f32,
}

#[async_trait]
impl NodeImpl for CameraNode {
    type Config = CameraConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}

// Legacy macro with instance support
run_node_with_instance!(
    CameraNode,
    "camera_fake",
    "CAMERA_INSTANCE",
    "CAMERA_CONFIG",
    "config/drivers/camera.yaml",
    "CAMERA"
);
```

### After

```rust
use mecha10::prelude::*;

#[derive(Debug, Node)]
#[node(
    name = "camera_fake",
    instance_env = "CAMERA_INSTANCE",  // Enable multi-instance
    config_env = "CAMERA_CONFIG",
    config_path = "config/drivers/camera.yaml",
    env_prefix = "CAMERA"
)]
pub struct CameraNode {
    config: CameraConfig,
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct CameraConfig {
    pub fps: f32,
}

#[async_trait]
impl NodeImpl for CameraNode {
    type Config = CameraConfig;

    async fn init(config: Self::Config) -> Result<Self> {
        Ok(Self { config })
    }

    async fn run(&mut self, ctx: &Context) -> Result<()> {
        // Node logic
        Ok(())
    }
}
```

### Migration Mapping

| `run_node_with_instance!` Argument | `#[node(...)]` Attribute |
|-----------------------------------|--------------------------|
| 1st: `CameraNode` (type)          | Applied to struct        |
| 2nd: `"camera_fake"` (name)       | `name = "camera_fake"`   |
| 3rd: `"CAMERA_INSTANCE"`          | `instance_env = "CAMERA_INSTANCE"` |
| 4th: `"CAMERA_CONFIG"`            | `config_env = "CAMERA_CONFIG"` |
| 5th: `"config/drivers/camera.yaml"` | `config_path = "config/drivers/camera.yaml"` |
| 6th: `"CAMERA"`                   | `env_prefix = "CAMERA"`  |

---

## Special Cases

### Multi-Instance with Minimal Config

If your node uses default paths, you can use minimal configuration:

**Before:**
```rust
run_node_with_instance!(
    SensorNode,
    "sensor",
    "SENSOR_INSTANCE",
    "SENSOR_CONFIG",
    "config/sensor.yaml",
    "SENSOR"
);
```

**After (minimal):**
```rust
#[derive(Debug, Node)]
#[node(
    name = "sensor",
    instance_env = "SENSOR_INSTANCE"  // Only override instance support
)]
pub struct SensorNode { ... }
```

The macro will generate defaults for the other attributes.

### Custom Health Reporting

The `#[derive(Node)]` macro uses `HealthReportingConfig::default()` (5 second interval).

If you need custom health reporting intervals, continue using a manual `run()` function:

```rust
#[derive(Debug)]
pub struct CustomNode { ... }

// Manual run() for custom health reporting
pub async fn run() -> Result<()> {
    let config = load_config_with_fallback(...)?;
    let ctx = Context::new("custom").await?;

    run_node::<CustomNode>(
        config,
        ctx,
        HealthReportingConfig {
            interval: Duration::from_secs(1),  // Custom interval
            enabled: true,
        }
    ).await
}
```

---

## Migration Checklist

For each node you migrate:

- [ ] Add `#[derive(Debug, Node)]` to struct (must have `Debug`)
- [ ] Add `#[node(...)]` attribute with configuration
- [ ] Remove manual `run()` function OR legacy macro invocation
- [ ] Verify attributes map correctly from old implementation
- [ ] Test that configuration loading still works
- [ ] Test multi-instance deployment (if applicable)

---

## Benefits After Migration

✅ **15-20 lines less code** per node
✅ **Consistent setup** across all nodes
✅ **Easier to maintain** - update macro instead of each node
✅ **Type-safe** - compile-time validation
✅ **Better documentation** - generated functions include docs

---

## Troubleshooting

### Error: "Node only supports structs with named fields"

**Problem:**
```rust
#[derive(Debug, Node)]
#[node(name = "my_node")]
pub struct MyNode(SomeConfig);  // Tuple struct not supported
```

**Solution:**
```rust
#[derive(Debug, Node)]
#[node(name = "my_node")]
pub struct MyNode {
    config: SomeConfig,  // Use named fields
}
```

### Error: "missing field `name`"

**Problem:**
```rust
#[derive(Debug, Node)]
#[node(config_path = "config.yaml")]  // Missing required 'name'
pub struct MyNode { ... }
```

**Solution:**
```rust
#[derive(Debug, Node)]
#[node(
    name = "my_node",  // Add required name
    config_path = "config.yaml"
)]
pub struct MyNode { ... }
```

### Error: "the trait bound `MyNode: Debug` is not satisfied"

**Problem:**
```rust
#[derive(Node)]  // Missing Debug
#[node(name = "my_node")]
pub struct MyNode { ... }
```

**Solution:**
```rust
#[derive(Debug, Node)]  // Add Debug
#[node(name = "my_node")]
pub struct MyNode { ... }
```

---

## See Also

- [Node Macro README](./README_NODE_MACRO.md) - Full documentation
- [Examples](./examples/node_macro_usage.rs) - Usage examples
- [Config Macro](./README_CONFIG_MACRO.md) - Config loading automation
