cal-redis 0.1.80

Callable Redis Implementation
Documentation
// File: cal-redis/src/constants.rs

/// Base prefix for all Redis keys
pub const KEY_PREFIX: &str = "cal";

/// Separator for key components
pub const KEY_SEPARATOR: &str = ":";

// ==================== Global Keys (not account-scoped) ====================

/// Regions hash - stores all jambonz objects
/// Format: cal:jambonz
pub const JAMBONZ_KEY: &str = "cal:jambonz";

/// Regions hash - stores all regions
/// Format: cal:regions
pub const REGIONS_KEY: &str = "cal:regions";

/// Region identifiers hash - maps various IDs to region ID
/// Format: cal:region:idents
pub const REGION_IDENTS_KEY: &str = "cal:region:idents";

/// Proxies hash - stores all proxies
/// Format: cal:proxies
pub const PROXIES_KEY: &str = "cal:proxies";

/// WebSocket connections hash - maps connection ID to account ID
/// Format: cal:ws:connections
pub const WS_CONNECTIONS_KEY: &str = "cal:ws:connections";

/// Global events channel
/// Format: cal:events
pub const GLOBAL_EVENTS_CHANNEL: &str = "cal:events";

// ==================== Account-Scoped Keys ====================

/// Accounts hash - stores account summaries
/// Format: cal:accounts
pub const ACCOUNTS_KEY: &str = "cal:accounts";

/// Account identifiers hash - maps various IDs to account ID
/// Format: cal:account:idents
pub const ACCOUNT_IDENTS_KEY: &str = "cal:account:idents";

/// Users hash - stores all users
/// Format: cal:users
pub const USERS_KEY: &str = "cal:users";

/// User identifiers hash - maps email/username to user ID
/// Format: cal:user:idents
pub const USER_IDENTS_KEY: &str = "cal:user:idents";

// ==================== Key Builders ====================

/// Build a key with the standard prefix
#[inline]
pub fn build_key(components: &[&str]) -> String {
    let mut key = String::with_capacity(128);
    key.push_str(KEY_PREFIX);
    for component in components {
        key.push_str(KEY_SEPARATOR);
        key.push_str(component);
    }
    key
}

/// Build an account-scoped key
#[inline]
pub fn build_account_key(account_id: &str, components: &[&str]) -> String {
    let mut parts = vec!["account", account_id];
    parts.extend_from_slice(components);
    build_key(&parts)
}

/// Build a trunk lookup key
#[inline]
pub fn build_trunk_key(trunk_ip: &str) -> String {
    build_key(&["trunk", trunk_ip])
}

// ==================== Account Collection Keys ====================

pub struct AccountKeys;

impl AccountKeys {
    /// Account devices hash
    /// Format: cal:account:{account_id}:devices
    pub fn devices(account_id: &str) -> String {
        build_account_key(account_id, &["devices"])
    }

    /// Account device identifiers hash
    /// Format: cal:account:{account_id}:device:idents
    pub fn device_idents(account_id: &str) -> String {
        build_account_key(account_id, &["device", "idents"])
    }

    /// Account DDIs hash
    /// Format: cal:account:{account_id}:ddis
    pub fn ddis(account_id: &str) -> String {
        build_account_key(account_id, &["ddis"])
    }

    /// Account trunks hash
    /// Format: cal:account:{account_id}:trunks
    pub fn trunks(account_id: &str) -> String {
        build_account_key(account_id, &["trunks"])
    }

    /// Account hooks hash
    /// Format: cal:account:{account_id}:hooks
    pub fn hooks(account_id: &str) -> String {
        build_account_key(account_id, &["hooks"])
    }

    /// Account assets hash
    /// Format: cal:account:{account_id}:assets
    pub fn assets(account_id: &str) -> String {
        build_account_key(account_id, &["assets"])
    }

    /// Account addresses hash
    /// Format: cal:account:{account_id}:addresses
    pub fn addresses(account_id: &str) -> String {
        build_account_key(account_id, &["addresses"])
    }

    // Account contacts hash
    /// Format: cal:account:{account_id}:contacts
    pub fn contacts(account_id: &str) -> String {
        build_account_key(account_id, &["contacts"])
    }

    /// Account contact identifiers hash (phone numbers and emails to contact ID mapping)
    /// Format: cal:account:{account_id}:contact:idents
    pub fn contact_idents(account_id: &str) -> String {
        build_account_key(account_id, &["contact", "idents"])
    }
}

// ==================== Agent Keys ====================

pub struct AgentKeys;

impl AgentKeys {
    /// Agent status
    /// Format: cal:account:{account_id}:agent:status:{user_id}
    pub fn status(account_id: &str, user_id: &str) -> String {
        build_account_key(account_id, &["agent", "status", user_id])
    }

    /// Account agents by user hash
    /// Format: cal:account:{account_id}:agents:by_user
    pub fn by_user(account_id: &str) -> String {
        build_account_key(account_id, &["agents", "by_user"])
    }

    /// Account registered agents set
    /// Format: cal:account:{account_id}:agents:registered
    pub fn registered(account_id: &str) -> String {
        build_account_key(account_id, &["agents", "registered"])
    }

    /// Account available agents set
    /// Format: cal:account:{account_id}:agents:available
    pub fn available(account_id: &str) -> String {
        build_account_key(account_id, &["agents", "available"])
    }

    /// Account connected agents set
    /// Format: cal:account:{account_id}:agents:connected
    pub fn connected(account_id: &str) -> String {
        build_account_key(account_id, &["agents", "connected"])
    }
}

// ==================== Session Keys ====================

pub struct SessionKeys;

impl SessionKeys {
    /// Session data
    /// Format: cal:account:{account_id}:session:{session_id}
    pub fn session(account_id: &str, session_id: &str) -> String {
        build_account_key(account_id, &["session", session_id])
    }

    /// Active sessions set
    /// Format: cal:account:{account_id}:sessions:active
    pub fn active(account_id: &str) -> String {
        build_account_key(account_id, &["sessions", "active"])
    }
}

// ==================== Queue Keys ====================

pub struct QueueKeys;

impl QueueKeys {
    /// Queue data
    /// Format: cal:account:{account_id}:queue:{queue_name}
    pub fn queue(account_id: &str, queue_name: &str) -> String {
        build_account_key(account_id, &["queue", queue_name])
    }

    /// Queue metadata hash
    /// Format: cal:account:{account_id}:queues:meta
    pub fn metadata(account_id: &str) -> String {
        build_account_key(account_id, &["queues", "meta"])
    }

    /// Active queues set
    /// Format: cal:account:{account_id}:queues:active
    pub fn active(account_id: &str) -> String {
        build_account_key(account_id, &["queues", "active"])
    }
}

// ==================== Conversation Keys ====================

pub struct ConversationKeys;

impl ConversationKeys {
    /// Conversation data
    /// Format: cal:account:{account_id}:conversation:{conversation_id}
    pub fn conversation(account_id: &str, conversation_id: &str) -> String {
        build_account_key(account_id, &["conversation", conversation_id])
    }

    /// Active conversations set
    /// Format: cal:account:{account_id}:conversations:active
    pub fn active(account_id: &str) -> String {
        build_account_key(account_id, &["conversations", "active"])
    }

    /// Conversations by phone number
    /// Format: cal:account:{account_id}:conversations:by_phone
    pub fn by_phone(account_id: &str) -> String {
        build_account_key(account_id, &["conversations", "by_phone"])
    }
}

// ==================== Channel Keys ====================

pub struct ChannelKeys;

impl ChannelKeys {
    /// Account events channel
    /// Format: cal:account:{account_id}:events
    pub fn account_events(account_id: &str) -> String {
        build_account_key(account_id, &["events"])
    }

    /// Agent events channel
    /// Format: cal:account:{account_id}:agent:{user_id}:events
    pub fn agent_events(account_id: &str, user_id: &str) -> String {
        build_account_key(account_id, &["agent", user_id, "events"])
    }
}

// ==================== Helper Functions ====================

/// Extract account ID from a key
/// Returns None if the key doesn't follow the account pattern
pub fn extract_account_id(key: &str) -> Option<&str> {
    let parts: Vec<&str> = key.split(KEY_SEPARATOR).collect();
    if parts.len() >= 3 && parts[0] == KEY_PREFIX && parts[1] == "account" {
        Some(parts[2])
    } else {
        None
    }
}

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

    #[test]
    fn test_build_key() {
        assert_eq!(build_key(&["test", "key"]), "cal:test:key");
    }

    #[test]
    fn test_build_account_key() {
        assert_eq!(
            build_account_key("123", &["devices"]),
            "cal:account:123:devices"
        );
    }

    #[test]
    fn test_extract_account_id() {
        assert_eq!(
            extract_account_id("cal:account:123:devices"),
            Some("123")
        );
        assert_eq!(
            extract_account_id("cal:regions"),
            None
        );
    }
}