use renkin::DEFAULT_BUILDING_BLOCKS;
use renkin::chem_env;
use renkin::search::{self, SearchConfig};
use anyhow::{Result, bail};
use serde::Serialize;
#[derive(Serialize)]
struct Output {
target: String,
routes_found: usize,
routes: Vec<search::Route>,
}
fn main() -> Result<()> {
let args: Vec<String> = std::env::args().collect();
let mut target: Option<String> = None;
let mut max_depth: u32 = 5;
let mut bb_path: Option<String> = None;
let mut templates_path: Option<String> = None;
let mut max_routes: usize = 5;
let mut beam_width: usize = 0;
let mut i = 1;
while i < args.len() {
match args[i].as_str() {
"--target" | "-t" => {
i += 1;
if i < args.len() {
target = Some(args[i].clone());
}
}
"--depth" | "-d" => {
i += 1;
if i < args.len() {
max_depth = args[i].parse().unwrap_or(5);
}
}
"--building-blocks" | "-b" => {
i += 1;
if i < args.len() {
bb_path = Some(args[i].clone());
}
}
"--templates" => {
i += 1;
if i < args.len() {
templates_path = Some(args[i].clone());
}
}
"--max-routes" | "-n" => {
i += 1;
if i < args.len() {
max_routes = args[i].parse().unwrap_or(5);
}
}
"--beam-width" | "-w" => {
i += 1;
if i < args.len() {
beam_width = args[i].parse().unwrap_or(0);
}
}
_ => {}
}
i += 1;
}
let Some(target_smiles) = target else {
bail!(
"Usage: renkin --target <SMILES> [--depth <N>] [--max-routes <N>] \
[--beam-width <N>] [--building-blocks <path>] [--templates <path>]\n\
\n\
Options:\n \
--target / -t Target molecule SMILES\n \
--depth / -d Max retrosynthesis depth (default: 5)\n \
--max-routes / -n Max routes to return (default: 5)\n \
--beam-width / -w Beam search width, 0 = unlimited A* (default: 0)\n \
--building-blocks Path to .smi file of commercial starting materials\n \
--templates Path to extracted SMIRKS templates file (tab-separated)"
);
};
let env = match bb_path {
Some(ref path) => chem_env::ChemEnv::load(path)?,
None => chem_env::ChemEnv::load("data/building_blocks.smi")
.unwrap_or_else(|_| chem_env::ChemEnv::in_memory(DEFAULT_BUILDING_BLOCKS)),
};
let mut rules = chem_env::default_rules();
if let Some(ref path) = templates_path {
let extra = chem_env::load_rules_from_file(path);
eprintln!("Loaded {} templates from {path}", extra.len());
rules.extend(extra);
}
let config = SearchConfig {
max_depth,
max_routes,
beam_width,
};
let routes = search::find_routes(&target_smiles, &env, &rules, &config)?;
let output = Output {
target: target_smiles,
routes_found: routes.len(),
routes,
};
println!("{}", serde_json::to_string_pretty(&output)?);
Ok(())
}