1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4mod data;
7
8use data::ELEMENTS;
9
10#[derive(Clone, Copy, Debug, PartialEq)]
12pub struct Element {
13 pub atomic_number: u8,
14 pub symbol: &'static str,
15 pub name: &'static str,
16 pub atomic_mass: f64,
17 pub period: u8,
18 pub group: Option<u8>,
19}
20
21impl Element {
22 #[must_use]
24 pub const fn new(
25 atomic_number: u8,
26 symbol: &'static str,
27 name: &'static str,
28 atomic_mass: f64,
29 period: u8,
30 group: Option<u8>,
31 ) -> Self {
32 Self {
33 atomic_number,
34 symbol,
35 name,
36 atomic_mass,
37 period,
38 group,
39 }
40 }
41}
42
43#[must_use]
45pub fn all_elements() -> &'static [Element] {
46 &ELEMENTS
47}
48
49#[must_use]
64pub fn element_by_symbol(symbol: &str) -> Option<Element> {
65 let normalized = symbol.trim();
66
67 if normalized.is_empty() {
68 return None;
69 }
70
71 ELEMENTS
72 .iter()
73 .copied()
74 .find(|element| element.symbol.eq_ignore_ascii_case(normalized))
75}
76
77#[must_use]
90pub fn element_by_atomic_number(atomic_number: u8) -> Option<Element> {
91 atomic_number
92 .checked_sub(1)
93 .and_then(|index| ELEMENTS.get(usize::from(index)))
94 .copied()
95}
96
97#[must_use]
108pub fn element_name(symbol: &str) -> Option<&'static str> {
109 element_by_symbol(symbol).map(|element| element.name)
110}
111
112#[must_use]
123pub fn element_symbol(atomic_number: u8) -> Option<&'static str> {
124 element_by_atomic_number(atomic_number).map(|element| element.symbol)
125}
126
127#[cfg(test)]
128mod tests {
129 use super::{
130 all_elements, element_by_atomic_number, element_by_symbol, element_name, element_symbol,
131 };
132
133 #[test]
134 fn exposes_all_known_elements() {
135 assert_eq!(all_elements().len(), 118);
136 assert_eq!(
137 all_elements().first().map(|element| element.symbol),
138 Some("H")
139 );
140 assert_eq!(
141 all_elements().last().map(|element| element.symbol),
142 Some("Og")
143 );
144 }
145
146 #[test]
147 fn looks_up_expected_symbols_case_insensitively() {
148 assert_eq!(
149 element_by_symbol("H").map(|element| element.atomic_number),
150 Some(1)
151 );
152 assert_eq!(
153 element_by_symbol("c").map(|element| element.name),
154 Some("Carbon")
155 );
156 assert_eq!(
157 element_by_symbol("O").map(|element| element.atomic_number),
158 Some(8)
159 );
160 assert_eq!(
161 element_by_symbol(" na ").map(|element| element.atomic_number),
162 Some(11)
163 );
164 assert_eq!(
165 element_by_symbol("Fe").map(|element| element.name),
166 Some("Iron")
167 );
168 assert_eq!(
169 element_by_symbol("Au").map(|element| element.atomic_number),
170 Some(79)
171 );
172 assert_eq!(
173 element_by_symbol("U").map(|element| element.name),
174 Some("Uranium")
175 );
176 assert_eq!(
177 element_by_symbol("Og").map(|element| element.atomic_number),
178 Some(118)
179 );
180 assert_eq!(element_by_symbol("Xx"), None);
181 assert_eq!(element_by_symbol(" "), None);
182 }
183
184 #[test]
185 fn looks_up_expected_atomic_numbers() {
186 assert_eq!(
187 element_by_atomic_number(1).map(|element| element.symbol),
188 Some("H")
189 );
190 assert_eq!(
191 element_by_atomic_number(6).map(|element| element.name),
192 Some("Carbon")
193 );
194 assert_eq!(
195 element_by_atomic_number(8).map(|element| element.symbol),
196 Some("O")
197 );
198 assert_eq!(
199 element_by_atomic_number(11).map(|element| element.symbol),
200 Some("Na")
201 );
202 assert_eq!(
203 element_by_atomic_number(26).map(|element| element.name),
204 Some("Iron")
205 );
206 assert_eq!(
207 element_by_atomic_number(79).map(|element| element.name),
208 Some("Gold")
209 );
210 assert_eq!(
211 element_by_atomic_number(92).map(|element| element.name),
212 Some("Uranium")
213 );
214 assert_eq!(
215 element_by_atomic_number(118).map(|element| element.name),
216 Some("Oganesson")
217 );
218 assert_eq!(element_by_atomic_number(0), None);
219 assert_eq!(element_by_atomic_number(119), None);
220 }
221
222 #[test]
223 fn exposes_name_and_symbol_helpers() {
224 assert_eq!(element_name("H"), Some("Hydrogen"));
225 assert_eq!(element_name("na"), Some("Sodium"));
226 assert_eq!(element_name("invalid"), None);
227 assert_eq!(element_symbol(8), Some("O"));
228 assert_eq!(element_symbol(79), Some("Au"));
229 assert_eq!(element_symbol(119), None);
230 }
231
232 #[test]
233 fn stores_expected_period_and_group_metadata() {
234 let hydrogen = element_by_symbol("H").expect("hydrogen should exist");
235 let carbon = element_by_symbol("C").expect("carbon should exist");
236 let iron = element_by_symbol("Fe").expect("iron should exist");
237 let uranium = element_by_symbol("U").expect("uranium should exist");
238 let oganesson = element_by_symbol("Og").expect("oganesson should exist");
239
240 assert_eq!((hydrogen.period, hydrogen.group), (1, Some(1)));
241 assert_eq!((carbon.period, carbon.group), (2, Some(14)));
242 assert_eq!((iron.period, iron.group), (4, Some(8)));
243 assert_eq!((uranium.period, uranium.group), (7, None));
244 assert_eq!((oganesson.period, oganesson.group), (7, Some(18)));
245 }
246}