legion-server 0.1.0

🏛️ Legion Server - Secure IRC server with Legion Protocol support
use chrono::{DateTime, Utc};
use std::collections::HashMap;

#[derive(Debug, Clone)]
pub struct MessageExtensions {
    pub msgid: Option<String>,
    pub time: Option<DateTime<Utc>>,
    pub account: Option<String>,
    pub label: Option<String>,
    pub batch: Option<String>,
    pub reply: Option<String>,
    pub react: Option<String>,
    pub typing: Option<TypingState>,
    pub custom: HashMap<String, Option<String>>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum TypingState {
    Active,
    Paused,
    Done,
}

impl MessageExtensions {
    pub fn new() -> Self {
        Self {
            msgid: None,
            time: None,
            account: None,
            label: None,
            batch: None,
            reply: None,
            react: None,
            typing: None,
            custom: HashMap::new(),
        }
    }
    
    pub fn from_tags(tags: &HashMap<String, Option<String>>) -> Self {
        let mut ext = Self::new();
        
        for (key, value) in tags {
            match key.as_str() {
                "msgid" => ext.msgid = value.clone(),
                "time" => {
                    if let Some(time_str) = value {
                        if let Ok(time) = DateTime::parse_from_rfc3339(time_str) {
                            ext.time = Some(time.with_timezone(&Utc));
                        }
                    }
                }
                "account" => ext.account = value.clone(),
                "label" => ext.label = value.clone(),
                "batch" => ext.batch = value.clone(),
                "+draft/reply" => ext.reply = value.clone(),
                "+draft/react" => ext.react = value.clone(),
                "+typing" => {
                    ext.typing = value.as_ref().and_then(|v| match v.as_str() {
                        "active" => Some(TypingState::Active),
                        "paused" => Some(TypingState::Paused),
                        "done" => Some(TypingState::Done),
                        _ => None,
                    });
                }
                _ => {
                    ext.custom.insert(key.clone(), value.clone());
                }
            }
        }
        
        ext
    }
    
    pub fn to_tags(&self) -> HashMap<String, Option<String>> {
        let mut tags = HashMap::new();
        
        if let Some(msgid) = &self.msgid {
            tags.insert("msgid".to_string(), Some(msgid.clone()));
        }
        
        if let Some(time) = &self.time {
            tags.insert("time".to_string(), Some(time.to_rfc3339()));
        }
        
        if let Some(account) = &self.account {
            tags.insert("account".to_string(), Some(account.clone()));
        }
        
        if let Some(label) = &self.label {
            tags.insert("label".to_string(), Some(label.clone()));
        }
        
        if let Some(batch) = &self.batch {
            tags.insert("batch".to_string(), Some(batch.clone()));
        }
        
        if let Some(reply) = &self.reply {
            tags.insert("+draft/reply".to_string(), Some(reply.clone()));
        }
        
        if let Some(react) = &self.react {
            tags.insert("+draft/react".to_string(), Some(react.clone()));
        }
        
        if let Some(typing) = &self.typing {
            let state = match typing {
                TypingState::Active => "active",
                TypingState::Paused => "paused",
                TypingState::Done => "done",
            };
            tags.insert("+typing".to_string(), Some(state.to_string()));
        }
        
        tags.extend(self.custom.clone());
        
        tags
    }
}

impl Default for MessageExtensions {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug, Clone)]
pub struct Batch {
    pub reference: String,
    pub batch_type: String,
    pub params: Vec<String>,
    pub messages: Vec<crate::protocol::Message>,
    pub parent_batch: Option<String>,
}

impl Batch {
    pub fn new(reference: String, batch_type: String, params: Vec<String>) -> Self {
        Self {
            reference,
            batch_type,
            params,
            messages: Vec::new(),
            parent_batch: None,
        }
    }
    
    pub fn add_message(&mut self, msg: crate::protocol::Message) {
        self.messages.push(msg);
    }
}