1#[cfg(feature = "arbitrary")]
34use arbitrary::{Arbitrary, Unstructured};
35use arrayvec::ArrayString;
36#[cfg(feature = "serde")]
37use serde::{Deserialize, Serialize};
38#[cfg(feature = "serde")]
39use std::ops::Not;
40use std::{
41 cmp::Ordering,
42 io::{BufRead, Lines},
43 ops::{ControlFlow, Range},
44};
45
46pub use crate::error::AmeError;
47
48mod error;
49#[cfg(test)]
50mod tests;
51
52#[derive(Clone, PartialEq, Debug, Default)]
57#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
58#[cfg_attr(feature = "arbitrary", derive(Arbitrary))]
59pub struct Value {
60 pub mean: f64,
61 pub uncertainty: f64,
62 #[cfg_attr(feature = "serde", serde(default, skip_serializing_if = "Not::not"))]
63 pub is_estimated: bool,
64}
65
66impl PartialOrd for Value {
67 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
68 self.mean.partial_cmp(&other.mean)
69 }
70}
71
72#[derive(Clone, PartialEq, Debug)]
91#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
92pub struct Nuclide {
93 pub n: u32,
95 pub z: u32,
97 pub element: ArrayString<3>,
99 pub mass_excess: Value,
103 pub binding_energy_per_a: Value,
105 pub beta_decay_energy: Option<Value>,
107 pub atomic_mass: Value,
109}
110
111#[cfg(feature = "arbitrary")]
112impl<'a> Arbitrary<'a> for Nuclide {
113 fn arbitrary(u: &mut Unstructured) -> arbitrary::Result<Self> {
114 fn array_string<const CAP: usize>(
116 u: &mut Unstructured,
117 ) -> arbitrary::Result<ArrayString<CAP>> {
118 let size = usize::min(u.arbitrary_len::<u8>()?, CAP);
119 match std::str::from_utf8(u.peek_bytes(size).unwrap()) {
120 Ok(s) => {
121 u.bytes(size).unwrap();
122 Ok(ArrayString::from(s).expect("size is limited to CAP"))
123 }
124 Err(e) => {
125 let i = e.valid_up_to();
126 let valid = u.bytes(i).unwrap();
127 let s = ArrayString::from(
128 std::str::from_utf8(valid).expect("we already checked for validity"),
129 )
130 .expect("size is limited to CAP");
131 Ok(s)
132 }
133 }
134 }
135
136 let n = u.arbitrary()?;
137 let z = u.arbitrary()?;
138 let element = array_string(u)?;
139 let mass_excess = u.arbitrary()?;
140 let binding_energy_per_a = u.arbitrary()?;
141 let beta_decay_energy = u.arbitrary()?;
142 let atomic_mass = u.arbitrary()?;
143
144 Ok(Self {
145 n,
146 z,
147 element,
148 mass_excess,
149 binding_energy_per_a,
150 beta_decay_energy,
151 atomic_mass,
152 })
153 }
154}
155
156#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
157enum ReadState {
158 Start,
159 Preamble,
160 Headers,
161 Body,
162}
163
164pub struct Iter<R: BufRead> {
188 lines: Lines<R>,
189 state: ReadState,
190}
191
192impl<R: BufRead> Iter<R> {
193 pub fn new(reader: R) -> Self {
195 let lines = reader.lines();
196 Self {
197 lines,
198 state: ReadState::Start,
199 }
200 }
201
202 fn parse_line(&mut self, line: &str) -> ControlFlow<Result<Nuclide, AmeError>> {
203 fn range_err(line: &str, range: Range<usize>) -> Result<&str, AmeError> {
204 if line.len() < range.end {
205 Err(AmeError::TooShortLine)
206 } else {
207 Ok(line.get(range).ok_or(AmeError::StrIndex)?.trim())
208 }
209 }
210
211 fn parse_value(
212 (s_mean, r_mean): (&str, Range<usize>),
213 (s_unc, r_unc): (&str, Range<usize>),
214 ) -> Result<Value, AmeError> {
215 let mean = range_err(&s_mean.replace('#', "."), r_mean)?.parse()?;
216 let uncertainty = range_err(&s_unc.replace('#', "."), r_unc)?.parse()?;
217 let is_estimated = s_mean.contains('#');
218 Ok(Value {
219 mean,
220 uncertainty,
221 is_estimated,
222 })
223 }
224
225 fn inner(line: &str) -> Result<Nuclide, AmeError> {
226 let n = range_err(line, 4..9)?.parse()?;
227 let z = range_err(line, 9..14)?.parse()?;
228 let element = ArrayString::from(range_err(line, 20..23)?)
229 .expect("the range is 3 and the capacity is 3");
230 let mass_excess = parse_value((line, 28..42), (line, 42..54))?;
231 let binding_energy_per_a = parse_value((line, 54..67), (line, 68..78))?;
232 let beta_decay_energy = (range_err(line, 87..88)? != "*")
233 .then(|| parse_value((line, 81..94), (line, 94..105)))
234 .transpose()?;
235
236 let mut atomic_mass = parse_value((line, 110..123), (line, 123..(line.len())))?;
243 atomic_mass.mean *= 1e-6;
244 atomic_mass.uncertainty *= 1e-6;
245 atomic_mass.mean += f64::from(range_err(line, 106..109)?.parse::<u16>()?);
246
247 Ok(Nuclide {
248 n,
249 z,
250 element,
251 mass_excess,
252 binding_energy_per_a,
253 beta_decay_energy,
254 atomic_mass,
255 })
256 }
257
258 match self.state {
259 ReadState::Start => {
260 if line.starts_with('1') {
261 self.state = ReadState::Preamble;
262 }
263 ControlFlow::Continue(())
264 }
265 ReadState::Preamble => {
266 if line.starts_with('1') {
267 self.state = ReadState::Headers;
268 }
269 ControlFlow::Continue(())
270 }
271 ReadState::Headers => {
272 if line.starts_with('0') {
273 self.state = ReadState::Body;
274 ControlFlow::Break(inner(line))
275 } else {
276 ControlFlow::Continue(())
277 }
278 }
279 ReadState::Body => ControlFlow::Break(inner(line)),
280 }
281 }
282}
283
284impl<R: BufRead> Iterator for Iter<R> {
285 type Item = Result<Nuclide, AmeError>;
286
287 fn next(&mut self) -> Option<Self::Item> {
288 loop {
289 match self.lines.next()? {
290 Ok(line) => match self.parse_line(&line) {
291 ControlFlow::Continue(()) => continue,
292 ControlFlow::Break(res) => return Some(res),
293 },
294 Err(e) => return Some(Err(e.into())),
295 }
296 }
297 }
298}