Skip to main content

filthy_rich/types/
payloads.rs

1//! Payload types which are used for immutable sends and receives. These do not need to be imported separately as the
2//! values are often already supplied to you via closures, and are clonable / can be debugged.
3//!
4use serde::{Deserialize, Serialize, ser::SerializeStruct};
5
6use crate::{
7    ds,
8    errors::InnerParsingError,
9    str,
10    types::{ActivitySpec, ActivityType, StatusDisplayType},
11    utils::get_current_timestamp,
12};
13
14/// The assets payload for the activity. Usually contains the large image and the small image, and their respective
15/// subsets of information (i.e. URL and text).
16#[derive(Debug, Deserialize, Clone)]
17pub struct AssetsPayload {
18    pub(crate) large_image: Option<String>,
19    pub(crate) large_url: Option<String>,
20    pub(crate) large_text: Option<String>,
21    pub(crate) small_image: Option<String>,
22    pub(crate) small_text: Option<String>,
23    pub(crate) small_url: Option<String>,
24}
25
26impl AssetsPayload {
27    ds!(large_image, "The key for the large image.");
28    ds!(large_url, "The URL for the large image.");
29    ds!(large_text, "The hover text for the large image.");
30    ds!(small_image, "The key for the small image.");
31    ds!(small_url, "The URL for the small image.");
32    ds!(small_text, "The hover text for the small image.");
33}
34
35// redundant: already filtered during [`crate::types::ActivityBuilder::build`]
36impl Serialize for AssetsPayload {
37    fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
38    where
39        S: serde::Serializer,
40    {
41        let mut state = serializer.serialize_struct("AssetsPayload", 6)?;
42
43        if let Some(v) = &self.large_image {
44            state.serialize_field("large_image", v)?;
45
46            if let Some(v) = &self.large_text {
47                state.serialize_field("large_text", v)?;
48            }
49            if let Some(v) = &self.large_url {
50                state.serialize_field("large_url", v)?;
51            }
52        }
53
54        if let Some(v) = &self.small_image {
55            state.serialize_field("small_image", v)?;
56
57            if let Some(v) = &self.small_text {
58                state.serialize_field("small_text", v)?;
59            }
60            if let Some(v) = &self.small_url {
61                state.serialize_field("small_url", v)?;
62            }
63        }
64
65        state.end()
66    }
67}
68
69/// Represents a button of the activity.
70#[derive(Debug, Serialize, Deserialize, Clone)]
71pub struct ButtonPayload {
72    pub(crate) label: String,
73    pub(crate) url: String,
74}
75
76impl ButtonPayload {
77    str!(label, "The label for the button");
78    str!(url, "The URL the button redirects to when clicked on.");
79}
80
81/// The timestamps (calculated with `UNIX_EPOCH`) for the activity.
82#[derive(Debug, Serialize, Deserialize, Clone)]
83pub struct TimestampPayload {
84    start: u64,
85    #[serde(skip_serializing_if = "Option::is_none")]
86    end: Option<u64>,
87}
88
89impl TimestampPayload {
90    /// The starting timestamp for the activity.
91    #[must_use]
92    pub fn start(&self) -> u64 {
93        self.start
94    }
95    /// The end timestamp for the activity.
96    #[must_use]
97    pub fn end(&self) -> Option<u64> {
98        self.end
99    }
100}
101
102/// Represents an immutable activity which has been sent.
103#[derive(Debug, Clone, Serialize, Deserialize)]
104pub struct ActivityPayload {
105    #[serde(skip_serializing_if = "Option::is_none")]
106    name: Option<String>,
107    #[serde(skip_serializing_if = "Option::is_none")]
108    r#type: Option<ActivityType>,
109    created_at: u64,
110    #[serde(skip_serializing_if = "Option::is_none")]
111    instance: Option<bool>,
112    #[serde(skip_serializing_if = "Option::is_none")]
113    status_display_type: Option<StatusDisplayType>,
114    #[serde(skip_serializing_if = "Option::is_none")]
115    details: Option<String>,
116    #[serde(skip_serializing_if = "Option::is_none")]
117    details_url: Option<String>,
118    #[serde(skip_serializing_if = "Option::is_none")]
119    state: Option<String>,
120    #[serde(skip_serializing_if = "Option::is_none")]
121    state_url: Option<String>,
122    timestamps: TimestampPayload,
123    #[serde(skip_serializing_if = "Option::is_none")]
124    assets: Option<AssetsPayload>,
125    #[serde(skip_serializing_if = "Option::is_none")]
126    buttons: Option<Vec<ButtonPayload>>,
127}
128
129impl ActivityPayload {
130    ds!(name, "The name for the activity.");
131
132    /// The type of the activity.
133    #[must_use]
134    pub fn activity_type(&self) -> Option<ActivityType> {
135        self.r#type
136    }
137    /// When the activity was created.
138    #[must_use]
139    pub fn created_at(&self) -> u64 {
140        self.created_at
141    }
142    /// Whether or not the activity is an instance.
143    #[must_use]
144    pub fn instance(&self) -> Option<bool> {
145        self.instance
146    }
147    /// Which element the activity displays as the status (primary text on members banner).
148    #[must_use]
149    pub fn status_display_type(&self) -> Option<StatusDisplayType> {
150        self.status_display_type
151    }
152
153    ds!(details, "The details (top text) for the activity.");
154    ds!(
155        details_url,
156        "The URL which the details field redirects to when clicked on."
157    );
158    ds!(
159        state,
160        "The state (usually the bottom text but could be on top) for the activity."
161    );
162    ds!(
163        state_url,
164        "The URL which the state redirects to when clicked on."
165    );
166
167    /// The timestamp payload object for the activity, containing the start and optionally the end timestamps.
168    #[must_use]
169    pub fn timestamps(&self) -> &TimestampPayload {
170        &self.timestamps
171    }
172    /// The assets payload object for the activity, containing all the detials of the
173    /// assets which were optionally sent with the activity.
174    #[must_use]
175    pub fn assets(&self) -> Option<&AssetsPayload> {
176        self.assets.as_ref()
177    }
178    /// The buttons for the activity. Each button is represented by a [`ButtonPayload`] object instance, and returned as a borrowed
179    /// vector of multiple [`ButtonPayload`] objects through this function.
180    #[must_use]
181    pub fn buttons(&self) -> Option<&Vec<ButtonPayload>> {
182        self.buttons.as_ref()
183    }
184
185    pub(crate) fn create(
186        value: ActivitySpec,
187        session_start: u64,
188    ) -> Result<Self, InnerParsingError> {
189        let current_t = get_current_timestamp()?;
190        let end_timestamp = value.duration.map(|d| current_t + d.as_secs());
191
192        Ok(Self {
193            name: value.name,
194            r#type: value.r#type,
195            created_at: current_t,
196            instance: value.instance,
197            status_display_type: value.status_display_type,
198            details: value.details,
199            details_url: value.details_url,
200            state: value.state,
201            state_url: value.state_url,
202            timestamps: TimestampPayload {
203                start: session_start,
204                end: end_timestamp,
205            },
206            assets: value.assets,
207            buttons: value.buttons,
208        })
209    }
210}