use aurora_core::{AuroraResult, Pipeline, Value};
use std::process::Command;
use std::path::Path;
fn check_tool(name: &str) -> AuroraResult<()> {
let status = Command::new("which")
.arg(name)
.output()
.ok()
.map(|o| o.status.success())
.unwrap_or(false);
if !status {
return Err(aurora_core::AuroraError::CommandNotFound(
format!("{} is not installed", name)
));
}
Ok(())
}
fn extension(path: &str) -> &str {
let p = Path::new(path);
p.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
}
pub fn archive_list(file: &str) -> AuroraResult<Pipeline> {
let ext = extension(file);
match ext {
"tar" | "tar.gz" | "tgz" | "tar.bz2" | "tar.xz" => list_tar(file),
"zip" => list_zip(file),
"7z" => list_7z(file),
"gz" => list_gz(file),
_ => Err(aurora_core::AuroraError::InvalidInput(
format!("unsupported archive format: {}", ext)
)),
}
}
fn list_tar(file: &str) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let output = Command::new("tar")
.args(["tf", file])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let rows: Vec<Vec<Value>> = stdout.lines()
.filter(|l| !l.is_empty())
.map(|l| vec![Value::String(l.into())])
.collect();
Ok(Pipeline::table(vec!["entry".into()], rows))
}
fn list_zip(file: &str) -> AuroraResult<Pipeline> {
check_tool("unzip")?;
let output = Command::new("unzip")
.args(["-l", file])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("unzip failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rows: Vec<Vec<Value>> = Vec::new();
for line in stdout.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with("Archive:") || line.starts_with("Length") || line.contains("----") || line.ends_with("file") {
continue;
}
rows.push(vec![Value::String(line.into())]);
}
Ok(Pipeline::table(vec!["entry".into()], rows))
}
fn list_7z(file: &str) -> AuroraResult<Pipeline> {
check_tool("7z")?;
let output = Command::new("7z")
.args(["l", file])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("7z failed: {}", e)
))?;
let stdout = String::from_utf8_lossy(&output.stdout);
let mut rows: Vec<Vec<Value>> = Vec::new();
let mut capture = false;
for line in stdout.lines() {
if line.contains("----") && !capture {
capture = true;
continue;
}
if line.contains("----") && capture {
break;
}
if capture {
let line = line.trim();
if !line.is_empty() {
rows.push(vec![Value::String(line.into())]);
}
}
}
Ok(Pipeline::table(vec!["entry".into()], rows))
}
fn list_gz(file: &str) -> AuroraResult<Pipeline> {
check_tool("gunzip")?;
let output = Command::new("gunzip")
.args(["-l", file])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("gunzip 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; }
rows.push(vec![Value::String(line.into())]);
}
Ok(Pipeline::table(vec!["entry".into()], rows))
}
pub fn archive_extract(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
let ext = extension(file);
match ext {
"tar" | "tar.gz" | "tgz" | "tar.bz2" | "tar.xz" => extract_tar(file, output),
"zip" => extract_zip(file, output),
"7z" => extract_7z(file, output),
"gz" => extract_gz(file, output),
_ => Err(aurora_core::AuroraError::InvalidInput(
format!("unsupported archive format: {}", ext)
)),
}
}
fn extract_tar(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let mut cmd = Command::new("tar");
cmd.arg("xf");
cmd.arg(file);
if let Some(dir) = output {
cmd.args(["-C", dir]);
}
let result = cmd.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar extract failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("tar extract failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "file".into(), "status".into()],
vec![vec![
Value::String("extract".into()),
Value::String(file.into()),
Value::String("ok".into()),
]],
))
}
fn extract_zip(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
check_tool("unzip")?;
let mut cmd = Command::new("unzip");
cmd.arg(file);
if let Some(dir) = output {
cmd.args(["-d", dir]);
}
let result = cmd.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("unzip failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("unzip failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "file".into(), "status".into()],
vec![vec![
Value::String("extract".into()),
Value::String(file.into()),
Value::String("ok".into()),
]],
))
}
fn extract_7z(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
check_tool("7z")?;
let mut cmd = Command::new("7z");
cmd.args(["x", file]);
if let Some(dir) = output {
cmd.args([&format!("-o{}", dir)]);
}
let result = cmd.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("7z extract failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("7z extract failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "file".into(), "status".into()],
vec![vec![
Value::String("extract".into()),
Value::String(file.into()),
Value::String("ok".into()),
]],
))
}
fn extract_gz(file: &str, output: Option<&str>) -> AuroraResult<Pipeline> {
check_tool("gunzip")?;
let result = if let Some(out) = output {
Command::new("gunzip")
.args(["-c", file])
.stdout(std::process::Stdio::piped())
.spawn()
.and_then(|child| {
use std::fs::File;
let _out_file = File::create(out)?;
let output = child.wait_with_output()?;
if output.status.success() {
use std::io::Write;
let mut f = File::create(out)?;
f.write_all(&output.stdout)?;
}
Ok(output)
})
} else {
Command::new("gunzip")
.arg(file)
.output()
};
match result {
Ok(r) if r.status.success() => {
Ok(Pipeline::table(
vec!["action".into(), "file".into(), "status".into()],
vec![vec![
Value::String("extract".into()),
Value::String(file.into()),
Value::String("ok".into()),
]],
))
}
Ok(r) => {
let stderr = String::from_utf8_lossy(&r.stderr);
Err(aurora_core::AuroraError::ModuleError(
format!("gunzip failed: {}", stderr)
))
}
Err(e) => Err(aurora_core::AuroraError::ModuleError(
format!("gunzip failed: {}", e)
)),
}
}
pub fn archive_compress(input: &str, output: &str) -> AuroraResult<Pipeline> {
let ext = extension(output);
match ext {
"tar.gz" | "tgz" => compress_tar_gz(input, output),
"tar.bz2" => compress_tar_bz2(input, output),
"tar.xz" => compress_tar_xz(input, output),
"tar" => compress_tar(input, output),
"zip" => compress_zip(input, output),
"7z" => compress_7z(input, output),
"gz" => compress_gz(input, output),
_ => Err(aurora_core::AuroraError::InvalidInput(
format!("unsupported output format: {}", ext)
)),
}
}
fn compress_tar_gz(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let result = Command::new("tar")
.args(["czf", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_tar_bz2(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let result = Command::new("tar")
.args(["cjf", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_tar_xz(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let result = Command::new("tar")
.args(["cJf", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_tar(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("tar")?;
let result = Command::new("tar")
.args(["cf", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("tar failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_zip(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("zip")?;
let result = Command::new("zip")
.args(["-r", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("zip failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("zip failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_7z(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("7z")?;
let result = Command::new("7z")
.args(["a", output, input])
.output()
.map_err(|e| aurora_core::AuroraError::ModuleError(
format!("7z failed: {}", e)
))?;
if !result.status.success() {
let stderr = String::from_utf8_lossy(&result.stderr);
return Err(aurora_core::AuroraError::ModuleError(
format!("7z failed: {}", stderr)
));
}
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
fn compress_gz(input: &str, output: &str) -> AuroraResult<Pipeline> {
check_tool("gzip")?;
let result = Command::new("gzip")
.args(["-c", input])
.stdout(std::process::Stdio::piped())
.spawn()
.and_then(|child| {
use std::fs::File;
use std::io::Write;
let mut out_file = File::create(output)?;
let output = child.wait_with_output()?;
out_file.write_all(&output.stdout)?;
Ok(output)
});
match result {
Ok(r) if r.status.success() => {
Ok(Pipeline::table(
vec!["action".into(), "input".into(), "output".into(), "status".into()],
vec![vec![
Value::String("compress".into()),
Value::String(input.into()),
Value::String(output.into()),
Value::String("ok".into()),
]],
))
}
Ok(r) => {
let stderr = String::from_utf8_lossy(&r.stderr);
Err(aurora_core::AuroraError::ModuleError(
format!("gzip failed: {}", stderr)
))
}
Err(e) => Err(aurora_core::AuroraError::ModuleError(
format!("gzip failed: {}", e)
)),
}
}