chemp/
lib.rs

1//! chemp is a tool for parsing chemical formulas.
2//!
3//! takes molecular formula of substance. for given correct formula it extracts information about compound:
4//! - chemical composition
5//! - molar mass
6//! - mass percent of each element in composition
7//!
8//! ##### Usage
9//!
10//! ```rust
11//! use chemp;
12//!
13//! let compound = chemp::parse("MgSO4*7H2O").unwrap();
14//!
15//! // getters for each component of compound
16//! compound.components().values().for_each(|component| {
17//!     // get mass of all atoms of element in compound
18//!     component.mass();
19//!
20//!     // get percent of component mass to compound mass
21//!     component.mass_percent();
22//!
23//!     // get atoms count of element in compound
24//!     component.atoms_count();
25//!
26//!     // get chemical element symbol
27//!     component.chemical_element().symbol();
28//!
29//!     // get chemical element atomic weight
30//!     component.chemical_element().atomic_weight();
31//! });
32//!
33//! // list of elements in order they parsed
34//! // nested groups are flattened
35//! compound.composition().iter().for_each(|element| {
36//!     // get subscript of element
37//!     element.subscript();
38//!
39//!     // get chemical element symbol
40//!     element.chemical_element().symbol();
41//!
42//!     // get chemical element atomic weight
43//!     element.chemical_element().atomic_weight();     
44//! });
45//!
46//! // get molar mass of compound
47//! compound.molar_mass();
48//!
49//! println!("compound: {:#?}", compound);
50//!
51//! // compound: Compound {
52//! //     composition: [
53//! //         Element {
54//! //             chemical_element: Magnesium,
55//! //             subscript: 1,
56//! //         },
57//! //         Element {
58//! //             chemical_element: Sulfur,
59//! //             subscript: 1,
60//! //         },
61//! //         Element {
62//! //             chemical_element: Oxygen,
63//! //             subscript: 4,
64//! //         },
65//! //         Element {
66//! //             chemical_element: Hydrogen,
67//! //             subscript: 14,
68//! //         },
69//! //         Element {
70//! //             chemical_element: Oxygen,
71//! //             subscript: 7,
72//! //         },
73//! //     ],
74//! //     components: {
75//! //         "O": Component {
76//! //             chemical_element: Oxygen,
77//! //             atoms_count: 11,
78//! //             mass_percent: 71.40498,
79//! //         },
80//! //         "S": Component {
81//! //             chemical_element: Sulfur,
82//! //             atoms_count: 1,
83//! //             mass_percent: 13.007879,
84//! //         },
85//! //         "Mg": Component {
86//! //             chemical_element: Magnesium,
87//! //             atoms_count: 1,
88//! //             mass_percent: 9.861401,
89//! //         },
90//! //         "H": Component {
91//! //             chemical_element: Hydrogen,
92//! //             atoms_count: 14,
93//! //             mass_percent: 5.725739,
94//! //         },
95//! //     },
96//! //     molar_mass: 246.466,
97//! // }
98//! ```
99//!
100//! ##### The parser grammar
101//!
102//! ```
103//! substance = coefficient? component+ hydrate?
104//! component = element | group
105//! group = '(' component+ ')' subscript?
106//! element = symbol subscript?
107//! hydrate = '*' coefficient? water
108//! symbol = uppercased | uppercased lowercased
109//! subscript = digit+
110//! coefficient = digit+
111//! water = 'H2O'
112//! uppercased = {'A'..'Z'}
113//! lowercased = {'a'..'z'}
114//! digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'
115//! ```
116
117mod chemistry;
118mod compounds;
119mod error;
120mod parser;
121mod tokens;
122
123pub use chemistry::ChemicalElement;
124pub use compounds::{Component, Compound};
125pub use error::Error;
126use once_cell::sync::Lazy;
127use parser::Parser;
128pub use tokens::Element;
129
130static PERIODIC_TABLE: Lazy<chemistry::Table> = Lazy::new(|| chemistry::Table::new());
131
132/// A function takes raw formula string and produce compound or error
133pub fn parse<'a>(formula: impl Into<&'a str>) -> Result<Compound, Error> {
134    let mut parser = Parser::new(&PERIODIC_TABLE, formula.into());
135
136    let substance = parser.parse()?;
137
138    Ok(Compound::from(substance))
139}
140
141#[cfg(test)]
142mod tests {
143    use super::parse;
144    use crate::tokens::{Component, Element, Hydrate, Substance};
145    use crate::Compound;
146
147    #[test]
148    fn simple() {
149        let compound = parse("MgSO4*7H2O").unwrap();
150
151        assert_eq!(
152            compound,
153            Compound::from(Substance::from(
154                1,
155                vec![
156                    Component::Element(Element::from("Mg", 1)),
157                    Component::Element(Element::from("S", 1)),
158                    Component::Element(Element::from("O", 4)),
159                ],
160                Some(Hydrate::from(
161                    7,
162                    vec![Element::from("H", 2), Element::from("O", 1),]
163                )),
164            ))
165        );
166    }
167}