From 1046f511496e9f10a36dfdec5c3f5ac646203cd0 Mon Sep 17 00:00:00 2001
From: Jonas Kruckenberg <iterpre@protonmail.com>
Date: Mon, 14 Nov 2022 14:01:25 +0100
Subject: [PATCH] rework dialog module

---
 src/dialog.rs | 788 +++++++++++++++++++++++++++++++++++---------------
 1 file changed, 554 insertions(+), 234 deletions(-)

diff --git a/src/dialog.rs b/src/dialog.rs
index e98a2b3..8074d5c 100644
--- a/src/dialog.rs
+++ b/src/dialog.rs
@@ -1,267 +1,589 @@
-//! User interaction with the file system using dialog boxes.
-//!
-//! # Example
-//!
-//! ```rust,no_run
-//! use tauri_api::dialog::open;
-//!
-//! let path = open(None).await;
-//! ```
 use serde::Serialize;
-use std::path::PathBuf;
+use std::path::{Path, PathBuf};
 
-/// Extension filter for the file dialog.
-///
-/// # Example
-///
-/// ```rust,no_run
-/// let filter = DialogFilter {
-///   extension: vec![".jpg", ".jpeg", ".png", ".bmp"],
-///   name: "images",
-/// };
-/// ```
-#[derive(Serialize)]
-pub struct DialogFilter {
-    /// Extensions to filter, without a `.` prefix.
-    pub extensions: Vec<String>,
-
-    /// Filter name
-    pub name: String,
+#[derive(Debug, Serialize)]
+struct DialogFilter<'a> {
+    extensions: &'a [&'a str],
+    name: &'a str,
 }
 
-/// Types of a [`message`] dialog.
-#[derive(Serialize)]
+#[derive(Debug, Default, Serialize)]
+#[serde(rename = "camelCase")]
+pub struct FileDialogBuilder<'a> {
+    default_path: Option<&'a Path>,
+    filters: Vec<DialogFilter<'a>>,
+    title: Option<&'a str>,
+    directory: bool,
+    multiple: bool,
+    recursive: bool,
+}
+
+impl<'a> FileDialogBuilder<'a> {
+    /// Gets the default file dialog builder.
+    pub fn new() -> Self {
+        Self::default()
+    }
+
+    /// Set starting file name or directory of the dialog.
+    pub fn set_default_path(&mut self, default_path: &'a Path) {
+        self.default_path = Some(default_path);
+    }
+
+    /// If directory is true, indicates that it will be read recursively later.
+    /// Defines whether subdirectories will be allowed on the scope or not.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = FileDialogBuilder::new().set_recursive(true);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn set_recursive(&mut self, recursive: bool) {
+        self.recursive = recursive;
+    }
+
+    /// Set the title of the dialog.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = FileDialogBuilder::new().set_title("Test Title");
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn set_title(&mut self, title: &'a str) {
+        self.title = Some(title);
+    }
+
+    /// Add file extension filter. Takes in the name of the filter, and list of extensions
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = FileDialogBuilder::new().add_filter("Image", &["png", "jpeg"]);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn add_filter(&mut self, name: &'a str, extensions: &'a [&'a str]) {
+        self.filters.push(DialogFilter { name, extensions });
+    }
+
+    /// Add many file extension filters.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = FileDialogBuilder::new().add_filters(&[("Image", &["png", "jpeg"]),("Video", &["mp4"])]);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn add_filters(&mut self, filters: impl IntoIterator<Item = (&'a str, &'a [&'a str])>) {
+        for (name, extensions) in filters.into_iter() {
+            self.filters.push(DialogFilter {
+                name: name.as_ref(),
+                extensions,
+            });
+        }
+    }
+
+    /// Shows the dialog to select a single file.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let file = FileDialogBuilder::new().pick_file().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn pick_file(self) -> crate::Result<Option<PathBuf>> {
+        let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+
+    /// Shows the dialog to select multiple files.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let files = FileDialogBuilder::new().pick_files().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn pick_files(mut self) -> crate::Result<Option<Vec<PathBuf>>> {
+        self.multiple = true;
+
+        let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+
+    /// Shows the dialog to select a single folder.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let files = FileDialogBuilder::new().pick_folder().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn pick_folder(mut self) -> crate::Result<Option<PathBuf>> {
+        self.directory = true;
+
+        let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+
+    /// Shows the dialog to select multiple folders.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let files = FileDialogBuilder::new().pick_folders().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn pick_folders(mut self) -> crate::Result<Option<Vec<PathBuf>>> {
+        self.directory = true;
+        self.multiple = true;
+
+        let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+
+    /// Open a file/directory save dialog.
+    ///
+    /// The selected path is added to the filesystem and asset protocol allowlist scopes.
+    /// When security is more important than the easy of use of this API, prefer writing a dedicated command instead.
+    ///
+    /// Note that the allowlist scope change is not persisted, so the values are cleared when the application is restarted.
+    /// You can save it to the filesystem using tauri-plugin-persisted-scope.
+    ///
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::FileDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let file = FileDialogBuilder::new().save().await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn save(self) -> crate::Result<Option<PathBuf>> {
+        let raw = inner::save(serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+}
+
+#[derive(Debug, Default)]
 pub enum MessageDialogType {
-    Error,
+    #[default]
     Info,
     Warning,
+    Error,
 }
 
-/// Options for the [`message`] dialog.
-#[derive(Serialize)]
-pub struct MessageDialogOptions {
-    /// The title of the dialog. Defaults to the app name.
-    pub title: Option<String>,
-
-    /// The type of the dialog. Defaults to MessageDialogType::Info.
-    #[serde(rename(serialize = "type"))]
-    pub kind: MessageDialogType,
-}
-
-impl MessageDialogOptions {
-    /// Creates a new `MessageDialogOptions` with sensible default values.
-    pub fn new() -> Self {
-        Self {
-            title: None,
-            kind: MessageDialogType::Info,
+impl Serialize for MessageDialogType {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        match self {
+            MessageDialogType::Info => serializer.serialize_str("info"),
+            MessageDialogType::Warning => serializer.serialize_str("warning"),
+            MessageDialogType::Error => serializer.serialize_str("error"),
         }
     }
 }
 
-/// Options for an [`open`] dialog.
-#[derive(Serialize)]
-pub struct OpenDialogOptions {
-    /// Initial directory or file path.
-    #[serde(rename(serialize = "defaultPath"))]
-    pub default_path: Option<PathBuf>,
-
-    /// Whether the dialog is a directory selection or not.
-    pub directory: bool,
-
-    /// The filters of the dialog.
-    pub filters: Vec<DialogFilter>,
-
-    /// Whether the dialgo allows multiple selection or not.
-    pub multiple: bool,
-
-    /// If `directory` is `true`, indicatees that it will be read recursivley later.
-    /// Defines whether subdirectories will be allowed on the scope or not.
-    pub recursive: bool,
-
-    /// The title of the dialog window.
-    pub title: Option<String>,
+#[derive(Debug, Default, Serialize)]
+pub struct MessageDialogBuilder<'a> {
+    title: Option<&'a str>,
+    r#type: MessageDialogType,
 }
 
-impl OpenDialogOptions {
-    /// Creates a new `OpenDialogOptions` with sensible default values.
+impl<'a> MessageDialogBuilder<'a> {
     pub fn new() -> Self {
-        Self {
-            default_path: None,
-            directory: false,
-            filters: Vec::new(),
-            multiple: false,
-            recursive: false,
-            title: None,
-        }
+        Self::default()
+    }
+
+    /// Set the title of the dialog.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::MessageDialogBuilder;
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = MessageDialogBuilder::new().set_title("Test Title");
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn set_title(&mut self, title: &'a str) {
+        self.title = Some(title);
+    }
+
+    /// Set the type of the dialog.
+    ///
+    /// # Example
+    ///
+    /// ```rust
+    /// use tauri_sys::dialog::{MessageDialogBuilder,MessageDialogType};
+    ///
+    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let _builder = MessageDialogBuilder::new().set_type(MessageDialogType::Error);
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub fn set_type(&mut self, r#type: MessageDialogType) {
+        self.r#type = r#type;
+    }
+
+    /// Shows a message dialog with an `Ok` button.
+    /// 
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::MessageDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let file = MessageDialogBuilder::new().message("Tauri is awesome").await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn message(self, message: &str) -> crate::Result<()> {
+        Ok(inner::message(message, serde_wasm_bindgen::to_value(&self)?).await?)
+    }
+
+    /// Shows a question dialog with `Yes` and `No` buttons.
+    /// 
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::MessageDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let confirmation = MessageDialogBuilder::new().ask("Are you sure?").await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn ask(self, message: &str) -> crate::Result<bool> {
+        let raw = inner::ask(message, serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
+    }
+
+    /// Shows a question dialog with `Ok` and `Cancel` buttons.
+    /// 
+    /// # Example
+    ///
+    /// ```rust,no_run
+    /// use tauri_sys::dialog::MessageDialogBuilder;
+    ///
+    /// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
+    /// let confirmation = MessageDialogBuilder::new().confirm("Are you sure?").await?;
+    /// # Ok(())
+    /// # }
+    /// ```
+    pub async fn confirm(self, message: &str) -> crate::Result<bool> {
+        let raw = inner::confirm(message, serde_wasm_bindgen::to_value(&self)?).await?;
+
+        Ok(serde_wasm_bindgen::from_value(raw)?)
     }
 }
 
-/// Options for the save dialog.
-#[derive(Serialize)]
-pub struct SaveDialogOptions {
-    /// Initial directory of the file path.
-    /// If it's not a directory path, the dialog interface will change to that folder.
-    /// If it's not an existing directory, the file name will be set to the dialog's
-    /// file name input and the dialog will be set to the parent folder.
-    #[serde(rename(serialize = "defaultPath"))]
-    pub default_path: Option<PathBuf>,
+// //! User interaction with the file system using dialog boxes.
+// //!
+// //! # Example
+// //!
+// //! ```rust,no_run
+// //! use tauri_api::dialog::open;
+// //!
+// //! let path = open(None).await;
+// //! ```
+// use serde::Serialize;
+// use std::path::PathBuf;
 
-    /// The filters of the dialog.
-    pub filters: Vec<DialogFilter>,
+// /// Extension filter for the file dialog.
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// let filter = DialogFilter {
+// ///   extension: vec![".jpg", ".jpeg", ".png", ".bmp"],
+// ///   name: "images",
+// /// };
+// /// ```
+// #[derive(Serialize)]
+// pub struct DialogFilter {
+//     /// Extensions to filter, without a `.` prefix.
+//     pub extensions: Vec<String>,
 
-    /// The title of the dialog window.
-    pub title: Option<String>,
-}
+//     /// Filter name
+//     pub name: String,
+// }
 
-impl SaveDialogOptions {
-    /// Creates a new `SaveDialogOptions` with sensible default values.
-    pub fn new() -> Self {
-        Self {
-            default_path: None,
-            filters: Vec::new(),
-            title: None,
-        }
-    }
-}
+// /// Types of a [`message`] dialog.
+// #[derive(Serialize)]
+// pub enum MessageDialogType {
+//     Error,
+//     Info,
+//     Warning,
+// }
 
-/// Show a question dialog with `Yes` and `No` buttons.
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{ask, MessageDialogOptions};
-///
-/// let yes = ask("Are you sure?", None).await;
-/// ```
-/// @param message Message to display.
-/// @param options Dialog options.
-/// @returns Whether the user selected `Yes` or `No`.
-#[inline(always)]
-pub async fn ask(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
-    let js_val = inner::ask(message, serde_wasm_bindgen::to_value(&options)?).await?;
+// /// Options for the [`message`] dialog.
+// #[derive(Serialize)]
+// pub struct MessageDialogOptions {
+//     /// The title of the dialog. Defaults to the app name.
+//     pub title: Option<String>,
 
-    Ok(serde_wasm_bindgen::from_value(js_val)?)
-}
+//     /// The type of the dialog. Defaults to MessageDialogType::Info.
+//     #[serde(rename(serialize = "type"))]
+//     pub kind: MessageDialogType,
+// }
 
-/// Shows a question dialog with `Ok` and `Cancel` buttons.
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{confirm, MessageDialogOptions};
-///
-/// let confirmed = confirm("Are you sure?", None).await;
-/// ```
-/// @returns Whether the user selelced `Ok` or `Cancel`.
-pub async fn confirm(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
-    let js_val = inner::confirm(message, serde_wasm_bindgen::to_value(&options)?).await?;
+// impl MessageDialogOptions {
+//     /// Creates a new `MessageDialogOptions` with sensible default values.
+//     pub fn new() -> Self {
+//         Self {
+//             title: None,
+//             kind: MessageDialogType::Info,
+//         }
+//     }
+// }
 
-    Ok(serde_wasm_bindgen::from_value(js_val)?)
-}
+// /// Options for an [`open`] dialog.
+// #[derive(Serialize)]
+// pub struct OpenDialogOptions {
+//     /// Initial directory or file path.
+//     #[serde(rename(serialize = "defaultPath"))]
+//     pub default_path: Option<PathBuf>,
 
-/// Shows a message dialog with an `Ok` button.
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{message, MessageDialogOptions};
-///
-/// message("Tauri is awesome", None).await;
-/// ```
-/// @param message Message to display.
-/// @param options Dialog options.
-/// @returns Promise resolved when user closes the dialog.
-pub async fn message(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<()> {
-    Ok(inner::message(message, serde_wasm_bindgen::to_value(&options)?).await?)
-}
+//     /// Whether the dialog is a directory selection or not.
+//     pub directory: bool,
 
-/// Opens a file/directory selection dialog for a single file.
-/// `multiple` field of [`options`](OpenDialogOptions) must be `false`, if provided.
-///
-/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
-/// When security is mroe important than the ease of use of this API,
-/// prefer writing a dedicated command instead.
-///
-/// Note that the allowlist scope change is not persisited,
-/// so the values are cleared when the applicaiton is restarted.
-/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{open, OpenDialogOptions};
-///
-/// let file = open(None).await;
-///
-/// let mut opts = OpenDialogOptions::new();
-/// opts.directory = true;
-/// let dir = open(Some(opts)).await;
-/// ```
-/// @param options Dialog options.
-/// @returns List of file paths, or `None` if user cancelled the dialog.
-pub async fn open(options: Option<OpenDialogOptions>) -> crate::Result<Option<PathBuf>> {
-    let file = inner::open(serde_wasm_bindgen::to_value(&options)?).await?;
+//     /// The filters of the dialog.
+//     pub filters: Vec<DialogFilter>,
 
-    Ok(serde_wasm_bindgen::from_value(file)?)
-}
+//     /// Whether the dialog allows multiple selection or not.
+//     pub multiple: bool,
 
-/// Opens a file/directory selection dialog for multiple files.
-/// `multiple` field of [`options`](OpenDialogOptions) must be `true`, if provided.
-///
-/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
-/// When security is mroe important than the ease of use of this API,
-/// prefer writing a dedicated command instead.
-///
-/// Note that the allowlist scope change is not persisited,
-/// so the values are cleared when the applicaiton is restarted.
-/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{open, OpenDialogOptions};
-///
-/// let files = open_multiple(None).await;
-///
-/// let mut opts = OpenDialogOptions::new();
-/// opts.multiple = true;
-/// opts.directory = true;
-/// let dirs = open(Some(opts)).await;
-/// ```
-/// @param options Dialog options.
-/// @returns List of file paths, or `None` if user cancelled the dialog.
-pub async fn open_multiple(
-    options: Option<OpenDialogOptions>,
-) -> crate::Result<Option<Vec<PathBuf>>> {
-    let files = inner::open_multiple(serde_wasm_bindgen::to_value(&options)?).await?;
+//     /// If `directory` is `true`, indicatees that it will be read recursivley later.
+//     /// Defines whether subdirectories will be allowed on the scope or not.
+//     pub recursive: bool,
 
-    Ok(serde_wasm_bindgen::from_value(files)?)
-}
+//     /// The title of the dialog window.
+//     pub title: Option<String>,
+// }
 
-/// Opens a file/directory save dialog.
-///
-/// The selected paths are added to the filesystem and asset protocol allowlist scopes.
-/// When security is mroe important than the ease of use of this API,
-/// prefer writing a dedicated command instead.
-///
-/// Note that the allowlist scope change is not persisited,
-/// so the values are cleared when the applicaiton is restarted.
-/// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
-///
-/// # Example
-///
-/// ```rust,no_run
-/// use tauri_api::dialog::{save, SaveDialogOptions};
-///
-/// let file = save(None).await;
-/// ```
-/// @param options Dialog options.
-/// @returns File path, or `None` if user cancelled the dialog.
-pub async fn save(options: Option<SaveDialogOptions>) -> crate::Result<Option<PathBuf>> {
-    let path = inner::save(serde_wasm_bindgen::to_value(&options)?).await?;
-    
-    Ok(serde_wasm_bindgen::from_value(path)?)
-}
+// impl OpenDialogOptions {
+//     /// Creates a new `OpenDialogOptions` with sensible default values.
+//     pub fn new() -> Self {
+//         Self {
+//             default_path: None,
+//             directory: false,
+//             filters: Vec::new(),
+//             multiple: false,
+//             recursive: false,
+//             title: None,
+//         }
+//     }
+// }
+
+// /// Options for the save dialog.
+// #[derive(Serialize)]
+// pub struct SaveDialogOptions {
+//     /// Initial directory of the file path.
+//     /// If it's not a directory path, the dialog interface will change to that folder.
+//     /// If it's not an existing directory, the file name will be set to the dialog's
+//     /// file name input and the dialog will be set to the parent folder.
+//     #[serde(rename(serialize = "defaultPath"))]
+//     pub default_path: Option<PathBuf>,
+
+//     /// The filters of the dialog.
+//     pub filters: Vec<DialogFilter>,
+
+//     /// The title of the dialog window.
+//     pub title: Option<String>,
+// }
+
+// impl SaveDialogOptions {
+//     /// Creates a new `SaveDialogOptions` with sensible default values.
+//     pub fn new() -> Self {
+//         Self {
+//             default_path: None,
+//             filters: Vec::new(),
+//             title: None,
+//         }
+//     }
+// }
+
+// /// Show a question dialog with `Yes` and `No` buttons.
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{ask, MessageDialogOptions};
+// ///
+// /// let yes = ask("Are you sure?", None).await;
+// /// ```
+// /// @param message Message to display.
+// /// @param options Dialog options.
+// /// @returns Whether the user selected `Yes` or `No`.
+// #[inline(always)]
+// pub async fn ask(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
+//     let js_val = inner::ask(message, serde_wasm_bindgen::to_value(&options)?).await?;
+
+//     Ok(serde_wasm_bindgen::from_value(js_val)?)
+// }
+
+// /// Shows a question dialog with `Ok` and `Cancel` buttons.
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{confirm, MessageDialogOptions};
+// ///
+// /// let confirmed = confirm("Are you sure?", None).await;
+// /// ```
+// /// @returns Whether the user selelced `Ok` or `Cancel`.
+// pub async fn confirm(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
+//     let js_val = inner::confirm(message, serde_wasm_bindgen::to_value(&options)?).await?;
+
+//     Ok(serde_wasm_bindgen::from_value(js_val)?)
+// }
+
+// /// Shows a message dialog with an `Ok` button.
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{message, MessageDialogOptions};
+// ///
+// /// message("Tauri is awesome", None).await;
+// /// ```
+// /// @param message Message to display.
+// /// @param options Dialog options.
+// /// @returns Promise resolved when user closes the dialog.
+// pub async fn message(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<()> {
+//     Ok(inner::message(message, serde_wasm_bindgen::to_value(&options)?).await?)
+// }
+
+// /// Opens a file/directory selection dialog for a single file.
+// /// `multiple` field of [`options`](OpenDialogOptions) must be `false`, if provided.
+// ///
+// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
+// /// When security is mroe important than the ease of use of this API,
+// /// prefer writing a dedicated command instead.
+// ///
+// /// Note that the allowlist scope change is not persisited,
+// /// so the values are cleared when the applicaiton is restarted.
+// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{open, OpenDialogOptions};
+// ///
+// /// let file = open(None).await;
+// ///
+// /// let mut opts = OpenDialogOptions::new();
+// /// opts.directory = true;
+// /// let dir = open(Some(opts)).await;
+// /// ```
+// /// @param options Dialog options.
+// /// @returns List of file paths, or `None` if user cancelled the dialog.
+// pub async fn open(options: Option<OpenDialogOptions>) -> crate::Result<Option<PathBuf>> {
+//     let file = inner::open(serde_wasm_bindgen::to_value(&options)?).await?;
+
+//     Ok(serde_wasm_bindgen::from_value(file)?)
+// }
+
+// /// Opens a file/directory selection dialog for multiple files.
+// /// `multiple` field of [`options`](OpenDialogOptions) must be `true`, if provided.
+// ///
+// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
+// /// When security is mroe important than the ease of use of this API,
+// /// prefer writing a dedicated command instead.
+// ///
+// /// Note that the allowlist scope change is not persisited,
+// /// so the values are cleared when the applicaiton is restarted.
+// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{open, OpenDialogOptions};
+// ///
+// /// let files = open_multiple(None).await;
+// ///
+// /// let mut opts = OpenDialogOptions::new();
+// /// opts.multiple = true;
+// /// opts.directory = true;
+// /// let dirs = open(Some(opts)).await;
+// /// ```
+// /// @param options Dialog options.
+// /// @returns List of file paths, or `None` if user cancelled the dialog.
+// pub async fn open_multiple(
+//     options: Option<OpenDialogOptions>,
+// ) -> crate::Result<Option<Vec<PathBuf>>> {
+//     let files = inner::open_multiple(serde_wasm_bindgen::to_value(&options)?).await?;
+
+//     Ok(serde_wasm_bindgen::from_value(files)?)
+// }
+
+// /// Opens a file/directory save dialog.
+// ///
+// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
+// /// When security is mroe important than the ease of use of this API,
+// /// prefer writing a dedicated command instead.
+// ///
+// /// Note that the allowlist scope change is not persisited,
+// /// so the values are cleared when the applicaiton is restarted.
+// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
+// ///
+// /// # Example
+// ///
+// /// ```rust,no_run
+// /// use tauri_api::dialog::{save, SaveDialogOptions};
+// ///
+// /// let file = save(None).await;
+// /// ```
+// /// @param options Dialog options.
+// /// @returns File path, or `None` if user cancelled the dialog.
+// pub async fn save(options: Option<SaveDialogOptions>) -> crate::Result<Option<PathBuf>> {
+//     let path = inner::save(serde_wasm_bindgen::to_value(&options)?).await?;
+
+//     Ok(serde_wasm_bindgen::from_value(path)?)
+// }
 
 mod inner {
     use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
@@ -275,8 +597,6 @@ mod inner {
         #[wasm_bindgen(catch)]
         pub async fn open(options: JsValue) -> Result<JsValue, JsValue>;
         #[wasm_bindgen(catch)]
-        pub async fn open_multiple(options: JsValue) -> Result<JsValue, JsValue>;
-        #[wasm_bindgen(catch)]
         pub async fn message(message: &str, option: JsValue) -> Result<(), JsValue>;
         #[wasm_bindgen(catch)]
         pub async fn save(options: JsValue) -> Result<JsValue, JsValue>;