#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum PolymerUnitKind {
#[default]
None,
StructureBasedRepeat,
SourceBasedMonomer,
Copolymer,
Modification,
CrossLink,
Mer,
Other(i32),
}
impl PolymerUnitKind {
fn from_code(code: i32) -> Self {
match code {
0 => PolymerUnitKind::None,
1 => PolymerUnitKind::StructureBasedRepeat,
2 => PolymerUnitKind::SourceBasedMonomer,
3 => PolymerUnitKind::Copolymer,
4 => PolymerUnitKind::Modification,
5 => PolymerUnitKind::CrossLink,
6 => PolymerUnitKind::Mer,
other => PolymerUnitKind::Other(other),
}
}
pub(crate) fn code(self) -> i32 {
match self {
PolymerUnitKind::None => 0,
PolymerUnitKind::StructureBasedRepeat => 1,
PolymerUnitKind::SourceBasedMonomer => 2,
PolymerUnitKind::Copolymer => 3,
PolymerUnitKind::Modification => 4,
PolymerUnitKind::CrossLink => 5,
PolymerUnitKind::Mer => 6,
PolymerUnitKind::Other(c) => c,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum PolymerSubtype {
#[default]
None,
Alternating,
Random,
Block,
Other(i32),
}
impl PolymerSubtype {
fn from_code(code: i32) -> Self {
match code {
0 => PolymerSubtype::None,
1 => PolymerSubtype::Alternating,
2 => PolymerSubtype::Random,
3 => PolymerSubtype::Block,
other => PolymerSubtype::Other(other),
}
}
pub(crate) fn code(self) -> i32 {
match self {
PolymerSubtype::None => 0,
PolymerSubtype::Alternating => 1,
PolymerSubtype::Random => 2,
PolymerSubtype::Block => 3,
PolymerSubtype::Other(c) => c,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
#[non_exhaustive]
pub enum PolymerConnection {
#[default]
Unspecified,
HeadToTail,
HeadToHead,
EitherUnknown,
Other(i32),
}
impl PolymerConnection {
fn from_code(code: i32) -> Self {
match code {
0 => PolymerConnection::Unspecified,
1 => PolymerConnection::HeadToTail,
2 => PolymerConnection::HeadToHead,
3 => PolymerConnection::EitherUnknown,
other => PolymerConnection::Other(other),
}
}
pub(crate) fn code(self) -> i32 {
match self {
PolymerConnection::Unspecified => 0,
PolymerConnection::HeadToTail => 1,
PolymerConnection::HeadToHead => 2,
PolymerConnection::EitherUnknown => 3,
PolymerConnection::Other(c) => c,
}
}
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct PolymerUnit {
pub id: i32,
pub label: i32,
pub kind: PolymerUnitKind,
pub subtype: PolymerSubtype,
pub connection: PolymerConnection,
pub subscript: String,
pub atoms: Vec<usize>,
pub crossing_bonds: Vec<[usize; 2]>,
}
impl PolymerUnit {
#[must_use]
pub fn sru(
atoms: impl IntoIterator<Item = usize>,
crossing_bonds: impl IntoIterator<Item = [usize; 2]>,
) -> Self {
PolymerUnit {
id: 1,
label: 0,
kind: PolymerUnitKind::StructureBasedRepeat,
subtype: PolymerSubtype::None,
connection: PolymerConnection::HeadToTail,
subscript: "n".to_string(),
atoms: atoms.into_iter().collect(),
crossing_bonds: crossing_bonds.into_iter().collect(),
}
}
}
pub(crate) unsafe fn read_unit(raw: *const inchi_sys::inchi_Input_PolymerUnit) -> Option<PolymerUnit> {
if raw.is_null() {
return None;
}
let u = unsafe { &*raw };
let na = usize::try_from(u.na).unwrap_or(0);
let nb = usize::try_from(u.nb).unwrap_or(0);
let atoms = if u.alist.is_null() || na == 0 {
Vec::new()
} else {
let slice = unsafe { std::slice::from_raw_parts(u.alist, na) };
slice.iter().map(|&a| zero_based(a)).collect()
};
let crossing_bonds = if u.blist.is_null() || nb == 0 {
Vec::new()
} else {
let slice = unsafe { std::slice::from_raw_parts(u.blist, nb * 2) };
slice
.chunks_exact(2)
.filter_map(|p| match p {
[a, b] => Some([zero_based(*a), zero_based(*b)]),
_ => None,
})
.collect()
};
Some(PolymerUnit {
id: u.id,
label: u.label,
kind: PolymerUnitKind::from_code(u.type_),
subtype: PolymerSubtype::from_code(u.subtype),
connection: PolymerConnection::from_code(u.conn),
subscript: read_smt(&u.smt),
atoms,
crossing_bonds,
})
}
fn zero_based(n: std::os::raw::c_int) -> usize {
(n.max(1) as usize) - 1
}
fn read_smt(raw: &[std::os::raw::c_char]) -> String {
let mut s = String::new();
for &c in raw {
if c == 0 {
break;
}
s.push(c as u8 as char);
}
s
}