torrust-actix 4.1.2

A rich, fast and efficient Bittorrent Tracker.
use crate::cache::errors::CacheError;
use crate::cache::structs::cache_connector_memcache::CacheConnectorMemcache;
use crate::cache::traits::cache_backend::CacheBackend;
use crate::tracker::structs::info_hash::InfoHash;
use async_trait::async_trait;
use log::debug;
use parking_lot::Mutex;
use std::sync::Arc;

impl CacheConnectorMemcache {
    pub fn connect(url: &str, prefix: &str) -> Result<Self, CacheError> {
        let client = memcache::connect(url)
            .map_err(|e| CacheError::ConnectionError(format!("Failed to connect to Memcache: {}", e)))?;
        Ok(Self {
            client: Arc::new(Mutex::new(client)),
            prefix: prefix.to_string(),
        })
    }

    fn torrent_key(&self, info_hash: &InfoHash) -> String {
        format!("{}t:{}", self.prefix, info_hash)
    }

    fn serialize_peers(seeds: u64, peers: u64) -> String {
        format!("{}:{}", seeds, peers)
    }

    fn deserialize_peers(value: &str) -> Option<(u64, u64)> {
        let parts: Vec<&str> = value.split(':').collect();
        if parts.len() == 2 {
            let seeds = parts[0].parse::<u64>().ok()?;
            let peers = parts[1].parse::<u64>().ok()?;
            Some((seeds, peers))
        } else {
            None
        }
    }
}

#[async_trait]
impl CacheBackend for CacheConnectorMemcache {
    async fn ping(&self) -> Result<(), CacheError> {
        let client = self.client.lock();
        client.version()
            .map_err(CacheError::MemcacheError)?;
        Ok(())
    }

    async fn set_torrent_peers(
        &self,
        info_hash: &InfoHash,
        seeds: u64,
        peers: u64,
        ttl: Option<u64>,
    ) -> Result<(), CacheError> {
        let client = self.client.lock();
        let key = self.torrent_key(info_hash);
        let value = Self::serialize_peers(seeds, peers);
        let expiration = ttl.unwrap_or(0) as u32;
        client.set(&key, value.as_str(), expiration)
            .map_err(CacheError::MemcacheError)?;
        debug!("[Memcache] Set torrent {} seeds={} peers={}", info_hash, seeds, peers);
        Ok(())
    }

    async fn get_torrent_peers(
        &self,
        info_hash: &InfoHash,
    ) -> Result<Option<(u64, u64)>, CacheError> {
        let client = self.client.lock();
        let key = self.torrent_key(info_hash);
        match client.get::<String>(&key) {
            Ok(Some(value)) => Ok(Self::deserialize_peers(&value)),
            Ok(None) => Ok(None),
            Err(e) => Err(CacheError::MemcacheError(e)),
        }
    }

    async fn delete_torrent(&self, info_hash: &InfoHash) -> Result<(), CacheError> {
        let client = self.client.lock();
        let key = self.torrent_key(info_hash);
        let _ = client.delete(&key);
        debug!("[Memcache] Deleted torrent {}", info_hash);
        Ok(())
    }

    async fn set_torrent_peers_batch(
        &self,
        data: &[(InfoHash, u64, u64)],
        ttl: Option<u64>,
    ) -> Result<(), CacheError> {
        if data.is_empty() {
            return Ok(());
        }
        let client = self.client.lock();
        let expiration = ttl.unwrap_or(0) as u32;
        for (info_hash, seeds, peers) in data {
            let key = self.torrent_key(info_hash);
            let value = Self::serialize_peers(*seeds, *peers);
            client.set(&key, value.as_str(), expiration)
                .map_err(CacheError::MemcacheError)?;
        }
        debug!("[Memcache] Batch set {} torrents", data.len());
        Ok(())
    }
}