1use std::os::fd::OwnedFd;
28
29use serde::Serialize;
30use zbus::zvariant::{self, SerializeDict, Type};
31
32use super::{HandleToken, Request};
33use crate::{proxy::Proxy, ActivationToken, Error, WindowIdentifier};
34
35#[derive(SerializeDict, Type, Debug, Default)]
36#[zvariant(signature = "dict")]
37struct EmailOptions {
38 handle_token: HandleToken,
39 address: Option<String>,
40 addresses: Option<Vec<String>>,
41 cc: Option<Vec<String>>,
42 bcc: Option<Vec<String>>,
43 subject: Option<String>,
44 body: Option<String>,
45 attachment_fds: Option<Vec<zvariant::OwnedFd>>,
46 activation_token: Option<ActivationToken>,
47}
48
49#[derive(Debug)]
50#[doc(alias = "org.freedesktop.portal.Email")]
51struct EmailProxy<'a>(Proxy<'a>);
52
53impl<'a> EmailProxy<'a> {
54 pub async fn new() -> Result<EmailProxy<'a>, Error> {
56 let proxy = Proxy::new_desktop("org.freedesktop.portal.Email").await?;
57 Ok(Self(proxy))
58 }
59
60 #[doc(alias = "ComposeEmail")]
74 pub async fn compose(
75 &self,
76 identifier: Option<&WindowIdentifier>,
77 options: EmailOptions,
78 ) -> Result<Request<()>, Error> {
79 let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
80 self.0
81 .empty_request(
82 &options.handle_token,
83 "ComposeEmail",
84 &(&identifier, &options),
85 )
86 .await
87 }
88}
89
90impl<'a> std::ops::Deref for EmailProxy<'a> {
91 type Target = zbus::Proxy<'a>;
92
93 fn deref(&self) -> &Self::Target {
94 &self.0
95 }
96}
97
98#[derive(Debug, Default)]
99#[doc(alias = "xdp_portal_compose_email")]
100pub struct EmailRequest {
104 identifier: Option<WindowIdentifier>,
105 options: EmailOptions,
106}
107
108impl EmailRequest {
109 #[must_use]
111 pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
112 self.identifier = identifier.into();
113 self
114 }
115
116 #[must_use]
118 pub fn address<'a>(mut self, address: impl Into<Option<&'a str>>) -> Self {
119 self.options.address = address.into().map(ToOwned::to_owned);
120 self
121 }
122
123 #[must_use]
125 pub fn addresses<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
126 mut self,
127 addresses: impl Into<Option<P>>,
128 ) -> Self {
129 self.options.addresses = addresses
130 .into()
131 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
132 self
133 }
134
135 #[must_use]
137 pub fn bcc<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
138 mut self,
139 bcc: impl Into<Option<P>>,
140 ) -> Self {
141 self.options.bcc = bcc
142 .into()
143 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
144 self
145 }
146
147 #[must_use]
149 pub fn cc<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
150 mut self,
151 cc: impl Into<Option<P>>,
152 ) -> Self {
153 self.options.cc = cc
154 .into()
155 .map(|a| a.into_iter().map(|s| s.as_ref().to_owned()).collect());
156 self
157 }
158
159 #[must_use]
161 pub fn subject<'a>(mut self, subject: impl Into<Option<&'a str>>) -> Self {
162 self.options.subject = subject.into().map(ToOwned::to_owned);
163 self
164 }
165
166 #[must_use]
168 pub fn body<'a>(mut self, body: impl Into<Option<&'a str>>) -> Self {
169 self.options.body = body.into().map(ToOwned::to_owned);
170 self
171 }
172
173 #[must_use]
175 pub fn attach(mut self, attachment: OwnedFd) -> Self {
176 self.add_attachment(attachment);
177 self
178 }
179
180 #[must_use]
183 pub fn activation_token(
184 mut self,
185 activation_token: impl Into<Option<ActivationToken>>,
186 ) -> Self {
187 self.options.activation_token = activation_token.into();
188 self
189 }
190
191 pub fn add_attachment(&mut self, attachment: OwnedFd) {
193 let attachment = zvariant::OwnedFd::from(attachment);
194 match self.options.attachment_fds {
195 Some(ref mut attachments) => attachments.push(attachment),
196 None => {
197 self.options.attachment_fds.replace(vec![attachment]);
198 }
199 };
200 }
201
202 pub async fn send(self) -> Result<Request<()>, Error> {
204 let proxy = EmailProxy::new().await?;
205 proxy.compose(self.identifier.as_ref(), self.options).await
206 }
207}