Skip to main content

aurora_modules/
archive.rs

1use aurora_core::{AuroraResult, Pipeline, Value};
2use std::process::Command;
3use std::path::Path;
4
5fn check_tool(name: &str) -> AuroraResult<()> {
6    let status = Command::new("which")
7        .arg(name)
8        .output()
9        .ok()
10        .map(|o| o.status.success())
11        .unwrap_or(false);
12    if !status {
13        return Err(aurora_core::AuroraError::CommandNotFound(
14            format!("{} is not installed", name)
15        ));
16    }
17    Ok(())
18}
19
20fn extension(path: &str) -> &str {
21    let p = Path::new(path);
22    p.extension()
23        .and_then(|e| e.to_str())
24        .unwrap_or("")
25}
26
27pub fn archive_list(file: &str) -> AuroraResult<Pipeline> {
28    let ext = extension(file);
29    match ext {
30        "tar" | "tar.gz" | "tgz" | "tar.bz2" | "tar.xz" => list_tar(file),
31        "zip" => list_zip(file),
32        "7z" => list_7z(file),
33        "gz" => list_gz(file),
34        _ => Err(aurora_core::AuroraError::InvalidInput(
35            format!("unsupported archive format: {}", ext)
36        )),
37    }
38}
39
40fn list_tar(file: &str) -> AuroraResult<Pipeline> {
41    check_tool("tar")?;
42    let output = Command::new("tar")
43        .args(["tf", file])
44        .output()
45        .map_err(|e| aurora_core::AuroraError::ModuleError(
46            format!("tar failed: {}", e)
47        ))?;
48
49    let stdout = String::from_utf8_lossy(&output.stdout);
50    let rows: Vec<Vec<Value>> = stdout.lines()
51        .filter(|l| !l.is_empty())
52        .map(|l| vec![Value::String(l.into())])
53        .collect();
54
55    Ok(Pipeline::table(vec!["entry".into()], rows))
56}
57
58fn list_zip(file: &str) -> AuroraResult<Pipeline> {
59    check_tool("unzip")?;
60    let output = Command::new("unzip")
61        .args(["-l", file])
62        .output()
63        .map_err(|e| aurora_core::AuroraError::ModuleError(
64            format!("unzip failed: {}", e)
65        ))?;
66
67    let stdout = String::from_utf8_lossy(&output.stdout);
68    let mut rows: Vec<Vec<Value>> = Vec::new();
69
70    for line in stdout.lines() {
71        let line = line.trim();
72        if line.is_empty() || line.starts_with("Archive:") || line.starts_with("Length") || line.contains("----") || line.ends_with("file") {
73            continue;
74        }
75        rows.push(vec![Value::String(line.into())]);
76    }
77
78    Ok(Pipeline::table(vec!["entry".into()], rows))
79}
80
81fn list_7z(file: &str) -> AuroraResult<Pipeline> {
82    check_tool("7z")?;
83    let output = Command::new("7z")
84        .args(["l", file])
85        .output()
86        .map_err(|e| aurora_core::AuroraError::ModuleError(
87            format!("7z failed: {}", e)
88        ))?;
89
90    let stdout = String::from_utf8_lossy(&output.stdout);
91    let mut rows: Vec<Vec<Value>> = Vec::new();
92    let mut capture = false;
93
94    for line in stdout.lines() {
95        if line.contains("----") && !capture {
96            capture = true;
97            continue;
98        }
99        if line.contains("----") && capture {
100            break;
101        }
102        if capture {
103            let line = line.trim();
104            if !line.is_empty() {
105                rows.push(vec![Value::String(line.into())]);
106            }
107        }
108    }
109
110    Ok(Pipeline::table(vec!["entry".into()], rows))
111}
112
113fn list_gz(file: &str) -> AuroraResult<Pipeline> {
114    check_tool("gunzip")?;
115    let output = Command::new("gunzip")
116        .args(["-l", file])
117        .output()
118        .map_err(|e| aurora_core::AuroraError::ModuleError(
119            format!("gunzip failed: {}", e)
120        ))?;
121
122    let stdout = String::from_utf8_lossy(&output.stdout);
123    let mut rows: Vec<Vec<Value>> = Vec::new();
124
125    for (i, line) in stdout.lines().enumerate() {
126        if i == 0 || line.trim().is_empty() { continue; }
127        rows.push(vec![Value::String(line.into())]);
128    }
129
130    Ok(Pipeline::table(vec!["entry".into()], rows))
131}
132
133pub fn archive_extract(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
134    let ext = extension(file);
135    match ext {
136        "tar" | "tar.gz" | "tgz" | "tar.bz2" | "tar.xz" => extract_tar(file, output),
137        "zip" => extract_zip(file, output),
138        "7z" => extract_7z(file, output),
139        "gz" => extract_gz(file, output),
140        _ => Err(aurora_core::AuroraError::InvalidInput(
141            format!("unsupported archive format: {}", ext)
142        )),
143    }
144}
145
146fn extract_tar(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
147    check_tool("tar")?;
148    let mut cmd = Command::new("tar");
149    cmd.arg("xf");
150    cmd.arg(file);
151    if let Some(dir) = output {
152        cmd.args(["-C", dir]);
153    }
154    let result = cmd.output()
155        .map_err(|e| aurora_core::AuroraError::ModuleError(
156            format!("tar extract failed: {}", e)
157        ))?;
158    if !result.status.success() {
159        let stderr = String::from_utf8_lossy(&result.stderr);
160        return Err(aurora_core::AuroraError::ModuleError(
161            format!("tar extract failed: {}", stderr)
162        ));
163    }
164    Ok(Pipeline::table(
165        vec!["action".into(), "file".into(), "status".into()],
166        vec![vec![
167            Value::String("extract".into()),
168            Value::String(file.into()),
169            Value::String("ok".into()),
170        ]],
171    ))
172}
173
174fn extract_zip(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
175    check_tool("unzip")?;
176    let mut cmd = Command::new("unzip");
177    cmd.arg(file);
178    if let Some(dir) = output {
179        cmd.args(["-d", dir]);
180    }
181    let result = cmd.output()
182        .map_err(|e| aurora_core::AuroraError::ModuleError(
183            format!("unzip failed: {}", e)
184        ))?;
185    if !result.status.success() {
186        let stderr = String::from_utf8_lossy(&result.stderr);
187        return Err(aurora_core::AuroraError::ModuleError(
188            format!("unzip failed: {}", stderr)
189        ));
190    }
191    Ok(Pipeline::table(
192        vec!["action".into(), "file".into(), "status".into()],
193        vec![vec![
194            Value::String("extract".into()),
195            Value::String(file.into()),
196            Value::String("ok".into()),
197        ]],
198    ))
199}
200
201fn extract_7z(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
202    check_tool("7z")?;
203    let mut cmd = Command::new("7z");
204    cmd.args(["x", file]);
205    if let Some(dir) = output {
206        cmd.args([&format!("-o{}", dir)]);
207    }
208    let result = cmd.output()
209        .map_err(|e| aurora_core::AuroraError::ModuleError(
210            format!("7z extract failed: {}", e)
211        ))?;
212    if !result.status.success() {
213        let stderr = String::from_utf8_lossy(&result.stderr);
214        return Err(aurora_core::AuroraError::ModuleError(
215            format!("7z extract failed: {}", stderr)
216        ));
217    }
218    Ok(Pipeline::table(
219        vec!["action".into(), "file".into(), "status".into()],
220        vec![vec![
221            Value::String("extract".into()),
222            Value::String(file.into()),
223            Value::String("ok".into()),
224        ]],
225    ))
226}
227
228fn extract_gz(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
229    check_tool("gunzip")?;
230    let result = if let Some(out) = output {
231        Command::new("gunzip")
232            .args(["-c", file])
233            .stdout(std::process::Stdio::piped())
234            .spawn()
235            .and_then(|child| {
236                use std::fs::File;
237                let _out_file = File::create(out)?;
238                let output = child.wait_with_output()?;
239                if output.status.success() {
240                    use std::io::Write;
241                    let mut f = File::create(out)?;
242                    f.write_all(&output.stdout)?;
243                }
244                Ok(output)
245            })
246    } else {
247        Command::new("gunzip")
248            .arg(file)
249            .output()
250    };
251
252    match result {
253        Ok(r) if r.status.success() => {
254            Ok(Pipeline::table(
255                vec!["action".into(), "file".into(), "status".into()],
256                vec![vec![
257                    Value::String("extract".into()),
258                    Value::String(file.into()),
259                    Value::String("ok".into()),
260                ]],
261            ))
262        }
263        Ok(r) => {
264            let stderr = String::from_utf8_lossy(&r.stderr);
265            Err(aurora_core::AuroraError::ModuleError(
266                format!("gunzip failed: {}", stderr)
267            ))
268        }
269        Err(e) => Err(aurora_core::AuroraError::ModuleError(
270            format!("gunzip failed: {}", e)
271        )),
272    }
273}
274
275pub fn archive_compress(input: &str, output: &str) -> AuroraResult<Pipeline> {
276    let ext = extension(output);
277    match ext {
278        "tar.gz" | "tgz" => compress_tar_gz(input, output),
279        "tar.bz2" => compress_tar_bz2(input, output),
280        "tar.xz" => compress_tar_xz(input, output),
281        "tar" => compress_tar(input, output),
282        "zip" => compress_zip(input, output),
283        "7z" => compress_7z(input, output),
284        "gz" => compress_gz(input, output),
285        _ => Err(aurora_core::AuroraError::InvalidInput(
286            format!("unsupported output format: {}", ext)
287        )),
288    }
289}
290
291fn compress_tar_gz(input: &str, output: &str) -> AuroraResult<Pipeline> {
292    check_tool("tar")?;
293    let result = Command::new("tar")
294        .args(["czf", output, input])
295        .output()
296        .map_err(|e| aurora_core::AuroraError::ModuleError(
297            format!("tar failed: {}", e)
298        ))?;
299    if !result.status.success() {
300        let stderr = String::from_utf8_lossy(&result.stderr);
301        return Err(aurora_core::AuroraError::ModuleError(
302            format!("tar failed: {}", stderr)
303        ));
304    }
305    Ok(Pipeline::table(
306        vec!["action".into(), "input".into(), "output".into(), "status".into()],
307        vec![vec![
308            Value::String("compress".into()),
309            Value::String(input.into()),
310            Value::String(output.into()),
311            Value::String("ok".into()),
312        ]],
313    ))
314}
315
316fn compress_tar_bz2(input: &str, output: &str) -> AuroraResult<Pipeline> {
317    check_tool("tar")?;
318    let result = Command::new("tar")
319        .args(["cjf", output, input])
320        .output()
321        .map_err(|e| aurora_core::AuroraError::ModuleError(
322            format!("tar failed: {}", e)
323        ))?;
324    if !result.status.success() {
325        let stderr = String::from_utf8_lossy(&result.stderr);
326        return Err(aurora_core::AuroraError::ModuleError(
327            format!("tar failed: {}", stderr)
328        ));
329    }
330    Ok(Pipeline::table(
331        vec!["action".into(), "input".into(), "output".into(), "status".into()],
332        vec![vec![
333            Value::String("compress".into()),
334            Value::String(input.into()),
335            Value::String(output.into()),
336            Value::String("ok".into()),
337        ]],
338    ))
339}
340
341fn compress_tar_xz(input: &str, output: &str) -> AuroraResult<Pipeline> {
342    check_tool("tar")?;
343    let result = Command::new("tar")
344        .args(["cJf", output, input])
345        .output()
346        .map_err(|e| aurora_core::AuroraError::ModuleError(
347            format!("tar failed: {}", e)
348        ))?;
349    if !result.status.success() {
350        let stderr = String::from_utf8_lossy(&result.stderr);
351        return Err(aurora_core::AuroraError::ModuleError(
352            format!("tar failed: {}", stderr)
353        ));
354    }
355    Ok(Pipeline::table(
356        vec!["action".into(), "input".into(), "output".into(), "status".into()],
357        vec![vec![
358            Value::String("compress".into()),
359            Value::String(input.into()),
360            Value::String(output.into()),
361            Value::String("ok".into()),
362        ]],
363    ))
364}
365
366fn compress_tar(input: &str, output: &str) -> AuroraResult<Pipeline> {
367    check_tool("tar")?;
368    let result = Command::new("tar")
369        .args(["cf", output, input])
370        .output()
371        .map_err(|e| aurora_core::AuroraError::ModuleError(
372            format!("tar failed: {}", e)
373        ))?;
374    if !result.status.success() {
375        let stderr = String::from_utf8_lossy(&result.stderr);
376        return Err(aurora_core::AuroraError::ModuleError(
377            format!("tar failed: {}", stderr)
378        ));
379    }
380    Ok(Pipeline::table(
381        vec!["action".into(), "input".into(), "output".into(), "status".into()],
382        vec![vec![
383            Value::String("compress".into()),
384            Value::String(input.into()),
385            Value::String(output.into()),
386            Value::String("ok".into()),
387        ]],
388    ))
389}
390
391fn compress_zip(input: &str, output: &str) -> AuroraResult<Pipeline> {
392    check_tool("zip")?;
393    let result = Command::new("zip")
394        .args(["-r", output, input])
395        .output()
396        .map_err(|e| aurora_core::AuroraError::ModuleError(
397            format!("zip failed: {}", e)
398        ))?;
399    if !result.status.success() {
400        let stderr = String::from_utf8_lossy(&result.stderr);
401        return Err(aurora_core::AuroraError::ModuleError(
402            format!("zip failed: {}", stderr)
403        ));
404    }
405    Ok(Pipeline::table(
406        vec!["action".into(), "input".into(), "output".into(), "status".into()],
407        vec![vec![
408            Value::String("compress".into()),
409            Value::String(input.into()),
410            Value::String(output.into()),
411            Value::String("ok".into()),
412        ]],
413    ))
414}
415
416fn compress_7z(input: &str, output: &str) -> AuroraResult<Pipeline> {
417    check_tool("7z")?;
418    let result = Command::new("7z")
419        .args(["a", output, input])
420        .output()
421        .map_err(|e| aurora_core::AuroraError::ModuleError(
422            format!("7z failed: {}", e)
423        ))?;
424    if !result.status.success() {
425        let stderr = String::from_utf8_lossy(&result.stderr);
426        return Err(aurora_core::AuroraError::ModuleError(
427            format!("7z failed: {}", stderr)
428        ));
429    }
430    Ok(Pipeline::table(
431        vec!["action".into(), "input".into(), "output".into(), "status".into()],
432        vec![vec![
433            Value::String("compress".into()),
434            Value::String(input.into()),
435            Value::String(output.into()),
436            Value::String("ok".into()),
437        ]],
438    ))
439}
440
441fn compress_gz(input: &str, output: &str) -> AuroraResult<Pipeline> {
442    check_tool("gzip")?;
443    let result = Command::new("gzip")
444        .args(["-c", input])
445        .stdout(std::process::Stdio::piped())
446        .spawn()
447        .and_then(|child| {
448            use std::fs::File;
449            use std::io::Write;
450            let mut out_file = File::create(output)?;
451            let output = child.wait_with_output()?;
452            out_file.write_all(&output.stdout)?;
453            Ok(output)
454        });
455    match result {
456        Ok(r) if r.status.success() => {
457            Ok(Pipeline::table(
458                vec!["action".into(), "input".into(), "output".into(), "status".into()],
459                vec![vec![
460                    Value::String("compress".into()),
461                    Value::String(input.into()),
462                    Value::String(output.into()),
463                    Value::String("ok".into()),
464                ]],
465            ))
466        }
467        Ok(r) => {
468            let stderr = String::from_utf8_lossy(&r.stderr);
469            Err(aurora_core::AuroraError::ModuleError(
470                format!("gzip failed: {}", stderr)
471            ))
472        }
473        Err(e) => Err(aurora_core::AuroraError::ModuleError(
474            format!("gzip failed: {}", e)
475        )),
476    }
477}