From 66b70ee79eff33823f0b16ffd3910902fab02884 Mon Sep 17 00:00:00 2001 From: Mauro D Date: Mon, 13 Jun 2022 15:24:32 +0000 Subject: [PATCH] JMAP Sharing Principals --- src/core/request.rs | 49 +++++++++++ src/core/response.rs | 88 ++++++++++++++++++- src/core/set.rs | 4 + src/lib.rs | 17 ++++ src/principal/get.rs | 71 +++++++++++++++ src/principal/helpers.rs | 106 ++++++++++++++++++++++ src/principal/mod.rs | 184 +++++++++++++++++++++++++++++++++++++++ src/principal/query.rs | 119 +++++++++++++++++++++++++ src/principal/set.rs | 125 ++++++++++++++++++++++++++ 9 files changed, 760 insertions(+), 3 deletions(-) create mode 100644 src/principal/get.rs create mode 100644 src/principal/helpers.rs create mode 100644 src/principal/mod.rs create mode 100644 src/principal/query.rs create mode 100644 src/principal/set.rs diff --git a/src/core/request.rs b/src/core/request.rs index 0145791..b36af3d 100644 --- a/src/core/request.rs +++ b/src/core/request.rs @@ -9,6 +9,7 @@ use crate::{ email_submission::EmailSubmission, identity::Identity, mailbox::Mailbox, + principal::Principal, push_subscription::PushSubscription, thread::Thread, vacation_response::VacationResponse, @@ -78,6 +79,10 @@ pub enum Arguments { EmailSubmissionSet(SetRequest>), VacationResponseGet(GetRequest>), VacationResponseSet(SetRequest>), + PrincipalGet(GetRequest>), + PrincipalQuery(QueryRequest>), + PrincipalQueryChanges(QueryChangesRequest>), + PrincipalSet(SetRequest>), } impl Arguments { @@ -180,6 +185,22 @@ impl Arguments { Arguments::VacationResponseSet(SetRequest::new(params)) } + pub fn principal_get(params: RequestParams) -> Self { + Arguments::PrincipalGet(GetRequest::new(params)) + } + + pub fn principal_query(params: RequestParams) -> Self { + Arguments::PrincipalQuery(QueryRequest::new(params)) + } + + pub fn principal_query_changes(params: RequestParams, since_query_state: String) -> Self { + Arguments::PrincipalQueryChanges(QueryChangesRequest::new(params, since_query_state)) + } + + pub fn principal_set(params: RequestParams) -> Self { + Arguments::PrincipalSet(SetRequest::new(params)) + } + pub fn changes_mut(&mut self) -> &mut ChangesRequest { match self { Arguments::Changes(ref mut r) => r, @@ -349,6 +370,34 @@ impl Arguments { _ => unreachable!(), } } + + pub fn principal_get_mut(&mut self) -> &mut GetRequest> { + match self { + Arguments::PrincipalGet(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn principal_query_mut(&mut self) -> &mut QueryRequest> { + match self { + Arguments::PrincipalQuery(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn principal_query_changes_mut(&mut self) -> &mut QueryChangesRequest> { + match self { + Arguments::PrincipalQueryChanges(ref mut r) => r, + _ => unreachable!(), + } + } + + pub fn principal_set_mut(&mut self) -> &mut SetRequest> { + match self { + Arguments::PrincipalSet(ref mut r) => r, + _ => unreachable!(), + } + } } impl<'x> Request<'x> { diff --git a/src/core/response.rs b/src/core/response.rs index b3e1bb7..683793b 100644 --- a/src/core/response.rs +++ b/src/core/response.rs @@ -8,6 +8,7 @@ use crate::{ email_submission::EmailSubmission, identity::Identity, mailbox::Mailbox, + principal::Principal, push_subscription::PushSubscription, thread::Thread, vacation_response::VacationResponse, @@ -100,7 +101,7 @@ pub enum Error { pub type PushSubscriptionSetResponse = SetResponse>; pub type PushSubscriptionGetResponse = GetResponse>; -pub type MaiboxChangesResponse = ChangesResponse>; +pub type MailboxChangesResponse = ChangesResponse>; pub type MailboxSetResponse = SetResponse>; pub type MailboxGetResponse = GetResponse>; pub type ThreadGetResponse = GetResponse; @@ -118,6 +119,9 @@ pub type EmailSubmissionGetResponse = GetResponse>; pub type EmailSubmissionChangesResponse = ChangesResponse>; pub type VacationResponseGetResponse = GetResponse>; pub type VacationResponseSetResponse = SetResponse>; +pub type PrincipalChangesResponse = ChangesResponse>; +pub type PrincipalSetResponse = SetResponse>; +pub type PrincipalGetResponse = GetResponse>; #[derive(Debug)] pub struct TaggedMethodResponse { @@ -131,7 +135,7 @@ pub enum MethodResponse { GetPushSubscription(PushSubscriptionGetResponse), SetPushSubscription(PushSubscriptionSetResponse), GetMailbox(MailboxGetResponse), - ChangesMailbox(MaiboxChangesResponse), + ChangesMailbox(MailboxChangesResponse), QueryMailbox(QueryResponse), QueryChangesMailbox(QueryChangesResponse), SetMailbox(MailboxSetResponse), @@ -156,6 +160,13 @@ pub enum MethodResponse { SetEmailSubmission(EmailSubmissionSetResponse), GetVacationResponse(VacationResponseGetResponse), SetVacationResponse(VacationResponseSetResponse), + + GetPrincipal(PrincipalGetResponse), + ChangesPrincipal(PrincipalChangesResponse), + QueryPrincipal(QueryResponse), + QueryChangesPrincipal(QueryChangesResponse), + SetPrincipal(PrincipalSetResponse), + Echo(serde_json::Value), Error(MethodError), } @@ -233,6 +244,17 @@ impl TaggedMethodResponse { MethodResponse::SetVacationResponse(_), Method::SetVacationResponse ) + | (MethodResponse::GetPrincipal(_), Method::GetPrincipal) + | ( + MethodResponse::ChangesPrincipal(_), + Method::ChangesPrincipal + ) + | (MethodResponse::QueryPrincipal(_), Method::QueryPrincipal) + | ( + MethodResponse::QueryChangesPrincipal(_), + Method::QueryChangesPrincipal + ) + | (MethodResponse::SetPrincipal(_), Method::SetPrincipal) | (MethodResponse::Echo(_), Method::Echo) | (MethodResponse::Error(_), Method::Error) ) @@ -270,7 +292,7 @@ impl TaggedMethodResponse { } } - pub fn unwrap_changes_mailbox(self) -> crate::Result { + pub fn unwrap_changes_mailbox(self) -> crate::Result { match self.response { MethodResponse::ChangesMailbox(response) => Ok(response), MethodResponse::Error(err) => Err(err.into()), @@ -470,6 +492,46 @@ impl TaggedMethodResponse { } } + pub fn unwrap_get_principal(self) -> crate::Result { + match self.response { + MethodResponse::GetPrincipal(response) => Ok(response), + MethodResponse::Error(err) => Err(err.into()), + _ => Err("Response type mismatch".into()), + } + } + + pub fn unwrap_changes_principal(self) -> crate::Result { + match self.response { + MethodResponse::ChangesPrincipal(response) => Ok(response), + MethodResponse::Error(err) => Err(err.into()), + _ => Err("Response type mismatch".into()), + } + } + + pub fn unwrap_query_principal(self) -> crate::Result { + match self.response { + MethodResponse::QueryPrincipal(response) => Ok(response), + MethodResponse::Error(err) => Err(err.into()), + _ => Err("Response type mismatch".into()), + } + } + + pub fn unwrap_query_changes_principal(self) -> crate::Result { + match self.response { + MethodResponse::QueryChangesPrincipal(response) => Ok(response), + MethodResponse::Error(err) => Err(err.into()), + _ => Err("Response type mismatch".into()), + } + } + + pub fn unwrap_set_principal(self) -> crate::Result { + match self.response { + MethodResponse::SetPrincipal(response) => Ok(response), + MethodResponse::Error(err) => Err(err.into()), + _ => Err("Response type mismatch".into()), + } + } + pub fn unwrap_echo(self) -> crate::Result { match self.response { MethodResponse::Echo(response) => Ok(response), @@ -629,6 +691,26 @@ impl<'de> Visitor<'de> for TaggedMethodResponseVisitor { seq.next_element()? .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, ), + Method::GetPrincipal => MethodResponse::GetPrincipal( + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, + ), + Method::ChangesPrincipal => MethodResponse::ChangesPrincipal( + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, + ), + Method::QueryPrincipal => MethodResponse::QueryPrincipal( + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, + ), + Method::QueryChangesPrincipal => MethodResponse::QueryChangesPrincipal( + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, + ), + Method::SetPrincipal => MethodResponse::SetPrincipal( + seq.next_element()? + .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, + ), Method::Error => MethodResponse::Error( seq.next_element()? .ok_or_else(|| serde::de::Error::custom("Expected a method response"))?, diff --git a/src/core/set.rs b/src/core/set.rs index 03846c2..7fb41a1 100644 --- a/src/core/set.rs +++ b/src/core/set.rs @@ -383,3 +383,7 @@ pub fn date_not_set(date: &Option>) -> bool { pub fn list_not_set(list: &Option>) -> bool { matches!(list, Some(list) if list.is_empty() ) } + +pub fn map_not_set(list: &Option>) -> bool { + matches!(list, Some(list) if list.is_empty() ) +} diff --git a/src/lib.rs b/src/lib.rs index 6e0c89c..6bf7ced 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub mod email_submission; pub mod event_source; pub mod identity; pub mod mailbox; +pub mod principal; pub mod push_subscription; pub mod thread; pub mod vacation_response; @@ -36,6 +37,10 @@ pub enum URI { Calendars, #[serde(rename = "urn:ietf:params:jmap:websocket")] WebSocket, + #[serde(rename = "urn:ietf:params:jmap:principals")] + Principals, + #[serde(rename = "urn:ietf:params:jmap:principals:owner")] + PrincipalsOwner, } impl AsRef for URI { @@ -48,6 +53,8 @@ impl AsRef for URI { URI::Contacts => "urn:ietf:params:jmap:contacts", URI::Calendars => "urn:ietf:params:jmap:calendars", URI::WebSocket => "urn:ietf:params:jmap:websocket", + URI::Principals => "urn:ietf:params:jmap:principals", + URI::PrincipalsOwner => "urn:ietf:params:jmap:principals:owner", } } } @@ -114,6 +121,16 @@ pub enum Method { GetVacationResponse, #[serde(rename = "VacationResponse/set")] SetVacationResponse, + #[serde(rename = "Principal/get")] + GetPrincipal, + #[serde(rename = "Principal/changes")] + ChangesPrincipal, + #[serde(rename = "Principal/query")] + QueryPrincipal, + #[serde(rename = "Principal/queryChanges")] + QueryChangesPrincipal, + #[serde(rename = "Principal/set")] + SetPrincipal, #[serde(rename = "error")] Error, } diff --git a/src/principal/get.rs b/src/principal/get.rs new file mode 100644 index 0000000..69386b6 --- /dev/null +++ b/src/principal/get.rs @@ -0,0 +1,71 @@ +use std::collections::HashMap; + +use crate::{core::get::GetObject, Get, Set}; + +use super::{Principal, Type, ACL, DKIM}; + +impl Principal { + pub fn id(&self) -> &str { + self.id.as_ref().unwrap() + } + + pub fn unwrap_id(self) -> String { + self.id.unwrap() + } + + pub fn ptype(&self) -> Option<&Type> { + self.ptype.as_ref() + } + + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + pub fn email(&self) -> Option<&str> { + self.email.as_deref() + } + + pub fn description(&self) -> Option<&str> { + self.description.as_deref() + } + + pub fn secret(&self) -> Option<&str> { + self.secret.as_deref() + } + + pub fn picture(&self) -> Option<&str> { + self.picture.as_deref() + } + + pub fn quota(&self) -> Option { + self.quota + } + + pub fn capabilities(&self) -> Option<&[String]> { + self.capabilities.as_deref() + } + + pub fn aliases(&self) -> Option<&[String]> { + self.aliases.as_deref() + } + + pub fn members(&self) -> Option<&[String]> { + self.members.as_deref() + } + + pub fn dkim(&self) -> Option<&DKIM> { + self.dkim.as_ref() + } + + pub fn acl(&self) -> Option<&HashMap>> { + self.acl.as_ref() + } +} + +impl GetObject for Principal { + type GetArguments = (); +} + +impl GetObject for Principal { + type GetArguments = (); +} diff --git a/src/principal/helpers.rs b/src/principal/helpers.rs new file mode 100644 index 0000000..5fef54b --- /dev/null +++ b/src/principal/helpers.rs @@ -0,0 +1,106 @@ +use crate::{ + client::Client, + core::{ + changes::{ChangesRequest, ChangesResponse}, + get::GetRequest, + query::{QueryRequest, QueryResponse}, + query_changes::{QueryChangesRequest, QueryChangesResponse}, + request::{Arguments, Request}, + response::{PrincipalGetResponse, PrincipalSetResponse}, + set::SetRequest, + }, + Get, Method, Set, +}; + +use super::Principal; + +impl Client { + pub async fn individual_create( + &mut self, + name: impl Into, + parent_id: Option>, + ) -> crate::Result { + /*let mut request = self.build(); + let id = request + .set_mailbox() + .create() + .name(name) + .role(role) + .parent_id(parent_id) + .create_id() + .unwrap(); + request + .send_single::() + .await? + .created(&id)*/ + todo!() + } +} + +impl Request<'_> { + pub fn get_principal(&mut self) -> &mut GetRequest> { + self.add_method_call( + Method::GetPrincipal, + Arguments::principal_get(self.params(Method::GetPrincipal)), + ) + .principal_get_mut() + } + + pub async fn send_get_principal(self) -> crate::Result { + self.send_single().await + } + + pub fn changes_principal(&mut self, since_state: impl Into) -> &mut ChangesRequest { + self.add_method_call( + Method::ChangesPrincipal, + Arguments::changes(self.params(Method::ChangesPrincipal), since_state.into()), + ) + .changes_mut() + } + + pub async fn send_changes_principal(self) -> crate::Result>> { + self.send_single().await + } + + pub fn query_principal(&mut self) -> &mut QueryRequest> { + self.add_method_call( + Method::QueryPrincipal, + Arguments::principal_query(self.params(Method::QueryPrincipal)), + ) + .principal_query_mut() + } + + pub async fn send_query_principal(self) -> crate::Result { + self.send_single().await + } + + pub fn query_principal_changes( + &mut self, + since_query_state: impl Into, + ) -> &mut QueryChangesRequest> { + self.add_method_call( + Method::QueryChangesPrincipal, + Arguments::principal_query_changes( + self.params(Method::QueryChangesPrincipal), + since_query_state.into(), + ), + ) + .principal_query_changes_mut() + } + + pub async fn send_query_principal_changes(self) -> crate::Result { + self.send_single().await + } + + pub fn set_principal(&mut self) -> &mut SetRequest> { + self.add_method_call( + Method::SetPrincipal, + Arguments::principal_set(self.params(Method::SetPrincipal)), + ) + .principal_set_mut() + } + + pub async fn send_set_principal(self) -> crate::Result { + self.send_single().await + } +} diff --git a/src/principal/mod.rs b/src/principal/mod.rs new file mode 100644 index 0000000..ce92d65 --- /dev/null +++ b/src/principal/mod.rs @@ -0,0 +1,184 @@ +pub mod get; +pub mod helpers; +pub mod query; +pub mod set; + +use crate::core::set::{list_not_set, map_not_set, string_not_set}; +use std::{collections::HashMap, fmt::Display}; + +use serde::{Deserialize, Serialize}; + +use crate::{ + core::{changes::ChangesObject, Object}, + Get, Set, +}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct Principal { + #[serde(skip)] + _create_id: Option, + + #[serde(skip)] + _state: std::marker::PhantomData, + + #[serde(skip_serializing_if = "Option::is_none")] + id: Option, + #[serde(rename = "type")] + #[serde(skip_serializing_if = "Option::is_none")] + ptype: Option, + #[serde(skip_serializing_if = "string_not_set")] + name: Option, + #[serde(skip_serializing_if = "string_not_set")] + description: Option, + #[serde(skip_serializing_if = "string_not_set")] + email: Option, + #[serde(skip_serializing_if = "string_not_set")] + timezone: Option, + #[serde(skip_serializing_if = "list_not_set")] + capabilities: Option>, + #[serde(skip_serializing_if = "list_not_set")] + aliases: Option>, + #[serde(skip_serializing_if = "string_not_set")] + secret: Option, + #[serde(skip_serializing_if = "Option::is_none")] + dkim: Option, + #[serde(skip_serializing_if = "Option::is_none")] + quota: Option, + #[serde(skip_serializing_if = "string_not_set")] + picture: Option, + #[serde(skip_serializing_if = "list_not_set")] + members: Option>, + #[serde(skip_serializing_if = "map_not_set")] + acl: Option>>, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy)] +pub enum Property { + #[serde(rename = "id")] + Id = 0, + #[serde(rename = "type")] + Type = 1, + #[serde(rename = "name")] + Name = 2, + #[serde(rename = "description")] + Description = 3, + #[serde(rename = "email")] + Email = 4, + #[serde(rename = "timezone")] + Timezone = 5, + #[serde(rename = "capabilities")] + Capabilities = 6, + #[serde(rename = "aliases")] + Aliases = 7, + #[serde(rename = "secret")] + Secret = 8, + #[serde(rename = "dkim")] + DKIM = 9, + #[serde(rename = "quota")] + Quota = 10, + #[serde(rename = "picture")] + Picture = 11, + #[serde(rename = "members")] + Members = 12, + #[serde(rename = "acl")] + ACL = 13, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash, Copy)] +pub enum ACL { + #[serde(rename = "read")] + Read = 0, + #[serde(rename = "modify")] + Modify = 1, + #[serde(rename = "delete")] + Delete = 2, + #[serde(rename = "readItems")] + ReadItems = 3, + #[serde(rename = "addItems")] + AddItems = 4, + #[serde(rename = "modifyItems")] + ModifyItems = 5, + #[serde(rename = "removeItems")] + RemoveItems = 6, + #[serde(rename = "createChild")] + CreateChild = 7, + #[serde(rename = "administer")] + Administer = 8, + #[serde(rename = "setSeen")] + SetSeen = 9, + #[serde(rename = "setKeywords")] + SetKeywords = 10, + #[serde(rename = "submit")] + Submit = 11, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum Type { + #[serde(rename = "individual")] + Individual, + #[serde(rename = "group")] + Group, + #[serde(rename = "resource")] + Resource, + #[serde(rename = "location")] + Location, + #[serde(rename = "domain")] + Domain, + #[serde(rename = "list")] + List, + #[serde(rename = "other")] + Other, +} + +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub struct DKIM { + #[serde(rename = "dkimSelector")] + pub dkim_selector: Option, + #[serde(rename = "dkimExpiration")] + pub dkim_expiration: Option, +} + +impl Display for Property { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Property::Id => write!(f, "id"), + Property::Type => write!(f, "type"), + Property::Name => write!(f, "name"), + Property::Description => write!(f, "description"), + Property::Email => write!(f, "email"), + Property::Timezone => write!(f, "timezone"), + Property::Capabilities => write!(f, "capabilities"), + Property::Aliases => write!(f, "aliases"), + Property::Secret => write!(f, "secret"), + Property::DKIM => write!(f, "dkim"), + Property::Quota => write!(f, "quota"), + Property::Picture => write!(f, "picture"), + Property::Members => write!(f, "members"), + Property::ACL => write!(f, "acl"), + } + } +} + +impl Object for Principal { + type Property = Property; + + fn requires_account_id() -> bool { + true + } +} + +impl Object for Principal { + type Property = Property; + + fn requires_account_id() -> bool { + true + } +} + +impl ChangesObject for Principal { + type ChangesResponse = (); +} + +impl ChangesObject for Principal { + type ChangesResponse = (); +} diff --git a/src/principal/query.rs b/src/principal/query.rs new file mode 100644 index 0000000..8c9e81f --- /dev/null +++ b/src/principal/query.rs @@ -0,0 +1,119 @@ +use serde::Serialize; + +use crate::{ + core::query::{self, QueryObject}, + Set, +}; + +use super::{Principal, Type}; + +#[derive(Serialize, Clone, Debug)] +#[serde(untagged)] +pub enum Filter { + Email { + #[serde(rename = "email")] + value: String, + }, + Name { + #[serde(rename = "name")] + value: String, + }, + Text { + #[serde(rename = "text")] + value: String, + }, + Type { + #[serde(rename = "type")] + value: Type, + }, + Timezone { + #[serde(rename = "timezone")] + value: String, + }, + Members { + #[serde(rename = "members")] + value: String, + }, + QuotaLt { + #[serde(rename = "quotaLowerThan")] + value: u32, + }, + QuotaGt { + #[serde(rename = "quotaGreaterThan")] + value: u32, + }, +} + +#[derive(Serialize, Debug, Clone)] +#[serde(tag = "property")] +pub enum Comparator { + #[serde(rename = "type")] + Type, + #[serde(rename = "name")] + Name, + #[serde(rename = "email")] + Email, +} + +impl Filter { + pub fn name(value: impl Into) -> Self { + Filter::Name { + value: value.into(), + } + } + pub fn email(value: impl Into) -> Self { + Filter::Email { + value: value.into(), + } + } + pub fn text(value: impl Into) -> Self { + Filter::Text { + value: value.into(), + } + } + pub fn timezone(value: impl Into) -> Self { + Filter::Timezone { + value: value.into(), + } + } + + pub fn members(value: impl Into) -> Self { + Filter::Members { + value: value.into(), + } + } + + pub fn ptype(value: Type) -> Self { + Filter::Type { value } + } + + pub fn quota_lower_than(value: u32) -> Self { + Filter::QuotaLt { value } + } + + pub fn quota_greater_than(value: u32) -> Self { + Filter::QuotaGt { value } + } +} + +impl Comparator { + pub fn name() -> query::Comparator { + query::Comparator::new(Comparator::Name) + } + + pub fn email() -> query::Comparator { + query::Comparator::new(Comparator::Email) + } + + pub fn ptype() -> query::Comparator { + query::Comparator::new(Comparator::Type) + } +} + +impl QueryObject for Principal { + type QueryArguments = (); + + type Filter = Filter; + + type Sort = Comparator; +} diff --git a/src/principal/set.rs b/src/principal/set.rs new file mode 100644 index 0000000..f84fe83 --- /dev/null +++ b/src/principal/set.rs @@ -0,0 +1,125 @@ +use std::collections::HashMap; + +use crate::{core::set::SetObject, Get, Set}; + +use super::{Principal, Type, ACL, DKIM}; + +impl Principal { + pub fn name(&mut self, name: Option>) -> &mut Self { + self.name = name.map(|s| s.into()); + self + } + + pub fn description(&mut self, description: Option>) -> &mut Self { + self.description = description.map(|s| s.into()); + self + } + + pub fn email(&mut self, email: Option>) -> &mut Self { + self.email = email.map(|s| s.into()); + self + } + + pub fn secret(&mut self, secret: Option>) -> &mut Self { + self.secret = secret.map(|s| s.into()); + self + } + + pub fn timezone(&mut self, timezone: Option>) -> &mut Self { + self.timezone = timezone.map(|s| s.into()); + self + } + + pub fn picture(&mut self, picture: Option>) -> &mut Self { + self.picture = picture.map(|s| s.into()); + self + } + + pub fn quota(&mut self, quota: Option) -> &mut Self { + self.quota = quota; + self + } + + pub fn ptype(&mut self, ptype: Type) -> &mut Self { + self.ptype = ptype.into(); + self + } + + pub fn dkim(&mut self, dkim: DKIM) -> &mut Self { + self.dkim = dkim.into(); + self + } + + pub fn acl(&mut self, acl: Option>>) -> &mut Self { + self.acl = acl; + self + } + + pub fn aliases(&mut self, aliases: Option) -> &mut Self + where + T: IntoIterator, + U: Into, + { + self.aliases = aliases.map(|l| l.into_iter().map(|v| v.into()).collect()); + self + } + + pub fn capabilities(&mut self, capabilities: Option) -> &mut Self + where + T: IntoIterator, + U: Into, + { + self.capabilities = capabilities.map(|l| l.into_iter().map(|v| v.into()).collect()); + self + } + + pub fn members(&mut self, members: Option) -> &mut Self + where + T: IntoIterator, + U: Into, + { + self.members = members.map(|l| l.into_iter().map(|v| v.into()).collect()); + self + } +} + +impl SetObject for Principal { + type SetArguments = (); + + fn new(_create_id: Option) -> Self { + Principal { + _create_id, + _state: Default::default(), + id: None, + ptype: None, + name: "".to_string().into(), + description: "".to_string().into(), + email: "".to_string().into(), + timezone: "".to_string().into(), + capabilities: Vec::with_capacity(0).into(), + aliases: Vec::with_capacity(0).into(), + secret: "".to_string().into(), + dkim: None, + quota: None, + picture: "".to_string().into(), + members: Vec::with_capacity(0).into(), + acl: HashMap::with_capacity(0).into(), + } + } + + fn create_id(&self) -> Option { + self._create_id.map(|id| format!("c{}", id)) + } +} + +impl SetObject for Principal { + type SetArguments = (); + + fn new(_create_id: Option) -> Self { + unimplemented!() + } + + fn create_id(&self) -> Option { + None + } +}