chaotic_semantic_memory 0.3.4

AI memory systems with hyperdimensional vectors and chaotic reservoirs
Documentation
use std::path::Path;

use tracing::instrument;

use crate::cli::args::{AssociateArgs, OutputFormat};
use crate::cli::error::{CliError, Result};

use super::{
    create_framework, print_error, print_success, print_warning, validate_concept_id,
    validate_strength,
};

#[instrument(name = "cli_associate")]
pub async fn run_associate(
    args: AssociateArgs,
    db_path: Option<&Path>,
    format: OutputFormat,
) -> Result<()> {
    validate_concept_id(&args.source_id)?;
    validate_concept_id(&args.target_id)?;
    validate_strength(args.strength)?;

    let framework = create_framework(db_path).await?;

    let source_exists = framework
        .get_concept(&args.source_id)
        .await
        .map_err(|e| CliError::Persistence(format!("failed to check source concept: {e}")))?
        .is_some();
    if !source_exists {
        print_error(&format!("concept '{}' not found", args.source_id));
        return Err(CliError::Input(format!(
            "concept '{}' not found",
            args.source_id
        )));
    }

    let target_exists = framework
        .get_concept(&args.target_id)
        .await
        .map_err(|e| CliError::Persistence(format!("failed to check target concept: {e}")))?
        .is_some();
    if !target_exists {
        print_error(&format!("concept '{}' not found", args.target_id));
        return Err(CliError::Input(format!(
            "concept '{}' not found",
            args.target_id
        )));
    }

    let is_self_assoc = args.source_id == args.target_id;

    framework
        .associate(&args.source_id, &args.target_id, args.strength as f32)
        .await
        .map_err(|e| {
            CliError::Persistence(format!(
                "failed to associate '{}' -> '{}': {e}",
                args.source_id, args.target_id
            ))
        })?;

    match format {
        OutputFormat::Json => {
            println!(
                "{}",
                serde_json::json!({
                    "status": "created",
                    "source": args.source_id,
                    "target": args.target_id,
                    "strength": args.strength
                })
            );
        }
        OutputFormat::Table => {
            if is_self_assoc {
                print_warning(
                    &format!(
                        "self-association created: {} -> {} (strength: {:.2})",
                        args.source_id, args.target_id, args.strength
                    ),
                    format,
                );
            } else {
                print_success(
                    &format!(
                        "association created: {} -> {} (strength: {:.2})",
                        args.source_id, args.target_id, args.strength
                    ),
                    format,
                );
            }
        }
        OutputFormat::Quiet => {}
    }

    Ok(())
}

pub async fn run_associate_batch(
    associations: Vec<(String, String, f64)>,
    db_path: Option<&Path>,
    format: OutputFormat,
    continue_on_error: bool,
) -> Result<()> {
    let framework = create_framework(db_path).await?;

    let mut created = 0usize;
    let mut failed = 0usize;

    for (source_id, target_id, strength) in associations {
        match async {
            validate_concept_id(&source_id)?;
            validate_concept_id(&target_id)?;
            validate_strength(strength)?;

            framework
                .associate(&source_id, &target_id, strength as f32)
                .await
                .map_err(|e| CliError::Persistence(format!("association failed: {e}")))?;
            Ok::<(), CliError>(())
        }
        .await
        {
            Err(e) => {
                failed += 1;
                if !continue_on_error {
                    return Err(CliError::Persistence(format!(
                        "batch failed at {} -> {}: {}",
                        source_id, target_id, e
                    )));
                }
                if matches!(format, OutputFormat::Table) {
                    print_warning(
                        &format!("skipped {} -> {}: {}", source_id, target_id, e),
                        format,
                    );
                }
            }
            Ok(()) => {
                created += 1;
            }
        }
    }

    match format {
        OutputFormat::Json => {
            println!(
                "{}",
                serde_json::json!({"created": created, "failed": failed})
            );
        }
        OutputFormat::Table => {
            print_success(
                &format!("batch complete: {} created, {} failed", created, failed),
                format,
            );
        }
        OutputFormat::Quiet => {}
    }

    Ok(())
}