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