rustybook-messenger 0.2.1

Messenger client for Rustybook
Documentation
use std::sync::Arc;
use std::sync::atomic::AtomicU64;

use rustybook_http::client::Client as HttpClient;
use tokio::sync::{
    Mutex,
    RwLock,
    broadcast,
};

use super::{
    Inner,
    MessengerClient,
    MessengerConfig,
};
#[cfg(feature = "cache")]
use crate::cache::Cache;
use crate::error::MessengerError;
use crate::http::send::PendingRequests;

#[derive(Debug, Clone)]
pub struct MessengerClientBuilder {
    cookies_file: Option<String>,
    user_id: Option<String>,
    cookies: Option<String>,
    http: Option<HttpClient>,
    user_agent: Option<String>,
    proxy: Option<String>,
    online: bool,
}

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

impl MessengerClientBuilder {
    /// Creates a builder with default options.
    pub fn new() -> Self {
        Self {
            cookies_file: None,
            user_id: None,
            cookies: None,
            http: None,
            user_agent: None,
            proxy: None,
            online: true,
        }
    }

    /// Uses cookie file auth for standalone messenger mode.
    pub fn cookies_file_path(mut self, path: impl Into<String>) -> Self {
        self.cookies_file = Some(path.into());
        self
    }

    /// Uses externally managed auth/session and HTTP client.
    pub fn shared_session(
        mut self,
        user_id: impl Into<String>,
        cookie_header: impl Into<String>,
        http: HttpClient,
    ) -> Self {
        self.user_id = Some(user_id.into());
        self.cookies = Some(cookie_header.into());
        self.http = Some(http);
        self
    }

    /// Sets a custom user agent header.
    pub fn user_agent(mut self, user_agent: impl Into<String>) -> Self {
        self.user_agent = Some(user_agent.into());
        self
    }

    /// Sets an HTTP proxy URL for state bootstrap requests.
    pub fn proxy(mut self, proxy: impl Into<String>) -> Self {
        self.proxy = Some(proxy.into());
        self
    }

    /// Controls online/foreground state sent during MQTT connect.
    pub fn online(mut self, online: bool) -> Self {
        self.online = online;
        self
    }

    /// Builds a messenger client.
    pub fn build(self) -> Result<MessengerClient, MessengerError> {
        let has_cookie_path = self.cookies_file.is_some();
        let has_shared = self.user_id.is_some() || self.cookies.is_some() || self.http.is_some();

        if !has_cookie_path && !has_shared {
            return Err(MessengerError::Config(
                "cookies_file_path or shared_session must be provided".to_string(),
            ));
        }

        if has_cookie_path && has_shared {
            return Err(MessengerError::Config(
                "cookies_file_path cannot be combined with shared_session".to_string(),
            ));
        }

        if has_shared && (self.user_id.is_none() || self.cookies.is_none() || self.http.is_none()) {
            return Err(MessengerError::Config(
                "shared_session requires user_id, cookie_header, and http client".to_string(),
            ));
        }

        let (events, _) = broadcast::channel(1024);
        let inner = Inner {
            config: MessengerConfig {
                cookies_file_path: self.cookies_file,
                shared_user_id: self.user_id,
                shared_cookie_header: self.cookies,
                shared_http: self.http,
                user_agent: self.user_agent,
                proxy: self.proxy,
                online: self.online,
            },
            state: RwLock::new(None),
            mqtt: Mutex::new(None),
            listener: Mutex::new(None),
            handler: Mutex::new(None),
            events,
            pending: PendingRequests::new(),
            request_id: AtomicU64::new(rand::random::<u64>() & 0x7fff_ffff_ffff_ffff),
            task_id: AtomicU64::new(1),
            #[cfg(feature = "cache")]
            cache: Arc::new(Cache::new()),
        };

        Ok(MessengerClient {
            inner: Arc::new(inner),
        })
    }
}