aurora-modules 0.1.0

Git, filesystem, system, network, archive, docker, unix, crypto, calculator, QR, color, timer, notes, clipboard, and text processing modules
Documentation
use aurora_core::{AuroraResult, Pipeline, Value};
use std::process::Command;

fn check_docker() -> AuroraResult<()> {
    let status = Command::new("docker")
        .arg("--version")
        .output()
        .ok()
        .map(|o| o.status.success())
        .unwrap_or(false);
    if !status {
        return Err(aurora_core::AuroraError::CommandNotFound(
            "docker is not installed".into()
        ));
    }
    Ok(())
}

pub fn docker_ps() -> AuroraResult<Pipeline> {
    check_docker()?;
    let output = Command::new("docker")
        .args(["ps", "-a", "--format", "{{json .}}"])
        .output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("docker ps failed: {}", e)
        ))?;

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut rows: Vec<Vec<Value>> = Vec::new();

    for line in stdout.lines() {
        if line.is_empty() { continue; }
        if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(&line) {
            let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let image = parsed.get("Image").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let status = parsed.get("Status").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let ports = parsed.get("Ports").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let names = parsed.get("Names").and_then(|v| v.as_str()).unwrap_or("").to_string();

            rows.push(vec![
                Value::String(id),
                Value::String(image),
                Value::String(status),
                Value::String(ports),
                Value::String(names),
            ]);
        }
    }

    Ok(Pipeline::table(
        vec!["id".into(), "image".into(), "status".into(), "ports".into(), "names".into()],
        rows,
    ))
}

pub fn docker_logs(container: &str, tail: Option<usize>) -> AuroraResult<Pipeline> {
    check_docker()?;
    let mut cmd = Command::new("docker");
    cmd.args(["logs"]);
    if let Some(n) = tail {
        cmd.args(["--tail", &n.to_string()]);
    }
    cmd.arg(container);

    let output = cmd.output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("docker logs failed: {}", e)
        ))?;

    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);
    let combined = format!("{}{}", stdout, stderr);

    let rows: Vec<Vec<Value>> = combined.lines()
        .map(|l| vec![Value::String(l.into())])
        .collect();

    Ok(Pipeline::table(vec!["line".into()], rows))
}

pub fn docker_exec(container: &str, command: &[String]) -> AuroraResult<Pipeline> {
    check_docker()?;
    let args: Vec<&str> = command.iter().map(|s| s.as_str()).collect();

    let output = Command::new("docker")
        .arg("exec")
        .arg(container)
        .args(&args)
        .output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("docker exec failed: {}", e)
        ))?;

    let stdout = String::from_utf8_lossy(&output.stdout);
    let stderr = String::from_utf8_lossy(&output.stderr);
    let combined = format!("{}{}", stdout, stderr);

    Ok(Pipeline::table(
        vec!["output".into()],
        vec![vec![Value::String(combined)]],
    ))
}

pub fn docker_images() -> AuroraResult<Pipeline> {
    check_docker()?;
    let output = Command::new("docker")
        .args(["images", "--format", "{{json .}}"])
        .output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("docker images failed: {}", e)
        ))?;

    let stdout = String::from_utf8_lossy(&output.stdout);
    let mut rows: Vec<Vec<Value>> = Vec::new();

    for line in stdout.lines() {
        if line.is_empty() { continue; }
        if let Ok(parsed) = serde_json::from_str::<serde_json::Value>(line) {
            let repo = parsed.get("Repository").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let tag = parsed.get("Tag").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let id = parsed.get("ID").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let created = parsed.get("CreatedAt").and_then(|v| v.as_str()).unwrap_or("").to_string();
            let size = parsed.get("Size").and_then(|v| v.as_str()).unwrap_or("").to_string();

            rows.push(vec![
                Value::String(repo),
                Value::String(tag),
                Value::String(id),
                Value::String(created),
                Value::String(size),
            ]);
        }
    }

    Ok(Pipeline::table(
        vec!["repository".into(), "tag".into(), "id".into(), "created".into(), "size".into()],
        rows,
    ))
}