diff --git a/path.cfg b/path.cfg index dd711ee..2e2a049 100644 --- a/path.cfg +++ b/path.cfg @@ -1,7 +1,8 @@ mmseqs=mmseqs foldseek=foldseek +foldmason=foldmason mafft=mafft mafft-linsi=mafft-linsi -foldmason=foldmason iqtree=iqtree -#fasttree=fasttree \ No newline at end of file +#fasttree= +#raxml= \ No newline at end of file diff --git a/src/envs/error_handler.rs b/src/envs/error_handler.rs index 2d383ec..e677e25 100644 --- a/src/envs/error_handler.rs +++ b/src/envs/error_handler.rs @@ -4,11 +4,15 @@ use crate::util::message; pub const WRN_GENERAL: i32 = 0x00; pub const ERR_GENERAL: i32 = 0x01; -pub const ERR_FILE_NOT_FOUND: i32 = 0x02; -pub const ERR_MODULE_NOT_IMPLEMENTED: i32 = 0x03; -pub const ERR_ARGPARSE: i32 = 0x04; -pub const ERR_BINARY_NOT_FOUND: i32 = 0x05; -pub const ERR_OUTPUT_EXISTS: i32 = 0x06; +pub const ERR_FILE_NOT_FOUND: i32 = 0x10; +pub const ERR_FILE_INVALID: i32 = 0x11; +pub const ERR_BINARY_NOT_FOUND: i32 = 0x20; +pub const ERR_BINARY_NOT_EXECUTABLE: i32 = 0x21; +pub const ERR_BINARY_INVALID: i32 = 0x22; +pub const ERR_MODULE_NOT_IMPLEMENTED: i32 = 0x30; +pub const ERR_ARGPARSE: i32 = 0x40; +pub const ERR_OUTPUT_EXISTS: i32 = 0x50; + fn build_message(code: i32, passed_object: Option) -> String { let object = passed_object.unwrap_or_else(|| "".to_string()); @@ -16,10 +20,14 @@ fn build_message(code: i32, passed_object: Option) -> String { WRN_GENERAL => format!("Warning: {}", object), ERR_GENERAL => format!("Error: {}", object), ERR_FILE_NOT_FOUND => format!("File not found: {}", object), + ERR_FILE_INVALID => format!("Invalid file given: {}", object), + ERR_BINARY_NOT_FOUND => format!("Binary not found: {}", object), + ERR_BINARY_NOT_EXECUTABLE => format!("Binary not executable: {}", object), + ERR_BINARY_INVALID => format!("Invalid binary given: {}", object), ERR_MODULE_NOT_IMPLEMENTED => format!("Module not implemented: {}", object), ERR_ARGPARSE => format!("Argument parsing error: {}", object), - ERR_BINARY_NOT_FOUND => format!("Binary not found: {}", object), ERR_OUTPUT_EXISTS => format!("Output file already exists: {}; use -o to overwrite", object), + _ => "Unknown error".to_string(), } } diff --git a/src/envs/variables.rs b/src/envs/variables.rs index af83a35..849a335 100644 --- a/src/envs/variables.rs +++ b/src/envs/variables.rs @@ -76,18 +76,20 @@ pub fn locate_path_cfg() -> String { } // binary paths -const VALID_BINARY: [&str; 7] = [ - "mmseqs", "foldseek", "mafft", "mafft-linsi", "foldmason", "iqtree", "fasttree", +pub const VALID_BINARY: [&str; 8] = [ + "mmseqs", "foldseek", "mafft", "mafft-linsi", "foldmason", "iqtree", "fasttree", "raxml" ]; pub struct Binary { name: String, pub path: String, + pub set: bool, } impl Binary { fn new(name: &str, path: &str) -> Self { Binary { name: name.to_string(), path: path.to_string(), + set: false, } } fn test(&self, args: Vec<&str>) -> bool { @@ -120,8 +122,10 @@ impl BinaryPaths { let mut split = line.split('='); let name = split.next().unwrap_or(""); let path = split.next().unwrap_or(""); + if path.len() == 0 { continue; } if let Some(&i) = self.map.get(name) { self.bin[i].path = path.to_string(); + self.bin[i].set = true; } } Ok(()) diff --git a/src/main.rs b/src/main.rs index c367a8c..b857fbf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,9 @@ fn run(args: &parser::Args, bin: &var::BinaryPaths, test: bool) -> Result<(), Bo Some(parser::Commands::EasySearch { .. }) => { workflow::easy_search::run(args, bin).unwrap_or_else(|e| err::error(err::ERR_GENERAL, Some(e.to_string()))); } + Some(parser::Commands::Config { .. }) => { + modules::config::run(args, bin).unwrap_or_else(|e| err::error(err::ERR_GENERAL, Some(e.to_string()))); + }, /* Some(_) => { err::error(err::ERR_MODULE_NOT_IMPLEMENTED, std::env::args().nth(1)); } */ diff --git a/src/modules/config.rs b/src/modules/config.rs new file mode 100644 index 0000000..4467cf9 --- /dev/null +++ b/src/modules/config.rs @@ -0,0 +1,93 @@ +use std::os::unix::fs::MetadataExt; +use std::io::Write; +use crate::envs::error_handler as err; +use crate::envs::variables as var; +use crate::envs::variables::BinaryPaths; +use crate::util::command as cmd; +use crate::util::message as msg; +use crate::util::arg_parser::Args; +use color_print::cstr; + +fn task_check(bin: &BinaryPaths) -> Result<(), Box> { + msg::println_message(&format!("{}", cstr!(r#"System:"#)), 3); + msg::println_message(&format!("Unicore version: {}", var::VERSION), 3); + msg::println_message(&format!("OS: {}", std::env::consts::OS), 3); + msg::println_message(&format!("Threads: {}", var::threads()), 3); + println!(); + msg::println_message(&format!("{}", cstr!(r#"Dependencies:"#)), 3); + msg::println_message(&format!("MMseqs2: {} .. {}", + if let Some(&ref bin) = &bin.get("mmseqs") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("mmseqs") { if bin.set { if binary_run_test(&bin.path, "mmseqs") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("Foldseek: {} .. {}", + if let Some(&ref bin) = &bin.get("foldseek") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("foldseek") { if bin.set { if binary_run_test(&bin.path, "foldseek") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("FoldMason: {} .. {}", + if let Some(&ref bin) = &bin.get("foldmason") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("foldmason") { if bin.set { if binary_run_test(&bin.path, "foldmason") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("MAFFT: {} .. {}", + if let Some(&ref bin) = &bin.get("mafft") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("mafft") { if bin.set { if binary_run_test(&bin.path, "mafft") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("IQ-TREE: {} .. {}", + if let Some(&ref bin) = &bin.get("iqtree") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("iqtree") { if bin.set { if binary_run_test(&bin.path, "iqtree") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("FastTree: {} .. {}", + if let Some(&ref bin) = &bin.get("fasttree") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("fasttree") { if bin.set { if binary_run_test(&bin.path, "fasttree") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + msg::println_message(&format!("RAxML: {} .. {}", + if let Some(&ref bin) = &bin.get("raxml") { if bin.set { bin.path.clone() } else { "Unset".to_string() } } else { "Undefined".to_string() }, + if let Some(&ref bin) = &bin.get("raxml") { if bin.set { if binary_run_test(&bin.path, "raxml") { cstr!(r#"ok"#) } else { cstr!(r#"no"#) } } else { cstr!(r#"n/a"#) } } else { cstr!(r#"n/a"#) }, + ), 3); + Ok(()) +} + +fn binary_run_test(path: &str, sw: &str) -> bool { + if !var::VALID_BINARY.contains(&sw) { return false; } + let mut test_command = std::process::Command::new(path); + let test_command = match sw { + "mmseqs" | "foldseek" | "foldmason" => test_command.arg("version"), + "mafft" | "mafft-linsi" | "iqtree" => test_command.arg("--version"), + "fasttree" => &mut test_command, + "raxml" => test_command.arg("-v"), + _ => return false, + }; + cmd::run_code(test_command) == 0 +} + +fn set_binary(bin: &BinaryPaths, path: &str, sw: &str) -> Result<(), Box> { + if !std::fs::File::open(path).is_ok() { err::error(err::ERR_BINARY_NOT_FOUND, Some(format!("{}", path))); } + if std::fs::metadata(path)?.is_dir() { err::error(err::ERR_FILE_INVALID, Some(format!("{}", path))); } + if std::fs::metadata(path)?.mode() & 0o111 == 0 { err::error(err::ERR_BINARY_NOT_EXECUTABLE, Some(format!("{}", path))); } + if !binary_run_test(path, sw) { err::error(err::ERR_BINARY_INVALID, Some(format!("{}", path))); } + + let path = std::fs::canonicalize(path)?.to_str().unwrap().to_string(); + msg::println_message(&format!("Setting dependency {} to {}...", sw, path), 3); + let mut cfg = std::fs::File::create(var::locate_path_cfg())?; + for &prog in var::VALID_BINARY.iter() { + if prog == sw { cfg.write_all(format!("{}={}\n", prog, path).as_bytes())?; } + else if bin.get(prog).is_none() || !bin.get(prog).unwrap().set { cfg.write_all(format!("#{}=\n", prog).as_bytes())?; } + else { cfg.write_all(format!("{}={}\n", prog, bin.get(prog).unwrap().path).as_bytes())?; } + } + msg::println_message(&"Done. Please run \"unicore config -c\" to check".to_string(), 3); + + Ok(()) +} + +pub fn run(args: &Args, bin: &BinaryPaths) -> Result<(), Box> { + if args.config_check.is_some() && args.config_check.unwrap() { task_check(bin)?; } + else if args.config_set_mmseqs.is_some() { set_binary(bin, args.config_set_mmseqs.clone().unwrap().as_str(), "mmseqs")?; } + else if args.config_set_foldseek.is_some() { set_binary(bin, args.config_set_foldseek.clone().unwrap().as_str(), "foldseek")?; } + else if args.config_set_foldmason.is_some() { set_binary(bin, args.config_set_foldmason.clone().unwrap().as_str(), "foldmason")?; } + else if args.config_set_mafft.is_some() { set_binary(bin, args.config_set_mafft.clone().unwrap().as_str(), "mafft")?; } + else if args.config_set_mafft_linsi.is_some() { set_binary(bin, args.config_set_mafft_linsi.clone().unwrap().as_str(), "mafft-linsi")?; } + else if args.config_set_iqtree.is_some() { set_binary(bin, args.config_set_iqtree.clone().unwrap().as_str(), "iqtree")?; } + else if args.config_set_fasttree.is_some() { set_binary(bin, args.config_set_fasttree.clone().unwrap().as_str(), "fasttree")?; } + else if args.config_set_raxml.is_some() { set_binary(bin, args.config_set_raxml.clone().unwrap().as_str(), "raxml")?; } + else { err::error(err::ERR_ARGPARSE, Some("No task specified".to_string())) }; + Ok(()) +} \ No newline at end of file diff --git a/src/modules/mod.rs b/src/modules/mod.rs index 4d3c15b..23c1bd7 100644 --- a/src/modules/mod.rs +++ b/src/modules/mod.rs @@ -4,4 +4,5 @@ pub mod cluster; pub mod search; pub mod profile; pub mod tree; -pub mod genetree; \ No newline at end of file +pub mod genetree; +pub mod config; \ No newline at end of file diff --git a/src/util/arg_parser.rs b/src/util/arg_parser.rs index d763fcd..d61cfcb 100644 --- a/src/util/arg_parser.rs +++ b/src/util/arg_parser.rs @@ -5,6 +5,7 @@ use crate::util::arg_parser::Commands::*; #[derive(Parser)] #[clap(disable_version_flag = true, arg_required_else_help = true)] +#[command(subcommand_value_name = "MODULE")] pub struct ClapArgs { #[command(subcommand)] pub command: Option, @@ -43,7 +44,130 @@ const GENETREE_HELP: &str = cstr!(r#"Example:unicore gene-tree --realign --threshold 30 --name /path/to/hashed/gene/names example/tree "#); #[derive(Subcommand)] +#[command(subcommand_help_heading = "Modules")] pub enum Commands { + /// Easy core gene phylogeny workflow, from fasta files to phylogenetic tree + #[clap(arg_required_else_help = true, allow_hyphen_values = true)] + EasyCore { + /// Input directory with fasta files or a single fasta file + input: PathBuf, + /// Output directory where all results will be saved + output: PathBuf, + /// ProstT5 model + model: PathBuf, + /// tmp directory + tmp: PathBuf, + /// Keep intermediate files + #[arg(short, long, default_value="false")] + keep: bool, + /// Force overwrite output database + #[arg(short='w', long, default_value="false")] + overwrite: bool, + /// Set maximum sequence length threshold + #[arg(long)] + max_len: Option, + /// Use GPU for foldseek createdb + #[arg(short, long, default_value="false")] + gpu: bool, + /// Use AFDB lookup for foldseek createdb. Useful for large databases + #[arg(long, default_value="false")] + afdb_lookup: bool, + /// Local path to the directory with AFDB lookup tables. hidden option + #[arg(long, hide = false)] + afdb_local: Option, + /// Arguments for foldseek options in string e.g. -c "-c 0.8" + #[arg(short, long, default_value="-c 0.8")] + cluster_options: String, + /// Coverage threshold for core structures. [0 - 100] + #[arg(short='C', long, default_value="80", value_parser = threshold_in_range)] + core_threshold: usize, + /// Generate tsv with copy number statistics + #[arg(short, long, default_value="true")] + print_copiness: bool, + /// Multiple sequence aligner [foldmason, mafft-linsi, mafft] + #[arg(short='A', long, default_value="foldmason")] + aligner: String, + /// Phylogenetic tree builder [iqtree, fasttree (under development), raxml (under development)] + #[arg(short='T', long, default_value="iqtree")] + tree_builder: String, + /// Options for sequence aligner + #[arg(short, long)] + aligner_options: Option, + /// Options for tree builder; please adjust if using different tree method + #[arg(short, long, default_value="-m JTT+F+I+G -B 1000")] + tree_options: String, + /// Gap threshold for multiple sequence alignment [0 - 100] + #[arg(short='G', long, default_value="50", value_parser = threshold_in_range)] + gap_threshold: usize, + /// Number of threads to use; 0 to use all + #[arg(long, default_value="0")] + threads: usize, + /// Verbosity (0: quiet, 1: +errors, 2: +warnings, 3: +info, 4: +debug) + #[arg(short='v', long, default_value="3")] + verbosity: u8, + }, + /// Easy search workflow, from fasta files to phylogenetic tree + #[clap(arg_required_else_help = true, allow_hyphen_values = true, hide = true)] + EasySearch { + /// Input directory with fasta files or a single fasta file + input: PathBuf, + /// Target database to search against + target: PathBuf, + /// Output directory where all results will be saved + output: PathBuf, + /// ProstT5 model + model: PathBuf, + /// tmp directory + tmp: PathBuf, + /// Keep intermediate files + #[arg(short, long, default_value="false")] + keep: bool, + /// Force overwrite output database + #[arg(short='w', long, default_value="false")] + overwrite: bool, + /// Set maximum sequence length threshold + #[arg(long)] + max_len: Option, + /// Use GPU for foldseek createdb + #[arg(short, long, default_value="false")] + gpu: bool, + /// Use AFDB lookup for foldseek createdb. Useful for large databases + #[arg(long, default_value="false")] + afdb_lookup: bool, + /// Local path to the directory with AFDB lookup tables. hidden option + #[arg(long, hide = true)] + afdb_local: Option, + /// Arguments for foldseek options in string e.g. -s "-c 0.8" + #[arg(short, long, default_value="-c 0.8")] + search_options: String, + /// Coverage threshold for core structures. [0 - 100] + #[arg(short='C', long, default_value="80", value_parser = threshold_in_range)] + core_threshold: usize, + /// Generate tsv with copy number statistics + #[arg(short, long, default_value="true")] + print_copiness: bool, + /// Multiple sequence aligner [foldmason, mafft-linsi, mafft] + #[arg(short='A', long, default_value="foldmason")] + aligner: String, + /// Phylogenetic tree builder [iqtree, fasttree (under development), raxml (under development)] + #[arg(short='T', long, default_value="iqtree")] + tree_builder: String, + /// Options for sequence aligner + #[arg(short, long)] + aligner_options: Option, + /// Options for tree builder; please adjust if using different tree method + #[arg(short, long, default_value="-m JTT+F+I+G -B 1000")] + tree_options: String, + /// Gap threshold for multiple sequence alignment [0 - 100] + #[arg(short='G', long, default_value="50", value_parser = threshold_in_range)] + gap_threshold: usize, + /// Number of threads to use; 0 to use all + #[arg(long, default_value="0")] + threads: usize, + /// Verbosity (0: quiet, 1: +errors, 2: +warnings, 3: +info, 4: +debug) + #[arg(short='v', long, default_value="3")] + verbosity: u8, + }, /// Create Foldseek database from amino acid sequences #[clap(arg_required_else_help = true, allow_hyphen_values = true, verbatim_doc_comment)] Createdb { @@ -186,7 +310,7 @@ pub enum Commands { #[arg(short='v', long, default_value="3")] verbosity: u8, }, - // Infer phylogenetic tree of each core structures + /// Infer phylogenetic tree of each core structures #[clap(arg_required_else_help = true, allow_hyphen_values = true)] #[command(after_help=GENETREE_HELP)] GeneTree { @@ -220,124 +344,36 @@ pub enum Commands { #[arg(short='v', long, default_value="3")] verbosity: u8, }, - /// Easy core gene phylogeny workflow, from fasta files to phylogenetic tree + /// Runtime environment configuration #[clap(arg_required_else_help = true, allow_hyphen_values = true)] - EasyCore { - /// Input directory with fasta files or a single fasta file - input: PathBuf, - /// Output directory where all results will be saved - output: PathBuf, - /// ProstT5 model - model: PathBuf, - /// tmp directory - tmp: PathBuf, - /// Keep intermediate files - #[arg(short, long, default_value="false")] - keep: bool, - /// Force overwrite output database - #[arg(short='w', long, default_value="false")] - overwrite: bool, - /// Set maximum sequence length threshold + Config { + /// Check current environment configuration + #[arg(short='c', long)] + check: bool, + /// Set mmseqs binary path #[arg(long)] - max_len: Option, - /// Use GPU for foldseek createdb - #[arg(short, long, default_value="false")] - gpu: bool, - /// Use AFDB lookup for foldseek createdb. Useful for large databases - #[arg(long, default_value="false")] - afdb_lookup: bool, - /// Local path to the directory with AFDB lookup tables. hidden option - #[arg(long, hide = false)] - afdb_local: Option, - /// Arguments for foldseek options in string e.g. -c "-c 0.8" - #[arg(short, long, default_value="-c 0.8")] - cluster_options: String, - /// Coverage threshold for core structures. [0 - 100] - #[arg(short='C', long, default_value="80", value_parser = threshold_in_range)] - core_threshold: usize, - /// Generate tsv with copy number statistics - #[arg(short, long, default_value="true")] - print_copiness: bool, - /// Multiple sequence aligner [foldmason, mafft-linsi, mafft] - #[arg(short='A', long, default_value="foldmason")] - aligner: String, - /// Phylogenetic tree builder [iqtree, fasttree (under development), raxml (under development)] - #[arg(short='T', long, default_value="iqtree")] - tree_builder: String, - /// Options for sequence aligner - #[arg(short, long)] - aligner_options: Option, - /// Options for tree builder; please adjust if using different tree method - #[arg(short, long, default_value="-m JTT+F+I+G -B 1000")] - tree_options: String, - /// Gap threshold for multiple sequence alignment [0 - 100] - #[arg(short='G', long, default_value="50", value_parser = threshold_in_range)] - gap_threshold: usize, - /// Number of threads to use; 0 to use all - #[arg(long, default_value="0")] - threads: usize, - /// Verbosity (0: quiet, 1: +errors, 2: +warnings, 3: +info, 4: +debug) - #[arg(short='v', long, default_value="3")] - verbosity: u8, - }, - /// Easy search workflow, from fasta files to phylogenetic tree - #[clap(arg_required_else_help = true, allow_hyphen_values = true, hide = true)] - EasySearch { - /// Input directory with fasta files or a single fasta file - input: PathBuf, - /// Target database to search against - target: PathBuf, - /// Output directory where all results will be saved - output: PathBuf, - /// ProstT5 model - model: PathBuf, - /// tmp directory - tmp: PathBuf, - /// Keep intermediate files - #[arg(short, long, default_value="false")] - keep: bool, - /// Force overwrite output database - #[arg(short='w', long, default_value="false")] - overwrite: bool, - /// Set maximum sequence length threshold + set_mmseqs: Option, + /// Set foldseek binary path #[arg(long)] - max_len: Option, - /// Use GPU for foldseek createdb - #[arg(short, long, default_value="false")] - gpu: bool, - /// Use AFDB lookup for foldseek createdb. Useful for large databases - #[arg(long, default_value="false")] - afdb_lookup: bool, - /// Local path to the directory with AFDB lookup tables. hidden option - #[arg(long, hide = true)] - afdb_local: Option, - /// Arguments for foldseek options in string e.g. -s "-c 0.8" - #[arg(short, long, default_value="-c 0.8")] - search_options: String, - /// Coverage threshold for core structures. [0 - 100] - #[arg(short='C', long, default_value="80", value_parser = threshold_in_range)] - core_threshold: usize, - /// Generate tsv with copy number statistics - #[arg(short, long, default_value="true")] - print_copiness: bool, - /// Multiple sequence aligner [foldmason, mafft-linsi, mafft] - #[arg(short='A', long, default_value="foldmason")] - aligner: String, - /// Phylogenetic tree builder [iqtree, fasttree (under development), raxml (under development)] - #[arg(short='T', long, default_value="iqtree")] - tree_builder: String, - /// Options for sequence aligner - #[arg(short, long)] - aligner_options: Option, - /// Options for tree builder; please adjust if using different tree method - #[arg(short, long, default_value="-m JTT+F+I+G -B 1000")] - tree_options: String, - /// Gap threshold for multiple sequence alignment [0 - 100] - #[arg(short='G', long, default_value="50", value_parser = threshold_in_range)] - gap_threshold: usize, - /// Number of threads to use; 0 to use all - #[arg(long, default_value="0")] - threads: usize, + set_foldseek: Option, + /// Set foldmason binary path + #[arg(long)] + set_foldmason: Option, + /// Set mafft binary path + #[arg(long)] + set_mafft: Option, + /// Set mafft-linsi binary path + #[arg(long)] + set_mafft_linsi: Option, + /// Set iqtree binary path + #[arg(long)] + set_iqtree: Option, + /// Set fasttree binary path + #[arg(long)] + set_fasttree: Option, + /// Set raxml binary path + #[arg(long)] + set_raxml: Option, /// Verbosity (0: quiet, 1: +errors, 2: +warnings, 3: +info, 4: +debug) #[arg(short='v', long, default_value="3")] verbosity: u8, @@ -397,6 +433,16 @@ pub struct Args { pub genetree_realign: Option, pub genetree_aligner: Option, pub genetree_aligner_options: Option>, + + pub config_check: Option, + pub config_set_mmseqs: Option, + pub config_set_foldseek: Option, + pub config_set_foldmason: Option, + pub config_set_mafft: Option, + pub config_set_mafft_linsi: Option, + pub config_set_iqtree: Option, + pub config_set_fasttree: Option, + pub config_set_raxml: Option, } fn own(path: &PathBuf) -> String { path.clone().to_string_lossy().into_owned() } impl Args { @@ -411,6 +457,7 @@ impl Args { Some(GeneTree { verbosity, .. }) => *verbosity, Some(EasyCore { verbosity, .. }) => *verbosity, Some(EasySearch { verbosity, .. }) => *verbosity, + Some(Config { verbosity, .. }) => *verbosity, _ => 3, }; let threads = match &args.command { @@ -609,6 +656,34 @@ impl Args { Some(GeneTree { threshold, .. }) => Some(*threshold), _ => None, }; + let config_check = match &args.command { + Some(Config { check, .. }) => Some(*check), _ => None, + }; + let config_set_mmseqs = match &args.command { + Some(Config { set_mmseqs, .. }) => match set_mmseqs { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_foldseek = match &args.command { + Some(Config { set_foldseek, .. }) => match set_foldseek { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_foldmason = match &args.command { + Some(Config { set_foldmason, .. }) => match set_foldmason { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_mafft = match &args.command { + Some(Config { set_mafft, .. }) => match set_mafft { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_mafft_linsi = match &args.command { + Some(Config { set_mafft_linsi, .. }) => match set_mafft_linsi { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_iqtree = match &args.command { + Some(Config { set_iqtree, .. }) => match set_iqtree { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_fasttree = match &args.command { + Some(Config { set_fasttree, .. }) => match set_fasttree { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + let config_set_raxml = match &args.command { + Some(Config { set_raxml, .. }) => match set_raxml { Some(p) => Some(own(p)), _ => None }, _ => None, + }; + Args { command: args.command, version: args.version, threads, verbosity, createdb_input, createdb_output, createdb_model, createdb_keep, createdb_overwrite, createdb_max_len, createdb_gpu, createdb_afdb_lookup, createdb_afdb_local, @@ -617,6 +692,7 @@ impl Args { cluster_input, cluster_output, cluster_tmp, cluster_keep_cluster_db, cluster_cluster_options, tree_db, tree_input, tree_output, tree_aligner, tree_tree_builder, tree_aligner_options, tree_tree_options, tree_threshold, genetree_input, genetree_names, genetree_tree_builder, genetree_tree_options, genetree_realign, genetree_aligner, genetree_aligner_options, genetree_threshold, + config_check, config_set_mmseqs, config_set_foldseek, config_set_foldmason, config_set_mafft, config_set_mafft_linsi, config_set_iqtree, config_set_fasttree, config_set_raxml, } } } \ No newline at end of file diff --git a/src/util/command.rs b/src/util/command.rs index c54a00a..169436a 100644 --- a/src/util/command.rs +++ b/src/util/command.rs @@ -23,6 +23,26 @@ pub fn run(cmd: &mut std::process::Command) { } } +pub fn run_code(cmd: &mut std::process::Command) -> i32 { + let cmd = cmd.stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null()); + let cmdstr = format!("{:?}", cmd).replace("\"", ""); + msg::println_message(&format!("Running command: {}", cmdstr), 4); + if let Ok(mut child) = cmd.spawn() { + let wait = child.wait(); + if let Ok(status) = wait { + if let Some(code) = status.code() { + code + } else { + 1 + } + } else { + 1 + } + } else { + 1 + } +} + pub fn _run_at(cmd: &mut std::process::Command, path: &std::path::Path) { let cmdstr = format!("{:?}", cmd); if let Ok(mut child) = cmd.current_dir(path).spawn() {