pub const MCS_DETAILS_JSON: &str =
r#"{"AtomCompare":"Isotopes","BondCompare":"Any","Timeout":2}"#;
pub const MIN_MCS_ATOMS: u32 = 3;
pub fn minimallib_align_details(reference_smarts: &str) -> String {
format!(
r#"{{"useCoordGen":false,"referenceSmarts":"{}","allowRGroups":true,"acceptFailure":false}}"#,
escape_json_string(reference_smarts)
)
}
pub fn align_succeeded(result: &str) -> bool {
let t = result.trim();
!t.is_empty() && t != "{}"
}
fn escape_json_string(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
match c {
'\\' => out.push_str("\\\\"),
'"' => out.push_str("\\\""),
'\n' => out.push_str("\\n"),
'\r' => out.push_str("\\r"),
'\t' => out.push_str("\\t"),
c if c.is_control() => out.push_str(&format!("\\u{:04x}", c as u32)),
c => out.push(c),
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn details_embeds_smarts() {
let d = minimallib_align_details("[62*]~[63*]");
assert!(d.contains("referenceSmarts"));
assert!(d.contains("[62*]~[63*]"));
assert!(d.contains("allowRGroups\":true"));
}
#[test]
fn success_predicate() {
assert!(!align_succeeded(""));
assert!(!align_succeeded("{}"));
assert!(!align_succeeded(" {} "));
assert!(align_succeeded(r#"{"atoms":[0,1]}"#));
}
#[test]
fn mcs_details_isotopes_any_for_minimallib() {
assert!(MCS_DETAILS_JSON.contains("Isotopes"));
assert!(MCS_DETAILS_JSON.contains("Any"));
assert!(!MCS_DETAILS_JSON.contains("RingMatchesRingOnly"));
}
}