use std::os::fd::OwnedFd;
use serde::{Deserialize, Serialize};
use zbus::zvariant::{
self, Optional, Type,
as_value::{self, optional},
};
use super::{HandleToken, Request};
use crate::{ActivationToken, Error, Uri, WindowIdentifier, proxy::Proxy};
#[derive(Serialize, Deserialize, Type, Debug, Default)]
#[zvariant(signature = "dict")]
pub struct EmailOptions {
#[serde(with = "as_value", skip_deserializing)]
handle_token: HandleToken,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
address: Option<String>,
#[serde(default, with = "as_value", skip_serializing_if = "Vec::is_empty")]
addresses: Vec<String>,
#[serde(default, with = "as_value", skip_serializing_if = "Vec::is_empty")]
cc: Vec<String>,
#[serde(default, with = "as_value", skip_serializing_if = "Vec::is_empty")]
bcc: Vec<String>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
subject: Option<String>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
body: Option<String>,
#[serde(
with = "as_value",
skip_serializing_if = "Vec::is_empty",
skip_deserializing
)]
attachment_fds: Vec<zvariant::OwnedFd>,
#[serde(default, with = "as_value", skip_serializing)]
#[cfg_attr(not(feature = "backend"), allow(dead_code))]
attachments: Vec<Uri>,
#[serde(default, with = "optional", skip_serializing_if = "Option::is_none")]
activation_token: Option<ActivationToken>,
}
impl EmailOptions {
#[cfg(feature = "backend")]
pub fn address(&self) -> Option<&str> {
self.address.as_deref()
}
#[cfg(feature = "backend")]
pub fn addresses(&self) -> &[String] {
&self.addresses
}
#[cfg(feature = "backend")]
pub fn cc(&self) -> &[String] {
&self.cc
}
#[cfg(feature = "backend")]
pub fn bcc(&self) -> &[String] {
&self.bcc
}
#[cfg(feature = "backend")]
pub fn subject(&self) -> Option<&str> {
self.subject.as_deref()
}
#[cfg(feature = "backend")]
pub fn body(&self) -> Option<&str> {
self.body.as_deref()
}
#[cfg(feature = "backend")]
pub fn attachments(&self) -> &[Uri] {
&self.attachments
}
#[cfg(feature = "backend")]
pub fn activation_token(&self) -> Option<&ActivationToken> {
self.activation_token.as_ref()
}
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.Email")]
pub struct EmailProxy(Proxy<'static>);
impl EmailProxy {
pub async fn new() -> Result<Self, Error> {
let proxy = Proxy::new_desktop("org.freedesktop.portal.Email").await?;
Ok(Self(proxy))
}
pub async fn with_connection(connection: zbus::Connection) -> Result<Self, Error> {
let proxy =
Proxy::new_desktop_with_connection(connection, "org.freedesktop.portal.Email").await?;
Ok(Self(proxy))
}
pub fn version(&self) -> u32 {
self.0.version()
}
#[doc(alias = "ComposeEmail")]
pub async fn compose(
&self,
identifier: Option<&WindowIdentifier>,
options: EmailOptions,
) -> Result<Request<()>, Error> {
let identifier = Optional::from(identifier);
self.0
.empty_request(
&options.handle_token,
"ComposeEmail",
&(identifier, &options),
)
.await
}
}
impl std::ops::Deref for EmailProxy {
type Target = zbus::Proxy<'static>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(Debug, Default)]
#[doc(alias = "xdp_portal_compose_email")]
pub struct EmailRequest {
identifier: Option<WindowIdentifier>,
options: EmailOptions,
connection: Option<zbus::Connection>,
}
impl EmailRequest {
#[must_use]
pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
self.identifier = identifier.into();
self
}
#[must_use]
pub fn address<'a>(mut self, address: impl Into<Option<&'a str>>) -> Self {
self.options.address = address.into().map(ToOwned::to_owned);
self
}
#[must_use]
pub fn addresses<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
mut self,
addresses: impl Into<Option<P>>,
) -> Self {
if let Some(a) = addresses.into() {
self.options.addresses = a.into_iter().map(|s| s.as_ref().to_owned()).collect();
}
self
}
#[must_use]
pub fn bcc<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
mut self,
bcc: impl Into<Option<P>>,
) -> Self {
if let Some(a) = bcc.into() {
self.options.bcc = a.into_iter().map(|s| s.as_ref().to_owned()).collect();
}
self
}
#[must_use]
pub fn cc<P: IntoIterator<Item = I>, I: AsRef<str> + Type + Serialize>(
mut self,
cc: impl Into<Option<P>>,
) -> Self {
if let Some(a) = cc.into() {
self.options.cc = a.into_iter().map(|s| s.as_ref().to_owned()).collect();
}
self
}
#[must_use]
pub fn subject<'a>(mut self, subject: impl Into<Option<&'a str>>) -> Self {
self.options.subject = subject.into().map(ToOwned::to_owned);
self
}
#[must_use]
pub fn body<'a>(mut self, body: impl Into<Option<&'a str>>) -> Self {
self.options.body = body.into().map(ToOwned::to_owned);
self
}
#[must_use]
pub fn attach_multiple(
mut self,
attachments: impl IntoIterator<Item = impl Into<OwnedFd>>,
) -> Self {
self.options.attachment_fds = attachments
.into_iter()
.map(|fd| zvariant::OwnedFd::from(fd.into()))
.collect();
self
}
#[must_use]
pub fn attach(mut self, attachment: OwnedFd) -> Self {
self.add_attachment(attachment);
self
}
#[must_use]
pub fn activation_token(
mut self,
activation_token: impl Into<Option<ActivationToken>>,
) -> Self {
self.options.activation_token = activation_token.into();
self
}
pub fn add_attachment(&mut self, attachment: OwnedFd) {
let attachment = zvariant::OwnedFd::from(attachment);
self.options.attachment_fds.push(attachment);
}
#[must_use]
pub fn connection(mut self, connection: Option<zbus::Connection>) -> Self {
self.connection = connection;
self
}
pub async fn send(mut self) -> Result<Request<()>, Error> {
let proxy = if let Some(connection) = self.connection {
EmailProxy::with_connection(connection).await?
} else {
EmailProxy::new().await?
};
if proxy.version() < 4 {
self.options.activation_token = None;
}
proxy.compose(self.identifier.as_ref(), self.options).await
}
}