zulip-api 0.0.8

Client library for interacting with the Zulip API
Documentation
use std::borrow::Cow;

use serde::{Deserialize, Serialize};

use crate::{impl_default, impl_display, Error, Result};

/// Represents the recipient for the Zulip message.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum MessageTo {
    /// Represents the recipient channel ID.
    ChannelId(u64),
    /// Represents the recipient channel name.
    ChannelName(String),
    /// Represents the list of recipient user IDs.
    UserIds(Vec<u64>),
    /// Represents the list of recipient user Zulip API email adddresses.
    UserEmails(Vec<String>),
}

impl MessageTo {
    /// Represents the field name for channel ID.
    pub const CHANNEL_ID_FIELD: &str = "channel_id";
    /// Represents the field name for channel name.
    pub const CHANNEL_NAME_FIELD: &str = "channel_name";
    /// Represents the field name for user IDs.
    pub const USER_IDS_FIELD: &str = "user_ids";
    /// Represents the field name for user emails.
    pub const USER_EMAILS_FIELD: &str = "user_emails";

    /// Creates a new [MessageTo].
    pub const fn new() -> Self {
        Self::ChannelId(0)
    }

    /// Gets the field name of the [Self] variant.
    pub const fn field_name(&self) -> &'static str {
        match self {
            Self::ChannelId(_) => Self::CHANNEL_ID_FIELD,
            Self::ChannelName(_) => Self::CHANNEL_NAME_FIELD,
            Self::UserIds(_) => Self::USER_IDS_FIELD,
            Self::UserEmails(_) => Self::USER_EMAILS_FIELD,
        }
    }

    /// Creates a new channel ID [Self].
    pub const fn channel_id(val: u64) -> Self {
        Self::ChannelId(val)
    }

    /// Attempts to get the channel ID [MessageTo] variant.
    pub fn as_channel_id(&self) -> Result<u64> {
        match self {
            Self::ChannelId(n) => Ok(*n),
            _ => Err(Error::invalid_field(
                "message to",
                self.field_name(),
                Self::CHANNEL_ID_FIELD,
            )),
        }
    }

    /// Creates a new channel name [Self].
    pub fn channel_name<'c, C: Into<Cow<'c, str>>>(val: C) -> Self {
        Self::ChannelName(C::into(val).into())
    }

    /// Attempts to get the channel name [MessageTo] variant.
    pub fn as_channel_name(&self) -> Result<&str> {
        match self {
            Self::ChannelName(n) => Ok(n.as_str()),
            _ => Err(Error::invalid_field(
                "message to",
                self.field_name(),
                Self::CHANNEL_NAME_FIELD,
            )),
        }
    }

    /// Creates a new user IDs [Self].
    pub fn user_ids<'c, C: Into<Cow<'c, [u64]>>>(val: C) -> Self {
        Self::UserIds(C::into(val).into())
    }

    /// Attempts to get the user IDs [MessageTo] variant.
    pub fn as_user_ids(&self) -> Result<&[u64]> {
        match self {
            Self::UserIds(n) => Ok(n.as_ref()),
            _ => Err(Error::invalid_field(
                "message to",
                self.field_name(),
                Self::USER_IDS_FIELD,
            )),
        }
    }

    /// Creates a new user emails [Self].
    pub fn user_emails<'c, C: Into<Cow<'c, [String]>>>(val: C) -> Self {
        Self::UserEmails(C::into(val).into())
    }

    /// Attempts to get the user emails [MessageTo] variant.
    pub fn as_user_emails(&self) -> Result<&[String]> {
        match self {
            Self::UserEmails(n) => Ok(n.as_ref()),
            _ => Err(Error::invalid_field(
                "message to",
                self.field_name(),
                Self::USER_EMAILS_FIELD,
            )),
        }
    }

    pub fn as_string(&self) -> String {
        serde_json::to_string(&self)
            .ok()
            .and_then(|s| {
                s.strip_prefix("\"")
                    .and_then(|v| v.strip_suffix("\"").map(String::from))
            })
            .unwrap_or_default()
    }
}

impl_default!(MessageTo);
impl_display!(MessageTo);

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

    #[test]
    fn test_message_to_channel_name() {
        let chan_name = String::from("#test");
        let chan_name_json = format!(r#""{chan_name}""#);
        let to_chan_name = MessageTo::ChannelName(chan_name.clone());

        assert_eq!(
            serde_json::to_string(&to_chan_name).unwrap().as_str(),
            chan_name_json
        );
        assert_eq!(
            serde_json::from_str::<MessageTo>(chan_name_json.as_str()).unwrap(),
            to_chan_name
        );
        assert_eq!(to_chan_name.as_channel_name(), Ok(chan_name.as_str()));
    }

    #[test]
    fn test_message_to_user_emails() {
        let user_emails = vec![String::from("test@example.com")];
        let user_emails_json = format!(r#"["{}"]"#, user_emails[0]);
        let to_user_emails = MessageTo::UserEmails(user_emails.clone());

        assert_eq!(
            serde_json::to_string(&to_user_emails).unwrap().as_str(),
            user_emails_json
        );
        assert_eq!(
            serde_json::from_str::<MessageTo>(user_emails_json.as_str()).unwrap(),
            to_user_emails
        );
        assert_eq!(to_user_emails.as_user_emails(), Ok(user_emails.as_slice()));
    }

    #[test]
    fn test_message_to_channel_id() {
        (0..=u64::BITS)
            .map(|r| ((1u128 << r) - 1) as u64)
            .for_each(|id| {
                let chan_id_json = format!("{id}");
                let to_chan_id = MessageTo::ChannelId(id);

                assert_eq!(
                    serde_json::to_string(&to_chan_id).unwrap().as_str(),
                    chan_id_json
                );
                assert_eq!(
                    serde_json::from_str::<MessageTo>(chan_id_json.as_str()).unwrap(),
                    to_chan_id
                );
                assert_eq!(to_chan_id.as_channel_id(), Ok(id));
            });
    }

    #[test]
    fn test_message_to_user_ids() {
        (0..=u64::BITS)
            .map(|r| ((1u128 << r) - 1) as u64)
            .for_each(|id| {
                let user_ids = vec![id];
                let user_ids_json = format!("[{id}]");
                let to_user_ids = MessageTo::UserIds(user_ids.clone());

                assert_eq!(
                    serde_json::to_string(&to_user_ids).unwrap().as_str(),
                    user_ids_json
                );
                assert_eq!(
                    serde_json::from_str::<MessageTo>(user_ids_json.as_str()).unwrap(),
                    to_user_ids
                );
                assert_eq!(to_user_ids.as_user_ids(), Ok(user_ids.as_slice()));
            });
    }
}