ashpd/desktop/
open_uri.rs

1//! Open a URI or a directory.
2//!
3//! Wrapper of the DBus interface: [`org.freedesktop.portal.OpenURI`](https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.OpenURI.html).
4//!
5//! # Examples
6//!
7//! ## Open a file
8//!
9//! ```rust,no_run
10//! use std::{fs::File, os::fd::AsFd};
11//!
12//! use ashpd::desktop::open_uri::OpenFileRequest;
13//!
14//! async fn run() -> ashpd::Result<()> {
15//!     let file = File::open("/home/bilelmoussaoui/adwaita-day.jpg").unwrap();
16//!     OpenFileRequest::default()
17//!         .ask(true)
18//!         .send_file(&file.as_fd())
19//!         .await?;
20//!     Ok(())
21//! }
22//! ```
23//!
24//! ## Open a file from a URI
25//!
26//!
27//! ```rust,no_run
28//! use ashpd::desktop::open_uri::OpenFileRequest;
29//!
30//! async fn run() -> ashpd::Result<()> {
31//!     let uri =
32//!         url::Url::parse("file:///home/bilelmoussaoui/Downloads/adwaita-night.jpg").unwrap();
33//!     OpenFileRequest::default().ask(true).send_uri(&uri).await?;
34//!     Ok(())
35//! }
36//! ```
37//!
38//! ## Open a directory
39//!
40//! ```rust,no_run
41//! use std::{fs::File, os::fd::AsFd};
42//!
43//! use ashpd::desktop::open_uri::OpenDirectoryRequest;
44//!
45//! async fn run() -> ashpd::Result<()> {
46//!     let directory = File::open("/home/bilelmoussaoui/Downloads").unwrap();
47//!     OpenDirectoryRequest::default()
48//!         .send(&directory.as_fd())
49//!         .await?;
50//!     Ok(())
51//! }
52//! ```
53
54use std::os::fd::AsFd;
55
56use url::Url;
57use zbus::zvariant::{Fd, SerializeDict, Type};
58
59use super::{HandleToken, Request};
60use crate::{proxy::Proxy, ActivationToken, Error, WindowIdentifier};
61
62#[derive(SerializeDict, Type, Debug, Default)]
63#[zvariant(signature = "dict")]
64struct OpenDirOptions {
65    handle_token: HandleToken,
66    activation_token: Option<ActivationToken>,
67}
68
69#[derive(SerializeDict, Type, Debug, Default)]
70#[zvariant(signature = "dict")]
71struct OpenFileOptions {
72    handle_token: HandleToken,
73    writeable: Option<bool>,
74    ask: Option<bool>,
75    activation_token: Option<ActivationToken>,
76}
77
78#[derive(Debug)]
79struct OpenURIProxy<'a>(Proxy<'a>);
80
81impl<'a> OpenURIProxy<'a> {
82    pub async fn new() -> Result<OpenURIProxy<'a>, Error> {
83        let proxy = Proxy::new_desktop("org.freedesktop.portal.OpenURI").await?;
84        Ok(Self(proxy))
85    }
86
87    pub async fn open_directory(
88        &self,
89        identifier: Option<&WindowIdentifier>,
90        directory: &impl AsFd,
91        options: OpenDirOptions,
92    ) -> Result<Request<()>, Error> {
93        let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
94        self.0
95            .empty_request(
96                &options.handle_token,
97                "OpenDirectory",
98                &(&identifier, Fd::from(directory), &options),
99            )
100            .await
101    }
102
103    pub async fn open_file(
104        &self,
105        identifier: Option<&WindowIdentifier>,
106        file: &impl AsFd,
107        options: OpenFileOptions,
108    ) -> Result<Request<()>, Error> {
109        let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
110        self.0
111            .empty_request(
112                &options.handle_token,
113                "OpenFile",
114                &(&identifier, Fd::from(file), &options),
115            )
116            .await
117    }
118
119    pub async fn open_uri(
120        &self,
121        identifier: Option<&WindowIdentifier>,
122        uri: &url::Url,
123        options: OpenFileOptions,
124    ) -> Result<Request<()>, Error> {
125        let identifier = identifier.map(|i| i.to_string()).unwrap_or_default();
126        self.0
127            .empty_request(
128                &options.handle_token,
129                "OpenURI",
130                &(&identifier, uri, &options),
131            )
132            .await
133    }
134}
135
136impl<'a> std::ops::Deref for OpenURIProxy<'a> {
137    type Target = zbus::Proxy<'a>;
138
139    fn deref(&self) -> &Self::Target {
140        &self.0
141    }
142}
143
144#[derive(Debug, Default)]
145#[doc(alias = "org.freedesktop.portal.OpenURI")]
146#[doc(alias = "xdp_portal_open_uri")]
147/// A [builder-pattern] type to open a file.
148///
149/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
150pub struct OpenFileRequest {
151    identifier: Option<WindowIdentifier>,
152    options: OpenFileOptions,
153}
154
155impl OpenFileRequest {
156    #[must_use]
157    /// Sets a window identifier.
158    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
159        self.identifier = identifier.into();
160        self
161    }
162
163    #[must_use]
164    /// Whether the file should be writeable or not.
165    pub fn writeable(mut self, writeable: impl Into<Option<bool>>) -> Self {
166        self.options.writeable = writeable.into();
167        self
168    }
169
170    #[must_use]
171    /// Whether to always ask the user which application to use or not.
172    pub fn ask(mut self, ask: impl Into<Option<bool>>) -> Self {
173        self.options.ask = ask.into();
174        self
175    }
176
177    /// Sets the token that can be used to activate the chosen application.
178    #[must_use]
179    pub fn activation_token(
180        mut self,
181        activation_token: impl Into<Option<ActivationToken>>,
182    ) -> Self {
183        self.options.activation_token = activation_token.into();
184        self
185    }
186
187    /// Send the request for a file.
188    pub async fn send_file(self, file: &impl AsFd) -> Result<Request<()>, Error> {
189        let proxy = OpenURIProxy::new().await?;
190        proxy
191            .open_file(self.identifier.as_ref(), file, self.options)
192            .await
193    }
194
195    /// Send the request for a URI.
196    pub async fn send_uri(self, uri: &Url) -> Result<Request<()>, Error> {
197        let proxy = OpenURIProxy::new().await?;
198        proxy
199            .open_uri(self.identifier.as_ref(), uri, self.options)
200            .await
201    }
202}
203
204#[derive(Debug, Default)]
205#[doc(alias = "xdp_portal_open_directory")]
206#[doc(alias = "org.freedesktop.portal.OpenURI")]
207/// A [builder-pattern] type to open a directory.
208///
209/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
210pub struct OpenDirectoryRequest {
211    identifier: Option<WindowIdentifier>,
212    options: OpenDirOptions,
213}
214
215impl OpenDirectoryRequest {
216    #[must_use]
217    /// Sets a window identifier.
218    pub fn identifier(mut self, identifier: impl Into<Option<WindowIdentifier>>) -> Self {
219        self.identifier = identifier.into();
220        self
221    }
222
223    /// Sets the token that can be used to activate the chosen application.
224    #[must_use]
225    pub fn activation_token(
226        mut self,
227        activation_token: impl Into<Option<ActivationToken>>,
228    ) -> Self {
229        self.options.activation_token = activation_token.into();
230        self
231    }
232
233    /// Send the request.
234    pub async fn send(self, directory: &impl AsFd) -> Result<Request<()>, Error> {
235        let proxy = OpenURIProxy::new().await?;
236        proxy
237            .open_directory(self.identifier.as_ref(), directory, self.options)
238            .await
239    }
240}