use super::{HandleToken, DESTINATION, PATH};
use crate::{helpers::call_request_method, Error, WindowIdentifier};
use serde::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use zvariant_derive::{DeserializeDict, SerializeDict, Type, TypeDict};
#[derive(Serialize, Deserialize, Type, Clone, Debug)]
pub struct FileFilter(String, Vec<(FilterType, String)>);
#[derive(Serialize_repr, Clone, Deserialize_repr, PartialEq, Debug, Type)]
#[repr(u32)]
#[doc(hidden)]
enum FilterType {
GlobPattern = 0,
MimeType = 1,
}
impl FileFilter {
pub fn new(label: &str) -> Self {
Self(label.to_string(), vec![])
}
pub fn mimetype(mut self, mimetype: &str) -> Self {
self.1.push((FilterType::MimeType, mimetype.to_string()));
self
}
pub fn glob(mut self, pattern: &str) -> Self {
self.1.push((FilterType::GlobPattern, pattern.to_string()));
self
}
}
#[derive(Serialize, Deserialize, Type, Clone, Debug)]
pub struct Choice(String, String, Vec<(String, String)>, String);
impl Choice {
pub fn boolean(id: &str, label: &str, state: bool) -> Self {
Self::new(id, label, &state.to_string())
}
pub fn new(id: &str, label: &str, initial_selection: &str) -> Self {
Self(
id.to_string(),
label.to_string(),
vec![],
initial_selection.to_string(),
)
}
pub fn insert(mut self, key: &str, value: &str) -> Self {
self.2.push((key.to_string(), value.to_string()));
self
}
pub fn id(&self) -> &str {
&self.0
}
pub fn label(&self) -> &str {
&self.1
}
pub fn initial_selection(&self) -> &str {
&self.3
}
}
#[derive(SerializeDict, DeserializeDict, TypeDict, Clone, Debug, Default)]
pub struct OpenFileOptions {
handle_token: HandleToken,
accept_label: Option<String>,
modal: Option<bool>,
multiple: Option<bool>,
directory: Option<bool>,
filters: Vec<FileFilter>,
current_filter: Option<FileFilter>,
choices: Vec<Choice>,
}
impl OpenFileOptions {
pub fn accept_label(mut self, accept_label: &str) -> Self {
self.accept_label = Some(accept_label.to_string());
self
}
pub fn modal(mut self, modal: bool) -> Self {
self.modal = Some(modal);
self
}
pub fn multiple(mut self, multiple: bool) -> Self {
self.multiple = Some(multiple);
self
}
pub fn directory(mut self, directory: bool) -> Self {
self.directory = Some(directory);
self
}
pub fn add_filter(mut self, filter: FileFilter) -> Self {
self.filters.push(filter);
self
}
pub fn current_filter(mut self, current_filter: FileFilter) -> Self {
self.current_filter = Some(current_filter);
self
}
pub fn add_choice(mut self, choice: Choice) -> Self {
self.choices.push(choice);
self
}
}
#[derive(SerializeDict, DeserializeDict, TypeDict, Debug, Default)]
pub struct SaveFileOptions {
handle_token: HandleToken,
accept_label: Option<String>,
modal: Option<bool>,
current_name: Option<String>,
current_folder: Option<Vec<u8>>,
current_file: Option<Vec<u8>>,
filters: Vec<FileFilter>,
current_filter: Option<FileFilter>,
choices: Vec<Choice>,
}
impl SaveFileOptions {
pub fn accept_label(mut self, accept_label: &str) -> Self {
self.accept_label = Some(accept_label.to_string());
self
}
pub fn current_name(mut self, current_name: &str) -> Self {
self.current_name = Some(current_name.to_string());
self
}
pub fn current_folder(mut self, current_folder: &str) -> Self {
let mut bytes: Vec<u8> = current_folder.into();
bytes.push(0);
self.current_folder = Some(bytes);
self
}
pub fn current_file(mut self, current_file: &str) -> Self {
let mut bytes: Vec<u8> = current_file.into();
bytes.push(0);
self.current_file = Some(bytes);
self
}
pub fn modal(mut self, modal: bool) -> Self {
self.modal = Some(modal);
self
}
pub fn add_filter(mut self, filter: FileFilter) -> Self {
self.filters.push(filter);
self
}
pub fn current_filter(mut self, current_filter: FileFilter) -> Self {
self.current_filter = Some(current_filter);
self
}
pub fn add_choice(mut self, choice: Choice) -> Self {
self.choices.push(choice);
self
}
}
#[derive(SerializeDict, DeserializeDict, TypeDict, Debug, Default)]
pub struct SaveFilesOptions {
handle_token: HandleToken,
accept_label: Option<String>,
modal: Option<bool>,
choices: Vec<Choice>,
current_folder: Option<Vec<u8>>,
files: Option<Vec<Vec<u8>>>,
}
impl SaveFilesOptions {
pub fn accept_label(mut self, accept_label: &str) -> Self {
self.accept_label = Some(accept_label.to_string());
self
}
pub fn modal(mut self, modal: bool) -> Self {
self.modal = Some(modal);
self
}
pub fn add_choice(mut self, choice: Choice) -> Self {
self.choices.push(choice);
self
}
pub fn current_folder(mut self, current_folder: &str) -> Self {
let mut bytes: Vec<u8> = current_folder.into();
bytes.push(0);
self.current_folder = Some(bytes);
self
}
pub fn files<S: AsRef<str> + zvariant::Type + Serialize>(mut self, files: &[S]) -> Self {
self.files = Some(
files
.iter()
.map(|s| {
let mut bytes: Vec<u8> = s.as_ref().into();
bytes.push(0);
bytes
})
.collect(),
);
self
}
}
#[derive(Debug, TypeDict, SerializeDict, Clone, DeserializeDict)]
pub struct SelectedFiles {
uris: Vec<String>,
choices: Option<Vec<(String, String)>>,
}
impl SelectedFiles {
pub fn uris(&self) -> &[String] {
self.uris.as_slice()
}
pub fn choices(&self) -> &[(String, String)] {
self.choices.as_deref().unwrap_or_default()
}
}
#[derive(Debug)]
#[doc(alias = "org.freedesktop.portal.FileChooser")]
pub struct FileChooserProxy<'a>(zbus::azync::Proxy<'a>);
impl<'a> FileChooserProxy<'a> {
pub async fn new(connection: &zbus::azync::Connection) -> Result<FileChooserProxy<'a>, Error> {
let proxy = zbus::ProxyBuilder::new_bare(connection)
.interface("org.freedesktop.portal.FileChooser")
.path(PATH)?
.destination(DESTINATION)
.build_async()
.await?;
Ok(Self(proxy))
}
pub fn inner(&self) -> &zbus::azync::Proxy<'_> {
&self.0
}
#[doc(alias = "OpenFile")]
pub async fn open_file(
&self,
identifier: WindowIdentifier,
title: &str,
options: OpenFileOptions,
) -> Result<SelectedFiles, Error> {
call_request_method(
&self.0,
&options.handle_token,
"OpenFile",
&(identifier, title, &options),
)
.await
}
#[doc(alias = "SaveFile")]
pub async fn save_file(
&self,
identifier: WindowIdentifier,
title: &str,
options: SaveFileOptions,
) -> Result<SelectedFiles, Error> {
call_request_method(
&self.0,
&options.handle_token,
"SaveFile",
&(identifier, title, &options),
)
.await
}
#[doc(alias = "SaveFiles")]
pub async fn save_files(
&self,
identifier: WindowIdentifier,
title: &str,
options: SaveFilesOptions,
) -> Result<SelectedFiles, Error> {
call_request_method(
&self.0,
&options.handle_token,
"SaveFiles",
&(identifier, title, &options),
)
.await
}
}