feat(plugins): add allowlist for current core plugins

pull/357/head
Lucas Nogueira 2 years ago
parent 702b7b36bd
commit 9d3e1aed69
No known key found for this signature in database
GPG Key ID: FFEA6C72E73482F1

14
Cargo.lock generated

@ -4960,7 +4960,7 @@ dependencies = [
[[package]]
name = "tauri"
version = "2.0.0-alpha.8"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"anyhow",
"bytes 1.4.0",
@ -5010,7 +5010,7 @@ dependencies = [
[[package]]
name = "tauri-build"
version = "2.0.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"anyhow",
"cargo_toml",
@ -5030,7 +5030,7 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.0.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"base64 0.21.0",
"brotli",
@ -5055,7 +5055,7 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.0.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"heck 0.4.1",
"proc-macro2",
@ -5449,7 +5449,7 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "0.13.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"gtk",
"http",
@ -5469,7 +5469,7 @@ dependencies = [
[[package]]
name = "tauri-runtime-wry"
version = "0.13.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"cocoa",
"gtk",
@ -5489,7 +5489,7 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.0.0-alpha.4"
source = "git+https://github.com/tauri-apps/tauri?branch=next#6d25c4d07fcf18c2a19ac4faa7d9bedd96d1a75f"
source = "git+https://github.com/tauri-apps/tauri?branch=feat/plugin-allowlist#90b1db0f33ff65e5dc5cc3254326bda71fa72346"
dependencies = [
"aes-gcm 0.10.1",
"brotli",

@ -5,8 +5,8 @@ resolver = "2"
[workspace.dependencies]
serde = { version = "1", features = ["derive"] }
log = "0.4"
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next" }
tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "next" }
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "feat/plugin-allowlist" }
tauri-build = { git = "https://github.com/tauri-apps/tauri", branch = "feat/plugin-allowlist" }
serde_json = "1"
thiserror = "1"

@ -19,15 +19,15 @@ tiny_http = "0.11"
log.workspace = true
tauri-plugin-app = { path = "../../../plugins/app" }
tauri-plugin-log = { path = "../../../plugins/log" }
tauri-plugin-fs = { path = "../../../plugins/fs" }
tauri-plugin-fs = { path = "../../../plugins/fs", features = [] }
tauri-plugin-clipboard = { path = "../../../plugins/clipboard" }
tauri-plugin-dialog = { path = "../../../plugins/dialog" }
tauri-plugin-http = { path = "../../../plugins/http", features = [ "http-multipart" ] }
tauri-plugin-http = { path = "../../../plugins/http", features = ["http-multipart"] }
tauri-plugin-notification = { path = "../../../plugins/notification", features = [ "windows7-compat" ] }
tauri-plugin-os = { path = "../../../plugins/os" }
tauri-plugin-process = { path = "../../../plugins/process" }
tauri-plugin-shell = { path = "../../../plugins/shell" }
tauri-plugin-updater = { path = "../../../plugins/updater" }
tauri-plugin-shell = { path = "../../../plugins/shell", features = [] }
tauri-plugin-updater = { path = "../../../plugins/updater", features = [] }
tauri-plugin-window = { path = "../../../plugins/window" }
[dependencies.tauri]
@ -42,7 +42,7 @@ features = [
]
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
tauri-plugin-cli = { path = "../../../plugins/cli" }
tauri-plugin-cli = { path = "../../../plugins/cli", features = ["allow-get-matches"] }
tauri-plugin-global-shortcut = { path = "../../../plugins/global-shortcut" }
[target."cfg(target_os = \"windows\")".dependencies]

@ -13,6 +13,9 @@
},
"plugins": {
"cli": {
"allowlist": {
"getMatches": true
},
"description": "Tauri API example",
"args": [
{

@ -12,3 +12,6 @@ tauri.workspace = true
log.workspace = true
thiserror.workspace = true
clap = { version = "4", features = ["string"] }
[features]
allow-get-matches = []

@ -64,7 +64,7 @@ interface CliMatches {
* @since 1.0.0
*/
async function getMatches(): Promise<CliMatches> {
return await invoke("plugin:cli|cli_matches");
return await invoke("plugin:cli|get_matches");
}
export type { ArgMatch, SubcommandMatch, CliMatches };

@ -1,6 +1,6 @@
use tauri::{
plugin::{Builder, PluginApi, TauriPlugin},
AppHandle, Manager, Runtime, State,
Manager, Runtime,
};
mod config;
@ -29,14 +29,21 @@ impl<R: Runtime, T: Manager<R>> CliExt<R> for T {
}
}
#[cfg(feature = "allow-get-matches")]
#[tauri::command]
fn cli_matches<R: Runtime>(_app: AppHandle<R>, cli: State<'_, Cli<R>>) -> Result<parser::Matches> {
fn get_matches<R: tauri::Runtime>(
_app: tauri::AppHandle<R>,
cli: tauri::State<'_, Cli<R>>,
) -> Result<parser::Matches> {
cli.matches()
}
pub fn init<R: Runtime>() -> TauriPlugin<R, Config> {
Builder::new("cli")
.invoke_handler(tauri::generate_handler![cli_matches])
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-get-matches")]
get_matches
])
.setup(|app, api| {
app.manage(Cli(api));
Ok(())

@ -17,3 +17,7 @@ thiserror.workspace = true
[target."cfg(any(target_os = \"macos\", windows, target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
arboard = "3"
[features]
allow-write = []
allow-read = []

@ -2,10 +2,13 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![allow(unused_imports)]
use tauri::{command, AppHandle, Runtime, State};
use crate::{ClipKind, Clipboard, ClipboardContents, Result};
#[cfg(feature = "allow-write")]
#[command]
pub(crate) async fn write<R: Runtime>(
_app: AppHandle<R>,
@ -15,6 +18,7 @@ pub(crate) async fn write<R: Runtime>(
clipboard.write(data)
}
#[cfg(feature = "allow-read")]
#[command]
pub(crate) async fn read<R: Runtime>(
_app: AppHandle<R>,

@ -39,7 +39,12 @@ impl<R: Runtime, T: Manager<R>> crate::ClipboardExt<R> for T {
/// Initializes the plugin.
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("clipboard")
.invoke_handler(tauri::generate_handler![commands::write, commands::read])
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-write")]
commands::write,
#[cfg(feature = "allow-read")]
commands::read
])
.setup(|app, api| {
#[cfg(mobile)]
let clipboard = mobile::init(app, api)?;

@ -24,3 +24,10 @@ raw-window-handle = "0.5"
[build-dependencies]
tauri-build.workspace = true
[features]
allow-open = []
allow-save = []
allow-message = []
allow-ask = []
allow-confirm = []

@ -2,6 +2,8 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![allow(unused_imports)]
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
@ -86,6 +88,7 @@ fn set_default_path<R: Runtime>(
}
}
#[cfg(feature = "allow-open")]
#[command]
pub(crate) async fn open<R: Runtime>(
window: Window<R>,
@ -161,6 +164,7 @@ pub(crate) async fn open<R: Runtime>(
Ok(res)
}
#[cfg(feature = "allow-save")]
#[allow(unused_variables)]
#[command]
pub(crate) async fn save<R: Runtime>(
@ -235,6 +239,7 @@ fn message_dialog<R: Runtime>(
builder.blocking_show()
}
#[cfg(feature = "allow-message")]
#[command]
pub(crate) async fn message<R: Runtime>(
window: Window<R>,
@ -255,6 +260,7 @@ pub(crate) async fn message<R: Runtime>(
))
}
#[cfg(feature = "allow-ask")]
#[command]
pub(crate) async fn ask<R: Runtime>(
window: Window<R>,
@ -276,6 +282,7 @@ pub(crate) async fn ask<R: Runtime>(
))
}
#[cfg(feature = "allow-confirm")]
#[command]
pub(crate) async fn confirm<R: Runtime>(
window: Window<R>,

@ -71,10 +71,15 @@ impl<R: Runtime> Dialog<R> {
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("dialog")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-open")]
commands::open,
#[cfg(feature = "allow-save")]
commands::save,
#[cfg(feature = "allow-message")]
commands::message,
#[cfg(feature = "allow-ask")]
commands::ask,
#[cfg(feature = "allow-confirm")]
commands::confirm
])
.setup(|app, api| {

@ -16,3 +16,15 @@ thiserror.workspace = true
anyhow = "1"
uuid = { version = "1", features = ["v4"] }
glob = "0.3"
[features]
allow-read-file = []
allow-write-file = []
allow-read-dir = []
allow-copy-file = []
allow-create-dir = []
allow-remove-dir = []
allow-remove-file = []
allow-rename-file = []
allow-exists = []
allow-metadata = []

@ -1,3 +1,5 @@
#![allow(unused_imports)]
use crate::Scope;
use anyhow::Context;
use serde::{Deserialize, Serialize, Serializer};
@ -6,15 +8,10 @@ use tauri::{
Manager, Runtime, Window,
};
#[cfg(unix)]
use std::os::unix::fs::{MetadataExt, PermissionsExt};
#[cfg(windows)]
use std::os::windows::fs::MetadataExt;
use std::{
fs::{self, symlink_metadata, File},
fs::{self, File},
io::Write,
path::{Path, PathBuf},
time::{SystemTime, UNIX_EPOCH},
};
use crate::{Error, FsExt, Result};
@ -36,6 +33,7 @@ impl Serialize for CommandError {
}
}
#[allow(dead_code)]
type CommandResult<T> = std::result::Result<T, CommandError>;
/// The options for the directory functions on the file system API.
@ -57,6 +55,7 @@ pub struct FileOperationOptions {
pub dir: Option<BaseDirectory>,
}
#[allow(dead_code)]
fn resolve_path<R: Runtime>(
window: &Window<R>,
path: SafePathBuf,
@ -77,6 +76,7 @@ fn resolve_path<R: Runtime>(
}
}
#[cfg(feature = "allow-read-file")]
#[tauri::command]
pub fn read_file<R: Runtime>(
window: Window<R>,
@ -89,6 +89,7 @@ pub fn read_file<R: Runtime>(
.map_err(Into::into)
}
#[cfg(feature = "allow-read-file")]
#[tauri::command]
pub fn read_text_file<R: Runtime>(
window: Window<R>,
@ -101,6 +102,7 @@ pub fn read_text_file<R: Runtime>(
.map_err(Into::into)
}
#[cfg(feature = "allow-write-file")]
#[tauri::command]
pub fn write_file<R: Runtime>(
window: Window<R>,
@ -119,84 +121,104 @@ pub fn write_file<R: Runtime>(
})
}
#[derive(Clone, Copy)]
struct ReadDirOptions<'a> {
pub scope: Option<&'a Scope>,
}
#[cfg(feature = "allow-read-dir")]
pub use read_dir::*;
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct DiskEntry {
/// The path to the entry.
pub path: PathBuf,
/// The name of the entry (file name with extension or directory name).
pub name: Option<String>,
/// The children of this entry if it's a directory.
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DiskEntry>>,
}
#[cfg(feature = "allow-read-dir")]
mod read_dir {
use std::{
fs::{self, symlink_metadata},
path::{Path, PathBuf},
};
use anyhow::Context;
use serde::Serialize;
use tauri::{path::SafePathBuf, Runtime, Window};
use crate::{FsExt, Result, Scope};
use super::{resolve_path, CommandResult, DirOperationOptions};
fn read_dir_with_options<P: AsRef<Path>>(
path: P,
recursive: bool,
options: ReadDirOptions<'_>,
) -> Result<Vec<DiskEntry>> {
let mut files_and_dirs: Vec<DiskEntry> = vec![];
for entry in fs::read_dir(path)? {
let path = entry?.path();
let path_as_string = path.display().to_string();
if let Ok(flag) = path.metadata().map(|m| m.is_dir()) {
let is_symlink = symlink_metadata(&path).map(|md| md.is_symlink())?;
files_and_dirs.push(DiskEntry {
path: path.clone(),
children: if flag {
Some(
if recursive
&& (!is_symlink
|| options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true))
{
read_dir_with_options(&path_as_string, true, options)?
} else {
vec![]
},
)
} else {
None
},
name: path
.file_name()
.map(|name| name.to_string_lossy())
.map(|name| name.to_string()),
});
#[derive(Clone, Copy)]
struct ReadDirOptions<'a> {
pub scope: Option<&'a Scope>,
}
#[derive(Debug, Serialize)]
#[non_exhaustive]
pub struct DiskEntry {
/// The path to the entry.
pub path: PathBuf,
/// The name of the entry (file name with extension or directory name).
pub name: Option<String>,
/// The children of this entry if it's a directory.
#[serde(skip_serializing_if = "Option::is_none")]
pub children: Option<Vec<DiskEntry>>,
}
fn read_dir_with_options<P: AsRef<Path>>(
path: P,
recursive: bool,
options: ReadDirOptions<'_>,
) -> Result<Vec<DiskEntry>> {
let mut files_and_dirs: Vec<DiskEntry> = vec![];
for entry in fs::read_dir(path)? {
let path = entry?.path();
let path_as_string = path.display().to_string();
if let Ok(flag) = path.metadata().map(|m| m.is_dir()) {
let is_symlink = symlink_metadata(&path).map(|md| md.is_symlink())?;
files_and_dirs.push(DiskEntry {
path: path.clone(),
children: if flag {
Some(
if recursive
&& (!is_symlink
|| options.scope.map(|s| s.is_allowed(&path)).unwrap_or(true))
{
read_dir_with_options(&path_as_string, true, options)?
} else {
vec![]
},
)
} else {
None
},
name: path
.file_name()
.map(|name| name.to_string_lossy())
.map(|name| name.to_string()),
});
}
}
Result::Ok(files_and_dirs)
}
Result::Ok(files_and_dirs)
}
#[tauri::command]
pub fn read_dir<R: Runtime>(
window: Window<R>,
path: SafePathBuf,
options: Option<DirOperationOptions>,
) -> CommandResult<Vec<DiskEntry>> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
} else {
(false, None)
};
let resolved_path = resolve_path(&window, path, dir)?;
read_dir_with_options(
&resolved_path,
recursive,
ReadDirOptions {
scope: Some(window.fs_scope()),
},
)
.with_context(|| format!("path: {}", resolved_path.display()))
.map_err(Into::into)
#[tauri::command]
pub fn read_dir<R: Runtime>(
window: Window<R>,
path: SafePathBuf,
options: Option<DirOperationOptions>,
) -> CommandResult<Vec<DiskEntry>> {
let (recursive, dir) = if let Some(options_value) = options {
(options_value.recursive, options_value.dir)
} else {
(false, None)
};
let resolved_path = resolve_path(&window, path, dir)?;
read_dir_with_options(
&resolved_path,
recursive,
ReadDirOptions {
scope: Some(window.fs_scope()),
},
)
.with_context(|| format!("path: {}", resolved_path.display()))
.map_err(Into::into)
}
}
#[cfg(feature = "allow-copy-file")]
#[tauri::command]
pub fn copy_file<R: Runtime>(
window: Window<R>,
@ -222,6 +244,7 @@ pub fn copy_file<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-create-dir")]
#[tauri::command]
pub fn create_dir<R: Runtime>(
window: Window<R>,
@ -245,6 +268,7 @@ pub fn create_dir<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-remove-dir")]
#[tauri::command]
pub fn remove_dir<R: Runtime>(
window: Window<R>,
@ -268,6 +292,7 @@ pub fn remove_dir<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-remove-file")]
#[tauri::command]
pub fn remove_file<R: Runtime>(
window: Window<R>,
@ -280,6 +305,7 @@ pub fn remove_file<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-rename-file")]
#[tauri::command]
pub fn rename_file<R: Runtime>(
window: Window<R>,
@ -300,6 +326,7 @@ pub fn rename_file<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-exists")]
#[tauri::command]
pub fn exists<R: Runtime>(
window: Window<R>,
@ -310,86 +337,106 @@ pub fn exists<R: Runtime>(
Ok(resolved_path.exists())
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Permissions {
readonly: bool,
#[cfg(unix)]
mode: u32,
}
#[cfg(feature = "allow-metadata")]
pub use metadata::*;
#[cfg(unix)]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UnixMetadata {
dev: u64,
ino: u64,
mode: u32,
nlink: u64,
uid: u32,
gid: u32,
rdev: u64,
blksize: u64,
blocks: u64,
}
#[cfg(feature = "allow-metadata")]
mod metadata {
use std::{
path::PathBuf,
time::{SystemTime, UNIX_EPOCH},
};
use serde::Serialize;
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Metadata {
accessed_at_ms: u64,
created_at_ms: u64,
modified_at_ms: u64,
is_dir: bool,
is_file: bool,
is_symlink: bool,
size: u64,
permissions: Permissions,
#[cfg(unix)]
#[serde(flatten)]
unix: UnixMetadata,
use std::os::unix::fs::{MetadataExt, PermissionsExt};
#[cfg(windows)]
file_attributes: u32,
}
use std::os::windows::fs::MetadataExt;
fn system_time_to_ms(time: std::io::Result<SystemTime>) -> u64 {
time.map(|t| {
let duration_since_epoch = t.duration_since(UNIX_EPOCH).unwrap();
duration_since_epoch.as_millis() as u64
})
.unwrap_or_default()
}
use crate::Result;
#[tauri::command]
pub async fn metadata(path: PathBuf) -> Result<Metadata> {
let metadata = std::fs::metadata(path)?;
let file_type = metadata.file_type();
let permissions = metadata.permissions();
Ok(Metadata {
accessed_at_ms: system_time_to_ms(metadata.accessed()),
created_at_ms: system_time_to_ms(metadata.created()),
modified_at_ms: system_time_to_ms(metadata.modified()),
is_dir: file_type.is_dir(),
is_file: file_type.is_file(),
is_symlink: file_type.is_symlink(),
size: metadata.len(),
permissions: Permissions {
readonly: permissions.readonly(),
#[cfg(unix)]
mode: permissions.mode(),
},
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Permissions {
readonly: bool,
#[cfg(unix)]
mode: u32,
}
#[cfg(unix)]
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UnixMetadata {
dev: u64,
ino: u64,
mode: u32,
nlink: u64,
uid: u32,
gid: u32,
rdev: u64,
blksize: u64,
blocks: u64,
}
#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Metadata {
accessed_at_ms: u64,
created_at_ms: u64,
modified_at_ms: u64,
is_dir: bool,
is_file: bool,
is_symlink: bool,
size: u64,
permissions: Permissions,
#[cfg(unix)]
unix: UnixMetadata {
dev: metadata.dev(),
ino: metadata.ino(),
mode: metadata.mode(),
nlink: metadata.nlink(),
uid: metadata.uid(),
gid: metadata.gid(),
rdev: metadata.rdev(),
blksize: metadata.blksize(),
blocks: metadata.blocks(),
},
#[serde(flatten)]
unix: UnixMetadata,
#[cfg(windows)]
file_attributes: metadata.file_attributes(),
})
file_attributes: u32,
}
fn system_time_to_ms(time: std::io::Result<SystemTime>) -> u64 {
time.map(|t| {
let duration_since_epoch = t.duration_since(UNIX_EPOCH).unwrap();
duration_since_epoch.as_millis() as u64
})
.unwrap_or_default()
}
#[tauri::command]
pub async fn metadata(path: PathBuf) -> Result<Metadata> {
let metadata = std::fs::metadata(path)?;
let file_type = metadata.file_type();
let permissions = metadata.permissions();
Ok(Metadata {
accessed_at_ms: system_time_to_ms(metadata.accessed()),
created_at_ms: system_time_to_ms(metadata.created()),
modified_at_ms: system_time_to_ms(metadata.modified()),
is_dir: file_type.is_dir(),
is_file: file_type.is_file(),
is_symlink: file_type.is_symlink(),
size: metadata.len(),
permissions: Permissions {
readonly: permissions.readonly(),
#[cfg(unix)]
mode: permissions.mode(),
},
#[cfg(unix)]
unix: UnixMetadata {
dev: metadata.dev(),
ino: metadata.ino(),
mode: metadata.mode(),
nlink: metadata.nlink(),
uid: metadata.uid(),
gid: metadata.gid(),
rdev: metadata.rdev(),
blksize: metadata.blksize(),
blocks: metadata.blocks(),
},
#[cfg(windows)]
file_attributes: metadata.file_attributes(),
})
}
}

@ -37,16 +37,27 @@ impl<R: Runtime, T: Manager<R>> FsExt<R> for T {
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
PluginBuilder::<R, Option<Config>>::new("fs")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-read-file")]
commands::read_file,
#[cfg(feature = "allow-read-file")]
commands::read_text_file,
#[cfg(feature = "allow-write-file")]
commands::write_file,
#[cfg(feature = "allow-read-dir")]
commands::read_dir,
#[cfg(feature = "allow-copy-file")]
commands::copy_file,
#[cfg(feature = "allow-create-dir")]
commands::create_dir,
#[cfg(feature = "allow-remove-dir")]
commands::remove_dir,
#[cfg(feature = "allow-remove-file")]
commands::remove_file,
#[cfg(feature = "allow-rename-file")]
commands::rename_file,
#[cfg(feature = "allow-exists")]
commands::exists,
#[cfg(feature = "allow-metadata")]
commands::metadata
])
.setup(|app: &tauri::AppHandle<R>, api| {

@ -12,3 +12,8 @@ tauri.workspace = true
log.workspace = true
thiserror.workspace = true
global-hotkey = "0.2.1"
[features]
allow-register = []
allow-unregister = []
allow-is-registered = []

@ -9,7 +9,7 @@ use global_hotkey::{GlobalHotKeyEvent, GlobalHotKeyManager};
use tauri::{
api::ipc::CallbackFn,
plugin::{Builder as PluginBuilder, TauriPlugin},
AppHandle, Manager, Runtime, State, Window,
AppHandle, Manager, Runtime, Window,
};
mod error;
@ -186,6 +186,7 @@ fn acquire_manager(
.map_err(|e| Error::GlobalHotkey(e.to_string()))
}
#[allow(dead_code)]
fn parse_shortcut<S: AsRef<str>>(shortcut: S) -> Result<Shortcut> {
shortcut.as_ref().parse().map_err(Into::into)
}
@ -200,10 +201,11 @@ where
.map_err(|e| Error::GlobalHotkey(e.to_string()))
}
#[cfg(feature = "allow-register")]
#[tauri::command]
fn register<R: Runtime>(
window: Window<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
global_shortcut: tauri::State<'_, GlobalShortcut<R>>,
shortcut: String,
handler: CallbackFn,
) -> Result<()> {
@ -213,10 +215,11 @@ fn register<R: Runtime>(
)
}
#[cfg(feature = "allow-register")]
#[tauri::command]
fn register_all<R: Runtime>(
window: Window<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
global_shortcut: tauri::State<'_, GlobalShortcut<R>>,
shortcuts: Vec<String>,
handler: CallbackFn,
) -> Result<()> {
@ -227,19 +230,21 @@ fn register_all<R: Runtime>(
global_shortcut.register_all_internal(hotkeys, ShortcutSource::Ipc { window, handler })
}
#[cfg(feature = "allow-unregister")]
#[tauri::command]
fn unregister<R: Runtime>(
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
global_shortcut: tauri::State<'_, GlobalShortcut<R>>,
shortcut: String,
) -> Result<()> {
global_shortcut.unregister(parse_shortcut(shortcut)?)
}
#[cfg(feature = "allow-unregister")]
#[tauri::command]
fn unregister_all<R: Runtime>(
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
global_shortcut: tauri::State<'_, GlobalShortcut<R>>,
shortcuts: Vec<String>,
) -> Result<()> {
let mut hotkeys = Vec::new();
@ -249,10 +254,11 @@ fn unregister_all<R: Runtime>(
global_shortcut.unregister_all(hotkeys)
}
#[cfg(feature = "allow-is-registered")]
#[tauri::command]
fn is_registered<R: Runtime>(
_app: AppHandle<R>,
global_shortcut: State<'_, GlobalShortcut<R>>,
global_shortcut: tauri::State<'_, GlobalShortcut<R>>,
shortcut: String,
) -> Result<bool> {
Ok(global_shortcut.is_registered(parse_shortcut(shortcut)?))
@ -278,10 +284,15 @@ impl Builder {
let handler = self.handler;
PluginBuilder::new("globalShortcut")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-register")]
register,
#[cfg(feature = "allow-register")]
register_all,
#[cfg(feature = "allow-unregister")]
unregister,
#[cfg(feature = "allow-unregister")]
unregister_all,
#[cfg(feature = "allow-is-registered")]
is_registered
])
.setup(move |app, _api| {

@ -23,3 +23,4 @@ http-multipart = [ "reqwest/multipart" ]
native-tls = [ "reqwest/native-tls" ]
native-tls-vendored = [ "reqwest/native-tls-vendored" ]
rustls-tls = [ "reqwest/rustls-tls" ]
allow-request = []

@ -1,3 +1,5 @@
#![allow(unused_imports, dead_code)]
use tauri::{path::SafePathBuf, AppHandle, Runtime, State};
use tauri_plugin_fs::FsExt;
@ -8,6 +10,7 @@ use client::{Body, ClientBuilder, FilePart, FormPart, HttpRequestBuilder, Respon
pub use client::Client;
#[cfg(feature = "allow-request")]
#[tauri::command]
pub async fn create_client<R: Runtime>(
_app: AppHandle<R>,
@ -21,6 +24,7 @@ pub async fn create_client<R: Runtime>(
Ok(id)
}
#[cfg(feature = "allow-request")]
#[tauri::command]
pub async fn drop_client<R: Runtime>(
_app: AppHandle<R>,
@ -32,6 +36,7 @@ pub async fn drop_client<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-request")]
#[tauri::command]
pub async fn request<R: Runtime>(
app: AppHandle<R>,

@ -16,8 +16,8 @@ pub use error::Error;
type Result<T> = std::result::Result<T, Error>;
type ClientId = u32;
#[allow(dead_code)]
pub struct Http<R: Runtime> {
#[allow(dead_code)]
app: AppHandle<R>,
pub(crate) clients: Mutex<HashMap<ClientId, commands::Client>>,
pub(crate) scope: scope::Scope,
@ -38,8 +38,11 @@ impl<R: Runtime, T: Manager<R>> HttpExt<R> for T {
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
Builder::<R, Option<Config>>::new("http")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-request")]
commands::create_client,
#[cfg(feature = "allow-request")]
commands::drop_client,
#[cfg(feature = "allow-request")]
commands::request
])
.setup(|app, api| {

@ -9,6 +9,7 @@ use reqwest::Url;
/// Scope for filesystem access.
#[derive(Debug, Clone)]
pub struct Scope {
#[allow(dead_code)]
allowed_urls: Vec<Pattern>,
}
@ -29,6 +30,7 @@ impl Scope {
}
/// Determines if the given URL is allowed on this scope.
#[allow(dead_code)]
pub fn is_allowed(&self, url: &Url) -> bool {
self.allowed_urls
.iter()

@ -29,3 +29,5 @@ win7-notifications = { version = "0.3.1", optional = true }
[features]
windows7-compat = [ "win7-notifications" ]
allow-request-permission = []
allow-notify = []

@ -4,7 +4,7 @@
use tauri::{command, AppHandle, Runtime, State};
use crate::{Notification, NotificationData, PermissionState, Result};
use crate::{Notification, PermissionState, Result};
#[command]
pub(crate) async fn is_permission_granted<R: Runtime>(
@ -19,6 +19,7 @@ pub(crate) async fn is_permission_granted<R: Runtime>(
}
}
#[cfg(feature = "allow-request-permission")]
#[command]
pub(crate) async fn request_permission<R: Runtime>(
_app: AppHandle<R>,
@ -27,11 +28,12 @@ pub(crate) async fn request_permission<R: Runtime>(
notification.request_permission()
}
#[cfg(feature = "allow-notify")]
#[command]
pub(crate) async fn notify<R: Runtime>(
_app: AppHandle<R>,
notification: State<'_, Notification<R>>,
options: NotificationData,
options: crate::NotificationData,
) -> Result<()> {
let mut builder = notification.builder();
builder.data = options;

@ -214,7 +214,9 @@ impl<R: Runtime, T: Manager<R>> crate::NotificationExt<R> for T {
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("notification")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-notify")]
commands::notify,
#[cfg(feature = "allow-request-permission")]
commands::request_permission,
commands::is_permission_granted
])

@ -7,3 +7,7 @@ license.workspace = true
[dependencies]
tauri.workspace = true
[features]
allow-exit = []
allow-restart = []

@ -1,10 +1,14 @@
#![allow(unused_imports)]
use tauri::{AppHandle, Runtime};
#[cfg(feature = "allow-exit")]
#[tauri::command]
pub fn exit<R: Runtime>(app: AppHandle<R>, code: i32) {
app.exit(code)
}
#[cfg(feature = "allow-restart")]
#[tauri::command]
pub fn restart<R: Runtime>(app: AppHandle<R>) {
app.restart()

@ -7,6 +7,11 @@ mod commands;
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("process")
.invoke_handler(tauri::generate_handler![commands::exit, commands::restart])
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-exit")]
commands::exit,
#[cfg(feature = "allow-restart")]
commands::restart
])
.build()
}

@ -15,4 +15,10 @@ shared_child = "1"
regex = "1"
open = "4"
encoding_rs = "0.8"
os_pipe = "1"
os_pipe = "1"
[features]
allow-execute = []
allow-stdin-write = []
allow-kill = []
allow-open = []

@ -1,177 +1,196 @@
use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error};
#![allow(unused_imports)]
use encoding_rs::Encoding;
use serde::{Deserialize, Serialize};
use tauri::{api::ipc::CallbackFn, Manager, Runtime, State, Window};
use crate::{
open::Program,
process::{CommandEvent, TerminatedPayload},
scope::ExecuteArgs,
Shell,
};
#[allow(dead_code)]
type ChildId = u32;
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "event", content = "payload")]
#[non_exhaustive]
enum JSCommandEvent {
/// Stderr bytes until a newline (\n) or carriage return (\r) is found.
Stderr(Buffer),
/// Stdout bytes until a newline (\n) or carriage return (\r) is found.
Stdout(Buffer),
/// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string.
Error(String),
/// Command process terminated.
Terminated(TerminatedPayload),
}
#[cfg(feature = "allow-execute")]
pub use execute::*;
#[cfg(feature = "allow-execute")]
mod execute {
use std::{collections::HashMap, path::PathBuf, string::FromUtf8Error};
use encoding_rs::Encoding;
use serde::{Deserialize, Serialize};
use tauri::{api::ipc::CallbackFn, Manager, Runtime, State, Window};
use crate::{
process::{CommandEvent, TerminatedPayload},
scope::ExecuteArgs,
Shell,
};
fn get_event_buffer(line: Vec<u8>, encoding: EncodingWrapper) -> Result<Buffer, FromUtf8Error> {
match encoding {
EncodingWrapper::Text(character_encoding) => match character_encoding {
Some(encoding) => Ok(Buffer::Text(
encoding.decode_with_bom_removal(&line).0.into(),
)),
None => String::from_utf8(line).map(Buffer::Text),
},
EncodingWrapper::Raw => Ok(Buffer::Raw(line)),
use super::ChildId;
#[derive(Debug, Clone, Serialize)]
#[serde(tag = "event", content = "payload")]
#[non_exhaustive]
enum JSCommandEvent {
/// Stderr bytes until a newline (\n) or carriage return (\r) is found.
Stderr(Buffer),
/// Stdout bytes until a newline (\n) or carriage return (\r) is found.
Stdout(Buffer),
/// An error happened waiting for the command to finish or converting the stdout/stderr bytes to an UTF-8 string.
Error(String),
/// Command process terminated.
Terminated(TerminatedPayload),
}
}
impl JSCommandEvent {
pub fn new(event: CommandEvent, encoding: EncodingWrapper) -> Self {
match event {
CommandEvent::Terminated(payload) => JSCommandEvent::Terminated(payload),
CommandEvent::Error(error) => JSCommandEvent::Error(error),
CommandEvent::Stderr(line) => get_event_buffer(line, encoding)
.map(JSCommandEvent::Stderr)
.unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
CommandEvent::Stdout(line) => get_event_buffer(line, encoding)
.map(JSCommandEvent::Stdout)
.unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
fn get_event_buffer(line: Vec<u8>, encoding: EncodingWrapper) -> Result<Buffer, FromUtf8Error> {
match encoding {
EncodingWrapper::Text(character_encoding) => match character_encoding {
Some(encoding) => Ok(Buffer::Text(
encoding.decode_with_bom_removal(&line).0.into(),
)),
None => String::from_utf8(line).map(Buffer::Text),
},
EncodingWrapper::Raw => Ok(Buffer::Raw(line)),
}
}
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[allow(missing_docs)]
pub enum Buffer {
Text(String),
Raw(Vec<u8>),
}
impl JSCommandEvent {
pub fn new(event: CommandEvent, encoding: EncodingWrapper) -> Self {
match event {
CommandEvent::Terminated(payload) => JSCommandEvent::Terminated(payload),
CommandEvent::Error(error) => JSCommandEvent::Error(error),
CommandEvent::Stderr(line) => get_event_buffer(line, encoding)
.map(JSCommandEvent::Stderr)
.unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
CommandEvent::Stdout(line) => get_event_buffer(line, encoding)
.map(JSCommandEvent::Stdout)
.unwrap_or_else(|e| JSCommandEvent::Error(e.to_string())),
}
}
}
#[derive(Debug, Copy, Clone)]
pub enum EncodingWrapper {
Raw,
Text(Option<&'static Encoding>),
}
#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(untagged)]
#[allow(missing_docs)]
pub enum Buffer {
Text(String),
Raw(Vec<u8>),
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CommandOptions {
#[serde(default)]
sidecar: bool,
cwd: Option<PathBuf>,
// by default we don't add any env variables to the spawned process
// but the env is an `Option` so when it's `None` we clear the env.
#[serde(default = "default_env")]
env: Option<HashMap<String, String>>,
// Character encoding for stdout/stderr
encoding: Option<String>,
}
#[derive(Debug, Copy, Clone)]
pub enum EncodingWrapper {
Raw,
Text(Option<&'static Encoding>),
}
#[allow(clippy::unnecessary_wraps)]
fn default_env() -> Option<HashMap<String, String>> {
Some(HashMap::default())
}
#[derive(Debug, Clone, Default, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct CommandOptions {
#[serde(default)]
sidecar: bool,
cwd: Option<PathBuf>,
// by default we don't add any env variables to the spawned process
// but the env is an `Option` so when it's `None` we clear the env.
#[serde(default = "default_env")]
env: Option<HashMap<String, String>>,
// Character encoding for stdout/stderr
encoding: Option<String>,
}
#[tauri::command]
pub fn execute<R: Runtime>(
window: Window<R>,
shell: State<'_, Shell<R>>,
program: String,
args: ExecuteArgs,
on_event_fn: CallbackFn,
options: CommandOptions,
) -> crate::Result<ChildId> {
let mut command = if options.sidecar {
let program = PathBuf::from(program);
let program_as_string = program.display().to_string();
let program_no_ext_as_string = program.with_extension("").display().to_string();
let configured_sidecar = window
.config()
.tauri
.bundle
.external_bin
.as_ref()
.and_then(|bins| {
bins.iter()
.find(|b| b == &&program_as_string || b == &&program_no_ext_as_string)
})
.cloned();
if let Some(sidecar) = configured_sidecar {
shell
.scope
.prepare_sidecar(&program.to_string_lossy(), &sidecar, args)?
#[allow(clippy::unnecessary_wraps)]
fn default_env() -> Option<HashMap<String, String>> {
Some(HashMap::default())
}
#[tauri::command]
pub fn execute<R: Runtime>(
window: Window<R>,
shell: State<'_, Shell<R>>,
program: String,
args: ExecuteArgs,
on_event_fn: CallbackFn,
options: CommandOptions,
) -> crate::Result<ChildId> {
let mut command = if options.sidecar {
let program = PathBuf::from(program);
let program_as_string = program.display().to_string();
let program_no_ext_as_string = program.with_extension("").display().to_string();
let configured_sidecar = window
.config()
.tauri
.bundle
.external_bin
.as_ref()
.and_then(|bins| {
bins.iter()
.find(|b| b == &&program_as_string || b == &&program_no_ext_as_string)
})
.cloned();
if let Some(sidecar) = configured_sidecar {
shell
.scope
.prepare_sidecar(&program.to_string_lossy(), &sidecar, args)?
} else {
return Err(crate::Error::SidecarNotAllowed(program));
}
} else {
return Err(crate::Error::SidecarNotAllowed(program));
}
} else {
match shell.scope.prepare(&program, args) {
Ok(cmd) => cmd,
Err(e) => {
#[cfg(debug_assertions)]
eprintln!("{e}");
return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program)));
match shell.scope.prepare(&program, args) {
Ok(cmd) => cmd,
Err(e) => {
#[cfg(debug_assertions)]
eprintln!("{e}");
return Err(crate::Error::ProgramNotAllowed(PathBuf::from(program)));
}
}
};
if let Some(cwd) = options.cwd {
command = command.current_dir(cwd);
}
};
if let Some(cwd) = options.cwd {
command = command.current_dir(cwd);
}
if let Some(env) = options.env {
command = command.envs(env);
} else {
command = command.env_clear();
}
let encoding = match options.encoding {
Option::None => EncodingWrapper::Text(None),
Some(encoding) => match encoding.as_str() {
"raw" => EncodingWrapper::Raw,
_ => {
if let Some(text_encoding) = Encoding::for_label(encoding.as_bytes()) {
EncodingWrapper::Text(Some(text_encoding))
} else {
return Err(crate::Error::UnknownEncoding(encoding));
if let Some(env) = options.env {
command = command.envs(env);
} else {
command = command.env_clear();
}
let encoding = match options.encoding {
Option::None => EncodingWrapper::Text(None),
Some(encoding) => match encoding.as_str() {
"raw" => EncodingWrapper::Raw,
_ => {
if let Some(text_encoding) = Encoding::for_label(encoding.as_bytes()) {
EncodingWrapper::Text(Some(text_encoding))
} else {
return Err(crate::Error::UnknownEncoding(encoding));
}
}
}
},
};
},
};
let (mut rx, child) = command.spawn()?;
let (mut rx, child) = command.spawn()?;
let pid = child.pid();
shell.children.lock().unwrap().insert(pid, child);
let children = shell.children.clone();
let pid = child.pid();
shell.children.lock().unwrap().insert(pid, child);
let children = shell.children.clone();
tauri::async_runtime::spawn(async move {
while let Some(event) = rx.recv().await {
if matches!(event, crate::process::CommandEvent::Terminated(_)) {
children.lock().unwrap().remove(&pid);
};
let js_event = JSCommandEvent::new(event, encoding);
let js = tauri::api::ipc::format_callback(on_event_fn, &js_event)
.expect("unable to serialize CommandEvent");
tauri::async_runtime::spawn(async move {
while let Some(event) = rx.recv().await {
if matches!(event, crate::process::CommandEvent::Terminated(_)) {
children.lock().unwrap().remove(&pid);
};
let js_event = JSCommandEvent::new(event, encoding);
let js = tauri::api::ipc::format_callback(on_event_fn, &js_event)
.expect("unable to serialize CommandEvent");
let _ = window.eval(js.as_str());
}
});
let _ = window.eval(js.as_str());
}
});
Ok(pid)
Ok(pid)
}
}
#[cfg(feature = "allow-stdin-write")]
#[tauri::command]
pub fn stdin_write<R: Runtime>(
_window: Window<R>,
@ -188,6 +207,7 @@ pub fn stdin_write<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-kill")]
#[tauri::command]
pub fn kill<R: Runtime>(
_window: Window<R>,
@ -200,6 +220,7 @@ pub fn kill<R: Runtime>(
Ok(())
}
#[cfg(feature = "allow-open")]
#[tauri::command]
pub fn open<R: Runtime>(
_window: Window<R>,

@ -65,9 +65,13 @@ impl<R: Runtime, T: Manager<R>> ShellExt<R> for T {
pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
Builder::<R, Option<Config>>::new("shell")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-execute")]
commands::execute,
#[cfg(feature = "allow-stdin-write")]
commands::stdin_write,
#[cfg(feature = "allow-kill")]
commands::kill,
#[cfg(feature = "allow-open")]
commands::open
])
.setup(|app, api| {

@ -159,6 +159,7 @@ impl Scope {
}
/// Validates argument inputs and creates a Tauri sidecar [`Command`].
#[allow(dead_code)]
pub fn prepare_sidecar(
&self,
command_name: &str,
@ -169,11 +170,13 @@ impl Scope {
}
/// Validates argument inputs and creates a Tauri [`Command`].
#[allow(dead_code)]
pub fn prepare(&self, command_name: &str, args: ExecuteArgs) -> Result<Command, Error> {
self._prepare(command_name, args, None)
}
/// Validates argument inputs and creates a Tauri [`Command`].
#[allow(dead_code)]
pub fn _prepare(
&self,
command_name: &str,

@ -36,3 +36,5 @@ tokio-test = "0.4.2"
native-tls = [ "reqwest/native-tls" ]
native-tls-vendored = [ "reqwest/native-tls-vendored" ]
rustls-tls = [ "reqwest/rustls-tls" ]
allow-check = []
allow-download-and-install = []

@ -1,3 +1,5 @@
#![allow(unused_imports, dead_code)]
use crate::{PendingUpdate, Result, UpdaterExt};
use http::header;
@ -42,6 +44,7 @@ impl<'de> Deserialize<'de> for HeaderMap {
}
}
#[cfg(feature = "allow-check")]
#[tauri::command]
pub(crate) async fn check<R: Runtime>(
app: AppHandle<R>,
@ -85,6 +88,7 @@ pub(crate) struct DownloadProgress {
content_length: Option<u64>,
}
#[cfg(feature = "allow-download-and-install")]
#[tauri::command]
pub(crate) async fn download_and_install<R: Runtime>(
_app: AppHandle<R>,

@ -88,7 +88,9 @@ impl Builder {
Ok(())
})
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-check")]
commands::check,
#[cfg(feature = "allow-download-and-install")]
commands::download_and_install
])
.build()

@ -13,3 +13,51 @@ thiserror.workspace = true
[features]
icon-png = ["tauri/icon-png"]
icon-ico = ["tauri/icon-ico"]
allow-create = []
allow-scale-factor = []
allow-inner-position = []
allow-outer-position = []
allow-inner-size = []
allow-outer-size = []
allow-is-fullscreen = []
allow-is-minimized = []
allow-is-maximized = []
allow-is-decorated = []
allow-is-resizable = []
allow-is-visible = []
allow-title = []
allow-current-monitor = []
allow-primary-monitor = []
allow-available-monitors = []
allow-theme = []
allow-center = []
allow-request-user-attention = []
allow-set-resizable = []
allow-set-title = []
allow-maximize = []
allow-unmaximize = []
allow-minimize = []
allow-unminimize = []
allow-show = []
allow-hide = []
allow-close = []
allow-set-decorations = []
allow-set-shadow = []
allow-set-always-on-top = []
allow-set-content-protected = []
allow-set-size = []
allow-set-min-size = []
allow-set-max-size = []
allow-set-position = []
allow-set-fullscreen = []
allow-set-focus = []
allow-set-skip-taskbar = []
allow-set-cursor-grab = []
allow-set-cursor-visible = []
allow-set-cursor-icon = []
allow-set-cursor-position = []
allow-set-ignore-cursor-events = []
allow-start-dragging = []
allow-print = []
allow-set-icon = []
allow-toggle-maximize = []

@ -1,3 +1,5 @@
#![allow(unused_imports, dead_code)]
use serde::{Deserialize, Serialize, Serializer};
use tauri::{
utils::config::WindowConfig, AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition,
@ -57,6 +59,7 @@ impl From<IconDto> for Icon {
}
}
#[cfg(feature = "allow-create")]
#[tauri::command]
pub fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> Result<()> {
tauri::window::WindowBuilder::from_config(&app, options).build()?;
@ -70,6 +73,7 @@ fn get_window<R: Runtime>(window: Window<R>, label: Option<String>) -> Result<Wi
}
}
#[allow(unused_macros)]
macro_rules! getter {
($cmd: ident, $ret: ty) => {
#[tauri::command]
@ -79,6 +83,7 @@ macro_rules! getter {
};
}
#[allow(unused_macros)]
macro_rules! setter {
($cmd: ident) => {
#[tauri::command]
@ -99,53 +104,99 @@ macro_rules! setter {
};
}
#[cfg(feature = "allow-scale-factor")]
getter!(scale_factor, f64);
#[cfg(feature = "allow-inner-position")]
getter!(inner_position, PhysicalPosition<i32>);
#[cfg(feature = "allow-outer-position")]
getter!(outer_position, PhysicalPosition<i32>);
#[cfg(feature = "allow-inner-size")]
getter!(inner_size, PhysicalSize<u32>);
#[cfg(feature = "allow-outer-size")]
getter!(outer_size, PhysicalSize<u32>);
#[cfg(feature = "allow-is-fullscreen")]
getter!(is_fullscreen, bool);
#[cfg(feature = "allow-is-minimized")]
getter!(is_minimized, bool);
#[cfg(feature = "allow-is-maximized")]
getter!(is_maximized, bool);
#[cfg(feature = "allow-is-decorated")]
getter!(is_decorated, bool);
#[cfg(feature = "allow-is-resizable")]
getter!(is_resizable, bool);
#[cfg(feature = "allow-is-visible")]
getter!(is_visible, bool);
#[cfg(feature = "allow-title")]
getter!(title, String);
#[cfg(feature = "allow-current-monitor")]
getter!(current_monitor, Option<Monitor>);
#[cfg(feature = "allow-primary-monitor")]
getter!(primary_monitor, Option<Monitor>);
#[cfg(feature = "allow-available-monitors")]
getter!(available_monitors, Vec<Monitor>);
#[cfg(feature = "allow-theme")]
getter!(theme, Theme);
#[cfg(feature = "allow-center")]
setter!(center);
#[cfg(feature = "allow-request-user-attention")]
setter!(request_user_attention, Option<UserAttentionType>);
#[cfg(feature = "allow-set-resizable")]
setter!(set_resizable, bool);
#[cfg(feature = "allow-set-title")]
setter!(set_title, &str);
#[cfg(feature = "allow-maximize")]
setter!(maximize);
#[cfg(feature = "allow-unmaximize")]
setter!(unmaximize);
#[cfg(feature = "allow-minimize")]
setter!(minimize);
#[cfg(feature = "allow-unminimize")]
setter!(unminimize);
#[cfg(feature = "allow-show")]
setter!(show);
#[cfg(feature = "allow-hide")]
setter!(hide);
#[cfg(feature = "allow-close")]
setter!(close);
#[cfg(feature = "allow-set-decorations")]
setter!(set_decorations, bool);
#[cfg(feature = "allow-set-shadow")]
setter!(set_shadow, bool);
#[cfg(feature = "allow-set-always-on-top")]
setter!(set_always_on_top, bool);
#[cfg(feature = "allow-set-content-protected")]
setter!(set_content_protected, bool);
#[cfg(feature = "allow-set-size")]
setter!(set_size, Size);
#[cfg(feature = "allow-set-min-size")]
setter!(set_min_size, Option<Size>);
#[cfg(feature = "allow-set-max-size")]
setter!(set_max_size, Option<Size>);
#[cfg(feature = "allow-set-position")]
setter!(set_position, Position);
#[cfg(feature = "allow-set-fullscreen")]
setter!(set_fullscreen, bool);
#[cfg(feature = "allow-set-focus")]
setter!(set_focus);
#[cfg(feature = "allow-set-skip-taskbar")]
setter!(set_skip_taskbar, bool);
#[cfg(feature = "allow-set-cursor-grab")]
setter!(set_cursor_grab, bool);
#[cfg(feature = "allow-set-cursor-visible")]
setter!(set_cursor_visible, bool);
#[cfg(feature = "allow-set-cursor-icon")]
setter!(set_cursor_icon, CursorIcon);
#[cfg(feature = "allow-set-cursor-position")]
setter!(set_cursor_position, Position);
#[cfg(feature = "allow-set-ignore-cursor-events")]
setter!(set_ignore_cursor_events, bool);
#[cfg(feature = "allow-start-dragging")]
setter!(start_dragging);
#[cfg(feature = "allow-print")]
setter!(print);
#[cfg(feature = "allow-set-icon")]
#[tauri::command]
pub fn set_icon<R: Runtime>(
window: Window<R>,
@ -157,6 +208,7 @@ pub fn set_icon<R: Runtime>(
.map_err(Into::into)
}
#[cfg(feature = "allow-toggle-maximize")]
#[tauri::command]
pub fn toggle_maximize<R: Runtime>(window: Window<R>, label: Option<String>) -> Result<()> {
let window = get_window(window, label)?;
@ -167,6 +219,7 @@ pub fn toggle_maximize<R: Runtime>(window: Window<R>, label: Option<String>) ->
Ok(())
}
#[cfg(feature = "allow-toggle-maximize")]
#[tauri::command]
pub fn internal_toggle_maximize<R: Runtime>(
window: Window<R>,
@ -182,6 +235,7 @@ pub fn internal_toggle_maximize<R: Runtime>(
Ok(())
}
#[cfg(debug_assertions)]
#[tauri::command]
pub fn internal_toggle_devtools<R: Runtime>(
window: Window<R>,

@ -8,57 +8,107 @@ mod commands;
pub fn init<R: Runtime>() -> TauriPlugin<R> {
Builder::new("window")
.invoke_handler(tauri::generate_handler![
#[cfg(feature = "allow-create")]
commands::create,
// getters
#[cfg(feature = "allow-scale-factor")]
commands::scale_factor,
#[cfg(feature = "allow-inner-position")]
commands::inner_position,
#[cfg(feature = "allow-outer-position")]
commands::outer_position,
#[cfg(feature = "allow-inner-size")]
commands::inner_size,
#[cfg(feature = "allow-outer-size")]
commands::outer_size,
#[cfg(feature = "allow-is-fullscreen")]
commands::is_fullscreen,
#[cfg(feature = "allow-is-minimized")]
commands::is_minimized,
#[cfg(feature = "allow-is-maximized")]
commands::is_maximized,
#[cfg(feature = "allow-is-decorated")]
commands::is_decorated,
#[cfg(feature = "allow-is-resizable")]
commands::is_resizable,
#[cfg(feature = "allow-is-visible")]
commands::is_visible,
#[cfg(feature = "allow-title")]
commands::title,
#[cfg(feature = "allow-current-monitor")]
commands::current_monitor,
#[cfg(feature = "allow-primary-monitor")]
commands::primary_monitor,
#[cfg(feature = "allow-available-monitors")]
commands::available_monitors,
#[cfg(feature = "allow-theme")]
commands::theme,
// setters
#[cfg(feature = "allow-center")]
commands::center,
#[cfg(feature = "allow-request-user-attention")]
commands::request_user_attention,
#[cfg(feature = "allow-set-resizable")]
commands::set_resizable,
#[cfg(feature = "allow-set-title")]
commands::set_title,
#[cfg(feature = "allow-maximize")]
commands::maximize,
#[cfg(feature = "allow-unmaximize")]
commands::unmaximize,
#[cfg(feature = "allow-minimize")]
commands::minimize,
#[cfg(feature = "allow-unminimize")]
commands::unminimize,
#[cfg(feature = "allow-show")]
commands::show,
#[cfg(feature = "allow-hide")]
commands::hide,
#[cfg(feature = "allow-close")]
commands::close,
#[cfg(feature = "allow-set-decorations")]
commands::set_decorations,
#[cfg(feature = "allow-set-shadow")]
commands::set_shadow,
#[cfg(feature = "allow-set-always-on-top")]
commands::set_always_on_top,
#[cfg(feature = "allow-set-content-protected")]
commands::set_content_protected,
#[cfg(feature = "allow-set-size")]
commands::set_size,
#[cfg(feature = "allow-set-min-size")]
commands::set_min_size,
#[cfg(feature = "allow-set-max-size")]
commands::set_max_size,
#[cfg(feature = "allow-set-position")]
commands::set_position,
#[cfg(feature = "allow-set-fullscreen")]
commands::set_fullscreen,
#[cfg(feature = "allow-set-focus")]
commands::set_focus,
#[cfg(feature = "allow-set-skip-taskbar")]
commands::set_skip_taskbar,
#[cfg(feature = "allow-set-cursor-grab")]
commands::set_cursor_grab,
#[cfg(feature = "allow-set-cursor-visible")]
commands::set_cursor_visible,
#[cfg(feature = "allow-set-cursor-icon")]
commands::set_cursor_icon,
#[cfg(feature = "allow-set-cursor-position")]
commands::set_cursor_position,
#[cfg(feature = "allow-set-ignore-cursor-events")]
commands::set_ignore_cursor_events,
#[cfg(feature = "allow-start-dragging")]
commands::start_dragging,
#[cfg(feature = "allow-print")]
commands::print,
#[cfg(feature = "allow-set-icon")]
commands::set_icon,
#[cfg(feature = "allow-toggle-maximize")]
commands::toggle_maximize,
#[cfg(feature = "allow-toggle-maximize")]
commands::internal_toggle_maximize,
#[cfg(debug_assertions)]
commands::internal_toggle_devtools,
])
.build()

Loading…
Cancel
Save