use crate::{MolHandle, escape_json_string};
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn estate_indices_json(mol: &MolHandle) -> String {
let vals = chematic_chem::estate_indices(&mol.inner);
let parts: Vec<String> = vals.iter().map(|v| format!("{v:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn pharmacophore_features_json(mol: &MolHandle) -> String {
let features = chematic_perception::detect_features(&mol.inner);
let parts: Vec<String> = features
.iter()
.map(|f| {
let ftype = match f.ftype {
chematic_perception::FeatureType::Donor => "Donor",
chematic_perception::FeatureType::Acceptor => "Acceptor",
chematic_perception::FeatureType::Aromatic => "Aromatic",
chematic_perception::FeatureType::Hydrophobic => "Hydrophobic",
chematic_perception::FeatureType::Positive => "Positive",
chematic_perception::FeatureType::Negative => "Negative",
};
format!(
"{{\"type\":\"{}\",\"atom\":{},\"neighbors\":{}}}",
ftype,
f.atom.0,
f.neighbors.len()
)
})
.collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn pharmacophore_fp_2d_summary(mol: &MolHandle) -> String {
let counts = chematic_fp::pharmacophore_feature_counts(&mol.inner);
format!(
"{{\"Donor\":{},\"Acceptor\":{},\"Aromatic\":{},\"Hydrophobic\":{},\"Positive\":{},\"Negative\":{}}}",
counts[0], counts[1], counts[2], counts[3], counts[4], counts[5]
)
}
#[wasm_bindgen]
pub fn mqn_json(mol: &MolHandle) -> String {
let desc = chematic_chem::mqn(&mol.inner);
let values: Vec<String> = desc.iter().map(|&v| v.to_string()).collect();
format!("[{}]", values.join(","))
}
#[wasm_bindgen]
pub fn autocorr_2d_json(mol: &MolHandle) -> String {
let ac = chematic_chem::autocorr_2d(&mol.inner);
format!(
"[{:.4},{:.4},{:.4},{:.4},{:.4},{:.4},{:.4}]",
ac[0], ac[1], ac[2], ac[3], ac[4], ac[5], ac[6]
)
}
#[wasm_bindgen]
pub fn xlogp3_json(mol: &MolHandle) -> String {
let v = chematic_chem::xlogp3(&mol.inner);
format!(r#"{{"xlogp3":{v:.4}}}"#)
}
#[wasm_bindgen]
pub fn xlogp3_per_atom_json(mol: &MolHandle) -> String {
let vals = chematic_chem::xlogp3_per_atom(&mol.inner);
let parts: Vec<String> = vals.iter().map(|v| format!("{v:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn sa_score(mol: &MolHandle) -> f64 {
chematic_chem::sa_score(&mol.inner)
}
#[wasm_bindgen]
pub fn slogp_vsa_json(mol: &MolHandle) -> String {
let v = chematic_chem::slogp_vsa(&mol.inner);
let parts: Vec<String> = v.iter().map(|x| format!("{x:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn smr_vsa_json(mol: &MolHandle) -> String {
let v = chematic_chem::smr_vsa(&mol.inner);
let parts: Vec<String> = v.iter().map(|x| format!("{x:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn peoe_vsa_json(mol: &MolHandle) -> String {
let v = chematic_chem::peoe_vsa(&mol.inner);
let parts: Vec<String> = v.iter().map(|x| format!("{x:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn get_descriptors_json(mol: &MolHandle) -> String {
let m = &mol.inner;
format!(
concat!(
"{{",
"\"mw\":{mw:.4},\"exactMass\":{em:.6},\"tpsa\":{tpsa:.4},",
"\"logP\":{logp:.4},\"molarRefractivity\":{mr:.4},",
"\"hbd\":{hbd},\"hba\":{hba},\"rotatableBonds\":{rb},",
"\"heavyAtomCount\":{hac},\"ringCount\":{rc},",
"\"aromaticRingCount\":{arc},\"numHeteroatoms\":{nh},",
"\"numStereocenters\":{nsc},\"numSpiroAtoms\":{nsp},",
"\"numBridgeheadAtoms\":{nbh},\"fsp3\":{fsp3:.4},",
"\"qed\":{qed:.4},\"saScore\":{sa:.4},",
"\"formalChargeSum\":{fc},\"labuteASA\":{asa:.4},",
"\"bertzCT\":{bertz:.4},\"wienerIndex\":{wi:.4},",
"\"lipinskiPasses\":{lip},\"veberPasses\":{veb},",
"\"eganPasses\":{egan},\"ghosePasses\":{ghose},",
"\"reosPasses\":{reos},\"painsPasses\":{pains},",
"\"bbbScore\":{bbb:.4},\"bbbPasses\":{bbp},",
"\"caco2\":{caco:.4},\"hergRisk\":{herg:.4},",
"\"cyp3a4Risk\":{cyp:.4},",
"\"pkaAcid\":{acid},\"pkaBase\":{base}",
"}}"
),
mw = chematic_chem::molecular_weight(m),
em = chematic_chem::exact_mass(m),
tpsa = chematic_chem::tpsa(m),
logp = chematic_chem::logp_crippen(m),
mr = chematic_chem::molar_refractivity(m),
hbd = chematic_chem::hbd_count(m),
hba = chematic_chem::hba_count(m),
rb = chematic_chem::rotatable_bond_count(m),
hac = chematic_chem::heavy_atom_count(m),
rc = chematic_chem::ring_count(m),
arc = chematic_chem::aromatic_ring_count(m),
nh = chematic_chem::num_heteroatoms(m),
nsc = chematic_chem::num_stereocenters(m),
nsp = chematic_chem::num_spiro_atoms(m),
nbh = chematic_chem::num_bridgehead_atoms(m),
fsp3 = chematic_chem::fsp3(m),
qed = chematic_chem::qed(m),
sa = chematic_chem::sa_score(m),
fc = chematic_chem::formal_charge_sum(m),
asa = chematic_chem::labute_asa(m),
bertz = chematic_chem::bertz_ct(m),
wi = chematic_chem::wiener_index(m),
lip = chematic_chem::lipinski_passes(m),
veb = chematic_chem::veber_passes(m),
egan = chematic_chem::egan_passes(m),
ghose = chematic_chem::ghose_passes(m),
reos = chematic_chem::reos_passes(m),
pains = chematic_chem::pains_passes(m),
bbb = chematic_chem::bbb_score(m),
bbp = chematic_chem::bbb_passes(m),
caco = chematic_chem::caco2_permeability(m),
herg = chematic_chem::herg_risk_score(m),
cyp = chematic_chem::cyp3a4_inhibition_risk(m),
acid = chematic_chem::pka_acid(m).map_or("null".to_string(), |v| format!("{v:.2}")),
base = chematic_chem::pka_base(m).map_or("null".to_string(), |v| format!("{v:.2}")),
)
}
#[wasm_bindgen]
pub fn predict_pka_json(smiles: &str) -> String {
let mol = match chematic_smiles::parse(smiles) {
Ok(m) => m,
Err(e) => return format!(r#"{{"error":"{}"}}"#, escape_json_string(&e.to_string())),
};
let sites = chematic_chem::predict_pka(&mol);
if sites.is_empty() {
return "[]".to_string();
}
let items: Vec<String> = sites
.iter()
.map(|s| {
let type_str = match s.site_type {
chematic_chem::PkaSiteType::Acid => "acid",
chematic_chem::PkaSiteType::Base => "base",
};
format!(
r#"{{"atom_idx":{},"pka":{:.2},"type":"{}","group":"{}"}}"#,
s.atom_idx.0, s.pka, type_str, s.group_name
)
})
.collect();
format!("[{}]", items.join(","))
}
#[wasm_bindgen]
pub fn admet_profile_json(smiles: &str) -> String {
let mol = match chematic_smiles::parse(smiles) {
Ok(m) => m,
Err(e) => return format!(r#"{{"error":"{}"}}"#, escape_json_string(&e.to_string())),
};
let p = chematic_chem::admet_profile(&mol);
let acid_str = p.pka_acid.map_or("null".to_string(), |v| format!("{v:.2}"));
let base_str = p.pka_base.map_or("null".to_string(), |v| format!("{v:.2}"));
let egg = chematic_chem::boiled_egg(&mol);
format!(
concat!(
"{{",
"\"bbb_score\":{bbb:.4},\"bbb_passes\":{bbp},",
"\"caco2\":{caco:.4},\"herg_risk\":{herg:.4},",
"\"cyp3a4_risk\":{cyp:.4},",
"\"pka_acid\":{acid},\"pka_base\":{base},",
"\"esol\":{esol:.4},\"logd74\":{logd:.4},",
"\"mw\":{mw:.4},\"logp\":{logp:.4},\"tpsa\":{tpsa:.4},",
"\"hbd\":{hbd},\"hba\":{hba},\"rotatable_bonds\":{rb},",
"\"gi_absorbed\":{gi},\"bbb_penetrant\":{bbbp}",
"}}"
),
bbb = p.bbb_score,
bbp = p.bbb_passes,
caco = p.caco2,
herg = p.herg_risk,
cyp = p.cyp3a4_risk,
acid = acid_str,
base = base_str,
esol = p.esol,
logd = p.logd74,
mw = p.mw,
logp = p.logp,
tpsa = p.tpsa,
hbd = p.hbd,
hba = p.hba,
rb = p.rotatable_bonds,
gi = egg.gi_absorbed,
bbbp = egg.bbb_penetrant,
)
}
#[wasm_bindgen]
pub fn boiled_egg_json(smiles: &str) -> String {
let mol = match chematic_smiles::parse(smiles) {
Ok(m) => m,
Err(e) => return format!(r#"{{"error":"{}"}}"#, escape_json_string(&e.to_string())),
};
let e = chematic_chem::boiled_egg(&mol);
format!(
r#"{{"gi_absorbed":{},"bbb_penetrant":{},"logp":{:.4},"tpsa":{:.4}}}"#,
e.gi_absorbed, e.bbb_penetrant, e.logp, e.tpsa
)
}
#[wasm_bindgen]
pub fn pains_matches_json(mol: &MolHandle) -> String {
let names = chematic_chem::pains_matches(&mol.inner);
let parts: Vec<String> = names
.iter()
.map(|n| format!("\"{}\"", n.replace('"', "\\\"")))
.collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn cip_assignments_json(mol: &MolHandle) -> String {
let assignment = chematic_chem::assign_cip(&mol.inner);
let parts: Vec<String> = assignment
.assignments
.iter()
.map(|(idx, code)| {
let code_str = match code {
chematic_core::CipCode::R => "R",
chematic_core::CipCode::S => "S",
chematic_core::CipCode::E => "E",
chematic_core::CipCode::Z => "Z",
};
format!("{{\"atomIdx\":{},\"cipCode\":\"{}\"}}", idx.0, code_str)
})
.collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn hdf_json(mol: &MolHandle, dim: usize, radius: usize, seed: u64) -> String {
let config = chematic_fp::HdfConfig { dim, radius, seed };
let fp = chematic_fp::hdf(&mol.inner, &config);
serde_json::to_string(&fp.0).unwrap_or_default()
}
#[wasm_bindgen]
pub fn logp_per_atom_json(mol: &MolHandle) -> String {
let vals = chematic_chem::logp_crippen_per_atom(&mol.inner);
let parts: Vec<String> = vals.iter().map(|v| format!("{v:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn mr_per_atom_json(mol: &MolHandle) -> String {
let vals = chematic_chem::mr_per_atom(&mol.inner);
let parts: Vec<String> = vals.iter().map(|v| format!("{v:.4}")).collect();
format!("[{}]", parts.join(","))
}
#[wasm_bindgen]
pub fn labute_asa_per_atom_json(mol: &MolHandle) -> String {
let vals = chematic_chem::labute_asa_per_atom(&mol.inner);
let parts: Vec<String> = vals
.iter()
.map(|v| {
if v.is_finite() {
format!("{v:.4}")
} else {
"null".to_string()
}
})
.collect();
format!("[{}]", parts.join(","))
}