torrust-actix 4.1.2

A rich, fast and efficient Bittorrent Tracker.
use crate::tracker::structs::torrent_tracker::TorrentTracker;
use crate::websocket::enums::protocol_type::ProtocolType;
use crate::websocket::enums::request_type::RequestType;
use crate::websocket::slave::client::{send_request, SLAVE_CLIENT};
use crate::websocket::structs::cluster_request::ClusterRequest;
use crate::websocket::structs::cluster_response::ClusterResponse;
use std::net::IpAddr;
use std::sync::Arc;

#[derive(Debug)]
pub enum ForwardError {
    NotConnected,
    Timeout,
    MasterError(String),
    ConnectionLost,
    EncodingError(String),
}

impl std::fmt::Display for ForwardError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ForwardError::NotConnected => write!(f, "Not connected to master"),
            ForwardError::Timeout => write!(f, "Cluster timeout"),
            ForwardError::MasterError(msg) => write!(f, "Master error: {}", msg),
            ForwardError::ConnectionLost => write!(f, "Cluster connection lost"),
            ForwardError::EncodingError(msg) => write!(f, "Encoding error: {}", msg),
        }
    }
}

impl std::error::Error for ForwardError {}

pub async fn forward_request(
    tracker: &Arc<TorrentTracker>,
    protocol: ProtocolType,
    request_type: RequestType,
    client_ip: IpAddr,
    client_port: u16,
    payload: Vec<u8>,
) -> Result<ClusterResponse, ForwardError> {
    let request_id = {
        let mut state = SLAVE_CLIENT.write();
        state.next_request_id()
    };
    let request = ClusterRequest::new(
        request_id,
        protocol,
        request_type,
        client_ip,
        client_port,
        payload,
    );
    send_request(tracker, request).await
}

pub fn create_cluster_error_response(error: &ForwardError) -> Vec<u8> {
    let message = match error {
        ForwardError::NotConnected => "Cluster connection lost",
        ForwardError::Timeout => "Cluster timeout",
        ForwardError::MasterError(msg) => msg.as_str(),
        ForwardError::ConnectionLost => "Cluster connection lost",
        ForwardError::EncodingError(_) => "Cluster encoding error",
    };
    format!("d14:failure reason{}:{}e", message.len(), message).into_bytes()
}

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

    #[test]
    fn test_forward_error_display_not_connected() {
        let error = ForwardError::NotConnected;
        assert_eq!(format!("{}", error), "Not connected to master");
    }

    #[test]
    fn test_forward_error_display_timeout() {
        let error = ForwardError::Timeout;
        assert_eq!(format!("{}", error), "Cluster timeout");
    }

    #[test]
    fn test_forward_error_display_master_error() {
        let error = ForwardError::MasterError("Internal server error".to_string());
        assert_eq!(format!("{}", error), "Master error: Internal server error");
    }

    #[test]
    fn test_forward_error_display_connection_lost() {
        let error = ForwardError::ConnectionLost;
        assert_eq!(format!("{}", error), "Cluster connection lost");
    }

    #[test]
    fn test_forward_error_display_encoding_error() {
        let error = ForwardError::EncodingError("Invalid msgpack".to_string());
        assert_eq!(format!("{}", error), "Encoding error: Invalid msgpack");
    }

    #[test]
    fn test_forward_error_debug() {
        let error = ForwardError::NotConnected;
        let debug_str = format!("{:?}", error);
        assert_eq!(debug_str, "NotConnected");
        let error = ForwardError::MasterError("test".to_string());
        let debug_str = format!("{:?}", error);
        assert!(debug_str.contains("MasterError"));
    }

    #[test]
    fn test_create_cluster_error_response_not_connected() {
        let error = ForwardError::NotConnected;
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert!(response_str.starts_with("d14:failure reason"));
        assert!(response_str.ends_with("e"));
        assert!(response_str.contains("Cluster connection lost"));
    }

    #[test]
    fn test_create_cluster_error_response_timeout() {
        let error = ForwardError::Timeout;
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert!(response_str.contains("Cluster timeout"));
    }

    #[test]
    fn test_create_cluster_error_response_master_error() {
        let error = ForwardError::MasterError("Custom error message".to_string());
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert!(response_str.contains("Custom error message"));
    }

    #[test]
    fn test_create_cluster_error_response_connection_lost() {
        let error = ForwardError::ConnectionLost;
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert!(response_str.contains("Cluster connection lost"));
    }

    #[test]
    fn test_create_cluster_error_response_encoding_error() {
        let error = ForwardError::EncodingError("bad data".to_string());
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert!(response_str.contains("Cluster encoding error"));
    }

    #[test]
    fn test_create_cluster_error_response_bencode_format() {
        let error = ForwardError::Timeout;
        let response = create_cluster_error_response(&error);
        let response_str = String::from_utf8(response).unwrap();
        assert_eq!(response_str, "d14:failure reason15:Cluster timeoute");
    }
}