Skip to main content

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}