use wasm_bindgen::prelude::*;
use crate::DEFAULT_BUILDING_BLOCKS;
use crate::chem_env::{self, ChemEnv, default_rules};
use crate::search::{SearchConfig, find_routes as rs_find_routes};
#[wasm_bindgen]
pub fn find_routes(target: &str, depth: u32, max_routes: usize, beam_width: usize) -> String {
let env = ChemEnv::in_memory(DEFAULT_BUILDING_BLOCKS);
let rules = default_rules();
let config = SearchConfig {
max_depth: depth,
max_routes,
beam_width,
..Default::default()
};
match rs_find_routes(target, &env, &rules, &config) {
Ok((routes, stats)) => {
let output = if routes.is_empty() {
serde_json::json!({
"target": target,
"routes_found": 0,
"routes": [],
"diagnostics": {"nodes_expanded": stats.nodes_expanded}
})
} else {
serde_json::json!({
"target": target,
"routes_found": routes.len(),
"routes": routes,
})
};
serde_json::to_string(&output)
.unwrap_or_else(|e| format!(r#"{{"error":"serialization: {e}"}}"#))
}
Err(e) => format!(r#"{{"error":"{e}"}}"#),
}
}
#[wasm_bindgen]
pub fn find_routes_v2(
target: &str,
depth: u32,
max_routes: usize,
beam_width: usize,
avoid_elements: &str,
require_elements: &str,
) -> String {
let env = ChemEnv::in_memory(DEFAULT_BUILDING_BLOCKS);
let rules = default_rules();
let config = SearchConfig {
max_depth: depth,
max_routes,
beam_width,
forbidden_elements: chem_env::elem_symbols_to_mask(avoid_elements),
required_element_present: chem_env::elem_symbols_to_mask(require_elements),
..Default::default()
};
match rs_find_routes(target, &env, &rules, &config) {
Ok((routes, stats)) => {
let output = if routes.is_empty() {
serde_json::json!({
"target": target,
"routes_found": 0,
"routes": [],
"diagnostics": {"nodes_expanded": stats.nodes_expanded}
})
} else {
serde_json::json!({
"target": target,
"routes_found": routes.len(),
"routes": routes,
})
};
serde_json::to_string(&output)
.unwrap_or_else(|e| format!(r#"{{"error":"serialization: {e}"}}"#))
}
Err(e) => format!(r#"{{"error":"{e}"}}"#),
}
}
#[wasm_bindgen]
pub fn find_routes_v3(
target: &str,
depth: u32,
max_routes: usize,
beam_width: usize,
avoid_elements: &str,
require_elements: &str,
spectator_bond_policy: &str,
) -> String {
let policy = match spectator_bond_policy {
"off" => crate::spectator_bond::SpectatorBondPolicy::Off,
"diagnostics_only" => crate::spectator_bond::SpectatorBondPolicy::DiagnosticsOnly,
"gated" => crate::spectator_bond::SpectatorBondPolicy::Gated,
other => {
return serde_json::to_string(&serde_json::json!({
"error": format!(
"invalid spectator_bond_policy {other:?} (expected \"off\", \
\"diagnostics_only\", or \"gated\")"
)
}))
.unwrap_or_else(|_| r#"{"error":"invalid spectator_bond_policy"}"#.to_string());
}
};
let env = ChemEnv::in_memory(DEFAULT_BUILDING_BLOCKS);
let rules = default_rules();
let config = SearchConfig {
max_depth: depth,
max_routes,
beam_width,
forbidden_elements: chem_env::elem_symbols_to_mask(avoid_elements),
required_element_present: chem_env::elem_symbols_to_mask(require_elements),
spectator_bond_policy: policy,
..Default::default()
};
match rs_find_routes(target, &env, &rules, &config) {
Ok((routes, stats)) => {
let output = if routes.is_empty() {
serde_json::json!({
"target": target,
"routes_found": 0,
"routes": [],
"diagnostics": {"nodes_expanded": stats.nodes_expanded}
})
} else {
serde_json::json!({
"target": target,
"routes_found": routes.len(),
"routes": routes,
})
};
serde_json::to_string(&output)
.unwrap_or_else(|e| format!(r#"{{"error":"serialization: {e}"}}"#))
}
Err(e) => format!(r#"{{"error":"{e}"}}"#),
}
}
#[wasm_bindgen]
pub fn version() -> String {
env!("CARGO_PKG_VERSION").to_string()
}
#[wasm_bindgen]
pub fn capabilities() -> String {
let env = ChemEnv::in_memory(DEFAULT_BUILDING_BLOCKS);
serde_json::json!({
"building_blocks": env.bb_count(),
"reaction_rules": default_rules().len(),
})
.to_string()
}
#[wasm_bindgen]
pub fn audit_route(content: &str, format: &str, stock_text: &str) -> String {
audit_route_v2(content, format, stock_text, "standard")
}
#[wasm_bindgen]
pub fn audit_route_v2(content: &str, format: &str, stock_text: &str, policy: &str) -> String {
let policy = match policy.parse::<crate::bridge::AuditPolicy>() {
Ok(p) => p,
Err(e) => {
return serde_json::to_string(&serde_json::json!({ "error": e }))
.unwrap_or_else(|_| r#"{"error":"invalid policy"}"#.to_string());
}
};
let stock =
(!stock_text.trim().is_empty()).then(|| crate::bridge::parse_stock_text(stock_text));
let rules = default_rules();
match crate::bridge::build_audit_route_report_with_policy(
content,
format,
stock.as_ref(),
&rules,
policy,
) {
Ok(report) => serde_json::to_string(&report)
.unwrap_or_else(|e| format!(r#"{{"error":"serialization: {e}"}}"#)),
Err(e) => serde_json::to_string(&serde_json::json!({ "error": format!("{e:#}") }))
.unwrap_or_else(|_| r#"{"error":"audit failed"}"#.to_string()),
}
}