use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum XrayDbError {
#[error("unknown element: {0}")]
UnknownElement(String),
#[error("unknown edge '{edge}' for element '{element}'{}", format_available(.available))]
UnknownEdge {
element: String,
edge: String,
available: Vec<String>,
},
#[error("unknown ion: {0}")]
UnknownIon(String),
#[error("unknown gas: {gas}{}", format_available(.available))]
UnknownGas {
gas: String,
available: Vec<String>,
},
#[error("invalid chemical formula '{formula}': {reason}")]
InvalidFormula {
formula: String,
reason: String,
},
#[error("invalid input: {0}")]
InvalidInput(String),
#[error("data error: {0}")]
DataError(String),
}
impl XrayDbError {
pub(crate) fn invalid_input(msg: impl Into<String>) -> Self {
Self::InvalidInput(msg.into())
}
pub(crate) fn invalid_formula(formula: impl Into<String>, reason: impl Into<String>) -> Self {
Self::InvalidFormula {
formula: formula.into(),
reason: reason.into(),
}
}
}
fn format_available(available: &[String]) -> String {
if available.is_empty() {
return String::new();
}
const MAX: usize = 12;
let shown: Vec<&str> = available.iter().take(MAX).map(String::as_str).collect();
if available.len() > MAX {
format!(
" (available: {}, … and {} more)",
shown.join(", "),
available.len() - MAX
)
} else {
format!(" (available: {})", shown.join(", "))
}
}
pub type Result<T> = core::result::Result<T, XrayDbError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_edge_lists_available_edges() {
let err = XrayDbError::UnknownEdge {
element: "Fe".into(),
edge: "Q9".into(),
available: vec!["K".into(), "L1".into()],
};
let msg = err.to_string();
assert!(msg.contains("unknown edge 'Q9'"), "{msg}");
assert!(msg.contains("available: K, L1"), "{msg}");
}
#[test]
fn empty_available_adds_no_hint() {
let err = XrayDbError::UnknownGas {
gas: "argonn".into(),
available: Vec::new(),
};
assert_eq!(err.to_string(), "unknown gas: argonn");
}
#[test]
fn available_list_is_truncated() {
let many: Vec<String> = (0..20).map(|i| format!("e{i}")).collect();
let msg = XrayDbError::UnknownGas {
gas: "x".into(),
available: many,
}
.to_string();
assert!(msg.contains("… and 8 more"), "{msg}");
}
}