Skip to main content

aurora_modules/
fs.rs

1use aurora_core::{AuroraResult, Pipeline, Value};
2use std::fs;
3use std::os::unix::fs::PermissionsExt;
4use std::path::Path;
5use std::time::UNIX_EPOCH;
6use walkdir::WalkDir;
7use regex::Regex;
8
9pub fn fs_ls(path: &str, long: bool) -> AuroraResult<Pipeline> {
10    let dir = fs::read_dir(path)
11        .map_err(|e| aurora_core::AuroraError::Io(e))?;
12
13    let mut rows: Vec<Vec<Value>> = Vec::new();
14
15    for entry in dir {
16        let entry = entry.map_err(|e| aurora_core::AuroraError::Io(e))?;
17        let name = entry.file_name().to_string_lossy().to_string();
18        let meta = entry.metadata().ok();
19
20        let size = meta.as_ref().map(|m| m.len() as i64).unwrap_or(0);
21        let file_type = if meta.as_ref().map(|m| m.is_dir()).unwrap_or(false) {
22            "dir"
23        } else if meta.as_ref().map(|m| m.is_symlink()).unwrap_or(false) {
24            "symlink"
25        } else {
26            "file"
27        };
28        let modified = meta.as_ref()
29            .and_then(|m| m.modified().ok())
30            .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
31            .map(|d| d.as_secs() as i64)
32            .unwrap_or(0);
33
34        if long {
35            rows.push(vec![
36                Value::String(name),
37                Value::Int(size),
38                Value::String(file_type.into()),
39                Value::Int(modified),
40            ]);
41        } else {
42            rows.push(vec![Value::String(name)]);
43        }
44    }
45
46    let headers = if long {
47        vec!["name".into(), "size".into(), "type".into(), "modified".into()]
48    } else {
49        vec!["name".into()]
50    };
51
52    Ok(Pipeline::table(headers, rows))
53}
54
55pub fn fs_tree(path: &str, depth: Option<usize>) -> AuroraResult<Pipeline> {
56    let mut walker = WalkDir::new(path);
57    if let Some(d) = depth {
58        walker = walker.max_depth(d);
59    }
60
61    let mut rows: Vec<Vec<Value>> = Vec::new();
62
63    for entry in walker.into_iter().filter_map(|e| e.ok()) {
64        let p = entry.path().to_string_lossy().to_string();
65        let level = entry.depth() as i64;
66        rows.push(vec![
67            Value::String(p),
68            Value::Int(level),
69        ]);
70    }
71
72    Ok(Pipeline::table(
73        vec!["path".into(), "level".into()],
74        rows,
75    ))
76}
77
78pub fn fs_find(pattern: &str, path: &str) -> AuroraResult<Pipeline> {
79    let re = Regex::new(pattern)
80        .map_err(|e| aurora_core::AuroraError::InvalidInput(
81            format!("invalid regex: {}", e)
82        ))?;
83
84    let mut rows: Vec<Vec<Value>> = Vec::new();
85
86    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
87        if entry.file_type().is_file() {
88            let name = entry.file_name().to_string_lossy();
89            if re.is_match(&name) {
90                let p = entry.path().to_string_lossy().to_string();
91                let size = entry.metadata()
92                    .map(|m| m.len() as i64)
93                    .unwrap_or(0);
94                rows.push(vec![
95                    Value::String(p),
96                    Value::Int(size),
97                ]);
98            }
99        }
100    }
101
102    Ok(Pipeline::table(
103        vec!["path".into(), "size".into()],
104        rows,
105    ))
106}
107
108pub fn fs_info(path: &str) -> AuroraResult<Pipeline> {
109    let p = Path::new(path);
110    let meta = fs::metadata(p)
111        .map_err(|e| aurora_core::AuroraError::Io(e))?;
112
113    let size = meta.len() as i64;
114    let created = meta.created().ok()
115        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
116        .map(|d| d.as_secs() as i64)
117        .unwrap_or(0);
118    let modified = meta.modified().ok()
119        .and_then(|t| t.duration_since(UNIX_EPOCH).ok())
120        .map(|d| d.as_secs() as i64)
121        .unwrap_or(0);
122    let file_type = if meta.is_dir() {
123        "directory"
124    } else if meta.is_symlink() {
125        "symlink"
126    } else {
127        "file"
128    };
129    let permissions = format!("{:o}", meta.permissions().mode());
130
131    let mut record = std::collections::BTreeMap::new();
132    record.insert("path".into(), Value::String(path.into()));
133    record.insert("size".into(), Value::Int(size));
134    record.insert("created".into(), Value::Int(created));
135    record.insert("modified".into(), Value::Int(modified));
136    record.insert("type".into(), Value::String(file_type.into()));
137    record.insert("permissions".into(), Value::String(permissions));
138
139    Ok(Pipeline::single(Value::Record(record)))
140}
141
142pub fn fs_du(path: &str) -> AuroraResult<Pipeline> {
143    let mut rows: Vec<Vec<Value>> = Vec::new();
144
145    for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
146        if entry.file_type().is_dir() {
147            let p = entry.path().to_string_lossy().to_string();
148            let mut size: i64 = 0;
149            if let Ok(dir) = fs::read_dir(entry.path()) {
150                for sub in dir.flatten() {
151                    if let Ok(meta) = sub.metadata() {
152                        size += meta.len() as i64;
153                    }
154                }
155            }
156            rows.push(vec![
157                Value::String(p),
158                Value::Int(size),
159            ]);
160        }
161    }
162
163    Ok(Pipeline::table(
164        vec!["path".into(), "size_bytes".into()],
165        rows,
166    ))
167}