#[cfg(not(feature = "std"))]
use alloc::string::String;
pub type Result<T> = core::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("Invalid EPSG code: {code}")]
InvalidEpsgCode {
code: u32,
},
#[error("EPSG code {code} not found in database")]
EpsgCodeNotFound {
code: u32,
},
#[error("Invalid PROJ string: {reason}")]
InvalidProjString {
reason: String,
},
#[error("Invalid WKT string: {reason}")]
InvalidWkt {
reason: String,
},
#[error("WKT parsing error at position {position}: {message}")]
WktParseError {
position: usize,
message: String,
},
#[error("Coordinate transformation failed: {reason}")]
TransformationError {
reason: String,
},
#[error("Unsupported CRS: {crs_type}")]
UnsupportedCrs {
crs_type: String,
},
#[error("Incompatible CRS for transformation: source={src}, target={tgt}")]
IncompatibleCrs {
src: String,
tgt: String,
},
#[error("Invalid coordinate: {reason}")]
InvalidCoordinate {
reason: String,
},
#[error("Coordinate out of valid bounds: ({x}, {y})")]
CoordinateOutOfBounds {
x: f64,
y: f64,
},
#[error("Invalid bounding box: {reason}")]
InvalidBoundingBox {
reason: String,
},
#[error("Missing required parameter: {parameter}")]
MissingParameter {
parameter: String,
},
#[error("Invalid parameter value for {parameter}: {reason}")]
InvalidParameter {
parameter: String,
reason: String,
},
#[error("Datum transformation failed: {reason}")]
DatumTransformError {
reason: String,
},
#[error("Failed to initialize projection: {reason}")]
ProjectionInitError {
reason: String,
},
#[error("Unsupported projection: {projection}")]
UnsupportedProjection {
projection: String,
},
#[error("Numerical error in projection calculation: {operation}")]
NumericalError {
operation: String,
},
#[error("Failed to converge after {iterations} iterations")]
ConvergenceError {
iterations: usize,
},
#[cfg(feature = "std")]
#[error("JSON error: {0}")]
JsonError(#[from] serde_json::Error),
#[cfg(feature = "std")]
#[error("I/O error: {0}")]
IoError(#[from] std::io::Error),
#[cfg(feature = "std")]
#[error("UTF-8 conversion error: {0}")]
Utf8Error(#[from] std::str::Utf8Error),
#[cfg(feature = "std")]
#[error("Proj4rs error: {0}")]
Proj4rsError(String),
#[cfg(feature = "proj-sys")]
#[error("PROJ library error: {0}")]
ProjSysError(String),
#[error("{0}")]
Other(String),
}
impl Error {
pub fn invalid_epsg_code(code: u32) -> Self {
Self::InvalidEpsgCode { code }
}
pub fn epsg_not_found(code: u32) -> Self {
Self::EpsgCodeNotFound { code }
}
pub fn invalid_proj_string<S: Into<String>>(reason: S) -> Self {
Self::InvalidProjString {
reason: reason.into(),
}
}
pub fn invalid_wkt<S: Into<String>>(reason: S) -> Self {
Self::InvalidWkt {
reason: reason.into(),
}
}
pub fn wkt_parse_error<S: Into<String>>(position: usize, message: S) -> Self {
Self::WktParseError {
position,
message: message.into(),
}
}
pub fn transformation_error<S: Into<String>>(reason: S) -> Self {
Self::TransformationError {
reason: reason.into(),
}
}
pub fn unsupported_crs<S: Into<String>>(crs_type: S) -> Self {
Self::UnsupportedCrs {
crs_type: crs_type.into(),
}
}
pub fn incompatible_crs<S: Into<String>>(src: S, tgt: S) -> Self {
Self::IncompatibleCrs {
src: src.into(),
tgt: tgt.into(),
}
}
pub fn invalid_coordinate<S: Into<String>>(reason: S) -> Self {
Self::InvalidCoordinate {
reason: reason.into(),
}
}
pub fn coordinate_out_of_bounds(x: f64, y: f64) -> Self {
Self::CoordinateOutOfBounds { x, y }
}
pub fn invalid_bounding_box<S: Into<String>>(reason: S) -> Self {
Self::InvalidBoundingBox {
reason: reason.into(),
}
}
pub fn missing_parameter<S: Into<String>>(parameter: S) -> Self {
Self::MissingParameter {
parameter: parameter.into(),
}
}
pub fn invalid_parameter<S: Into<String>>(parameter: S, reason: S) -> Self {
Self::InvalidParameter {
parameter: parameter.into(),
reason: reason.into(),
}
}
pub fn datum_transform_error<S: Into<String>>(reason: S) -> Self {
Self::DatumTransformError {
reason: reason.into(),
}
}
pub fn projection_init_error<S: Into<String>>(reason: S) -> Self {
Self::ProjectionInitError {
reason: reason.into(),
}
}
pub fn unsupported_projection<S: Into<String>>(projection: S) -> Self {
Self::UnsupportedProjection {
projection: projection.into(),
}
}
pub fn numerical_error<S: Into<String>>(operation: S) -> Self {
Self::NumericalError {
operation: operation.into(),
}
}
pub fn convergence_error(iterations: usize) -> Self {
Self::ConvergenceError { iterations }
}
#[cfg(feature = "std")]
pub fn from_proj4rs<S: Into<String>>(message: S) -> Self {
Self::Proj4rsError(message.into())
}
pub fn other<S: Into<String>>(message: S) -> Self {
Self::Other(message.into())
}
}
#[cfg(feature = "std")]
impl From<proj4rs::errors::Error> for Error {
fn from(err: proj4rs::errors::Error) -> Self {
Self::from_proj4rs(format!("{:?}", err))
}
}
#[cfg(feature = "proj-sys")]
impl From<proj::ProjError> for Error {
fn from(err: proj::ProjError) -> Self {
Self::ProjSysError(format!("{}", err))
}
}
#[cfg(test)]
#[allow(clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_error_creation() {
let err = Error::invalid_epsg_code(12345);
assert!(matches!(err, Error::InvalidEpsgCode { code: 12345 }));
let err = Error::epsg_not_found(4326);
assert!(matches!(err, Error::EpsgCodeNotFound { code: 4326 }));
let err = Error::invalid_proj_string("missing parameter");
assert!(matches!(err, Error::InvalidProjString { .. }));
let err = Error::transformation_error("invalid coordinates");
assert!(matches!(err, Error::TransformationError { .. }));
}
#[test]
fn test_error_display() {
let err = Error::invalid_epsg_code(12345);
assert_eq!(format!("{}", err), "Invalid EPSG code: 12345");
let err = Error::coordinate_out_of_bounds(180.5, 90.5);
assert_eq!(
format!("{}", err),
"Coordinate out of valid bounds: (180.5, 90.5)"
);
}
#[test]
fn test_result_type() {
fn returns_ok() -> Result<i32> {
Ok(42)
}
fn returns_error() -> Result<i32> {
Err(Error::invalid_epsg_code(0))
}
assert!(returns_ok().is_ok());
assert!(returns_error().is_err());
}
}