use alloc::vec::Vec;
use core::fmt::Display;
use elements_rs::Element;
use crate::{
CountLike, InChITree, MolecularFormula, MolecularFormulaMetadata, ParsableFormula,
prelude::SequenceNode,
};
#[derive(Debug, PartialEq, Clone, Eq, PartialOrd, Ord, Hash)]
pub struct InChIFormula<Count: CountLike = u16> {
mixtures: Vec<(Count, SequenceNode<InChITree<Count>>)>,
}
impl<Count: CountLike> InChIFormula<Count> {
pub fn subformulas(&self) -> impl Iterator<Item = Self> {
self.mixtures().cloned().map(Into::into)
}
}
impl<Count: CountLike> From<SequenceNode<InChITree<Count>>> for InChIFormula<Count> {
fn from(tree: SequenceNode<InChITree<Count>>) -> Self {
Self { mixtures: alloc::vec![(Count::one(), tree)] }
}
}
impl<Count: CountLike> From<Element> for InChIFormula<Count> {
fn from(element: Element) -> Self {
let mut sequence = SequenceNode::empty();
sequence.push(element.into());
Self { mixtures: alloc::vec![(Count::one(), sequence)] }
}
}
impl<Count: CountLike> MolecularFormulaMetadata for InChIFormula<Count> {
type Count = Count;
}
impl<Count: CountLike> MolecularFormula for InChIFormula<Count> {
type Tree = SequenceNode<InChITree<Count>>;
fn counted_mixtures(&self) -> impl Iterator<Item = (Self::Count, &Self::Tree)> {
self.mixtures.iter().map(|(count, tree)| (*count, tree))
}
fn counted_mixtures_mut(&mut self) -> impl Iterator<Item = (Self::Count, &mut Self::Tree)> {
self.mixtures.iter_mut().map(|(count, tree)| (*count, tree))
}
fn into_counted_mixtures(self) -> impl Iterator<Item = (Self::Count, Self::Tree)> {
self.mixtures.into_iter()
}
}
impl<Count: CountLike> ParsableFormula for InChIFormula<Count> {
type StartOutput = ();
type Tree = SequenceNode<InChITree<Count>>;
fn on_start<J>(
_chars: &mut core::iter::Peekable<J>,
) -> Result<Self::StartOutput, crate::errors::ParserError>
where
J: Iterator<Item = char>,
{
Ok(())
}
fn from_parsed(
_start_output: Self::StartOutput,
mixtures: Vec<(Count, Self::Tree)>,
) -> Result<Self, crate::errors::ParserError> {
assert!(!mixtures.is_empty(), "At least one mixture is required");
let inchi = InChIFormula { mixtures };
if !inchi.is_hill_sorted() {
return Err(crate::errors::ParserError::NotHillOrdered);
}
Ok(inchi)
}
}
impl<Count: CountLike> Display for InChIFormula<Count> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
for (i, (count, tree)) in self.mixtures.iter().enumerate() {
if i > 0 {
write!(f, ".")?;
}
if !count.is_one() {
write!(f, "{count}")?;
}
write!(f, "{tree}")?;
}
Ok(())
}
}