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::os::unix::fs::PermissionsExt;
use std::path::Path;
use std::time::UNIX_EPOCH;
use walkdir::WalkDir;
use regex::Regex;

pub fn fs_ls(path: &str, long: bool) -> AuroraResult<Pipeline> {
    let dir = fs::read_dir(path)
        .map_err(|e| aurora_core::AuroraError::Io(e))?;

    let mut rows: Vec<Vec<Value>> = Vec::new();

    for entry in dir {
        let entry = entry.map_err(|e| aurora_core::AuroraError::Io(e))?;
        let name = entry.file_name().to_string_lossy().to_string();
        let meta = entry.metadata().ok();

        let size = meta.as_ref().map(|m| m.len() as i64).unwrap_or(0);
        let file_type = if meta.as_ref().map(|m| m.is_dir()).unwrap_or(false) {
            "dir"
        } else if meta.as_ref().map(|m| m.is_symlink()).unwrap_or(false) {
            "symlink"
        } else {
            "file"
        };
        let modified = meta.as_ref()
            .and_then(|m| m.modified().ok())
            .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
            .map(|d| d.as_secs() as i64)
            .unwrap_or(0);

        if long {
            rows.push(vec![
                Value::String(name),
                Value::Int(size),
                Value::String(file_type.into()),
                Value::Int(modified),
            ]);
        } else {
            rows.push(vec![Value::String(name)]);
        }
    }

    let headers = if long {
        vec!["name".into(), "size".into(), "type".into(), "modified".into()]
    } else {
        vec!["name".into()]
    };

    Ok(Pipeline::table(headers, rows))
}

pub fn fs_tree(path: &str, depth: Option<usize>) -> AuroraResult<Pipeline> {
    let mut walker = WalkDir::new(path);
    if let Some(d) = depth {
        walker = walker.max_depth(d);
    }

    let mut rows: Vec<Vec<Value>> = Vec::new();

    for entry in walker.into_iter().filter_map(|e| e.ok()) {
        let p = entry.path().to_string_lossy().to_string();
        let level = entry.depth() as i64;
        rows.push(vec![
            Value::String(p),
            Value::Int(level),
        ]);
    }

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

pub fn fs_find(pattern: &str, path: &str) -> AuroraResult<Pipeline> {
    let re = Regex::new(pattern)
        .map_err(|e| aurora_core::AuroraError::InvalidInput(
            format!("invalid regex: {}", e)
        ))?;

    let mut rows: Vec<Vec<Value>> = Vec::new();

    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
        if entry.file_type().is_file() {
            let name = entry.file_name().to_string_lossy();
            if re.is_match(&name) {
                let p = entry.path().to_string_lossy().to_string();
                let size = entry.metadata()
                    .map(|m| m.len() as i64)
                    .unwrap_or(0);
                rows.push(vec![
                    Value::String(p),
                    Value::Int(size),
                ]);
            }
        }
    }

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

pub fn fs_info(path: &str) -> AuroraResult<Pipeline> {
    let p = Path::new(path);
    let meta = fs::metadata(p)
        .map_err(|e| aurora_core::AuroraError::Io(e))?;

    let size = meta.len() as i64;
    let created = meta.created().ok()
        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
        .map(|d| d.as_secs() as i64)
        .unwrap_or(0);
    let modified = meta.modified().ok()
        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
        .map(|d| d.as_secs() as i64)
        .unwrap_or(0);
    let file_type = if meta.is_dir() {
        "directory"
    } else if meta.is_symlink() {
        "symlink"
    } else {
        "file"
    };
    let permissions = format!("{:o}", meta.permissions().mode());

    let mut record = std::collections::BTreeMap::new();
    record.insert("path".into(), Value::String(path.into()));
    record.insert("size".into(), Value::Int(size));
    record.insert("created".into(), Value::Int(created));
    record.insert("modified".into(), Value::Int(modified));
    record.insert("type".into(), Value::String(file_type.into()));
    record.insert("permissions".into(), Value::String(permissions));

    Ok(Pipeline::single(Value::Record(record)))
}

pub fn fs_du(path: &str) -> AuroraResult<Pipeline> {
    let mut rows: Vec<Vec<Value>> = Vec::new();

    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
        if entry.file_type().is_dir() {
            let p = entry.path().to_string_lossy().to_string();
            let mut size: i64 = 0;
            if let Ok(dir) = fs::read_dir(entry.path()) {
                for sub in dir.flatten() {
                    if let Ok(meta) = sub.metadata() {
                        size += meta.len() as i64;
                    }
                }
            }
            rows.push(vec![
                Value::String(p),
                Value::Int(size),
            ]);
        }
    }

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