use rust_ise::{run, write_gff, write_sum, write_tsv, IseConfig};
use std::fs;
use std::path::{Path, PathBuf};
use std::time::Instant;
mod build_db;
fn main() {
let t0 = Instant::now();
let args: Vec<String> = std::env::args().collect();
if args.get(1).map(String::as_str) == Some("build-db") {
run_build_db(&args[2..]);
return;
}
let mut seqfile = String::new();
let mut output = String::new();
let mut threads = 16usize;
let mut keep = false;
let mut strict = false;
let mut db_dir: Option<PathBuf> = None; let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"-i" | "--seqfile" => {
i += 1;
seqfile = args[i].clone();
}
"-o" | "--output" => {
i += 1;
output = args[i].clone();
}
"-t" | "--threads" => {
i += 1;
threads = match args[i].parse() {
Ok(n) => n,
Err(_) => {
eprintln!("error: --threads expects a number, got '{}'", args[i]);
std::process::exit(2);
}
};
}
"--keep" => keep = true,
"--strict" => strict = true, "--db" => {
i += 1;
db_dir = Some(PathBuf::from(args[i].clone()));
}
_ => {
eprintln!("unknown arg {}", args[i]);
std::process::exit(2);
}
}
i += 1;
}
if seqfile.is_empty() || output.is_empty() {
eprintln!("usage: rust-ise -i <contigs.fasta> -o <out_dir> [-t N] [--db <dir>] [--keep] [--strict]");
eprintln!("error: -i/--seqfile and -o/--output are required.");
std::process::exit(2);
}
let db_dir = db_dir
.or_else(|| std::env::var_os("RUSTISE_DB").map(PathBuf::from))
.unwrap_or_else(|| {
eprintln!("error: IS database not set. Pass --db <dir> or set RUSTISE_DB (see README).");
std::process::exit(2);
});
rayon::ThreadPoolBuilder::new()
.num_threads(threads)
.build_global()
.ok();
fs::create_dir_all(&output).unwrap();
let config = IseConfig { threads, strict, db_dir };
let calls = match run(Path::new(&seqfile), Path::new(&output), &config) {
Ok(c) => c,
Err(e) => {
eprintln!("error: {e}");
std::process::exit(1);
}
};
let stem = Path::new(&output);
write_tsv(&calls, &stem.join("rust-ise.tsv"));
write_gff(&calls, &stem.join("rust-ise.gff"));
write_sum(&calls, &stem.join("rust-ise.sum"));
if !keep {
let _ = fs::remove_dir_all(stem.join("rust-ise_tmp"));
}
eprintln!(
"[{:.1}s] DONE -> {}/rust-ise.tsv ({} IS)",
t0.elapsed().as_secs_f64(),
output,
calls.len()
);
}
fn run_build_db(rest: &[String]) {
use build_db::BuildArgs;
let mut a: std::collections::HashMap<&str, String> = std::collections::HashMap::new();
let mut out: Option<PathBuf> = None;
let mut work: Option<PathBuf> = None;
let mut strict_version = false;
let mut i = 0;
while i < rest.len() {
match rest[i].as_str() {
"--isosdb-fna" => { i += 1; a.insert("iso", rest[i].clone()); }
"--fam-annot" => { i += 1; a.insert("fam", rest[i].clone()); }
"--isfinder-faa" => { i += 1; a.insert("faa", rest[i].clone()); }
"--isfinder-csv" => { i += 1; a.insert("csv", rest[i].clone()); }
"--out" | "--db" => { i += 1; out = Some(PathBuf::from(&rest[i])); }
"--work" => { i += 1; work = Some(PathBuf::from(&rest[i])); }
"--strict-mmseqs-version" => strict_version = true,
"-h" | "--help" => {
eprintln!("usage: rust-ise build-db --isosdb-fna F --fam-annot F --isfinder-faa F --isfinder-csv F --out DIR [--work DIR] [--strict-mmseqs-version]");
return;
}
x => { eprintln!("unknown build-db arg {x}"); std::process::exit(2); }
}
i += 1;
}
let get = |k: &str, flag: &str| -> PathBuf {
match a.get(k) {
Some(v) => PathBuf::from(v),
None => { eprintln!("build-db needs {flag}"); std::process::exit(2); }
}
};
let out_dir = out.unwrap_or_else(|| { eprintln!("build-db needs --out DIR"); std::process::exit(2); });
let work = work.unwrap_or_else(|| out_dir.join("work"));
let cfg = BuildArgs {
isosdb_fna: get("iso", "--isosdb-fna"),
fam_annot: get("fam", "--fam-annot"),
isfinder_faa: get("faa", "--isfinder-faa"),
isfinder_csv: get("csv", "--isfinder-csv"),
out_dir,
work,
strict_version,
};
if let Err(e) = build_db::run(&cfg) {
eprintln!("build-db error: {e}");
std::process::exit(1);
}
}