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::fs;
use std::process::Command;

pub fn sys_info() -> AuroraResult<Pipeline> {
    let mut rows: Vec<Vec<Value>> = Vec::new();

    if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
        for line in cpuinfo.lines() {
            if let Some(key) = line.split(':').next() {
                let key = key.trim();
                if key == "model name" {
                    if let Some(val) = line.split(':').nth(1) {
                        rows.push(vec![
                            Value::String("CPU".into()),
                            Value::String(val.trim().into()),
                        ]);
                    }
                    break;
                }
            }
        }
    }

    if let Ok(meminfo) = fs::read_to_string("/proc/meminfo") {
        let mut total = String::new();
        for line in meminfo.lines() {
            if line.starts_with("MemTotal:") {
                if let Some(val) = line.split(':').nth(1) {
                    total = val.trim().to_string();
                }
                break;
            }
        }
        rows.push(vec![
            Value::String("Memory".into()),
            Value::String(total),
        ]);
    }

    if let Ok(uptime) = fs::read_to_string("/proc/uptime") {
        if let Some(secs) = uptime.split(' ').next() {
            rows.push(vec![
                Value::String("Uptime".into()),
                Value::String(format!("{}s", secs.trim())),
            ]);
        }
    }

    if let Ok(output) = Command::new("uname").arg("-r").output() {
        let kernel = String::from_utf8_lossy(&output.stdout).trim().to_string();
        rows.push(vec![
            Value::String("Kernel".into()),
            Value::String(kernel),
        ]);
    }

    if let Ok(host) = fs::read_to_string("/proc/sys/kernel/hostname") {
        rows.push(vec![
            Value::String("Hostname".into()),
            Value::String(host.trim().into()),
        ]);
    }

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

pub fn sys_process() -> AuroraResult<Pipeline> {
    let mut rows: Vec<Vec<Value>> = Vec::new();

    let dir = fs::read_dir("/proc").map_err(|e| aurora_core::AuroraError::Io(e))?;
    for entry in dir.flatten() {
        let name = entry.file_name();
        let pid_str = name.to_string_lossy();
        let pid: i64 = match pid_str.parse() {
            Ok(n) => n,
            Err(_) => continue,
        };

        let status_path = entry.path().join("status");
        let status_content = match fs::read_to_string(&status_path) {
            Ok(c) => c,
            Err(_) => continue,
        };

        let mut proc_name = String::new();
        let mut state = String::new();
        for line in status_content.lines() {
            if line.starts_with("Name:") {
                if let Some(val) = line.split(':').nth(1) {
                    proc_name = val.trim().to_string();
                }
            }
            if line.starts_with("State:") {
                if let Some(val) = line.split(':').nth(1) {
                    state = val.trim().to_string();
                }
            }
        }

        rows.push(vec![
            Value::Int(pid),
            Value::String(proc_name),
            Value::String(state),
            Value::String("--".into()),
            Value::String("--".into()),
        ]);
    }

    Ok(Pipeline::table(
        vec!["pid".into(), "name".into(), "state".into(), "cpu%".into(), "mem%".into()],
        rows,
    ))
}

pub fn sys_disk() -> AuroraResult<Pipeline> {
    let output = Command::new("df")
        .arg("-h")
        .output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("df failed: {}", e)
        ))?;

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

    for (i, line) in stdout.lines().enumerate() {
        if i == 0 || line.trim().is_empty() { continue; }
        let parts: Vec<&str> = line.split_whitespace().collect();
        if parts.len() >= 6 {
            rows.push(vec![
                Value::String(parts[5].into()),
                Value::String(parts[1].into()),
                Value::String(parts[2].into()),
                Value::String(parts[3].into()),
                Value::String(parts[4].into()),
            ]);
        }
    }

    Ok(Pipeline::table(
        vec!["mount".into(), "total".into(), "used".into(), "avail".into(), "use%".into()],
        rows,
    ))
}

pub fn sys_memory() -> AuroraResult<Pipeline> {
    let content = fs::read_to_string("/proc/meminfo")
        .map_err(|e| aurora_core::AuroraError::Io(e))?;

    let mut total = String::new();
    let mut free = String::new();
    let mut buffers = String::new();
    let mut cached = String::new();

    for line in content.lines() {
        if line.starts_with("MemTotal:") {
            if let Some(v) = line.split(':').nth(1) {
                total = v.trim().to_string();
            }
        } else if line.starts_with("MemFree:") {
            if let Some(v) = line.split(':').nth(1) {
                free = v.trim().to_string();
            }
        } else if line.starts_with("Buffers:") {
            if let Some(v) = line.split(':').nth(1) {
                buffers = v.trim().to_string();
            }
        } else if line.starts_with("Cached:") {
            if let Some(v) = line.split(':').nth(1) {
                cached = v.trim().to_string();
            }
        }
    }

    let used_kb = parse_kb(&total).saturating_sub(parse_kb(&free));
    let used = format!("{} kB", used_kb);

    Ok(Pipeline::table(
        vec!["total".into(), "used".into(), "free".into(), "buffers".into(), "cached".into()],
        vec![vec![
            Value::String(total),
            Value::String(used),
            Value::String(free),
            Value::String(buffers),
            Value::String(cached),
        ]],
    ))
}

fn parse_kb(s: &str) -> u64 {
    let num: String = s.chars().filter(|c| c.is_ascii_digit()).collect();
    num.parse().unwrap_or(0)
}

pub fn sys_service(name: &str) -> AuroraResult<Pipeline> {
    let output = Command::new("systemctl")
        .args(["status", name, "--no-pager", "-l"])
        .output()
        .map_err(|e| aurora_core::AuroraError::ModuleError(
            format!("systemctl 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!["service".into(), "info".into()],
        vec![vec![
            Value::String(name.into()),
            Value::String(combined),
        ]],
    ))
}