chematic_core/stereo_group.rs
1//! Enhanced stereochemistry group data model.
2//!
3//! Corresponds to the ChemDraw V3000 `M V30 STEREOGROUP` / `MDLV30/STE*`
4//! collection entries, which distinguish three types of stereo specification:
5//!
6//! - [`StereoGroupKind::Absolute`] — the absolute configuration is known.
7//! - [`StereoGroupKind::Or`] — the compound is one of two configurations (or
8//! a mixture), but it is not known which; all atoms in the same Or-group
9//! share the same ambiguity.
10//! - [`StereoGroupKind::And`] — the compound is a mixture of configurations;
11//! each atom may independently be R or S.
12
13use crate::molecule::AtomIdx;
14
15// ---------------------------------------------------------------------------
16// Types
17// ---------------------------------------------------------------------------
18
19/// Kind of an enhanced stereo group.
20#[derive(Debug, Clone, PartialEq, Eq, Hash)]
21pub enum StereoGroupKind {
22 /// Absolute configuration — the drawn stereocenters are in the single
23 /// stated configuration (no uncertainty).
24 Absolute,
25 /// "Or" group — the drawn configuration OR its inverse; group number
26 /// identifies which Or-group this is (1, 2, ...).
27 Or(u32),
28 /// "And" group — independent mixture; group number identifies the
29 /// And-group (1, 2, ...).
30 And(u32),
31}
32
33/// A named collection of stereocenters with a shared ambiguity classification.
34///
35/// Stored in [`Molecule::stereo_groups`]. The `atom_indices` list the
36/// 0-based internal `AtomIdx` values of the stereocenters that belong to
37/// this group.
38#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct StereoGroup {
40 pub kind: StereoGroupKind,
41 pub atom_indices: Vec<AtomIdx>,
42}
43
44impl StereoGroup {
45 /// Construct a new stereo group.
46 ///
47 /// Duplicate atom indices are silently removed (RDKit PR #9258: duplicate
48 /// indices in StereoGroups caused heap-use-after-free during removeHs).
49 pub fn new(kind: StereoGroupKind, atom_indices: Vec<AtomIdx>) -> Self {
50 let mut seen = std::collections::HashSet::new();
51 let atom_indices: Vec<AtomIdx> = atom_indices
52 .into_iter()
53 .filter(|idx| seen.insert(*idx))
54 .collect();
55 StereoGroup { kind, atom_indices }
56 }
57}