files_sdk/messages/
messages.rs

1//! Message operations
2//!
3//! Messages are part of Files.com's project management features,
4//! representing messages posted by users to projects.
5
6use crate::{FilesClient, PaginationInfo, Result};
7use serde::{Deserialize, Serialize};
8use serde_json::json;
9
10/// A Message Comment entity
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct MessageCommentEntity {
13    /// Comment ID
14    #[serde(skip_serializing_if = "Option::is_none")]
15    pub id: Option<i64>,
16
17    /// Comment body
18    #[serde(skip_serializing_if = "Option::is_none")]
19    pub body: Option<String>,
20
21    /// Reactions to this comment
22    #[serde(skip_serializing_if = "Option::is_none")]
23    pub reactions: Option<Vec<serde_json::Value>>,
24}
25
26/// A Message entity
27#[derive(Debug, Clone, Serialize, Deserialize)]
28pub struct MessageEntity {
29    /// Message ID
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub id: Option<i64>,
32
33    /// Message subject
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub subject: Option<String>,
36
37    /// Message body
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub body: Option<String>,
40
41    /// Comments on this message
42    #[serde(skip_serializing_if = "Option::is_none")]
43    pub comments: Option<Vec<MessageCommentEntity>>,
44}
45
46/// Handler for message operations
47pub struct MessageHandler {
48    client: FilesClient,
49}
50
51impl MessageHandler {
52    /// Create a new message handler
53    pub fn new(client: FilesClient) -> Self {
54        Self { client }
55    }
56
57    /// List messages
58    ///
59    /// # Arguments
60    /// * `cursor` - Pagination cursor
61    /// * `per_page` - Results per page
62    /// * `project_id` - Filter by project ID
63    ///
64    /// # Returns
65    /// Tuple of (messages, pagination_info)
66    ///
67    /// # Example
68    /// ```no_run
69    /// use files_sdk::{FilesClient, MessageHandler};
70    ///
71    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
72    /// let client = FilesClient::builder().api_key("key").build()?;
73    /// let handler = MessageHandler::new(client);
74    /// let (messages, _) = handler.list(None, None, Some(1)).await?;
75    /// # Ok(())
76    /// # }
77    /// ```
78    pub async fn list(
79        &self,
80        cursor: Option<&str>,
81        per_page: Option<i64>,
82        project_id: Option<i64>,
83    ) -> Result<(Vec<MessageEntity>, PaginationInfo)> {
84        let mut params = vec![];
85        if let Some(c) = cursor {
86            params.push(("cursor", c.to_string()));
87        }
88        if let Some(pp) = per_page {
89            params.push(("per_page", pp.to_string()));
90        }
91        if let Some(pid) = project_id {
92            params.push(("project_id", pid.to_string()));
93        }
94
95        let query = if params.is_empty() {
96            String::new()
97        } else {
98            format!(
99                "?{}",
100                params
101                    .iter()
102                    .map(|(k, v)| format!("{}={}", k, v))
103                    .collect::<Vec<_>>()
104                    .join("&")
105            )
106        };
107
108        let response = self.client.get_raw(&format!("/messages{}", query)).await?;
109        let messages: Vec<MessageEntity> = serde_json::from_value(response)?;
110
111        let pagination = PaginationInfo {
112            cursor_next: None,
113            cursor_prev: None,
114        };
115
116        Ok((messages, pagination))
117    }
118
119    /// Get a specific message
120    ///
121    /// # Arguments
122    /// * `id` - Message ID
123    ///
124    /// # Returns
125    /// The message entity
126    pub async fn get(&self, id: i64) -> Result<MessageEntity> {
127        let response = self.client.get_raw(&format!("/messages/{}", id)).await?;
128        Ok(serde_json::from_value(response)?)
129    }
130
131    /// Create a new message
132    ///
133    /// # Arguments
134    /// * `project_id` - Project ID (required)
135    /// * `subject` - Message subject (required)
136    /// * `body` - Message body (required)
137    ///
138    /// # Returns
139    /// The created message
140    ///
141    /// # Example
142    /// ```no_run
143    /// use files_sdk::{FilesClient, MessageHandler};
144    ///
145    /// # async fn example() -> Result<(), Box<dyn std::error::Error>> {
146    /// let client = FilesClient::builder().api_key("key").build()?;
147    /// let handler = MessageHandler::new(client);
148    /// let message = handler.create(
149    ///     1,
150    ///     "Project Update",
151    ///     "Here's the latest status..."
152    /// ).await?;
153    /// # Ok(())
154    /// # }
155    /// ```
156    pub async fn create(
157        &self,
158        project_id: i64,
159        subject: &str,
160        body: &str,
161    ) -> Result<MessageEntity> {
162        let request_body = json!({
163            "project_id": project_id,
164            "subject": subject,
165            "body": body,
166        });
167
168        let response = self.client.post_raw("/messages", request_body).await?;
169        Ok(serde_json::from_value(response)?)
170    }
171
172    /// Update a message
173    ///
174    /// # Arguments
175    /// * `id` - Message ID
176    /// * `subject` - New subject (optional)
177    /// * `body` - New body (optional)
178    ///
179    /// # Returns
180    /// The updated message
181    pub async fn update(
182        &self,
183        id: i64,
184        subject: Option<&str>,
185        body: Option<&str>,
186    ) -> Result<MessageEntity> {
187        let mut request_body = json!({});
188
189        if let Some(s) = subject {
190            request_body["subject"] = json!(s);
191        }
192        if let Some(b) = body {
193            request_body["body"] = json!(b);
194        }
195
196        let response = self
197            .client
198            .patch_raw(&format!("/messages/{}", id), request_body)
199            .await?;
200        Ok(serde_json::from_value(response)?)
201    }
202
203    /// Delete a message
204    ///
205    /// # Arguments
206    /// * `id` - Message ID
207    pub async fn delete(&self, id: i64) -> Result<()> {
208        self.client.delete_raw(&format!("/messages/{}", id)).await?;
209        Ok(())
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216
217    #[test]
218    fn test_handler_creation() {
219        let client = FilesClient::builder().api_key("test-key").build().unwrap();
220        let _handler = MessageHandler::new(client);
221    }
222}