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