zwo_mount_control 0.2.1

Rust library for controlling ZWO AM5/AM3 telescope mounts with satellite tracking support
Documentation
//! Error types for ZWO mount control operations.

use thiserror::Error;

/// Main error type for ZWO mount control operations.
#[derive(Error, Debug)]
pub enum MountError {
    /// Serial port communication error
    #[error("Serial port error: {0}")]
    SerialPort(#[from] serialport::Error),

    /// I/O error during communication
    #[error("I/O error: {0}")]
    Io(#[from] std::io::Error),

    /// Mount returned an unexpected or invalid response
    #[error("Invalid response from mount: {0}")]
    InvalidResponse(String),

    /// Command timed out waiting for response
    #[error("Command timed out after {0}ms")]
    Timeout(u64),

    /// Mount is not connected
    #[error("Mount is not connected")]
    NotConnected,

    /// Mount is currently slewing and cannot accept new commands
    #[error("Mount is currently slewing")]
    Slewing,

    /// Coordinates are out of valid range
    #[error("Invalid coordinates: {0}")]
    InvalidCoordinates(String),

    /// TLE parsing or propagation error
    #[error("TLE/Satellite error: {0}")]
    SatelliteError(String),

    /// Mount configuration error
    #[error("Configuration error: {0}")]
    ConfigError(String),

    /// Target is below the horizon
    #[error("Target is below horizon (elevation: {0}°)")]
    BelowHorizon(f64),

    /// Cable wrap limit reached
    #[error("Cable wrap limit reached at azimuth {0}° (limits: {1}° to {2}°)")]
    CableWrapLimit(f64, f64, f64),

    /// Mount limit exceeded
    #[error("Mount limit exceeded: {0}")]
    LimitExceeded(String),

    /// Unsupported operation for current mount mode
    #[error("Unsupported operation in current mode: {0}")]
    UnsupportedOperation(String),

    /// Generic mount error with message
    #[error("Mount error: {0}")]
    Other(String),
}

/// Result type alias for mount operations.
pub type MountResult<T> = Result<T, MountError>;

impl MountError {
    /// Create a new invalid response error.
    pub fn invalid_response(msg: impl Into<String>) -> Self {
        MountError::InvalidResponse(msg.into())
    }

    /// Create a new satellite error.
    pub fn satellite_error(msg: impl Into<String>) -> Self {
        MountError::SatelliteError(msg.into())
    }

    /// Create a new configuration error.
    pub fn config_error(msg: impl Into<String>) -> Self {
        MountError::ConfigError(msg.into())
    }

    /// Create a new invalid coordinates error.
    pub fn invalid_coords(msg: impl Into<String>) -> Self {
        MountError::InvalidCoordinates(msg.into())
    }

    /// Create a new limit exceeded error.
    pub fn limit_exceeded(msg: impl Into<String>) -> Self {
        MountError::LimitExceeded(msg.into())
    }

    /// Create a new unsupported operation error.
    pub fn unsupported(msg: impl Into<String>) -> Self {
        MountError::UnsupportedOperation(msg.into())
    }

    /// Create a new generic error.
    pub fn other(msg: impl Into<String>) -> Self {
        MountError::Other(msg.into())
    }

    /// Check if this error is recoverable (operation can be retried).
    pub fn is_recoverable(&self) -> bool {
        matches!(
            self,
            MountError::Timeout(_) | MountError::Slewing | MountError::Io(_)
        )
    }

    /// Check if this error indicates a connection problem.
    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());
    }
}