use thiserror::Error;
#[derive(Error, Debug)]
pub enum RossbyError {
#[error("NetCDF error: {message}")]
NetCdf { message: String },
#[error("Conversion error: {message}")]
Conversion { message: String },
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Configuration error: {message}")]
Config { message: String },
#[error("Invalid coordinates: {message}")]
InvalidCoordinates { message: String },
#[error("Physical value not found: {dimension}={value}. Available values: {available:?}")]
PhysicalValueNotFound {
dimension: String,
value: f64,
available: Vec<f64>,
},
#[error("Invalid parameter: {param} - {message}")]
InvalidParameter { param: String, message: String },
#[error("Data not found: {message}")]
DataNotFound { message: String },
#[error("Variable not found: {name}")]
VariableNotFound { name: String },
#[error("Index out of bounds: {param}={value}, max allowed is {max}")]
IndexOutOfBounds {
param: String,
value: String,
max: usize,
},
#[error("Interpolation error: {message}")]
Interpolation { message: String },
#[error("Image generation error: {message}")]
ImageGeneration { message: String },
#[error("Invalid variable(s): [{names:?}]")]
InvalidVariables { names: Vec<String> },
#[error("Variable {name} is not suitable for image rendering. It must be a 2D grid with latitude and longitude dimensions.")]
VariableNotSuitableForImage { name: String },
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("Dimension not found: {name}. Available dimensions: {available:?}. If using a canonical name, try using it with an underscore prefix (e.g., '_latitude') or set up dimension_aliases in config.")]
DimensionNotFound {
name: String,
available: Vec<String>,
aliases: std::collections::HashMap<String, String>,
},
#[error("Server error: {message}")]
Server { message: String },
#[error("Payload too large: {message}. Requested points: {requested}, maximum allowed: {max_allowed}")]
PayloadTooLarge {
message: String,
requested: usize,
max_allowed: usize,
},
}
pub type Result<T> = std::result::Result<T, RossbyError>;
impl From<String> for RossbyError {
fn from(message: String) -> Self {
RossbyError::Server { message }
}
}
impl From<&str> for RossbyError {
fn from(message: &str) -> Self {
RossbyError::Server {
message: message.to_string(),
}
}
}
impl From<std::num::ParseIntError> for RossbyError {
fn from(err: std::num::ParseIntError) -> Self {
RossbyError::Conversion {
message: err.to_string(),
}
}
}
impl From<std::num::ParseFloatError> for RossbyError {
fn from(err: std::num::ParseFloatError) -> Self {
RossbyError::Conversion {
message: err.to_string(),
}
}
}
impl From<ndarray::ShapeError> for RossbyError {
fn from(err: ndarray::ShapeError) -> Self {
RossbyError::Conversion {
message: format!("Array shape error: {}", err),
}
}
}
impl From<netcdf::Error> for RossbyError {
fn from(err: netcdf::Error) -> Self {
RossbyError::NetCdf {
message: err.to_string(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_conversions() {
let err: RossbyError = "test error".into();
match err {
RossbyError::Server { message } => assert_eq!(message, "test error"),
_ => panic!("Wrong error variant"),
}
let parse_err = "abc".parse::<i32>().unwrap_err();
let err: RossbyError = parse_err.into();
match err {
RossbyError::Conversion { message } => assert!(message.contains("invalid digit")),
_ => panic!("Wrong error variant"),
}
}
}