use serde::{Deserialize, Serialize};
use crate::{Attachment, EmailAddress, FolderId, GrantId, MessageId, ThreadId};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Message {
pub id: MessageId,
pub grant_id: GrantId,
#[serde(skip_serializing_if = "Option::is_none")]
pub thread_id: Option<ThreadId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(default)]
pub from: Vec<EmailAddress>,
#[serde(default)]
pub to: Vec<EmailAddress>,
#[serde(default)]
pub cc: Vec<EmailAddress>,
#[serde(default)]
pub bcc: Vec<EmailAddress>,
#[serde(default)]
pub reply_to: Vec<EmailAddress>,
pub date: i64,
#[serde(skip_serializing_if = "Option::is_none")]
pub unread: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub starred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub snippet: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<String>,
#[serde(default)]
pub attachments: Vec<Attachment>,
#[serde(default)]
pub folders: Vec<FolderId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<i64>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct MessageQueryParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcc: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub any_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub thread_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unread: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub starred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_attachment: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub received_before: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub received_after: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub in_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
}
impl MessageQueryParams {
pub fn builder() -> MessageQueryParamsBuilder {
MessageQueryParamsBuilder::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct MessageQueryParamsBuilder {
params: MessageQueryParams,
}
impl MessageQueryParamsBuilder {
pub fn subject(mut self, subject: impl Into<String>) -> Self {
self.params.subject = Some(subject.into());
self
}
pub fn from(mut self, from: impl Into<String>) -> Self {
self.params.from = Some(from.into());
self
}
pub fn to(mut self, to: impl Into<String>) -> Self {
self.params.to = Some(to.into());
self
}
pub fn cc(mut self, cc: impl Into<String>) -> Self {
self.params.cc = Some(cc.into());
self
}
pub fn bcc(mut self, bcc: impl Into<String>) -> Self {
self.params.bcc = Some(bcc.into());
self
}
pub fn any_email(mut self, email: impl Into<String>) -> Self {
self.params.any_email = Some(email.into());
self
}
pub fn thread_id(mut self, thread_id: impl Into<String>) -> Self {
self.params.thread_id = Some(thread_id.into());
self
}
pub fn unread(mut self, unread: bool) -> Self {
self.params.unread = Some(unread);
self
}
pub fn starred(mut self, starred: bool) -> Self {
self.params.starred = Some(starred);
self
}
pub fn has_attachment(mut self, has_attachment: bool) -> Self {
self.params.has_attachment = Some(has_attachment);
self
}
pub fn received_before(mut self, timestamp: i64) -> Self {
self.params.received_before = Some(timestamp);
self
}
pub fn received_after(mut self, timestamp: i64) -> Self {
self.params.received_after = Some(timestamp);
self
}
pub fn in_folder(mut self, folder_id: impl Into<String>) -> Self {
self.params.in_ = Some(folder_id.into());
self
}
pub fn limit(mut self, limit: u32) -> Self {
self.params.limit = Some(limit);
self
}
pub fn page_token(mut self, token: impl Into<String>) -> Self {
self.params.page_token = Some(token.into());
self
}
pub fn build(self) -> MessageQueryParams {
self.params
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct UpdateMessageRequest {
#[serde(skip_serializing_if = "Option::is_none")]
pub unread: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub starred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folders: Option<Vec<String>>,
}
impl UpdateMessageRequest {
pub fn builder() -> UpdateMessageRequestBuilder {
UpdateMessageRequestBuilder::default()
}
}
#[derive(Debug, Clone, Default)]
pub struct UpdateMessageRequestBuilder {
request: UpdateMessageRequest,
}
impl UpdateMessageRequestBuilder {
pub fn unread(mut self, unread: bool) -> Self {
self.request.unread = Some(unread);
self
}
pub fn starred(mut self, starred: bool) -> Self {
self.request.starred = Some(starred);
self
}
pub fn folders(mut self, folders: Vec<String>) -> Self {
self.request.folders = Some(folders);
self
}
pub fn build(self) -> UpdateMessageRequest {
self.request
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_message_creation() {
let message = Message {
id: MessageId::new("msg_123"),
grant_id: GrantId::new("grant_123"),
thread_id: Some(ThreadId::new("thread_123")),
subject: Some("Test Subject".to_string()),
from: vec![],
to: vec![],
cc: vec![],
bcc: vec![],
reply_to: vec![],
date: 1234567890,
unread: Some(true),
starred: Some(false),
snippet: Some("Test snippet".to_string()),
body: Some("Test body".to_string()),
attachments: vec![],
folders: vec![],
created_at: Some(1234567890),
};
assert_eq!(message.id.as_str(), "msg_123");
assert_eq!(message.grant_id.as_str(), "grant_123");
assert_eq!(message.subject, Some("Test Subject".to_string()));
assert_eq!(message.unread, Some(true));
}
#[test]
fn test_message_serialization() {
let message = Message {
id: MessageId::new("msg_123"),
grant_id: GrantId::new("grant_123"),
thread_id: Some(ThreadId::new("thread_123")),
subject: Some("Test".to_string()),
from: vec![],
to: vec![],
cc: vec![],
bcc: vec![],
reply_to: vec![],
date: 1234567890,
unread: Some(true),
starred: Some(false),
snippet: Some("Snippet".to_string()),
body: Some("Body".to_string()),
attachments: vec![],
folders: vec![],
created_at: Some(1234567890),
};
let json = serde_json::to_string(&message).unwrap();
assert!(json.contains("msg_123"));
assert!(json.contains("grant_123"));
assert!(json.contains("Test"));
let deserialized: Message = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, message);
}
#[test]
fn test_query_params_builder() {
let params = MessageQueryParams::builder()
.subject("invoice")
.unread(true)
.limit(50)
.build();
assert_eq!(params.subject, Some("invoice".to_string()));
assert_eq!(params.unread, Some(true));
assert_eq!(params.limit, Some(50));
}
#[test]
fn test_query_params_all_fields() {
let params = MessageQueryParams::builder()
.subject("test")
.from("sender@example.com")
.to("recipient@example.com")
.cc("cc@example.com")
.bcc("bcc@example.com")
.any_email("any@example.com")
.thread_id("thread_123")
.unread(true)
.starred(false)
.has_attachment(true)
.received_before(2000000000)
.received_after(1000000000)
.in_folder("folder_123")
.limit(100)
.page_token("token_abc")
.build();
assert_eq!(params.subject, Some("test".to_string()));
assert_eq!(params.from, Some("sender@example.com".to_string()));
assert_eq!(params.to, Some("recipient@example.com".to_string()));
assert_eq!(params.limit, Some(100));
assert_eq!(params.page_token, Some("token_abc".to_string()));
}
#[test]
fn test_query_params_serialization() {
let params = MessageQueryParams::builder()
.subject("test")
.unread(true)
.limit(50)
.build();
let json = serde_json::to_string(¶ms).unwrap();
assert!(json.contains("test"));
assert!(json.contains("true"));
assert!(json.contains("50"));
}
#[test]
fn test_update_message_request_builder() {
let update = UpdateMessageRequest::builder()
.unread(false)
.starred(true)
.build();
assert_eq!(update.unread, Some(false));
assert_eq!(update.starred, Some(true));
}
#[test]
fn test_update_message_request_with_folders() {
let update = UpdateMessageRequest::builder()
.unread(false)
.folders(vec!["folder1".to_string(), "folder2".to_string()])
.build();
assert_eq!(update.unread, Some(false));
assert_eq!(
update.folders,
Some(vec!["folder1".to_string(), "folder2".to_string()])
);
}
#[test]
fn test_update_message_request_serialization() {
let update = UpdateMessageRequest::builder()
.unread(false)
.starred(true)
.build();
let json = serde_json::to_string(&update).unwrap();
let deserialized: UpdateMessageRequest = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized, update);
}
}