sockudo-core 4.6.0

Core traits, types, error handling, and configuration for Sockudo
Documentation
use crate::utils::wildcard_pattern_matches;
use serde::{Deserialize, Serialize};
use sonic_rs::Value;

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct UserInfo {
    pub id: String,
    pub watchlist: Option<Vec<String>>,
    pub info: Option<Value>,
    pub capabilities: Option<ConnectionCapabilities>,
    pub meta: Option<Value>,
}

#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq)]
#[serde(default)]
pub struct ConnectionCapabilities {
    pub subscribe: Option<Vec<String>>,
    pub publish: Option<Vec<String>>,
    pub history: Option<Vec<String>>,
    pub presence: Option<Vec<String>>,
    #[serde(rename = "annotation-subscribe", alias = "annotation_subscribe")]
    pub annotation_subscribe: Option<Vec<String>>,
    #[serde(rename = "annotation-publish", alias = "annotation_publish")]
    pub annotation_publish: Option<Vec<String>>,
    #[serde(rename = "annotation-delete-own", alias = "annotation_delete_own")]
    pub annotation_delete_own: Option<Vec<String>>,
    #[serde(rename = "annotation-delete-any", alias = "annotation_delete_any")]
    pub annotation_delete_any: Option<Vec<String>>,
    pub message_update_own: Option<Vec<String>>,
    pub message_update_any: Option<Vec<String>>,
    pub message_delete_own: Option<Vec<String>>,
    pub message_delete_any: Option<Vec<String>>,
    pub message_append_own: Option<Vec<String>>,
    pub message_append_any: Option<Vec<String>>,
}

impl ConnectionCapabilities {
    pub fn matches_any(patterns: &[String], channel: &str) -> bool {
        patterns.iter().any(|pattern| {
            pattern == "*" || pattern == channel || wildcard_pattern_matches(channel, pattern)
        })
    }

    pub fn allows_subscribe(&self, channel: &str) -> bool {
        if channel.starts_with("presence-")
            && let Some(patterns) = self.presence.as_deref()
        {
            return Self::matches_any(patterns, channel);
        }

        self.subscribe
            .as_deref()
            .is_none_or(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_publish(&self, channel: &str) -> bool {
        self.publish
            .as_deref()
            .is_none_or(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_history(&self, channel: &str) -> bool {
        self.history
            .as_deref()
            .is_none_or(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_annotation_subscribe(&self, channel: &str) -> bool {
        self.annotation_subscribe
            .as_deref()
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_annotation_publish(&self, channel: &str) -> bool {
        self.annotation_publish
            .as_deref()
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_annotation_delete_own(&self, channel: &str) -> bool {
        self.annotation_delete_own
            .as_deref()
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_annotation_delete_any(&self, channel: &str) -> bool {
        self.annotation_delete_any
            .as_deref()
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_message_mutation_own(
        &self,
        kind: crate::versioned_message_auth::MutationKind,
        channel: &str,
    ) -> bool {
        self.mutation_patterns(kind, false)
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    pub fn allows_message_mutation_any(
        &self,
        kind: crate::versioned_message_auth::MutationKind,
        channel: &str,
    ) -> bool {
        self.mutation_patterns(kind, true)
            .is_some_and(|patterns| Self::matches_any(patterns, channel))
    }

    fn mutation_patterns(
        &self,
        kind: crate::versioned_message_auth::MutationKind,
        any_scope: bool,
    ) -> Option<&[String]> {
        match (kind, any_scope) {
            (crate::versioned_message_auth::MutationKind::Update, false) => {
                self.message_update_own.as_deref()
            }
            (crate::versioned_message_auth::MutationKind::Update, true) => {
                self.message_update_any.as_deref()
            }
            (crate::versioned_message_auth::MutationKind::Delete, false) => {
                self.message_delete_own.as_deref()
            }
            (crate::versioned_message_auth::MutationKind::Delete, true) => {
                self.message_delete_any.as_deref()
            }
            (crate::versioned_message_auth::MutationKind::Append, false) => {
                self.message_append_own.as_deref()
            }
            (crate::versioned_message_auth::MutationKind::Append, true) => {
                self.message_append_any.as_deref()
            }
        }
    }
}