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