use thiserror::Error;
#[derive(Error, Debug)]
pub enum MountError {
#[error("Serial port error: {0}")]
SerialPort(#[from] serialport::Error),
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Invalid response from mount: {0}")]
InvalidResponse(String),
#[error("Command timed out after {0}ms")]
Timeout(u64),
#[error("Mount is not connected")]
NotConnected,
#[error("Mount is currently slewing")]
Slewing,
#[error("Invalid coordinates: {0}")]
InvalidCoordinates(String),
#[error("TLE/Satellite error: {0}")]
SatelliteError(String),
#[error("Configuration error: {0}")]
ConfigError(String),
#[error("Target is below horizon (elevation: {0}°)")]
BelowHorizon(f64),
#[error("Cable wrap limit reached at azimuth {0}° (limits: {1}° to {2}°)")]
CableWrapLimit(f64, f64, f64),
#[error("Mount limit exceeded: {0}")]
LimitExceeded(String),
#[error("Unsupported operation in current mode: {0}")]
UnsupportedOperation(String),
#[error("Mount error: {0}")]
Other(String),
}
pub type MountResult<T> = Result<T, MountError>;
impl MountError {
pub fn invalid_response(msg: impl Into<String>) -> Self {
MountError::InvalidResponse(msg.into())
}
pub fn satellite_error(msg: impl Into<String>) -> Self {
MountError::SatelliteError(msg.into())
}
pub fn config_error(msg: impl Into<String>) -> Self {
MountError::ConfigError(msg.into())
}
pub fn invalid_coords(msg: impl Into<String>) -> Self {
MountError::InvalidCoordinates(msg.into())
}
pub fn limit_exceeded(msg: impl Into<String>) -> Self {
MountError::LimitExceeded(msg.into())
}
pub fn unsupported(msg: impl Into<String>) -> Self {
MountError::UnsupportedOperation(msg.into())
}
pub fn other(msg: impl Into<String>) -> Self {
MountError::Other(msg.into())
}
pub fn is_recoverable(&self) -> bool {
matches!(
self,
MountError::Timeout(_) | MountError::Slewing | MountError::Io(_)
)
}
pub fn is_connection_error(&self) -> bool {
matches!(
self,
MountError::SerialPort(_) | MountError::NotConnected | MountError::Io(_)
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err = MountError::Timeout(1000);
assert_eq!(err.to_string(), "Command timed out after 1000ms");
let err = MountError::invalid_response("bad checksum");
assert_eq!(err.to_string(), "Invalid response from mount: bad checksum");
}
#[test]
fn test_error_recoverable() {
assert!(MountError::Timeout(1000).is_recoverable());
assert!(MountError::Slewing.is_recoverable());
assert!(!MountError::NotConnected.is_recoverable());
assert!(!MountError::InvalidCoordinates("test".into()).is_recoverable());
}
#[test]
fn test_error_connection() {
assert!(MountError::NotConnected.is_connection_error());
assert!(!MountError::Timeout(1000).is_connection_error());
assert!(!MountError::Slewing.is_connection_error());
}
}