Skip to main content

readcon_core/
error.rs

1use std::fmt;
2use std::num::{ParseFloatError, ParseIntError};
3
4#[derive(Debug)]
5pub enum ParseError {
6    IncompleteHeader,
7    IncompleteFrame,
8    IncompleteVelocitySection,
9    InvalidVectorLength { expected: usize, found: usize },
10    InvalidNumberFormat(String),
11    MissingSpecVersion,
12    UnsupportedSpecVersion(u32),
13    InvalidMetadataJson(String),
14    IncompleteForceSection,
15    IncompleteEnergySection,
16    /// Declared optional section was incomplete or mislabeled.
17    IncompleteSection(String),
18    UnknownSection(String),
19    ValidationError(String),
20    /// An in-place builder mutation
21    /// (`ConFrameBuilder::set_atom_position` / `set_atom_velocity` /
22    /// `set_atom_force` / `set_atom_energy` / `set_atom_fixed` /
23    /// `set_atom_mass` / clear_*) was called with an atom index past
24    /// the current length. Surfaces as `IndexError` in PyO3 and as
25    /// `RKR_STATUS_INDEX_OUT_OF_BOUNDS` over the C ABI.
26    IndexOutOfBounds { index: usize, len: usize },
27    /// Two atoms share a CON type (symbol) but their masses differ
28    /// beyond [`crate::types::ConFrameBuilder::TYPE_MASS_ABS_TOL`].
29    /// CON line 9 stores one mass per type, so the builder cannot
30    /// represent per-atom mass variation inside a type.
31    MassMismatch {
32        symbol: String,
33        first_mass: f64,
34        found_mass: f64,
35        atom_index: usize,
36    },
37}
38
39impl fmt::Display for ParseError {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            ParseError::IncompleteHeader => {
43                write!(f, "file ended unexpectedly while parsing frame header")
44            }
45            ParseError::IncompleteFrame => {
46                write!(f, "file ended unexpectedly while reading atom data")
47            }
48            ParseError::IncompleteVelocitySection => {
49                write!(f, "file ended unexpectedly while reading velocity section")
50            }
51            ParseError::InvalidVectorLength { expected, found } => {
52                write!(f, "expected {expected} values on line, found {found}")
53            }
54            ParseError::InvalidNumberFormat(msg) => {
55                write!(f, "invalid number format: {msg}")
56            }
57            ParseError::MissingSpecVersion => {
58                write!(
59                    f,
60                    "line 1 must be a JSON object containing \"con_spec_version\""
61                )
62            }
63            ParseError::UnsupportedSpecVersion(v) => {
64                write!(f, "unsupported con_spec_version: {v}")
65            }
66            ParseError::InvalidMetadataJson(msg) => {
67                write!(f, "invalid JSON metadata on line 1: {msg}")
68            }
69            ParseError::IncompleteForceSection => {
70                write!(f, "file ended unexpectedly while reading force section")
71            }
72            ParseError::IncompleteEnergySection => {
73                write!(f, "file ended unexpectedly while reading energy section")
74            }
75            ParseError::IncompleteSection(name) => {
76                write!(f, "file ended unexpectedly while reading {name} section")
77            }
78            ParseError::UnknownSection(name) => {
79                write!(f, "unknown section type in metadata: {name}")
80            }
81            ParseError::ValidationError(msg) => {
82                write!(f, "CON validation failed: {msg}")
83            }
84            ParseError::IndexOutOfBounds { index, len } => {
85                write!(
86                    f,
87                    "atom index {index} is out of bounds (builder holds {len} atoms)"
88                )
89            }
90            ParseError::MassMismatch {
91                symbol,
92                first_mass,
93                found_mass,
94                atom_index,
95            } => {
96                write!(
97                    f,
98                    "atoms of type {symbol} have inconsistent masses: first {first_mass}, atom {atom_index} has {found_mass} (CON stores one mass per type)"
99                )
100            }
101        }
102    }
103}
104
105impl std::error::Error for ParseError {}
106
107impl From<ParseFloatError> for ParseError {
108    fn from(e: ParseFloatError) -> Self {
109        ParseError::InvalidNumberFormat(e.to_string())
110    }
111}
112
113impl From<ParseIntError> for ParseError {
114    fn from(e: ParseIntError) -> Self {
115        ParseError::InvalidNumberFormat(e.to_string())
116    }
117}
118
119impl From<serde_json::Error> for ParseError {
120    fn from(e: serde_json::Error) -> Self {
121        ParseError::InvalidMetadataJson(e.to_string())
122    }
123}