zulip-api 0.0.8

Client library for interacting with the Zulip API
Documentation
use serde::{Deserialize, Serialize};

use crate::message::channel_permission::*;
use crate::{field_access, StreamPostPolicy, TopicsPolicy};

/// Represents basic details about the channel.
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct BasicChannel {
    #[serde(skip_serializing_if = "Option::is_none")]
    stream_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    is_archived: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    date_created: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    creator_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    invite_only: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    rendered_description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    is_web_public: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    stream_post_policy: Option<StreamPostPolicy>,
    #[serde(skip_serializing_if = "Option::is_none")]
    message_retention_days: Option<i64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    history_public_to_subscribers: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    topics_policy: Option<TopicsPolicy>,
    #[serde(skip_serializing_if = "Option::is_none")]
    first_message_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    folder_id: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    is_recently_active: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    is_announcement_only: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_add_subscribers_group: Option<CanAddSubscribersGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_remove_subscribers_group: Option<CanRemoveSubscribersGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_administer_channel_group: Option<CanAdministerChannelGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_delete_any_message_group: Option<CanDeleteAnyMessageGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_delete_own_message_group: Option<CanDeleteOwnMessageGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_move_messages_out_of_channel_group: Option<CanMoveMessagesOutOfChannelGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_move_messages_within_channel_group: Option<CanMoveMessagesWithinChannelGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_send_message_group: Option<CanSendMessageGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_subscribe_group: Option<CanSubscribeGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    can_resolve_topics_group: Option<CanResolveTopicsGroup>,
    #[serde(skip_serializing_if = "Option::is_none")]
    subscriber_count: Option<f64>,
}

impl BasicChannel {
    /// Creates a new [BasicChannel].
    pub const fn new() -> Self {
        Self {
            stream_id: None,
            name: None,
            is_archived: None,
            description: None,
            date_created: None,
            creator_id: None,
            invite_only: None,
            rendered_description: None,
            is_web_public: None,
            stream_post_policy: None,
            message_retention_days: None,
            history_public_to_subscribers: None,
            topics_policy: None,
            first_message_id: None,
            folder_id: None,
            is_recently_active: None,
            is_announcement_only: None,
            can_add_subscribers_group: None,
            can_remove_subscribers_group: None,
            can_administer_channel_group: None,
            can_delete_any_message_group: None,
            can_delete_own_message_group: None,
            can_move_messages_out_of_channel_group: None,
            can_move_messages_within_channel_group: None,
            can_send_message_group: None,
            can_subscribe_group: None,
            can_resolve_topics_group: None,
            subscriber_count: None,
        }
    }
}

field_access! {
    BasicChannel {
        /// The unique ID of the channel.
        stream_id: option { u64 },
        /// A boolean indicating whether the channel is [archived](https://zulip.com/help/archive-a-channel).
        ///
        /// **Changes**: New in Zulip 10.0 (feature level 315). Previously, this endpoint never returned archived channels.
        is_archived: option { bool },
        /// The UNIX timestamp for when the channel was created, in UTC seconds.
        ///
        /// **Changes**: New in Zulip 4.0 (feature level 30).
        date_created: option { u64 },
        /// The ID of the user who created this channel.
        ///
        /// A `null` value means the channel has no recorded creator, which is often because the channel is very old,
        /// was created during realm creation or because it was created via a data import tool or [management command](https://zulip.readthedocs.io/en/latest/production/management-commands.html).
        ///
        /// **Changes**: New in Zulip 9.0 (feature level 254).
        creator_id: option { u64 },
        /// Specifies whether the channel is private or not.
        ///
        /// Only people who have been invited can access a private channel.
        invite_only: option { bool },
        /// Whether the channel has been configured to allow unauthenticated access to its message history from the web.
        ///
        /// **Changes**: New in Zulip 2.1.0.
        is_web_public: option { bool },
        /// A deprecated representation of a superset of the users who have permission to post messages to the channel available for backwards-compatibility.
        stream_post_policy: option { StreamPostPolicy },
        /// Number of days that messages sent to this channel will be stored before being automatically deleted by the [message retention policy](https://zulip.com/help/message-retention-policy).
        ///
        /// There are two special values:
        ///
        /// - `null`, the default, means the channel will inherit the organization level setting.
        /// - `-1` encodes retaining messages in this channel forever.
        ///
        /// **Changes**: New in Zulip 3.0 (feature level 17).
        message_retention_days: option { i64 },
        /// Whether the history of the channel is public to its subscribers.
        ///
        /// Currently always true for public channels (i.e. `\"invite_only\": false` implies `\"history_public_to_subscribers\": true`), but clients should not make that assumption,
        /// as we may change that behavior in the future.
        history_public_to_subscribers: option { bool },
        /// Whether [named topics](https://zulip.com/help/introduction-to-topics) and the empty topic
        /// (i.e., ["general chat" topic](https://zulip.com/help/general-chat-topic)) are enabled in this channel.
        topics_policy: option { TopicsPolicy },
        /// The ID of the first message in the channel.
        ///
        /// Intended to help clients determine whether they need to display UI like the \"show all topics\" widget that would suggest the channel has older history that can be accessed.
        ///
        /// Is `null` for channels with no message history.
        ///
        /// **Changes**: New in Zulip 2.1.0.
        first_message_id: option { u64 },
        /// The ID of the folder to which the channel belongs.
        ///
        /// Is `null` if channel does not belong to any folder.
        ///
        /// **Changes**: New in Zulip 11.0 (feature level 389).
        folder_id: option { u64 },
        /// Whether the channel has recent message activity.
        ///
        /// Clients should use this to implement [hide inactive channels](https://zulip.com/help/manage-inactive-channels) if `demote_inactive_streams` is enabled.
        ///
        /// **Changes**: New in Zulip 10.0 (feature level 323). Previously, clients implemented the demote_inactive_streams from local message history, resulting in a choppy loading experience.
        is_recently_active: option { bool },
        /// Whether the given channel is announcement only or not.
        ///
        /// **Changes**: Deprecated in Zulip 3.0 (feature level 1). Clients should use `stream_post_policy` instead.
        is_announcement_only: option { bool },
        /// The total number of non-deactivated users (including bots) who are subscribed to the channel.
        ///
        /// Clients are responsible for updating this value using `peer_add` and `peer_remove` events.
        ///
        /// The server's internals cannot guarantee this value is correctly synced with `peer_add` and `peer_remove` events for the channel.
        /// As a result, if a (rare) race occurs between a change in the channel's subscribers and fetching this value, it is possible for a client that is correctly following the events
        /// protocol to end up with a permanently off-by-one error in the channel's subscriber count.
        ///
        /// Clients are recommended to fetch full subscriber data for a channel in contexts where it is important to avoid this risk.
        /// The official web application, for example, uses this field primarily while waiting to fetch a given channel's full subscriber list from the server.
        ///
        /// **Changes**: New in Zulip 11.0 (feature level 394).
        subscriber_count: option { f64 },
    }
}

field_access! {
    BasicChannel {
        /// The name of the channel.
        name: option_cow_deref { str, String },
        /// The short description of the channel in [Zulip-flavored Markdown](https://zulip.com/help/format-your-message-using-markdown) format, intended to be used to prepopulate UI for editing a channel's description.
        ///
        /// See [Markdown message formatting](https://zulip.com/api/message-formatting) for details on Zulip's HTML format.
        description: option_cow_deref { str, String },
        /// The short description of the channel rendered as HTML, intended to be used when displaying the channel description in a UI.
        ///
        /// One should use the standard Zulip rendered_markdown CSS when displaying this content so that emoji, LaTeX, and other syntax work correctly.
        /// And any client-side security logic for user-generated message content should be applied when displaying this HTML as though it were the body of a Zulip message.
        rendered_description: option_cow_deref { str, String },
    }
}

field_access! {
    BasicChannel {
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to add subscribers to this channel.
        can_add_subscribers_group: option_cow_ref { CanAddSubscribersGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to remove subscribers from this channel.
        can_remove_subscribers_group: option_cow_ref { CanRemoveSubscribersGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to administer this channel.
        can_administer_channel_group: option_cow_ref { CanAdministerChannelGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to delete any message in the channel.
        can_delete_any_message_group: option_cow_ref { CanDeleteAnyMessageGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to delete the messages that they have sent in the channel.
        can_delete_own_message_group: option_cow_ref { CanDeleteOwnMessageGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to move messages out of this channel.
        can_move_messages_out_of_channel_group: option_cow_ref { CanMoveMessagesOutOfChannelGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to move messages within this channel.
        can_move_messages_within_channel_group: option_cow_ref { CanMoveMessagesWithinChannelGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to post in this channel.
        can_send_message_group: option_cow_ref { CanSendMessageGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to subscribe themselves to this channel.
        can_subscribe_group: option_cow_ref { CanSubscribeGroup },
        /// A [group-setting value](https://zulip.com/api/group-setting-values) defining the set of users
        /// who have permission to resolve topics in the channel.
        can_resolve_topics_group: option_cow_ref { CanResolveTopicsGroup },
    }
}

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

impl std::fmt::Display for BasicChannel {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        serde_json::to_string(self)
            .map_err(|_| std::fmt::Error)
            .and_then(|s| write!(f, "{s}"))
    }
}

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

    #[test]
    fn test_basic_channel() {
        let stream_id = 1;
        let name = "test_name";
        let is_archived = false;
        let description = "test_description";
        let date_created = 2;
        let creator_id = 3;
        let invite_only = true;
        let rendered_description = "<p>test</p>";
        let is_web_public = true;
        let stream_post_policy = StreamPostPolicy::new();
        let message_retention_days = -1;
        let history_public_to_subscribers = true;
        let topics_policy = TopicsPolicy::new();
        let first_message_id = 4;
        let folder_id = 5;
        let is_recently_active = true;
        let is_announcement_only = false;
        let can_add_subscribers_group = CanAddSubscribersGroup::new();
        let can_remove_subscribers_group = CanRemoveSubscribersGroup::new();
        let can_administer_channel_group = CanAdministerChannelGroup::new();
        let can_delete_any_message_group = CanDeleteAnyMessageGroup::new();
        let can_delete_own_message_group = CanDeleteOwnMessageGroup::new();
        let can_move_messages_out_of_channel_group = CanMoveMessagesOutOfChannelGroup::new();
        let can_move_messages_within_channel_group = CanMoveMessagesWithinChannelGroup::new();
        let can_send_message_group = CanSendMessageGroup::new();
        let can_subscribe_group = CanSubscribeGroup::new();
        let can_resolve_topics_group = CanResolveTopicsGroup::new();
        let subscriber_count = 1.0f64;

        let basic_channel = BasicChannel::new()
            .with_stream_id(stream_id)
            .with_name(name)
            .with_is_archived(is_archived)
            .with_description(description)
            .with_date_created(date_created)
            .with_creator_id(creator_id)
            .with_invite_only(invite_only)
            .with_rendered_description(rendered_description)
            .with_is_web_public(is_web_public)
            .with_stream_post_policy(stream_post_policy)
            .with_message_retention_days(message_retention_days)
            .with_history_public_to_subscribers(history_public_to_subscribers)
            .with_topics_policy(topics_policy)
            .with_first_message_id(first_message_id)
            .with_folder_id(folder_id)
            .with_is_recently_active(is_recently_active)
            .with_is_announcement_only(is_announcement_only)
            .with_can_add_subscribers_group(&can_add_subscribers_group)
            .with_can_remove_subscribers_group(&can_remove_subscribers_group)
            .with_can_administer_channel_group(&can_administer_channel_group)
            .with_can_delete_any_message_group(&can_delete_any_message_group)
            .with_can_delete_own_message_group(&can_delete_own_message_group)
            .with_can_move_messages_out_of_channel_group(&can_move_messages_out_of_channel_group)
            .with_can_move_messages_within_channel_group(&can_move_messages_within_channel_group)
            .with_can_send_message_group(&can_send_message_group)
            .with_can_subscribe_group(&can_subscribe_group)
            .with_can_resolve_topics_group(&can_resolve_topics_group)
            .with_subscriber_count(subscriber_count);
        let basic_channel_json = basic_channel.to_string();

        assert_eq!(
            serde_json::to_string(&basic_channel).unwrap(),
            basic_channel_json
        );
        assert_eq!(
            serde_json::from_str::<BasicChannel>(&basic_channel_json).unwrap(),
            basic_channel
        );

        assert_eq!(basic_channel.stream_id(), Some(stream_id));
        assert_eq!(basic_channel.name(), Some(name));
        assert_eq!(basic_channel.is_archived(), Some(is_archived));
        assert_eq!(basic_channel.description(), Some(description));
        assert_eq!(basic_channel.date_created(), Some(date_created));
        assert_eq!(basic_channel.creator_id(), Some(creator_id));
        assert_eq!(basic_channel.invite_only(), Some(invite_only));
        assert_eq!(
            basic_channel.rendered_description(),
            Some(rendered_description)
        );
        assert_eq!(basic_channel.is_web_public(), Some(is_web_public));
        assert_eq!(basic_channel.stream_post_policy(), Some(stream_post_policy));
        assert_eq!(
            basic_channel.message_retention_days(),
            Some(message_retention_days)
        );
        assert_eq!(
            basic_channel.history_public_to_subscribers(),
            Some(history_public_to_subscribers)
        );
        assert_eq!(basic_channel.topics_policy(), Some(topics_policy));
        assert_eq!(basic_channel.first_message_id(), Some(first_message_id));
        assert_eq!(basic_channel.folder_id(), Some(folder_id));
        assert_eq!(basic_channel.is_recently_active(), Some(is_recently_active));
        assert_eq!(
            basic_channel.is_announcement_only(),
            Some(is_announcement_only)
        );
        assert_eq!(
            basic_channel.can_add_subscribers_group(),
            Some(&can_add_subscribers_group)
        );
        assert_eq!(
            basic_channel.can_remove_subscribers_group(),
            Some(&can_remove_subscribers_group)
        );
        assert_eq!(
            basic_channel.can_administer_channel_group(),
            Some(&can_administer_channel_group)
        );
        assert_eq!(
            basic_channel.can_delete_any_message_group(),
            Some(&can_delete_any_message_group)
        );
        assert_eq!(
            basic_channel.can_delete_own_message_group(),
            Some(&can_delete_own_message_group)
        );
        assert_eq!(
            basic_channel.can_move_messages_out_of_channel_group(),
            Some(&can_move_messages_out_of_channel_group)
        );
        assert_eq!(
            basic_channel.can_move_messages_within_channel_group(),
            Some(&can_move_messages_within_channel_group)
        );
        assert_eq!(
            basic_channel.can_send_message_group(),
            Some(&can_send_message_group)
        );
        assert_eq!(
            basic_channel.can_subscribe_group(),
            Some(&can_subscribe_group)
        );
        assert_eq!(
            basic_channel.can_resolve_topics_group(),
            Some(&can_resolve_topics_group)
        );
        assert_eq!(basic_channel.subscriber_count(), Some(subscriber_count));
    }
}