weavatrix-rust 2.1.1

Protocol-independent Rust repository intelligence: typed evidence graphs for impact, architecture, APIs, Git, search, and memory
Documentation
use std::env;
use std::path::PathBuf;
use std::process::ExitCode;
use weavatrix_rust::{Analyzer, Weavatrix, operations};

fn main() -> ExitCode {
    match run(env::args().skip(1).collect()) {
        Ok(()) => ExitCode::SUCCESS,
        Err(message) => {
            eprintln!("weavatrix-rust: {message}");
            ExitCode::FAILURE
        }
    }
}

fn run(arguments: Vec<String>) -> Result<(), String> {
    if arguments.first().is_some_and(|value| value == "--version") {
        println!("weavatrix-rust {}", env!("CARGO_PKG_VERSION"));
        return Ok(());
    }
    if arguments
        .first()
        .is_some_and(|value| value == "--help" || value == "-h")
    {
        print_help();
        return Ok(());
    }
    match arguments.first().map(String::as_str) {
        Some("list-tools") => {
            println!(
                "{}",
                blazingly_json::to_string_pretty(&operations::catalog())
                    .map_err(|error| error.to_string())?
            );
            return Ok(());
        }
        Some("tool") => {
            let name = arguments
                .get(1)
                .ok_or_else(|| "tool requires a tool name".to_owned())?;
            let repository = arguments.get(2).map_or(".", String::as_str);
            let input = arguments
                .get(3)
                .map_or_else(
                    || Ok(blazingly_json::json!({})),
                    |value| blazingly_json::from_str(value),
                )
                .map_err(|error| format!("invalid tool JSON: {error}"))?;
            let mut engine = Weavatrix::open(repository).map_err(|error| error.to_string())?;
            let output = operations::call(&mut engine, name, input)?;
            println!(
                "{}",
                blazingly_json::to_string_pretty(&output).map_err(|error| error.to_string())?
            );
            return Ok(());
        }
        Some("analyze") => {}
        _ => {
            print_help();
            return Err("expected the `analyze`, `tool`, or `list-tools` command".into());
        }
    }

    let mut repository = PathBuf::from(".");
    let mut pretty = false;
    let mut format = OutputFormat::Snapshot;
    for argument in arguments.into_iter().skip(1) {
        if argument == "--pretty" {
            pretty = true;
        } else if let Some(value) = argument.strip_prefix("--format=") {
            format = OutputFormat::parse(value)?;
        } else if argument.starts_with('-') {
            return Err(format!("unknown option: {argument}"));
        } else {
            repository = PathBuf::from(argument);
        }
    }

    let analyzer = Analyzer::default();
    let json = match format {
        OutputFormat::Snapshot => analyzer.analyze_json(repository, pretty),
        OutputFormat::Legacy => analyzer.analyze_legacy_json(repository, pretty),
    }
    .map_err(|error| error.to_string())?;
    println!("{json}");
    Ok(())
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum OutputFormat {
    Snapshot,
    Legacy,
}

impl OutputFormat {
    fn parse(value: &str) -> Result<Self, String> {
        match value {
            "snapshot" => Ok(Self::Snapshot),
            "legacy" => Ok(Self::Legacy),
            _ => Err(format!(
                "unknown format: {value}; expected snapshot or legacy"
            )),
        }
    }
}

fn print_help() {
    println!(
        "weavatrix-rust repository intelligence engine\n\n\
Usage:\n  weavatrix-rust analyze [REPOSITORY] [--pretty] [--format=snapshot|legacy]\n  weavatrix-rust --version\n\n\
  weavatrix-rust list-tools\n\n\
  weavatrix-rust tool NAME [REPOSITORY] ['{{\"argument\":\"value\"}}']\n\n\
Formats:\n  snapshot  Canonical weavatrix-rust snapshot (default)\n  legacy    JavaScript Weavatrix-compatible {{ nodes, links }} graph"
    );
}