rustybook-messenger 0.2.0

Messenger client for Rustybook
Documentation
use serde::Deserialize;

use crate::error::MessengerError;

#[derive(Debug, Clone, Deserialize)]
pub struct Cookie {
    pub name: String,
    pub value: String,
}

#[derive(Debug, Clone)]
pub struct AuthCookies {
    pub cookie_header: String,
    pub user_id: String,
}

pub fn load_cookies(path: &str) -> Result<AuthCookies, MessengerError> {
    let content = std::fs::read_to_string(path)?;
    let cookies: Vec<Cookie> = serde_json::from_str(&content)
        .map_err(|error| MessengerError::Auth(format!("failed to parse cookie file: {error}")))?;

    let mut parts = Vec::with_capacity(cookies.len());
    let mut user_id = None;
    let mut has_xs = false;

    for cookie in &cookies {
        if cookie.name.is_empty() {
            continue;
        }

        parts.push(format!("{}={}", cookie.name, cookie.value));
        if cookie.name == "c_user" {
            user_id = Some(cookie.value.clone());
        }

        if cookie.name == "xs" {
            has_xs = true;
        }
    }

    let Some(user_id) = user_id else {
        return Err(MessengerError::Auth(
            "missing c_user cookie in cookie file".to_string(),
        ));
    };

    if !has_xs {
        return Err(MessengerError::Auth(
            "missing xs cookie in cookie file".to_string(),
        ));
    }

    Ok(AuthCookies {
        cookie_header: parts.join("; "),
        user_id,
    })
}

#[cfg(test)]
mod tests {
    use std::time::{
        SystemTime,
        UNIX_EPOCH,
    };

    use super::load_cookies;

    fn write_temp(content: &str) -> String {
        let timestamp = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .expect("time went backwards")
            .as_nanos();
        let path =
            std::env::temp_dir().join(format!("rustybook-messenger-cookies-{timestamp}.json"));
        std::fs::write(&path, content).expect("failed to write cookie fixture");
        path.display().to_string()
    }

    #[test]
    fn loads_valid_cookie_file() {
        let path = write_temp(
            r#"[
                {"name":"c_user","value":"123"},
                {"name":"xs","value":"abcd"},
                {"name":"datr","value":"test"}
            ]"#,
        );

        let loaded = load_cookies(&path).expect("expected valid cookie file");
        assert_eq!(loaded.user_id, "123");
        assert!(loaded.cookie_header.contains("xs=abcd"));
    }

    #[test]
    fn fails_when_c_user_missing() {
        let path = write_temp(r#"[{"name":"xs","value":"abcd"}]"#);
        let result = load_cookies(&path);
        assert!(result.is_err());
    }

    #[test]
    fn fails_when_xs_missing() {
        let path = write_temp(r#"[{"name":"c_user","value":"123"}]"#);
        let result = load_cookies(&path);
        assert!(result.is_err());
    }
}