use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UserIdType {
#[serde(rename = "open_id")]
OpenId,
#[serde(rename = "user_id")]
UserId,
#[serde(rename = "union_id")]
UnionId,
}
impl UserIdType {
pub fn as_str(&self) -> &str {
match self {
UserIdType::OpenId => "open_id",
UserIdType::UserId => "user_id",
UserIdType::UnionId => "union_id",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Folder {
#[serde(skip_serializing_if = "Option::is_none")]
pub folder_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_folder_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder_type: Option<FolderType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder_path: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum FolderType {
#[serde(rename = "system")]
System,
#[serde(rename = "custom")]
Custom,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Message {
#[serde(skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subject: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<MailAddress>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<Vec<MailAddress>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cc: Option<Vec<MailAddress>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub bcc: Option<Vec<MailAddress>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub body: Option<MailBody>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sent_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_read: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<MessageStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attachments: Option<Vec<Attachment>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailAddress {
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailBody {
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub html: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MessageStatus {
#[serde(rename = "draft")]
Draft,
#[serde(rename = "sent")]
Sent,
#[serde(rename = "received")]
Received,
#[serde(rename = "deleted")]
Deleted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Attachment {
#[serde(skip_serializing_if = "Option::is_none")]
pub attachment_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub content_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub download_url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Rule {
#[serde(skip_serializing_if = "Option::is_none")]
pub rule_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rule_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enabled: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conditions: Option<Vec<RuleCondition>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub actions: Option<Vec<RuleAction>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuleCondition {
#[serde(skip_serializing_if = "Option::is_none")]
pub field: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub operator: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RuleAction {
#[serde(skip_serializing_if = "Option::is_none")]
pub action_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub target_folder_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mark_as_read: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub forward_to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Contact {
#[serde(skip_serializing_if = "Option::is_none")]
pub contact_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub remark: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailGroup {
#[serde(skip_serializing_if = "Option::is_none")]
pub mailgroup_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub allow_external_send: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission: Option<MailGroupPermission>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailGroupPermission {
#[serde(skip_serializing_if = "Option::is_none")]
pub join_permission: Option<JoinPermission>,
#[serde(skip_serializing_if = "Option::is_none")]
pub send_permission: Option<SendPermission>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum JoinPermission {
#[serde(rename = "all")]
All,
#[serde(rename = "admin_only")]
AdminOnly,
#[serde(rename = "invite_only")]
InviteOnly,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SendPermission {
#[serde(rename = "all")]
All,
#[serde(rename = "members_only")]
MembersOnly,
#[serde(rename = "admin_only")]
AdminOnly,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailGroupMember {
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub member_type: Option<MemberType>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MemberType {
#[serde(rename = "member")]
Member,
#[serde(rename = "admin")]
Admin,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailGroupAlias {
#[serde(skip_serializing_if = "Option::is_none")]
pub alias: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicMailbox {
#[serde(skip_serializing_if = "Option::is_none")]
pub public_mailbox_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<PublicMailboxStatus>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PublicMailboxStatus {
#[serde(rename = "active")]
Active,
#[serde(rename = "deleted")]
Deleted,
#[serde(rename = "in_recycle_bin")]
InRecycleBin,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PublicMailboxMember {
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission_type: Option<PublicMailboxPermissionType>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PublicMailboxPermissionType {
#[serde(rename = "admin")]
Admin,
#[serde(rename = "member")]
Member,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserMailboxAlias {
#[serde(skip_serializing_if = "Option::is_none")]
pub alias_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alias_email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_primary: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AddressStatus {
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<EmailStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mailbox_type: Option<MailboxType>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmailStatus {
#[serde(rename = "exists")]
Exists,
#[serde(rename = "not_exists")]
NotExists,
#[serde(rename = "deleted")]
Deleted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MailboxType {
#[serde(rename = "user")]
User,
#[serde(rename = "group")]
Group,
#[serde(rename = "public")]
Public,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SubscriptionStatus {
#[serde(skip_serializing_if = "Option::is_none")]
pub is_subscribed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_time: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct MailboxSubscription {
#[serde(skip_serializing_if = "Option::is_none")]
pub subscription_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mailbox_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub webhook_url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub event_types: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscription_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_subscribed: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_time: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscription_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub update_time: Option<String>,
}
#[cfg(test)]
#[allow(unused_variables, unused_unsafe)]
mod tests {
use super::*;
use serde_json;
#[test]
fn test_user_id_type_as_str() {
assert_eq!(UserIdType::OpenId.as_str(), "open_id");
assert_eq!(UserIdType::UserId.as_str(), "user_id");
assert_eq!(UserIdType::UnionId.as_str(), "union_id");
}
#[test]
fn test_user_id_type_serialization() {
let open_id = UserIdType::OpenId;
let json = serde_json::to_string(&open_id).unwrap();
assert_eq!(json, "\"open_id\"");
let user_id = UserIdType::UserId;
let json = serde_json::to_string(&user_id).unwrap();
assert_eq!(json, "\"user_id\"");
let union_id = UserIdType::UnionId;
let json = serde_json::to_string(&union_id).unwrap();
assert_eq!(json, "\"union_id\"");
}
#[test]
fn test_folder_type_serialization() {
let system = FolderType::System;
let json = serde_json::to_string(&system).unwrap();
assert_eq!(json, "\"system\"");
let custom = FolderType::Custom;
let json = serde_json::to_string(&custom).unwrap();
assert_eq!(json, "\"custom\"");
}
#[test]
fn test_folder_complete() {
let folder = Folder {
folder_id: Some("folder123".to_string()),
folder_name: Some("Inbox".to_string()),
parent_folder_id: Some("root".to_string()),
folder_type: Some(FolderType::System),
folder_path: Some("/inbox".to_string()),
};
let json = serde_json::to_string(&folder).unwrap();
assert!(json.contains("folder123"));
assert!(json.contains("Inbox"));
assert!(json.contains("system"));
}
#[test]
fn test_folder_optional_fields() {
let folder = Folder {
folder_id: Some("folder456".to_string()),
folder_name: None,
parent_folder_id: None,
folder_type: None,
folder_path: None,
};
let json = serde_json::to_string(&folder).unwrap();
assert!(json.contains("folder456"));
assert!(!json.contains("folder_name"));
assert!(!json.contains("parent_folder_id"));
}
#[test]
fn test_mail_address_complete() {
let address = MailAddress {
name: Some("John Doe".to_string()),
email: Some("john.doe@example.com".to_string()),
};
let json = serde_json::to_string(&address).unwrap();
assert!(json.contains("John Doe"));
assert!(json.contains("john.doe@example.com"));
}
#[test]
fn test_mail_body_types() {
let html_body = MailBody {
text: None,
html: Some("<p>HTML content</p>".to_string()),
};
let json = serde_json::to_string(&html_body).unwrap();
assert!(json.contains("html"));
assert!(json.contains("<p>HTML content</p>"));
let text_body = MailBody {
text: Some("Plain text content".to_string()),
html: None,
};
let json = serde_json::to_string(&text_body).unwrap();
assert!(json.contains("text"));
assert!(json.contains("Plain text content"));
}
#[test]
fn test_message_status_serialization() {
let draft = MessageStatus::Draft;
let json = serde_json::to_string(&draft).unwrap();
assert_eq!(json, "\"draft\"");
let sent = MessageStatus::Sent;
let json = serde_json::to_string(&sent).unwrap();
assert_eq!(json, "\"sent\"");
let deleted = MessageStatus::Deleted;
let json = serde_json::to_string(&deleted).unwrap();
assert_eq!(json, "\"deleted\"");
let received = MessageStatus::Received;
let json = serde_json::to_string(&received).unwrap();
assert_eq!(json, "\"received\"");
}
#[test]
fn test_message_with_recipients() {
let message = Message {
message_id: Some("msg123".to_string()),
subject: Some("Test Subject".to_string()),
from: Some(MailAddress {
name: Some("Sender Name".to_string()),
email: Some("sender@example.com".to_string()),
}),
to: Some(vec![
MailAddress {
name: Some("Recipient 1".to_string()),
email: Some("recipient1@example.com".to_string()),
},
MailAddress {
name: Some("Recipient 2".to_string()),
email: Some("recipient2@example.com".to_string()),
},
]),
cc: Some(vec![]),
bcc: None,
body: Some(MailBody {
text: None,
html: Some("<p>Email body content</p>".to_string()),
}),
sent_time: Some(1640995200),
is_read: Some(false),
status: Some(MessageStatus::Draft),
attachments: Some(vec![]),
};
let json = serde_json::to_string(&message).unwrap();
assert!(json.contains("msg123"));
assert!(json.contains("Test Subject"));
assert!(json.contains("sender@example.com"));
assert!(json.contains("recipient1@example.com"));
}
#[test]
fn test_attachment_with_size() {
let attachment = Attachment {
attachment_id: Some("att789".to_string()),
name: Some("document.pdf".to_string()),
content_type: Some("application/pdf".to_string()),
size: Some(1024000),
download_url: Some("https://example.com/download/att789".to_string()),
};
let json = serde_json::to_string(&attachment).unwrap();
assert!(json.contains("att789"));
assert!(json.contains("document.pdf"));
assert!(json.contains("application/pdf"));
assert!(json.contains("1024000"));
}
#[test]
fn test_attachment_inline() {
let attachment = Attachment {
attachment_id: Some("att456".to_string()),
name: Some("image.png".to_string()),
content_type: Some("image/png".to_string()),
size: Some(52000),
download_url: None,
};
let json = serde_json::to_string(&attachment).unwrap();
assert!(json.contains("att456"));
assert!(json.contains("image.png"));
assert!(json.contains("image/png"));
assert!(!json.contains("download_url"));
}
#[test]
fn test_rule_with_conditions() {
let rule = Rule {
rule_id: Some("rule123".to_string()),
rule_name: Some("Important Emails".to_string()),
enabled: Some(true),
conditions: Some(vec![
RuleCondition {
field: Some("from".to_string()),
operator: Some("contains".to_string()),
value: Some("boss@company.com".to_string()),
},
RuleCondition {
field: Some("subject".to_string()),
operator: Some("contains".to_string()),
value: Some("urgent".to_string()),
},
]),
actions: Some(vec![RuleAction {
action_type: Some("move_to_folder".to_string()),
target_folder_id: Some("important".to_string()),
mark_as_read: Some(false),
forward_to: None,
parameters: None,
}]),
priority: Some(1),
};
let json = serde_json::to_string(&rule).unwrap();
assert!(json.contains("rule123"));
assert!(json.contains("Important Emails"));
assert!(json.contains("boss@company.com"));
assert!(json.contains("move_to_folder"));
}
#[test]
fn test_rule_action_forward() {
let action = RuleAction {
action_type: Some("forward".to_string()),
target_folder_id: None,
mark_as_read: Some(true),
forward_to: Some("assistant@company.com".to_string()),
parameters: None,
};
let json = serde_json::to_string(&action).unwrap();
assert!(json.contains("forward"));
assert!(json.contains("assistant@company.com"));
assert!(!json.contains("target_folder_id"));
}
#[test]
fn test_mailbox_subscription() {
let subscription = MailboxSubscription {
subscription_id: Some("sub789".to_string()),
mailbox_id: Some("mailbox123".to_string()),
user_id: Some("user123".to_string()),
webhook_url: Some("https://webhook.example.com/mail".to_string()),
event_types: Some(vec![
"message_received".to_string(),
"message_sent".to_string(),
]),
subscription_type: Some("webhook".to_string()),
status: Some("active".to_string()),
is_subscribed: Some(true),
subscribe_time: Some(1640995200),
subscription_time: Some("2024-01-01T10:00:00Z".to_string()),
update_time: Some("2024-01-01T10:00:00Z".to_string()),
};
let json = serde_json::to_string(&subscription).unwrap();
assert!(json.contains("sub789"));
assert!(json.contains("mailbox123"));
assert!(json.contains("https://webhook.example.com/mail"));
assert!(json.contains("message_received"));
}
#[test]
fn test_mailbox_subscription_unsubscribed() {
let subscription = MailboxSubscription {
subscription_id: Some("sub456".to_string()),
mailbox_id: Some("mailbox456".to_string()),
user_id: None,
webhook_url: None,
event_types: None,
subscription_type: Some("inactive".to_string()),
status: Some("inactive".to_string()),
is_subscribed: Some(false),
subscribe_time: None,
subscription_time: None,
update_time: None,
};
let json = serde_json::to_string(&subscription).unwrap();
assert!(json.contains("sub456"));
assert!(json.contains("false"));
assert!(!json.contains("webhook_url"));
assert!(!json.contains("subscribe_time"));
}
}