Skip to main content

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}