Get/Set implementation for all RFC8620/8261 objects.

main
Mauro D 2022-05-09 17:40:04 +00:00
parent 1680fdcd30
commit cd74566ca3
23 changed files with 1924 additions and 7 deletions

View File

@ -17,6 +17,7 @@ serde_json = "1.0"
chrono = { version = "0.4", features = ["serde"]}
reqwest = "0.11"
ece = "2.2"
base64 = "0.13"
#[dev-dependencies]

View File

@ -1,3 +1,4 @@
use chrono::{DateTime, NaiveDateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::hash::Hash;
@ -66,4 +67,46 @@ pub enum SetErrorType {
InvalidProperties,
#[serde(rename = "singleton")]
Singleton,
#[serde(rename = "mailboxHasChild")]
MailboxHasChild,
#[serde(rename = "mailboxHasEmail")]
MailboxHasEmail,
#[serde(rename = "blobNotFound")]
BlobNotFound,
#[serde(rename = "tooManyKeywords")]
TooManyKeywords,
#[serde(rename = "tooManyMailboxes")]
TooManyMailboxes,
#[serde(rename = "forbiddenFrom")]
ForbiddenFrom,
#[serde(rename = "invalidEmail")]
InvalidEmail,
#[serde(rename = "tooManyRecipients")]
TooManyRecipients,
#[serde(rename = "noRecipients")]
NoRecipients,
#[serde(rename = "invalidRecipients")]
InvalidRecipients,
#[serde(rename = "forbiddenMailFrom")]
ForbiddenMailFrom,
#[serde(rename = "forbiddenToSend")]
ForbiddenToSend,
#[serde(rename = "cannotUnsend")]
CannotUnsend,
}
pub fn from_timestamp(timestamp: i64) -> DateTime<Utc> {
DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(timestamp, 0), Utc)
}
pub fn string_not_set(string: &Option<String>) -> bool {
matches!(string, Some(string) if string.is_empty())
}
pub fn date_not_set(date: &Option<DateTime<Utc>>) -> bool {
matches!(date, Some(date) if date.timestamp() == 0)
}
pub fn list_not_set<T>(list: &Option<Vec<T>>) -> bool {
matches!(list, Some(list) if list.is_empty() )
}

213
src/email/get.rs Normal file
View File

@ -0,0 +1,213 @@
use crate::Get;
use super::{
Email, EmailAddress, EmailAddressGroup, EmailBodyPart, EmailBodyValue, EmailHeader, Field,
};
impl Email<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn blob_id(&self) -> &str {
self.blob_id.as_ref().unwrap()
}
pub fn thread_id(&self) -> &str {
self.thread_id.as_ref().unwrap()
}
pub fn mailbox_ids(&self) -> Vec<&str> {
self.mailbox_ids
.as_ref()
.unwrap()
.iter()
.filter(|(_, v)| **v)
.map(|(k, _)| k.as_str())
.collect()
}
pub fn keywords(&self) -> Vec<&str> {
self.keywords
.as_ref()
.unwrap()
.iter()
.filter(|(_, v)| **v)
.map(|(k, _)| k.as_str())
.collect()
}
pub fn size(&self) -> usize {
self.size.unwrap()
}
pub fn received_at(&self) -> i64 {
self.received_at.as_ref().unwrap().timestamp()
}
pub fn message_id(&self) -> Option<&[String]> {
self.message_id.as_deref()
}
pub fn in_reply_to(&self) -> Option<&[String]> {
self.in_reply_to.as_deref()
}
pub fn references(&self) -> Option<&[String]> {
self.references.as_deref()
}
pub fn sender(&self) -> Option<&[EmailAddress]> {
self.sender.as_deref()
}
pub fn from(&self) -> Option<&[EmailAddress]> {
self.from.as_deref()
}
pub fn to(&self) -> Option<&[EmailAddress]> {
self.to.as_deref()
}
pub fn cc(&self) -> Option<&[EmailAddress]> {
self.cc.as_deref()
}
pub fn bcc(&self) -> Option<&[EmailAddress]> {
self.bcc.as_deref()
}
pub fn subject(&self) -> Option<&str> {
self.subject.as_deref()
}
pub fn sent_at(&self) -> Option<i64> {
self.sent_at.as_ref().map(|v| v.timestamp())
}
pub fn body_structure(&self) -> Option<&EmailBodyPart> {
self.body_structure.as_deref()
}
pub fn body_value(&self, id: &str) -> Option<&EmailBodyValue> {
self.body_values.as_ref().and_then(|v| v.get(id))
}
pub fn text_body(&self) -> Option<&[EmailBodyPart]> {
self.text_body.as_deref()
}
pub fn html_body(&self) -> Option<&[EmailBodyPart]> {
self.html_body.as_deref()
}
pub fn attachments(&self) -> Option<&[EmailBodyPart]> {
self.attachments.as_deref()
}
pub fn has_attachment(&self) -> bool {
*self.has_attachment.as_ref().unwrap_or(&false)
}
pub fn header(&self, id: &str) -> Option<&Field> {
self.others.get(id).and_then(|v| v.as_ref())
}
pub fn has_header(&self, id: &str) -> bool {
self.others.contains_key(id)
}
}
impl EmailBodyPart<Get> {
pub fn part_id(&self) -> Option<&str> {
self.part_id.as_deref()
}
pub fn blob_id(&self) -> Option<&str> {
self.blob_id.as_deref()
}
pub fn size(&self) -> usize {
*self.size.as_ref().unwrap_or(&0)
}
pub fn headers(&self) -> Option<&[EmailHeader]> {
self.headers.as_deref()
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn charset(&self) -> Option<&str> {
self.charset.as_deref()
}
pub fn content_type(&self) -> Option<&str> {
self.type_.as_deref()
}
pub fn content_disposition(&self) -> Option<&str> {
self.disposition.as_deref()
}
pub fn content_id(&self) -> Option<&str> {
self.cid.as_deref()
}
pub fn content_language(&self) -> Option<&[String]> {
self.language.as_deref()
}
pub fn content_location(&self) -> Option<&str> {
self.location.as_deref()
}
pub fn sub_parts(&self) -> Option<&[EmailBodyPart]> {
self.sub_parts.as_deref()
}
}
impl EmailBodyValue<Get> {
pub fn value(&self) -> &str {
self.value.as_str()
}
pub fn is_encoding_problem(&self) -> bool {
self.is_encoding_problem
}
pub fn is_truncated(&self) -> bool {
self.is_truncated
}
}
impl EmailAddress<Get> {
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn email(&self) -> &str {
self.email.as_str()
}
}
impl EmailAddressGroup<Get> {
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn addresses(&self) -> &[EmailAddress] {
self.addresses.as_ref()
}
}
impl EmailHeader<Get> {
pub fn name(&self) -> &str {
self.name.as_str()
}
pub fn value(&self) -> &str {
self.value.as_str()
}
}

307
src/email/mod.rs Normal file
View File

@ -0,0 +1,307 @@
pub mod get;
pub mod set;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use crate::Get;
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Email<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "blobId")]
#[serde(skip_serializing_if = "Option::is_none")]
blob_id: Option<String>,
#[serde(rename = "threadId")]
#[serde(skip_serializing_if = "Option::is_none")]
thread_id: Option<String>,
#[serde(rename = "mailboxIds")]
#[serde(skip_serializing_if = "Option::is_none")]
mailbox_ids: Option<HashMap<String, bool>>,
#[serde(rename = "keywords")]
#[serde(skip_serializing_if = "Option::is_none")]
keywords: Option<HashMap<String, bool>>,
#[serde(rename = "size")]
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<usize>,
#[serde(rename = "receivedAt")]
#[serde(skip_serializing_if = "Option::is_none")]
received_at: Option<DateTime<Utc>>,
#[serde(rename = "messageId", alias = "header:Message-ID:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
message_id: Option<Vec<String>>,
#[serde(rename = "inReplyTo", alias = "header:In-Reply-To:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
in_reply_to: Option<Vec<String>>,
#[serde(rename = "references", alias = "header:References:asMessageIds")]
#[serde(skip_serializing_if = "Option::is_none")]
references: Option<Vec<String>>,
#[serde(rename = "sender", alias = "header:Sender:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
sender: Option<Vec<EmailAddress>>,
#[serde(rename = "from", alias = "header:From:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
from: Option<Vec<EmailAddress>>,
#[serde(rename = "to", alias = "header:To:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
to: Option<Vec<EmailAddress>>,
#[serde(rename = "cc", alias = "header:Cc:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
cc: Option<Vec<EmailAddress>>,
#[serde(rename = "bcc", alias = "header:Bcc:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
bcc: Option<Vec<EmailAddress>>,
#[serde(rename = "replyTo", alias = "header:Reply-To:asAddresses")]
#[serde(skip_serializing_if = "Option::is_none")]
reply_to: Option<Vec<EmailAddress>>,
#[serde(rename = "subject", alias = "header:Subject:asText")]
#[serde(skip_serializing_if = "Option::is_none")]
subject: Option<String>,
#[serde(rename = "sentAt", alias = "header:Date:asDate")]
#[serde(skip_serializing_if = "Option::is_none")]
sent_at: Option<DateTime<Utc>>,
#[serde(rename = "bodyStructure")]
#[serde(skip_serializing_if = "Option::is_none")]
body_structure: Option<Box<EmailBodyPart>>,
#[serde(rename = "bodyValues")]
#[serde(skip_serializing_if = "Option::is_none")]
body_values: Option<HashMap<String, EmailBodyValue>>,
#[serde(rename = "textBody")]
#[serde(skip_serializing_if = "Option::is_none")]
text_body: Option<Vec<EmailBodyPart>>,
#[serde(rename = "htmlBody")]
#[serde(skip_serializing_if = "Option::is_none")]
html_body: Option<Vec<EmailBodyPart>>,
#[serde(rename = "attachments")]
#[serde(skip_serializing_if = "Option::is_none")]
attachments: Option<Vec<EmailBodyPart>>,
#[serde(rename = "hasAttachment")]
#[serde(skip_serializing_if = "Option::is_none")]
has_attachment: Option<bool>,
#[serde(rename = "preview")]
#[serde(skip_serializing_if = "Option::is_none")]
preview: Option<String>,
#[serde(flatten)]
#[serde(skip_serializing_if = "HashMap::is_empty")]
others: HashMap<String, Option<Field>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailBodyPart<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "partId")]
#[serde(skip_serializing_if = "Option::is_none")]
part_id: Option<String>,
#[serde(rename = "blobId")]
#[serde(skip_serializing_if = "Option::is_none")]
blob_id: Option<String>,
#[serde(rename = "size")]
#[serde(skip_serializing_if = "Option::is_none")]
size: Option<usize>,
#[serde(rename = "headers")]
#[serde(skip_serializing_if = "Option::is_none")]
headers: Option<Vec<EmailHeader>>,
#[serde(rename = "name")]
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(rename = "type")]
#[serde(skip_serializing_if = "Option::is_none")]
type_: Option<String>,
#[serde(rename = "charset")]
#[serde(skip_serializing_if = "Option::is_none")]
charset: Option<String>,
#[serde(rename = "disposition")]
#[serde(skip_serializing_if = "Option::is_none")]
disposition: Option<String>,
#[serde(rename = "cid")]
#[serde(skip_serializing_if = "Option::is_none")]
cid: Option<String>,
#[serde(rename = "language")]
#[serde(skip_serializing_if = "Option::is_none")]
language: Option<Vec<String>>,
#[serde(rename = "location")]
#[serde(skip_serializing_if = "Option::is_none")]
location: Option<String>,
#[serde(rename = "subParts")]
#[serde(skip_serializing_if = "Option::is_none")]
sub_parts: Option<Vec<EmailBodyPart>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailBodyValue<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "value")]
value: String,
#[serde(rename = "isEncodingProblem")]
is_encoding_problem: bool,
#[serde(rename = "isTruncated")]
is_truncated: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Field {
Text(String),
TextList(Vec<String>),
Date(DateTime<Utc>),
Addresses(Vec<EmailAddress>),
GroupedAddresses(Vec<EmailAddressGroup>),
Bool(bool),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAddress<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: Option<String>,
email: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailAddressGroup<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: Option<String>,
addresses: Vec<EmailAddress>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailHeader<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
name: String,
value: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmailProperty {
#[serde(rename = "id")]
Id,
#[serde(rename = "blobId")]
BlobId,
#[serde(rename = "threadId")]
ThreadId,
#[serde(rename = "mailboxIds")]
MailboxIds,
#[serde(rename = "keywords")]
Keywords,
#[serde(rename = "size")]
Size,
#[serde(rename = "receivedAt")]
ReceivedAt,
#[serde(rename = "messageId")]
MessageId,
#[serde(rename = "inReplyTo")]
InReplyTo,
#[serde(rename = "references")]
References,
#[serde(rename = "sender")]
Sender,
#[serde(rename = "from")]
From,
#[serde(rename = "to")]
To,
#[serde(rename = "cc")]
Cc,
#[serde(rename = "bcc")]
Bcc,
#[serde(rename = "replyTo")]
ReplyTo,
#[serde(rename = "subject")]
Subject,
#[serde(rename = "sentAt")]
SentAt,
#[serde(rename = "bodyStructure")]
BodyStructure,
#[serde(rename = "bodyValues")]
BodyValues,
#[serde(rename = "textBody")]
TextBody,
#[serde(rename = "htmlBody")]
HtmlBody,
#[serde(rename = "attachments")]
Attachments,
#[serde(rename = "hasAttachment")]
HasAttachment,
#[serde(rename = "preview")]
Preview,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmailBodyProperty {
#[serde(rename = "partId")]
PartId,
#[serde(rename = "blobId")]
BlobId,
#[serde(rename = "size")]
Size,
#[serde(rename = "headers")]
Headers,
#[serde(rename = "name")]
Name,
#[serde(rename = "type")]
Type,
#[serde(rename = "charset")]
Charset,
#[serde(rename = "disposition")]
Disposition,
#[serde(rename = "cid")]
Cid,
#[serde(rename = "language")]
Language,
#[serde(rename = "location")]
Location,
#[serde(rename = "subParts")]
SubParts,
}

355
src/email/set.rs Normal file
View File

@ -0,0 +1,355 @@
use std::collections::HashMap;
use crate::{core::set::from_timestamp, Set};
use super::{
Email, EmailAddress, EmailAddressGroup, EmailBodyPart, EmailBodyValue, EmailHeader, Field,
};
impl Email<Set> {
pub fn mailbox_ids(mut self, mailbox_ids: impl Iterator<Item = String>) -> Self {
self.mailbox_ids = Some(mailbox_ids.into_iter().map(|s| (s, true)).collect());
self
}
pub fn mailbox_id(mut self, mailbox_id: &str, set: bool) -> Self {
self.mailbox_ids = None;
self.others.insert(
format!("mailboxIds/{}", mailbox_id),
Field::Bool(set).into(),
);
self
}
pub fn keywords(mut self, keywords: impl Iterator<Item = String>) -> Self {
self.keywords = Some(keywords.into_iter().map(|s| (s, true)).collect());
self
}
pub fn keyword(mut self, keyword: &str, set: bool) -> Self {
self.keywords = None;
self.others
.insert(format!("keywords/{}", keyword), Field::Bool(set).into());
self
}
pub fn message_id(mut self, message_id: impl Iterator<Item = String>) -> Self {
self.message_id = Some(message_id.into_iter().collect());
self
}
pub fn in_reply_to(mut self, in_reply_to: impl Iterator<Item = String>) -> Self {
self.in_reply_to = Some(in_reply_to.into_iter().collect());
self
}
pub fn references(mut self, references: impl Iterator<Item = String>) -> Self {
self.references = Some(references.into_iter().collect());
self
}
pub fn sender<T, U>(mut self, sender: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.sender = Some(sender.map(|s| s.into()).collect());
self
}
pub fn from<T, U>(mut self, from: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.from = Some(from.map(|s| s.into()).collect());
self
}
pub fn to<T, U>(mut self, to: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.to = Some(to.map(|s| s.into()).collect());
self
}
pub fn cc<T, U>(mut self, cc: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.cc = Some(cc.map(|s| s.into()).collect());
self
}
pub fn bcc<T, U>(mut self, bcc: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.bcc = Some(bcc.map(|s| s.into()).collect());
self
}
pub fn reply_to<T, U>(mut self, reply_to: T) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.reply_to = Some(reply_to.map(|s| s.into()).collect());
self
}
pub fn subject(mut self, subject: String) -> Self {
self.subject = Some(subject);
self
}
pub fn sent_at(mut self, sent_at: i64) -> Self {
self.sent_at = Some(from_timestamp(sent_at));
self
}
pub fn body_structure(mut self, body_structure: EmailBodyPart) -> Self {
self.body_structure = Some(body_structure.into());
self
}
pub fn body_value(mut self, id: String, body_value: impl Into<EmailBodyValue>) -> Self {
self.body_values
.get_or_insert_with(HashMap::new)
.insert(id, body_value.into());
self
}
pub fn text_body(mut self, text_body: EmailBodyPart) -> Self {
self.text_body.get_or_insert_with(Vec::new).push(text_body);
self
}
pub fn html_body(mut self, html_body: EmailBodyPart) -> Self {
self.html_body.get_or_insert_with(Vec::new).push(html_body);
self
}
pub fn attachment(mut self, attachment: EmailBodyPart) -> Self {
self.attachments
.get_or_insert_with(Vec::new)
.push(attachment);
self
}
pub fn header(mut self, header: String, value: impl Into<Field>) -> Self {
self.others.insert(header, Some(value.into()));
self
}
}
impl Email {
pub fn new() -> Email<Set> {
Email {
_state: Default::default(),
id: Default::default(),
blob_id: Default::default(),
thread_id: Default::default(),
mailbox_ids: Default::default(),
keywords: Default::default(),
size: Default::default(),
received_at: Default::default(),
message_id: Default::default(),
in_reply_to: Default::default(),
references: Default::default(),
sender: Default::default(),
from: Default::default(),
to: Default::default(),
cc: Default::default(),
bcc: Default::default(),
reply_to: Default::default(),
subject: Default::default(),
sent_at: Default::default(),
body_structure: Default::default(),
body_values: Default::default(),
text_body: Default::default(),
html_body: Default::default(),
attachments: Default::default(),
has_attachment: Default::default(),
preview: Default::default(),
others: Default::default(),
}
}
}
impl EmailBodyPart {
pub fn new() -> EmailBodyPart<Set> {
EmailBodyPart {
part_id: None,
blob_id: None,
size: None,
headers: None,
name: None,
type_: None,
charset: None,
disposition: None,
cid: None,
language: None,
location: None,
sub_parts: None,
_state: Default::default(),
}
}
}
impl EmailBodyPart<Set> {
pub fn part_id(mut self, part_id: String) -> Self {
self.part_id = Some(part_id);
self
}
pub fn blob_id(mut self, blob_id: String) -> Self {
self.blob_id = Some(blob_id);
self
}
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn content_type(mut self, content_type: String) -> Self {
self.type_ = Some(content_type);
self
}
pub fn content_id(mut self, content_id: String) -> Self {
self.cid = Some(content_id);
self
}
pub fn content_language(mut self, content_language: impl Iterator<Item = String>) -> Self {
self.language = Some(content_language.into_iter().collect());
self
}
pub fn content_location(mut self, content_location: String) -> Self {
self.location = Some(content_location);
self
}
pub fn sub_part(mut self, sub_part: EmailBodyPart) -> Self {
self.sub_parts.get_or_insert_with(Vec::new).push(sub_part);
self
}
}
impl From<String> for EmailBodyValue {
fn from(value: String) -> Self {
EmailBodyValue {
value,
is_encoding_problem: false,
is_truncated: false,
_state: Default::default(),
}
}
}
impl From<&str> for EmailBodyValue {
fn from(value: &str) -> Self {
EmailBodyValue {
value: value.to_string(),
is_encoding_problem: false,
is_truncated: false,
_state: Default::default(),
}
}
}
impl EmailAddress {
pub fn new(email: String) -> EmailAddress<Set> {
EmailAddress {
_state: Default::default(),
name: None,
email,
}
}
}
impl EmailAddress<Set> {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
}
impl From<String> for EmailAddress {
fn from(email: String) -> Self {
EmailAddress {
_state: Default::default(),
name: None,
email,
}
}
}
impl From<(String, String)> for EmailAddress {
fn from(parts: (String, String)) -> Self {
EmailAddress {
_state: Default::default(),
name: parts.0.into(),
email: parts.1,
}
}
}
impl From<&str> for EmailAddress {
fn from(email: &str) -> Self {
EmailAddress {
_state: Default::default(),
name: None,
email: email.to_string(),
}
}
}
impl From<(&str, &str)> for EmailAddress {
fn from(parts: (&str, &str)) -> Self {
EmailAddress {
_state: Default::default(),
name: parts.0.to_string().into(),
email: parts.1.to_string(),
}
}
}
impl EmailAddressGroup {
pub fn new() -> EmailAddressGroup<Set> {
EmailAddressGroup {
_state: Default::default(),
name: None,
addresses: Vec::new(),
}
}
}
impl EmailAddressGroup<Set> {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn address(mut self, address: impl Into<EmailAddress>) -> Self {
self.addresses.push(address.into());
self
}
}
impl EmailHeader {
pub fn new(name: String, value: String) -> EmailHeader<Set> {
EmailHeader {
_state: Default::default(),
name,
value,
}
}
}

View File

@ -0,0 +1,80 @@
use crate::Get;
use super::{Address, Delivered, DeliveryStatus, Displayed, EmailSubmission, UndoStatus};
impl EmailSubmission<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn identity_id(&self) -> &str {
self.identity_id.as_ref().unwrap()
}
pub fn email_id(&self) -> &str {
self.email_id.as_ref().unwrap()
}
pub fn thread_id(&self) -> &str {
self.thread_id.as_ref().unwrap()
}
pub fn mail_from(&self) -> Option<&Address> {
self.envelope.as_ref().map(|e| &e.mail_from)
}
pub fn rcpt_to(&self) -> Option<&[Address]> {
self.envelope.as_ref().map(|e| e.rcpt_to.as_ref())
}
pub fn send_at(&self) -> i64 {
self.send_at.as_ref().unwrap().timestamp()
}
pub fn undo_status(&self) -> &UndoStatus {
self.undo_status.as_ref().unwrap()
}
pub fn delivery_status(&self, email: &str) -> Option<&DeliveryStatus> {
self.delivery_status.as_ref().and_then(|ds| ds.get(email))
}
pub fn dsn_blob_ids(&self) -> Option<&[String]> {
self.dsn_blob_ids.as_deref()
}
pub fn mdn_blob_ids(&self) -> Option<&[String]> {
self.mdn_blob_ids.as_deref()
}
}
impl Address<Get> {
pub fn email(&self) -> &str {
&self.email
}
pub fn parameter(&self, param: &str) -> Option<&str> {
self.parameters.as_ref()?.get(param)?.as_deref()
}
pub fn has_parameter(&self, param: &str) -> bool {
self.parameters
.as_ref()
.map(|ps| ps.contains_key(param))
.unwrap_or(false)
}
}
impl DeliveryStatus {
pub fn smtp_reply(&self) -> &str {
&self.smtp_reply
}
pub fn delivered(&self) -> &Delivered {
&self.delivered
}
pub fn displayed(&self) -> &Displayed {
&self.displayed
}
}

139
src/email_submission/mod.rs Normal file
View File

@ -0,0 +1,139 @@
pub mod get;
pub mod set;
use std::collections::HashMap;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::Get;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EmailSubmission<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "identityId")]
#[serde(skip_serializing_if = "Option::is_none")]
identity_id: Option<String>,
#[serde(rename = "emailId")]
#[serde(skip_serializing_if = "Option::is_none")]
email_id: Option<String>,
#[serde(rename = "threadId")]
#[serde(skip_serializing_if = "Option::is_none")]
thread_id: Option<String>,
#[serde(rename = "envelope")]
#[serde(skip_serializing_if = "Option::is_none")]
envelope: Option<Envelope>,
#[serde(rename = "sendAt")]
#[serde(skip_serializing_if = "Option::is_none")]
send_at: Option<DateTime<Utc>>,
#[serde(rename = "undoStatus")]
#[serde(skip_serializing_if = "Option::is_none")]
undo_status: Option<UndoStatus>,
#[serde(rename = "deliveryStatus")]
#[serde(skip_serializing_if = "Option::is_none")]
delivery_status: Option<HashMap<String, DeliveryStatus>>,
#[serde(rename = "dsnBlobIds")]
#[serde(skip_serializing_if = "Option::is_none")]
dsn_blob_ids: Option<Vec<String>>,
#[serde(rename = "mdnBlobIds")]
#[serde(skip_serializing_if = "Option::is_none")]
mdn_blob_ids: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Envelope {
#[serde(rename = "mailFrom")]
mail_from: Address,
#[serde(rename = "rcptTo")]
rcpt_to: Vec<Address>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Address<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
email: String,
parameters: Option<HashMap<String, Option<String>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum UndoStatus {
#[serde(rename = "pending")]
Pending,
#[serde(rename = "final")]
Final,
#[serde(rename = "canceled")]
Canceled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DeliveryStatus {
#[serde(rename = "smtpReply")]
smtp_reply: String,
#[serde(rename = "delivered")]
delivered: Delivered,
#[serde(rename = "displayed")]
displayed: Displayed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Delivered {
#[serde(rename = "queued")]
Queued,
#[serde(rename = "yes")]
Yes,
#[serde(rename = "no")]
No,
#[serde(rename = "unknown")]
Unknown,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Displayed {
#[serde(rename = "unknown")]
Unknown,
#[serde(rename = "yes")]
Yes,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum EmailSubmissionProperty {
#[serde(rename = "id")]
Id,
#[serde(rename = "identityId")]
IdentityId,
#[serde(rename = "emailId")]
EmailId,
#[serde(rename = "threadId")]
ThreadId,
#[serde(rename = "envelope")]
Envelope,
#[serde(rename = "sendAt")]
SendAt,
#[serde(rename = "undoStatus")]
UndoStatus,
#[serde(rename = "deliveryStatus")]
DeliveryStatus,
#[serde(rename = "dsnBlobIds")]
DsnBlobIds,
#[serde(rename = "mdnBlobIds")]
MdnBlobIds,
}

View File

@ -0,0 +1,91 @@
use std::collections::HashMap;
use crate::Set;
use super::{Address, EmailSubmission, Envelope, UndoStatus};
impl EmailSubmission<Set> {
pub fn identity_id(mut self, identity_id: String) -> Self {
self.identity_id = Some(identity_id);
self
}
pub fn email_id(mut self, email_id: String) -> Self {
self.email_id = Some(email_id);
self
}
pub fn envelope<T, U>(mut self, mail_from: U, rcpt_to: T) -> Self
where
T: Iterator<Item = U>,
U: Into<Address>,
{
self.envelope = Some(Envelope {
mail_from: mail_from.into(),
rcpt_to: rcpt_to.map(|s| s.into()).collect(),
});
self
}
pub fn undo_status(mut self, undo_status: UndoStatus) -> Self {
self.undo_status = Some(undo_status);
self
}
}
impl EmailSubmission {
pub fn new() -> EmailSubmission<Set> {
EmailSubmission {
_state: Default::default(),
id: None,
identity_id: None,
email_id: None,
thread_id: None,
envelope: None,
send_at: None,
undo_status: None,
delivery_status: None,
dsn_blob_ids: None,
mdn_blob_ids: None,
}
}
}
impl Address {
pub fn new(email: String) -> Address<Set> {
Address {
_state: Default::default(),
email,
parameters: None,
}
}
}
impl Address<Set> {
pub fn parameter(mut self, parameter: String, value: Option<String>) -> Self {
self.parameters
.get_or_insert_with(HashMap::new)
.insert(parameter, value);
self
}
}
impl From<String> for Address {
fn from(email: String) -> Self {
Address {
_state: Default::default(),
email,
parameters: None,
}
}
}
impl From<&str> for Address {
fn from(email: &str) -> Self {
Address {
_state: Default::default(),
email: email.to_string(),
parameters: None,
}
}
}

37
src/identity/get.rs Normal file
View File

@ -0,0 +1,37 @@
use crate::{email::EmailAddress, Get};
use super::Identity;
impl Identity<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn name(&self) -> Option<&str> {
self.name.as_deref()
}
pub fn email(&self) -> &str {
self.email.as_ref().unwrap()
}
pub fn reply_to(&self) -> Option<&[EmailAddress]> {
self.reply_to.as_deref()
}
pub fn bcc(&self) -> Option<&[EmailAddress]> {
self.bcc.as_deref()
}
pub fn text_signature(&self) -> Option<&str> {
self.text_signature.as_deref()
}
pub fn html_signature(&self) -> Option<&str> {
self.html_signature.as_deref()
}
pub fn may_delete(&self) -> bool {
self.may_delete.unwrap_or(false)
}
}

64
src/identity/mod.rs Normal file
View File

@ -0,0 +1,64 @@
pub mod get;
pub mod set;
use crate::core::set::list_not_set;
use crate::{email::EmailAddress, Get};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Identity<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(rename = "name")]
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "email")]
#[serde(skip_serializing_if = "Option::is_none")]
pub email: Option<String>,
#[serde(rename = "replyTo")]
#[serde(skip_serializing_if = "list_not_set")]
pub reply_to: Option<Vec<EmailAddress>>,
#[serde(rename = "bcc")]
#[serde(skip_serializing_if = "list_not_set")]
pub bcc: Option<Vec<EmailAddress>>,
#[serde(rename = "textSignature")]
#[serde(skip_serializing_if = "Option::is_none")]
pub text_signature: Option<String>,
#[serde(rename = "htmlSignature")]
#[serde(skip_serializing_if = "Option::is_none")]
pub html_signature: Option<String>,
#[serde(rename = "mayDelete")]
#[serde(skip_serializing_if = "Option::is_none")]
pub may_delete: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IdentityProperty {
#[serde(rename = "id")]
Id,
#[serde(rename = "name")]
Name,
#[serde(rename = "email")]
Email,
#[serde(rename = "replyTo")]
ReplyTo,
#[serde(rename = "bcc")]
Bcc,
#[serde(rename = "textSignature")]
TextSignature,
#[serde(rename = "htmlSignature")]
HtmlSignature,
#[serde(rename = "mayDelete")]
MayDelete,
}

59
src/identity/set.rs Normal file
View File

@ -0,0 +1,59 @@
use crate::{email::EmailAddress, Set};
use super::Identity;
impl Identity<Set> {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn email(mut self, email: String) -> Self {
self.email = Some(email);
self
}
pub fn bcc<T, U>(mut self, bcc: Option<T>) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.bcc = bcc.map(|s| s.map(|s| s.into()).collect());
self
}
pub fn reply_to<T, U>(mut self, reply_to: Option<T>) -> Self
where
T: Iterator<Item = U>,
U: Into<EmailAddress>,
{
self.reply_to = reply_to.map(|s| s.map(|s| s.into()).collect());
self
}
pub fn text_signature(mut self, text_signature: String) -> Self {
self.text_signature = Some(text_signature);
self
}
pub fn html_signature(mut self, html_signature: String) -> Self {
self.html_signature = Some(html_signature);
self
}
}
impl Identity {
pub fn new() -> Identity<Set> {
Identity {
_state: Default::default(),
id: None,
name: None,
email: None,
reply_to: Vec::with_capacity(0).into(),
bcc: Vec::with_capacity(0).into(),
text_signature: None,
html_signature: None,
may_delete: None,
}
}
}

View File

@ -4,7 +4,13 @@ use serde::{Deserialize, Serialize};
pub mod blob;
pub mod core;
pub mod email;
pub mod email_submission;
pub mod identity;
pub mod mailbox;
pub mod push_subscription;
pub mod thread;
pub mod vacation_response;
#[derive(Debug, Clone, Serialize, Deserialize, Hash, PartialEq, Eq)]
pub enum URI {
@ -94,6 +100,7 @@ pub enum Object {
Mailbox,
Thread,
Email,
EmailDelivery,
SearchSnippet,
Identity,
EmailSubmission,
@ -112,3 +119,8 @@ pub struct StateChange {
pub type_: StateChangeType,
pub changed: HashMap<String, HashMap<Object, String>>,
}
#[derive(Debug, Clone)]
pub struct Get;
#[derive(Debug, Clone)]
pub struct Set;

81
src/mailbox/get.rs Normal file
View File

@ -0,0 +1,81 @@
use crate::Get;
use super::{Mailbox, Role};
impl Mailbox<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn name(&self) -> &str {
self.name.as_ref().unwrap()
}
pub fn parent_id(&self) -> Option<&str> {
self.parent_id.as_deref()
}
pub fn role(&self) -> Role {
self.role.as_ref().cloned().unwrap_or(Role::None)
}
pub fn sort_order(&self) -> u32 {
self.sort_order.as_ref().copied().unwrap_or(0)
}
pub fn total_emails(&self) -> usize {
self.total_emails.as_ref().copied().unwrap_or(0)
}
pub fn unread_emails(&self) -> usize {
self.unread_emails.as_ref().copied().unwrap_or(0)
}
pub fn total_threads(&self) -> usize {
self.total_threads.as_ref().copied().unwrap_or(0)
}
pub fn unread_threads(&self) -> usize {
self.unread_threads.as_ref().copied().unwrap_or(0)
}
pub fn is_subscribed(&self) -> bool {
*self.is_subscribed.as_ref().unwrap_or(&false)
}
pub fn may_read_items(&self) -> bool {
self.my_rights.as_ref().unwrap().may_read_items
}
pub fn may_add_items(&self) -> bool {
self.my_rights.as_ref().unwrap().may_add_items
}
pub fn may_remove_items(&self) -> bool {
self.my_rights.as_ref().unwrap().may_remove_items
}
pub fn may_set_seen(&self) -> bool {
self.my_rights.as_ref().unwrap().may_set_seen
}
pub fn may_set_keywords(&self) -> bool {
self.my_rights.as_ref().unwrap().may_set_keywords
}
pub fn may_create_child(&self) -> bool {
self.my_rights.as_ref().unwrap().may_create_child
}
pub fn may_rename(&self) -> bool {
self.my_rights.as_ref().unwrap().may_rename
}
pub fn may_delete(&self) -> bool {
self.my_rights.as_ref().unwrap().may_delete
}
pub fn may_submit(&self) -> bool {
self.my_rights.as_ref().unwrap().may_submit
}
}

126
src/mailbox/mod.rs Normal file
View File

@ -0,0 +1,126 @@
pub mod get;
pub mod set;
use crate::core::set::string_not_set;
use crate::mailbox::set::role_not_set;
use crate::Get;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Mailbox<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "name")]
#[serde(skip_serializing_if = "Option::is_none")]
name: Option<String>,
#[serde(rename = "parentId")]
#[serde(skip_serializing_if = "string_not_set")]
parent_id: Option<String>,
#[serde(rename = "role")]
#[serde(skip_serializing_if = "role_not_set")]
role: Option<Role>,
#[serde(rename = "sortOrder")]
#[serde(skip_serializing_if = "Option::is_none")]
sort_order: Option<u32>,
#[serde(rename = "totalEmails")]
#[serde(skip_serializing_if = "Option::is_none")]
total_emails: Option<usize>,
#[serde(rename = "unreadEmails")]
#[serde(skip_serializing_if = "Option::is_none")]
unread_emails: Option<usize>,
#[serde(rename = "totalThreads")]
#[serde(skip_serializing_if = "Option::is_none")]
total_threads: Option<usize>,
#[serde(rename = "unreadThreads")]
#[serde(skip_serializing_if = "Option::is_none")]
unread_threads: Option<usize>,
#[serde(rename = "myRights")]
#[serde(skip_serializing_if = "Option::is_none")]
my_rights: Option<MailboxRights>,
#[serde(rename = "isSubscribed")]
#[serde(skip_serializing_if = "Option::is_none")]
is_subscribed: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Role {
Archive,
Drafts,
Important,
Inbox,
Junk,
Sent,
Trash,
None,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MailboxRights {
#[serde(rename = "mayReadItems")]
may_read_items: bool,
#[serde(rename = "mayAddItems")]
may_add_items: bool,
#[serde(rename = "mayRemoveItems")]
may_remove_items: bool,
#[serde(rename = "maySetSeen")]
may_set_seen: bool,
#[serde(rename = "maySetKeywords")]
may_set_keywords: bool,
#[serde(rename = "mayCreateChild")]
may_create_child: bool,
#[serde(rename = "mayRename")]
may_rename: bool,
#[serde(rename = "mayDelete")]
may_delete: bool,
#[serde(rename = "maySubmit")]
may_submit: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum MailboxProperty {
#[serde(rename = "id")]
Id,
#[serde(rename = "name")]
Name,
#[serde(rename = "parentId")]
ParentId,
#[serde(rename = "role")]
Role,
#[serde(rename = "sortOrder")]
SortOrder,
#[serde(rename = "totalEmails")]
TotalEmails,
#[serde(rename = "unreadEmails")]
UnreadEmails,
#[serde(rename = "totalThreads")]
TotalThreads,
#[serde(rename = "unreadThreads")]
UnreadThreads,
#[serde(rename = "myRights")]
MyRights,
#[serde(rename = "isSubscribed")]
IsSubscribed,
}

52
src/mailbox/set.rs Normal file
View File

@ -0,0 +1,52 @@
use crate::Set;
use super::{Mailbox, Role};
impl Mailbox<Set> {
pub fn name(mut self, name: String) -> Self {
self.name = Some(name);
self
}
pub fn parent_id(mut self, parent_id: Option<String>) -> Self {
self.parent_id = parent_id;
self
}
pub fn role(mut self, role: Role) -> Self {
if !matches!(role, Role::None) {
self.role = Some(role);
} else {
self.role = None;
}
self
}
pub fn sort_order(mut self, sort_order: u32) -> Self {
self.sort_order = sort_order.into();
self
}
}
pub fn role_not_set(role: &Option<Role>) -> bool {
matches!(role, Some(Role::None))
}
impl Mailbox {
pub fn new() -> Mailbox<Set> {
Mailbox {
_state: Default::default(),
id: None,
name: None,
parent_id: "".to_string().into(),
role: Role::None.into(),
sort_order: None,
total_emails: None,
unread_emails: None,
total_threads: None,
unread_threads: None,
my_rights: None,
is_subscribed: None,
}
}
}

View File

@ -0,0 +1,43 @@
use crate::{Get, Object};
use super::{Keys, PushSubscription};
impl PushSubscription<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn device_client_id(&self) -> &str {
self.device_client_id.as_ref().unwrap()
}
pub fn url(&self) -> &str {
self.url.as_ref().unwrap()
}
pub fn keys(&self) -> Option<&Keys> {
self.keys.as_ref()
}
pub fn verification_code(&self) -> Option<&str> {
self.verification_code.as_deref()
}
pub fn expires(&self) -> Option<i64> {
self.expires.map(|v| v.timestamp())
}
pub fn types(&self) -> Option<&[Object]> {
self.types.as_deref()
}
}
impl Keys {
pub fn p256dh(&self) -> Option<Vec<u8>> {
base64::decode_config(&self.p256dh, base64::URL_SAFE).ok()
}
pub fn auth(&self) -> Option<Vec<u8>> {
base64::decode_config(&self.auth, base64::URL_SAFE).ok()
}
}

View File

@ -1,13 +1,17 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::Object;
pub mod get;
pub mod set;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::core::set::list_not_set;
use crate::{Get, Object};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushSubscription {
pub struct PushSubscription<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
@ -33,7 +37,7 @@ pub struct PushSubscription {
expires: Option<DateTime<Utc>>,
#[serde(rename = "types")]
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(skip_serializing_if = "list_not_set")]
types: Option<Vec<Object>>,
}

View File

@ -0,0 +1,64 @@
use crate::{core::set::from_timestamp, Object, Set};
use super::{Keys, PushSubscription};
impl PushSubscription<Set> {
pub fn device_client_id(mut self, device_client_id: String) -> Self {
self.device_client_id = Some(device_client_id);
self
}
pub fn url(mut self, url: String) -> Self {
self.url = Some(url);
self
}
pub fn verification_code(mut self, verification_code: String) -> Self {
self.verification_code = Some(verification_code);
self
}
pub fn keys(mut self, keys: Keys) -> Self {
self.keys = Some(keys);
self
}
pub fn expires(mut self, expires: i64) -> Self {
self.expires = Some(from_timestamp(expires));
self
}
pub fn types(mut self, types: Option<impl Iterator<Item = Object>>) -> Self {
self.types = types.map(|s| s.collect());
self
}
}
impl PushSubscription {
pub fn new() -> PushSubscription<Set> {
PushSubscription {
_state: Default::default(),
id: None,
device_client_id: None,
url: None,
keys: None,
verification_code: None,
expires: None,
types: Vec::with_capacity(0).into(),
}
}
}
impl Keys {
pub fn new(p256dh: &[u8], auth: &[u8]) -> Self {
Keys {
p256dh: base64::encode_config(&p256dh, base64::URL_SAFE),
auth: base64::encode_config(&auth, base64::URL_SAFE),
}
}
pub fn generate() -> Option<Self> {
let (p256dh, auth) = ece::generate_keypair_and_auth_secret().ok()?;
Self::new(&p256dh.pub_as_raw().ok()?, &auth).into()
}
}

11
src/thread/get.rs Normal file
View File

@ -0,0 +1,11 @@
use super::Thread;
impl Thread {
pub fn id(&self) -> &str {
&self.id
}
pub fn email_ids(&self) -> &[String] {
&self.email_ids
}
}

10
src/thread/mod.rs Normal file
View File

@ -0,0 +1,10 @@
pub mod get;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Thread {
id: String,
#[serde(rename = "emailIds")]
email_ids: Vec<String>,
}

View File

@ -0,0 +1,33 @@
use crate::Get;
use super::VacationResponse;
impl VacationResponse<Get> {
pub fn id(&self) -> &str {
self.id.as_ref().unwrap()
}
pub fn is_enabled(&self) -> bool {
self.is_enabled.unwrap_or(false)
}
pub fn from_date(&self) -> Option<i64> {
self.from_date.as_ref().map(|dt| dt.timestamp())
}
pub fn to_date(&self) -> Option<i64> {
self.to_date.as_ref().map(|dt| dt.timestamp())
}
pub fn subject(&self) -> Option<&str> {
self.subject.as_deref()
}
pub fn text_body(&self) -> Option<&str> {
self.text_body.as_deref()
}
pub fn html_body(&self) -> Option<&str> {
self.html_body.as_deref()
}
}

View File

@ -0,0 +1,42 @@
pub mod get;
pub mod set;
use crate::core::set::date_not_set;
use crate::core::set::string_not_set;
use crate::Get;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct VacationResponse<State = Get> {
#[serde(skip)]
_state: std::marker::PhantomData<State>,
#[serde(rename = "id")]
#[serde(skip_serializing_if = "Option::is_none")]
id: Option<String>,
#[serde(rename = "isEnabled")]
#[serde(skip_serializing_if = "Option::is_none")]
is_enabled: Option<bool>,
#[serde(rename = "fromDate")]
#[serde(skip_serializing_if = "date_not_set")]
from_date: Option<DateTime<Utc>>,
#[serde(rename = "toDate")]
#[serde(skip_serializing_if = "date_not_set")]
to_date: Option<DateTime<Utc>>,
#[serde(rename = "subject")]
#[serde(skip_serializing_if = "string_not_set")]
subject: Option<String>,
#[serde(rename = "textBody")]
#[serde(skip_serializing_if = "string_not_set")]
text_body: Option<String>,
#[serde(rename = "htmlBody")]
#[serde(skip_serializing_if = "string_not_set")]
html_body: Option<String>,
}

View File

@ -0,0 +1,50 @@
use crate::{core::set::from_timestamp, Set};
use super::VacationResponse;
impl VacationResponse<Set> {
pub fn is_enabled(mut self, is_enabled: bool) -> Self {
self.is_enabled = Some(is_enabled);
self
}
pub fn from_date(mut self, from_date: Option<i64>) -> Self {
self.from_date = from_date.map(from_timestamp);
self
}
pub fn to_date(mut self, to_date: Option<i64>) -> Self {
self.to_date = to_date.map(from_timestamp);
self
}
pub fn subject(mut self, subject: Option<String>) -> Self {
self.subject = subject;
self
}
pub fn text_body(mut self, text_body: Option<String>) -> Self {
self.text_body = text_body;
self
}
pub fn html_body(mut self, html_body: Option<String>) -> Self {
self.html_body = html_body;
self
}
}
impl VacationResponse {
pub fn new() -> VacationResponse<Set> {
VacationResponse {
_state: Default::default(),
id: None,
is_enabled: None,
from_date: from_timestamp(0).into(),
to_date: from_timestamp(0).into(),
subject: "".to_string().into(),
text_body: "".to_string().into(),
html_body: "".to_string().into(),
}
}
}