crdhcpc 0.1.1

Standalone DHCP Client for Linux with DHCPv4, DHCPv6, PXE, and Dynamic DNS support
Documentation
/// Protocol error types
use std::io;
use thiserror::Error;

/// Protocol error type
#[derive(Debug, Error)]
pub enum ProtocolError {
    #[error("IO error: {0}")]
    Io(#[from] io::Error),

    #[error("JSON serialization error: {0}")]
    JsonSerialization(#[from] serde_json::Error),

    #[error("Message too large: {size} bytes (max: {max})")]
    MessageTooLarge { size: usize, max: usize },

    #[error("Invalid message format: {0}")]
    InvalidFormat(String),

    #[error("Connection closed")]
    ConnectionClosed,

    #[error("Timeout after {0:?}")]
    Timeout(std::time::Duration),

    #[error("Authentication failed: {0}")]
    AuthenticationFailed(String),

    #[error("Permission denied: {0}")]
    PermissionDenied(String),

    #[error("Invalid peer credentials")]
    InvalidCredentials,

    #[error("JSON-RPC error: {code} - {message}")]
    JsonRpc { code: i32, message: String },

    #[error("Method not found: {0}")]
    MethodNotFound(String),

    #[error("Invalid parameters: {0}")]
    InvalidParams(String),

    #[error("Internal error: {0}")]
    Internal(String),

    #[error("Rate limit exceeded")]
    RateLimitExceeded,

    #[error("ACL denied: {0}")]
    AclDenied(String),

    #[error("Plugin error: {0}")]
    PluginError(String),
}

/// Protocol result type
pub type ProtocolResult<T> = Result<T, ProtocolError>;

impl ProtocolError {
    /// Convert to JSON-RPC error code
    pub fn to_error_code(&self) -> i32 {
        use super::jsonrpc::error_codes;

        match self {
            ProtocolError::JsonSerialization(_) => error_codes::PARSE_ERROR,
            ProtocolError::InvalidFormat(_) => error_codes::INVALID_REQUEST,
            ProtocolError::MethodNotFound(_) => error_codes::METHOD_NOT_FOUND,
            ProtocolError::InvalidParams(_) => error_codes::INVALID_PARAMS,
            ProtocolError::Internal(_) => error_codes::INTERNAL_ERROR,
            ProtocolError::AuthenticationFailed(_) => error_codes::UNAUTHORIZED,
            ProtocolError::PermissionDenied(_) => error_codes::FORBIDDEN,
            ProtocolError::InvalidCredentials => error_codes::UNAUTHORIZED,
            ProtocolError::RateLimitExceeded => error_codes::RATE_LIMITED,
            ProtocolError::Timeout(_) => error_codes::TIMEOUT,
            ProtocolError::PluginError(_) => error_codes::PLUGIN_ERROR,
            ProtocolError::AclDenied(_) => error_codes::ACL_DENIED,
            ProtocolError::JsonRpc { code, .. } => *code,
            _ => error_codes::INTERNAL_ERROR,
        }
    }

    /// Convert to JSON-RPC error message
    pub fn to_error_message(&self) -> String {
        self.to_string()
    }

    /// Create from JSON-RPC error
    pub fn from_jsonrpc_error(code: i32, message: String) -> Self {
        use super::jsonrpc::error_codes;

        match code {
            error_codes::METHOD_NOT_FOUND => ProtocolError::MethodNotFound(message),
            error_codes::INVALID_PARAMS => ProtocolError::InvalidParams(message),
            error_codes::UNAUTHORIZED => ProtocolError::AuthenticationFailed(message),
            error_codes::FORBIDDEN => ProtocolError::PermissionDenied(message),
            error_codes::RATE_LIMITED => ProtocolError::RateLimitExceeded,
            error_codes::TIMEOUT => {
                ProtocolError::Timeout(std::time::Duration::from_secs(30))
            }
            error_codes::PLUGIN_ERROR => ProtocolError::PluginError(message),
            error_codes::ACL_DENIED => ProtocolError::AclDenied(message),
            _ => ProtocolError::JsonRpc { code, message },
        }
    }

    /// Create a JSON-RPC error object
    pub fn to_jsonrpc_error(&self) -> super::jsonrpc::JsonRpcError {
        super::jsonrpc::JsonRpcError {
            code: self.to_error_code(),
            message: self.to_error_message(),
            data: None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_error_code_mapping() {
        use crate::protocol::jsonrpc::error_codes;

        let err = ProtocolError::MethodNotFound("test".to_string());
        assert_eq!(err.to_error_code(), error_codes::METHOD_NOT_FOUND);

        let err = ProtocolError::InvalidParams("bad params".to_string());
        assert_eq!(err.to_error_code(), error_codes::INVALID_PARAMS);

        let err = ProtocolError::RateLimitExceeded;
        assert_eq!(err.to_error_code(), error_codes::RATE_LIMITED);
    }

    #[test]
    fn test_from_jsonrpc_error() {
        use crate::protocol::jsonrpc::error_codes;

        let err = ProtocolError::from_jsonrpc_error(
            error_codes::METHOD_NOT_FOUND,
            "method not found".to_string(),
        );
        assert!(matches!(err, ProtocolError::MethodNotFound(_)));

        let err = ProtocolError::from_jsonrpc_error(
            error_codes::ACL_DENIED,
            "access denied".to_string(),
        );
        assert!(matches!(err, ProtocolError::AclDenied(_)));
    }

    #[test]
    fn test_to_jsonrpc_error() {
        let err = ProtocolError::PermissionDenied("not allowed".to_string());
        let jsonrpc_err = err.to_jsonrpc_error();

        assert_eq!(jsonrpc_err.code, -32001); // FORBIDDEN
        assert_eq!(jsonrpc_err.message, "Permission denied: not allowed");
    }
}