dda-rs 0.1.0

Rust interface for the run_DDA_ASCII binary (Cosmopolitan Libc APE) for Delay Differential Analysis
Documentation
# Refactoring Guide: Using dda-rs in embedded_api.rs

## Overview

The `dda-rs` crate has been created to provide a clean interface to the `run_DDA_ASCII` binary. This document shows how to refactor `embedded_api.rs` to use it.

## Current vs. New Approach

### Current (embedded_api.rs)
```rust
// Lines 764-1180: All DDA logic mixed into embedded_api.rs
pub async fn run_dda_analysis(
    State(state): State<Arc<ApiState>>,
    Json(request): Json<DDARequest>,
) -> Result<Json<DDAResult>, StatusCode> {
    // 400+ lines of binary execution, parsing, formatting...
}
```

### New (with dda-rs)
```rust
use dda_rs::{DDARunner, DDARequest as DDAReq, DDAResult as DDARes};

pub async fn run_dda_analysis(
    State(state): State<Arc<ApiState>>,
    Json(request): Json<DDARequest>,
) -> Result<Json<DDAResult>, StatusCode> {
    // Create runner
    let runner = DDARunner::new(&dda_binary_path)
        .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;

    // Convert request to dda-rs format
    let dda_request = convert_request(&request);

    // Run analysis (handles binary execution and parsing)
    let dda_result = runner.run(&dda_request, end_bound).await
        .map_err(|e| {
            log::error!("DDA analysis failed: {}", e);
            StatusCode::INTERNAL_SERVER_ERROR
        })?;

    // Convert dda-rs result to API format
    let api_result = format_result_for_api(dda_result, &state, &request);

    // Store and return
    store_result(&state, &api_result);
    Ok(Json(api_result))
}
```

## Migration Steps

### 1. Add dda-rs dependency

Already done in `Cargo.toml`:
```toml
dda-rs = { path = "../../dda-rs" }
```

### 2. Import dda-rs types

```rust
use dda_rs::{
    DDARunner,
    DDARequest as DDAReq,
    DDAResult as DDARes,
    WindowParameters as DDAWindow,
    ScaleParameters as DDAScale,
    TimeRange as DDATimeRange,
    PreprocessingOptions as DDAPreprocessing,
    AlgorithmSelection as DDAAlgorithm,
};
```

### 3. Keep API types in embedded_api.rs

The API-facing types remain in `embedded_api.rs` for compatibility:
- `DDARequest` (API input)
- `DDAResult` (API output with frontend-specific formatting)
- `DDAParameters`

### 4. Create conversion functions

```rust
fn convert_to_dda_request(api_req: &DDARequest) -> DDAReq {
    DDAReq {
        file_path: api_req.file_path.clone(),
        channels: api_req.channels.clone(),
        time_range: DDATimeRange {
            start: api_req.time_range.start,
            end: api_req.time_range.end,
        },
        preprocessing_options: DDAPreprocessing {
            detrending: api_req.preprocessing_options.detrending.clone(),
            highpass: api_req.preprocessing_options.highpass,
            lowpass: api_req.preprocessing_options.lowpass,
        },
        algorithm_selection: DDAAlgorithm {
            enabled_variants: api_req.algorithm_selection.enabled_variants.clone(),
        },
        window_parameters: DDAWindow {
            window_length: api_req.window_parameters.window_length,
            window_step: api_req.window_parameters.window_step,
        },
        scale_parameters: DDAScale {
            scale_min: api_req.scale_parameters.scale_min,
            scale_max: api_req.scale_parameters.scale_max,
            scale_num: api_req.scale_parameters.scale_num,
        },
    }
}

fn format_result_for_api(
    dda_result: DDARes,
    state: &ApiState,
    request: &DDARequest,
) -> DDAResult {
    // Extract Q matrix
    let q_matrix = dda_result.q_matrix;
    let num_channels = q_matrix.len();
    let num_timepoints = q_matrix[0].len();

    // Get channel names from file cache (same logic as current)
    let channel_names = get_channel_names(state, request, num_channels);

    // Format for frontend
    let mut dda_matrix = serde_json::Map::new();
    for (i, channel_name) in channel_names.iter().enumerate() {
        dda_matrix.insert(
            channel_name.clone(),
            serde_json::json!(q_matrix[i])
        );
    }

    // Build frontend-compatible result
    DDAResult {
        id: dda_result.id,
        file_path: dda_result.file_path,
        channels: channel_names,
        parameters: DDAParameters {
            variants: request.algorithm_selection.enabled_variants.clone(),
            window_length: dda_result.window_parameters.window_length,
            window_step: dda_result.window_parameters.window_step,
            detrending: request.preprocessing_options.detrending.clone()
                .unwrap_or_else(|| "linear".to_string()),
            scale_min: dda_result.scale_parameters.scale_min,
            scale_max: dda_result.scale_parameters.scale_max,
            scale_num: dda_result.scale_parameters.scale_num,
        },
        results: format_results_json(&q_matrix, num_timepoints, &dda_matrix),
        plot_data: Some(format_plot_data(&q_matrix, num_timepoints)),
        q_matrix: Some(q_matrix),
        created_at: dda_result.created_at,
        status: "completed".to_string(),
    }
}
```

### 5. Remove from embedded_api.rs

After refactoring, you can remove these functions:
- `parse_dda_output` (now in `dda-rs/parser.rs`)
- Binary execution logic (now in `dda-rs/runner.rs`)
- Lines 906-1040 in current `run_dda_analysis`

## Benefits

1. **Separation of Concerns**: DDA binary interface is isolated
2. **Reusability**: dda-rs can be used in other projects
3. **Testability**: Binary interface can be tested independently
4. **Maintainability**: Smaller, focused modules
5. **Type Safety**: Strong types for DDA operations

## Testing

```rust
#[tokio::test]
async fn test_dda_analysis_with_runner() {
    let runner = DDARunner::new("/path/to/run_DDA_ASCII").unwrap();
    let request = /* build request */;
    let result = runner.run(&request, 10000).await.unwrap();
    assert!(!result.q_matrix.is_empty());
}
```

## Next Steps

1. Implement conversion functions
2. Refactor `run_dda_analysis` to use `DDARunner`
3. Test with existing EDF files
4. Remove old binary execution code
5. Update tests to use dda-rs