ashpd/documents/file_transfer.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
//! # Examples
//!
//! ```rust,no_run
//! use std::{fs::File, os::fd::AsFd};
//!
//! use ashpd::documents::FileTransfer;
//!
//! async fn run() -> ashpd::Result<()> {
//! let proxy = FileTransfer::new().await?;
//!
//! let key = proxy.start_transfer(true, true).await?;
//! let file = File::open("/home/bilelmoussaoui/Downloads/adwaita-night.jpg").unwrap();
//! proxy.add_files(&key, &[&file.as_fd()]).await?;
//!
//! // The files would be retrieved by another process
//! let files = proxy.retrieve_files(&key).await?;
//! println!("{:#?}", files);
//!
//! proxy.stop_transfer(&key).await?;
//!
//! Ok(())
//! }
//! ```
use std::{collections::HashMap, os::fd::AsFd};
use futures_util::Stream;
use zbus::zvariant::{Fd, SerializeDict, Type, Value};
use crate::{proxy::Proxy, Error};
#[derive(SerializeDict, Debug, Type, Default)]
/// Specified options for a [`FileTransfer::start_transfer`] request.
#[zvariant(signature = "dict")]
struct TransferOptions {
/// Whether to allow the chosen application to write to the files.
writeable: Option<bool>,
/// Whether to stop the transfer automatically after the first
/// [`retrieve_files()`][`FileTransfer::retrieve_files`] call.
#[zvariant(rename = "autostop")]
auto_stop: Option<bool>,
}
impl TransferOptions {
/// Sets whether the chosen application can write to the files or not.
#[must_use]
pub fn writeable(mut self, writeable: impl Into<Option<bool>>) -> Self {
self.writeable = writeable.into();
self
}
/// Whether to stop the transfer automatically after the first
/// [`retrieve_files()`][`FileTransfer::retrieve_files`] call.
#[must_use]
pub fn auto_stop(mut self, auto_stop: impl Into<Option<bool>>) -> Self {
self.auto_stop = auto_stop.into();
self
}
}
/// The interface operates as a middle-man between apps when transferring files
/// via drag-and-drop or copy-paste, taking care of the necessary exporting of
/// files in the document portal.
///
/// Toolkits are expected to use the application/vnd.portal.filetransfer
/// mimetype when using this mechanism for file exchange via copy-paste or
/// drag-and-drop.
///
/// The data that is transmitted with this mimetype should be the key returned
/// by the StartTransfer method. Upon receiving this mimetype, the target should
/// call RetrieveFiles with the key, to obtain the list of files. The portal
/// will take care of exporting files in the document store as necessary to make
/// them accessible to the target.
///
/// Wrapper of the DBus interface: [`org.freedesktop.portal.FileTransfer`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html).
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.FileTransfer")]
pub struct FileTransfer<'a>(Proxy<'a>);
impl<'a> FileTransfer<'a> {
/// Create a new instance of [`FileTransfer`].
pub async fn new() -> Result<FileTransfer<'a>, Error> {
let proxy = Proxy::new_documents("org.freedesktop.portal.FileTransfer").await?;
Ok(Self(proxy))
}
/// Adds files to a session. This method can be called multiple times on a
/// given session. **Note** only regular files (not directories) can be
/// added.
///
/// # Arguments
///
/// * `key` - A key returned by
/// [`start_transfer()`][`FileTransfer::start_transfer`].
/// * `fds` - A list of file descriptors of the files to register.
///
/// # Specifications
///
/// See also [`AddFiles`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html#org-freedesktop-portal-filetransfer-addfiles).
#[doc(alias = "AddFiles")]
pub async fn add_files(&self, key: &str, fds: &[impl AsFd]) -> Result<(), Error> {
// `options` parameter doesn't seems to be used yet
let options: HashMap<&str, Value<'_>> = HashMap::new();
let files: Vec<Fd> = fds.iter().map(Fd::from).collect();
self.0.call("AddFiles", &(key, files, options)).await
}
/// Retrieves files that were previously added to the session with
/// [`add_files()`][`FileTransfer::add_files`]. The files will be
/// exported in the document portal as-needed for the caller, and they
/// will be writeable if the owner of the session allowed it.
///
/// # Arguments
///
/// * `key` - A key returned by
/// [`start_transfer()`][`FileTransfer::start_transfer`].
///
/// # Returns
///
/// The list of file paths.
///
/// # Specifications
///
/// See also [`RetrieveFiles`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html#org-freedesktop-portal-filetransfer-retrievefiles).
#[doc(alias = "RetrieveFiles")]
pub async fn retrieve_files(&self, key: &str) -> Result<Vec<String>, Error> {
// `options` parameter doesn't seems to be used yet
// see https://github.com/GNOME/gtk/blob/master/gdk/filetransferportal.c#L284
let options: HashMap<&str, Value<'_>> = HashMap::new();
self.0.call("RetrieveFiles", &(key, options)).await
}
/// Starts a session for a file transfer.
/// The caller should call [`add_files()`][`FileTransfer::add_files`]
/// at least once, to add files to this session.
///
/// # Arguments
///
/// * `writeable` - Sets whether the chosen application can write to the
/// files or not.
/// * `auto_stop` - Whether to stop the transfer automatically after the
/// first [`retrieve_files()`][`FileTransfer::retrieve_files`] call.
///
/// # Returns
///
/// Key that can be passed to
/// [`retrieve_files()`][`FileTransfer::retrieve_files`] to obtain the
/// files.
///
/// # Specifications
///
/// See also [`StartTransfer`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html#org-freedesktop-portal-filetransfer-starttransfer).
pub async fn start_transfer(&self, writeable: bool, auto_stop: bool) -> Result<String, Error> {
let options = TransferOptions::default()
.writeable(writeable)
.auto_stop(auto_stop);
self.0.call("StartTransfer", &(options)).await
}
/// Ends the transfer.
/// Further calls to [`add_files()`][`FileTransfer::add_files`] or
/// [`retrieve_files()`][`FileTransfer::retrieve_files`] for this key
/// will return an error.
///
/// # Arguments
///
/// * `key` - A key returned by
/// [`start_transfer()`][`FileTransfer::start_transfer`].
///
/// # Specifications
///
/// See also [`StopTransfer`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html#org-freedesktop-portal-filetransfer-stoptransfer).
#[doc(alias = "StopTransfer")]
pub async fn stop_transfer(&self, key: &str) -> Result<(), Error> {
self.0.call("StopTransfer", &(key)).await
}
/// Emitted when the transfer is closed.
///
/// # Returns
///
/// * The key returned by
/// [`start_transfer()`][`FileTransfer::start_transfer`].
///
/// # Specifications
///
/// See also [`TransferClosed`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.FileTransfer.html#org-freedesktop-portal-filetransfer-transferclosed).
#[doc(alias = "TransferClosed")]
pub async fn transfer_closed(&self) -> Result<impl Stream<Item = String>, Error> {
self.0.signal("TransferClosed").await
}
}
impl<'a> std::ops::Deref for FileTransfer<'a> {
type Target = zbus::Proxy<'a>;
fn deref(&self) -> &Self::Target {
&self.0
}
}