use std::path::PathBuf;
use std::process::ExitCode;
use clap::{Parser, Subcommand};
use text_mirror::bundle;
use text_mirror::pipeline::{self, Rules, RunOptions};
use text_mirror::walk::WalkOptions;
#[derive(Parser)]
#[command(
name = "text-mirror",
version,
about = "Converts files into plain text and builds a replica text-mirror tree \
beside a manifest recording how every file was handled"
)]
struct Cli {
#[command(subcommand)]
command: Command,
}
#[derive(Subcommand)]
enum Command {
Scan {
root: PathBuf,
},
Run {
root: PathBuf,
#[arg(long)]
mirror: PathBuf,
#[arg(long)]
manifest: PathBuf,
#[arg(long)]
division: String,
},
Status {
#[arg(long)]
manifest: PathBuf,
},
Explain {
path: String,
#[arg(long)]
manifest: PathBuf,
},
Bundle {
division: String,
#[arg(long)]
mirror: PathBuf,
#[arg(long)]
manifest: PathBuf,
#[arg(long)]
output: PathBuf,
},
Verify {
bundle: PathBuf,
},
Merge {
#[arg(required = true, num_args = 2..)]
inputs: Vec<PathBuf>,
#[arg(long)]
output: PathBuf,
},
}
fn execute(command: Command) -> Result<String, text_mirror::Error> {
let rules = Rules::builtin()?;
let json = match command {
Command::Scan { root } => {
let report = pipeline::scan(&root, &rules, &WalkOptions::default())?;
serde_json::to_string_pretty(&report)
}
Command::Run {
root,
mirror,
manifest,
division,
} => {
let report = pipeline::run(
&rules,
&RunOptions {
root: &root,
mirror_root: &mirror,
manifest_dir: &manifest,
division: &division,
walk: WalkOptions::default(),
},
)?;
serde_json::to_string_pretty(&report)
}
Command::Status { manifest } => {
let report = pipeline::status(&manifest)?;
serde_json::to_string_pretty(&report)
}
Command::Explain { path, manifest } => {
let entries = pipeline::explain(&manifest, &path)?;
serde_json::to_string_pretty(&entries)
}
Command::Bundle {
division,
mirror,
manifest,
output,
} => {
let report = bundle::bundle(&bundle::BundleOptions {
mirror_root: &mirror,
manifest_dir: &manifest,
division: &division,
output: &output,
})?;
serde_json::to_string_pretty(&report)
}
Command::Verify { bundle: root } => {
let report = bundle::verify(&root)?;
serde_json::to_string_pretty(&report)
}
Command::Merge { inputs, output } => {
let report = bundle::merge(&inputs, &output)?;
serde_json::to_string_pretty(&report)
}
};
json.map_err(|e| text_mirror::Error::Encode {
path: PathBuf::from("stdout"),
message: e.to_string(),
})
}
fn main() -> ExitCode {
let cli = Cli::parse();
match execute(cli.command) {
Ok(json) => {
println!("{json}");
ExitCode::SUCCESS
}
Err(text_mirror::Error::Refused { verb, problems }) => {
println!(
"{}",
serde_json::json!({ "error": "refused", "verb": verb, "problems": problems })
);
ExitCode::from(3)
}
Err(e) => {
println!("{}", serde_json::json!({ "error": e.to_string() }));
ExitCode::from(1)
}
}
}