feat: use tauri next branch, fix tests, MSRV 1.65 (#354)

This commit is contained in:
Lucas Fernandes Nogueira
2023-05-12 13:16:50 -07:00
committed by GitHub
parent e0e7b4fc71
commit 937e6a5be6
64 changed files with 867 additions and 463 deletions
+1 -1
View File
@@ -4,7 +4,7 @@ This plugin provides APIs to read application metadata and macOS app visibility
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Use hardware security-keys in your Tauri App.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Automatically launch your application at startup. Supports Windows, Mac (via App
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -120,7 +120,7 @@ pub fn init<R: Runtime>(
let exe_path = current_exe.canonicalize()?.display().to_string();
let parts: Vec<&str> = exe_path.split(".app/").collect();
let app_path = if parts.len() == 2 {
format!("{}.app", parts.get(0).unwrap().to_string())
format!("{}.app", parts.first().unwrap())
} else {
exe_path
};
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+2 -2
View File
@@ -72,10 +72,10 @@ impl Matches {
/// # Examples
///
/// ```rust,no_run
/// use tauri_plugin_cli::get_matches;
/// use tauri_plugin_cli::CliExt;
/// tauri::Builder::default()
/// .setup(|app| {
/// let matches = get_matches(app.config().tauri.cli.as_ref().unwrap(), app.package_info())?;
/// let matches = app.cli().matches()?;
/// Ok(())
/// });
/// ```
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+35 -35
View File
@@ -324,12 +324,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().pick_file(|file_path| {
/// .run(|app, _event| {
/// app.dialog().file().pick_file(|file_path| {
/// // do something with the optional file path here
/// // the file path is `None` if the user closed the dialog
/// })
@@ -348,12 +348,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().pick_files(|file_paths| {
/// .run(|app, _event| {
/// app.dialog().file().pick_files(|file_paths| {
/// // do something with the optional file paths here
/// // the file paths value is `None` if the user closed the dialog
/// })
@@ -378,12 +378,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().pick_folder(|folder_path| {
/// .run(|app, _event| {
/// app.dialog().file().pick_folder(|folder_path| {
/// // do something with the optional folder path here
/// // the folder path is `None` if the user closed the dialog
/// })
@@ -401,12 +401,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().pick_folders(|file_paths| {
/// .run(|app, _event| {
/// app.dialog().file().pick_folders(|file_paths| {
/// // do something with the optional folder paths here
/// // the folder paths value is `None` if the user closed the dialog
/// })
@@ -425,12 +425,12 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// tauri::Builder::default()
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("failed to build tauri app")
/// .run(|_app, _event| {
/// FileDialogBuilder::new().save_file(|file_path| {
/// .run(|app, _event| {
/// app.dialog().file().save_file(|file_path| {
/// // do something with the optional file path here
/// // the file path is `None` if the user closed the dialog
/// })
@@ -451,10 +451,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// #[tauri::command]
/// async fn my_command() {
/// let file_path = FileDialogBuilder::new().pick_file();
/// async fn my_command(app: tauri::AppHandle) {
/// let file_path = app.dialog().file().blocking_pick_file();
/// // do something with the optional file path here
/// // the file path is `None` if the user closed the dialog
/// }
@@ -470,10 +470,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// #[tauri::command]
/// async fn my_command() {
/// let file_path = FileDialogBuilder::new().pick_files();
/// async fn my_command(app: tauri::AppHandle) {
/// let file_path = app.dialog().file().blocking_pick_files();
/// // do something with the optional file paths here
/// // the file paths value is `None` if the user closed the dialog
/// }
@@ -489,10 +489,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// #[tauri::command]
/// async fn my_command() {
/// let folder_path = FileDialogBuilder::new().pick_folder();
/// async fn my_command(app: tauri::AppHandle) {
/// let folder_path = app.dialog().file().blocking_pick_folder();
/// // do something with the optional folder path here
/// // the folder path is `None` if the user closed the dialog
/// }
@@ -509,10 +509,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// #[tauri::command]
/// async fn my_command() {
/// let folder_paths = FileDialogBuilder::new().pick_folders();
/// async fn my_command(app: tauri::AppHandle) {
/// let folder_paths = app.dialog().file().blocking_pick_folders();
/// // do something with the optional folder paths here
/// // the folder paths value is `None` if the user closed the dialog
/// }
@@ -529,10 +529,10 @@ impl<R: Runtime> FileDialogBuilder<R> {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::dialog::blocking::FileDialogBuilder;
/// use tauri_plugin_dialog::DialogExt;
/// #[tauri::command]
/// async fn my_command() {
/// let file_path = FileDialogBuilder::new().save_file();
/// async fn my_command(app: tauri::AppHandle) {
/// let file_path = app.dialog().file().blocking_save_file();
/// // do something with the optional file path here
/// // the file path is `None` if the user closed the dialog
/// }
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "../../../node_modules/.pnpm/@tauri-apps+cli@2.0.0-alpha.8/node_modules/@tauri-apps/cli/schema.json",
"build": {
"distDir": ".",
"devPath": "http://localhost:4000"
},
"tauri": {
"bundle": {
"identifier": "studio.tauri.example",
"active": true,
"icon": ["../../../examples/api/src-tauri/icons/icon.png"]
},
"windows": [
{
"title": "Tauri App"
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'"
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ Watch files and directories for changes using [notify](https://github.com/notify
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+39 -23
View File
@@ -1,8 +1,4 @@
import { invoke } from "@tauri-apps/api/tauri";
import { UnlistenFn } from "@tauri-apps/api/event";
import { appWindow, WebviewWindow } from "tauri-plugin-window-api";
const w: WebviewWindow = appWindow;
import { invoke, transformCallback } from "@tauri-apps/api/tauri";
export interface WatchOptions {
recursive?: boolean;
@@ -42,11 +38,39 @@ async function unwatch(id: number): Promise<void> {
await invoke("plugin:fs-watch|unwatch", { id });
}
// TODO: use channel from @tauri-apps/api on v2
class Channel<T = unknown> {
id: number;
// @ts-expect-error field used by the IPC serializer
private readonly __TAURI_CHANNEL_MARKER__ = true;
#onmessage: (response: T) => void = () => {
// no-op
};
constructor() {
this.id = transformCallback((response: T) => {
this.#onmessage(response);
});
}
set onmessage(handler: (response: T) => void) {
this.#onmessage = handler;
}
get onmessage(): (response: T) => void {
return this.#onmessage;
}
toJSON(): string {
return `__CHANNEL__:${this.id}`;
}
}
export async function watch(
paths: string | string[],
cb: (event: DebouncedEvent) => void,
options: DebouncedWatchOptions = {}
): Promise<UnlistenFn> {
): Promise<() => void> {
const opts = {
recursive: false,
delayMs: 2000,
@@ -61,22 +85,18 @@ export async function watch(
const id = window.crypto.getRandomValues(new Uint32Array(1))[0];
const onEvent = new Channel<DebouncedEvent>();
onEvent.onmessage = cb;
await invoke("plugin:fs-watch|watch", {
id,
paths: watchPaths,
options: opts,
onEvent,
});
const unlisten = await w.listen<DebouncedEvent>(
`watcher://debounced-event/${id}`,
(event) => {
cb(event.payload);
}
);
return () => {
void unwatch(id);
unlisten();
};
}
@@ -84,7 +104,7 @@ export async function watchImmediate(
paths: string | string[],
cb: (event: RawEvent) => void,
options: WatchOptions = {}
): Promise<UnlistenFn> {
): Promise<() => void> {
const opts = {
recursive: false,
...options,
@@ -99,21 +119,17 @@ export async function watchImmediate(
const id = window.crypto.getRandomValues(new Uint32Array(1))[0];
const onEvent = new Channel<RawEvent>();
onEvent.onmessage = cb;
await invoke("plugin:fs-watch|watch", {
id,
paths: watchPaths,
options: opts,
onEvent,
});
const unlisten = await w.listen<RawEvent>(
`watcher://raw-event/${id}`,
(event) => {
cb(event.payload);
}
);
return () => {
void unwatch(id);
unlisten();
};
}
+1 -2
View File
@@ -28,7 +28,6 @@
"tslib": "^2.5.0"
},
"dependencies": {
"@tauri-apps/api": "^1.2.0",
"tauri-plugin-window-api": "0.0.0"
"@tauri-apps/api": "^1.2.0"
}
}
+9 -10
View File
@@ -2,9 +2,10 @@ use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
use serde::{ser::Serializer, Deserialize, Serialize};
use tauri::{
api::ipc::Channel,
command,
plugin::{Builder as PluginBuilder, TauriPlugin},
Manager, Runtime, State, Window,
Manager, Runtime, State,
};
use std::{
@@ -44,25 +45,23 @@ enum WatcherKind {
Watcher(RecommendedWatcher),
}
fn watch_raw<R: Runtime>(window: Window<R>, rx: Receiver<notify::Result<Event>>, id: Id) {
fn watch_raw<R: Runtime>(on_event: Channel<R>, rx: Receiver<notify::Result<Event>>) {
spawn(move || {
let event_name = format!("watcher://raw-event/{id}");
while let Ok(event) = rx.recv() {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = window.emit(&event_name, event);
let _ = on_event.send(&event);
}
}
});
}
fn watch_debounced<R: Runtime>(window: Window<R>, rx: Receiver<DebounceEventResult>, id: Id) {
fn watch_debounced<R: Runtime>(on_event: Channel<R>, rx: Receiver<DebounceEventResult>) {
spawn(move || {
let event_name = format!("watcher://debounced-event/{id}");
while let Ok(event) = rx.recv() {
if let Ok(event) = event {
// TODO: Should errors be emitted too?
let _ = window.emit(&event_name, event);
let _ = on_event.send(&event);
}
}
});
@@ -77,11 +76,11 @@ struct WatchOptions {
#[command]
async fn watch<R: Runtime>(
window: Window<R>,
watchers: State<'_, WatcherCollection>,
id: Id,
paths: Vec<PathBuf>,
options: WatchOptions,
on_event: Channel<R>,
) -> Result<()> {
let mode = if options.recursive {
RecursiveMode::Recursive
@@ -96,7 +95,7 @@ async fn watch<R: Runtime>(
for path in &paths {
watcher.watch(path, mode)?;
}
watch_debounced(window, rx, id);
watch_debounced(on_event, rx);
WatcherKind::Debouncer(debouncer)
} else {
let (tx, rx) = channel();
@@ -104,7 +103,7 @@ async fn watch<R: Runtime>(
for path in &paths {
watcher.watch(path, mode)?;
}
watch_raw(window, rx, id);
watch_raw(on_event, rx);
WatcherKind::Watcher(watcher)
};
+7 -13
View File
@@ -2,21 +2,15 @@
name = "tauri-plugin-fs"
version = "0.0.0"
description = "Access the file system."
edition = "2021"
#authors.workspace = true
#license.workspace = true
#edition.workspace = true
#rust-version.workspace = true
authors.workspace = true
license.workspace = true
edition.workspace = true
rust-version.workspace = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
#serde.workspace = true
#serde_json.workspace = true
#tauri.workspace = true
#log.workspace = true
#thiserror.workspace = true
serde = "1"
thiserror = "1"
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next" }
serde.workspace = true
tauri.workspace = true
thiserror.workspace = true
anyhow = "1"
+1 -1
View File
@@ -4,7 +4,7 @@ Access the file system.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+7 -14
View File
@@ -1,22 +1,15 @@
[package]
name = "tauri-plugin-http"
version = "0.0.0"
edition = "2021"
#edition.workspace = true
#authors.workspace = true
#license.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
[dependencies]
#serde.workspace = true
#serde_json.workspace = true
#tauri.workspace = true
#log.workspace = true
#thiserror.workspace = true
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next" }
serde = "1"
serde_json = "1"
thiserror = "1"
serde.workspace = true
serde_json.workspace = true
tauri.workspace = true
thiserror.workspace = true
glob = "0.3"
rand = "0.8"
bytes = { version = "1", features = [ "serde" ] }
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -4
View File
@@ -1,9 +1,6 @@
use tauri::{path::SafePathBuf, AppHandle, Runtime, State};
use crate::{
ClientId, Http,
};
use crate::{ClientId, Http};
mod client;
use client::{Body, ClientBuilder, FilePart, FormPart, HttpRequestBuilder, ResponseData};
+10 -14
View File
@@ -38,12 +38,12 @@ impl Scope {
#[cfg(test)]
mod tests {
use tauri_utils::config::HttpAllowlistScope;
use tauri::utils::config::HttpAllowlistScope;
#[test]
fn is_allowed() {
// plain URL
let scope = super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080"
let scope = super::Scope::new(&HttpAllowlistScope(vec!["http://localhost:8080"
.parse()
.unwrap()]));
assert!(scope.is_allowed(&"http://localhost:8080".parse().unwrap()));
@@ -56,10 +56,9 @@ mod tests {
assert!(!scope.is_allowed(&"http://local:8080".parse().unwrap()));
// URL with fixed path
let scope =
super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080/file.png"
.parse()
.unwrap()]));
let scope = super::Scope::new(&HttpAllowlistScope(vec!["http://localhost:8080/file.png"
.parse()
.unwrap()]));
assert!(scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap()));
@@ -68,25 +67,22 @@ mod tests {
assert!(!scope.is_allowed(&"http://localhost:8080/file.png/other.jpg".parse().unwrap()));
// URL with glob pattern
let scope =
super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://localhost:8080/*.png"
.parse()
.unwrap()]));
let scope = super::Scope::new(&HttpAllowlistScope(vec!["http://localhost:8080/*.png"
.parse()
.unwrap()]));
assert!(scope.is_allowed(&"http://localhost:8080/file.png".parse().unwrap()));
assert!(scope.is_allowed(&"http://localhost:8080/assets/file.png".parse().unwrap()));
assert!(!scope.is_allowed(&"http://localhost:8080/file.jpeg".parse().unwrap()));
let scope =
super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://*".parse().unwrap()]));
let scope = super::Scope::new(&HttpAllowlistScope(vec!["http://*".parse().unwrap()]));
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
assert!(!scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
assert!(!scope.is_allowed(&"https://something.else".parse().unwrap()));
let scope =
super::Scope::for_http_api(&HttpAllowlistScope(vec!["http://**".parse().unwrap()]));
let scope = super::Scope::new(&HttpAllowlistScope(vec!["http://**".parse().unwrap()]));
assert!(scope.is_allowed(&"http://something.else".parse().unwrap()));
assert!(scope.is_allowed(&"http://something.else/path/to/file".parse().unwrap()));
+1 -1
View File
@@ -6,7 +6,7 @@ Expose your apps assets through a localhost server instead of the default custom
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Configurable logging for your Tauri app.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+21 -19
View File
@@ -72,15 +72,16 @@ mod imp {
///
/// # Examples
/// ```rust,no_run
/// use tauri::api::notification::Notification;
/// use tauri_plugin_notification::NotificationExt;
/// // first we build the application to access the Tauri configuration
/// let app = tauri::Builder::default()
/// // on an actual app, remove the string argument
/// .build(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .build(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("error while building tauri application");
///
/// // shows a notification with the given title and body
/// Notification::new(&app.config().tauri.bundle.identifier)
/// app.notification()
/// .builder()
/// .title("New message")
/// .body("You've got a new message.")
/// .show();
@@ -136,15 +137,20 @@ mod imp {
/// # Examples
///
/// ```no_run
/// use tauri::api::notification::Notification;
/// use tauri_plugin_notification::NotificationExt;
///
/// // on an actual app, remove the string argument
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
/// Notification::new(&context.config().tauri.bundle.identifier)
/// .title("Tauri")
/// .body("Tauri is awesome!")
/// .show()
/// .unwrap();
/// tauri::Builder::default()
/// .setup(|app| {
/// app.notification()
/// .builder()
/// .title("Tauri")
/// .body("Tauri is awesome!")
/// .show()
/// .unwrap();
/// Ok(())
/// })
/// .run(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("error while running tauri application");
/// ```
///
/// ## Platform-specific
@@ -200,22 +206,18 @@ mod imp {
/// # Examples
///
/// ```no_run
/// use tauri::api::notification::Notification;
///
/// // on an actual app, remove the string argument
/// let context = tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json");
/// let identifier = context.config().tauri.bundle.identifier.clone();
/// use tauri_plugin_notification::NotificationExt;
///
/// tauri::Builder::default()
/// .setup(move |app| {
/// Notification::new(&identifier)
/// app.notification().builder()
/// .title("Tauri")
/// .body("Tauri is awesome!")
/// .notify(&app.handle())
/// .show()
/// .unwrap();
/// Ok(())
/// })
/// .run(context)
/// .run(tauri::generate_context!("test/tauri.conf.json"))
/// .expect("error while running tauri application");
/// ```
#[cfg(feature = "windows7-compat")]
+22
View File
@@ -0,0 +1,22 @@
{
"$schema": "../../../node_modules/.pnpm/@tauri-apps+cli@2.0.0-alpha.8/node_modules/@tauri-apps/cli/schema.json",
"build": {
"distDir": ".",
"devPath": "http://localhost:4000"
},
"tauri": {
"bundle": {
"identifier": "studio.tauri.example",
"active": true,
"icon": ["../../../examples/api/src-tauri/icons/icon.png"]
},
"windows": [
{
"title": "Tauri App"
}
],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'"
}
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ Read information about the operating system.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Save filesystem and asset scopes and restore them when the app is reopened.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -6,7 +6,7 @@ This plugin is a port of [electron-positioner](https://github.com/jenslind/elect
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ This plugin provides APIs to access the current process. To spawn child processe
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+45 -32
View File
@@ -200,24 +200,29 @@ impl Command {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::process::{Command, CommandEvent};
/// tauri::async_runtime::spawn(async move {
/// let (mut rx, mut child) = Command::new("cargo")
/// .args(["tauri", "dev"])
/// .spawn()
/// .expect("Failed to spawn cargo");
/// use tauri_plugin_shell::{process::CommandEvent, ShellExt};
/// tauri::Builder::default()
/// .setup(|app| {
/// let handle = app.handle();
/// tauri::async_runtime::spawn(async move {
/// let (mut rx, mut child) = handle.shell().command("cargo")
/// .args(["tauri", "dev"])
/// .spawn()
/// .expect("Failed to spawn cargo");
///
/// let mut i = 0;
/// while let Some(event) = rx.recv().await {
/// if let CommandEvent::Stdout(line) = event {
/// println!("got: {}", String::from_utf8(line).unwrap());
/// i += 1;
/// if i == 4 {
/// child.write("message from Rust\n".as_bytes()).unwrap();
/// i = 0;
/// let mut i = 0;
/// while let Some(event) = rx.recv().await {
/// if let CommandEvent::Stdout(line) = event {
/// println!("got: {}", String::from_utf8(line).unwrap());
/// i += 1;
/// if i == 4 {
/// child.write("message from Rust\n".as_bytes()).unwrap();
/// i = 0;
/// }
/// }
/// }
/// }
/// }
/// });
/// Ok(())
/// });
/// ```
pub fn spawn(self) -> crate::Result<(Receiver<CommandEvent>, CommandChild)> {
@@ -288,9 +293,13 @@ impl Command {
///
/// # Examples
/// ```rust,no_run
/// use tauri::api::process::Command;
/// let status = Command::new("which").args(["ls"]).status().unwrap();
/// println!("`which` finished with status: {:?}", status.code());
/// use tauri_plugin_shell::ShellExt;
/// tauri::Builder::default()
/// .setup(|app| {
/// let status = tauri::async_runtime::block_on(async move { app.shell().command("which").args(["ls"]).status().await.unwrap() });
/// println!("`which` finished with status: {:?}", status.code());
/// Ok(())
/// });
/// ```
pub async fn status(self) -> crate::Result<ExitStatus> {
let (mut rx, _child) = self.spawn()?;
@@ -310,10 +319,14 @@ impl Command {
/// # Examples
///
/// ```rust,no_run
/// use tauri::api::process::Command;
/// let output = Command::new("echo").args(["TAURI"]).output().unwrap();
/// assert!(output.status.success());
/// assert_eq!(String::from_utf8(output.stdout).unwrap(), "TAURI");
/// use tauri_plugin_shell::ShellExt;
/// tauri::Builder::default()
/// .setup(|app| {
/// let output = tauri::async_runtime::block_on(async move { app.shell().command("echo").args(["TAURI"]).output().await.unwrap() });
/// assert!(output.status.success());
/// assert_eq!(String::from_utf8(output.stdout).unwrap(), "TAURI");
/// Ok(())
/// });
/// ```
pub async fn output(self) -> crate::Result<Output> {
let (mut rx, _child) = self.spawn()?;
@@ -387,7 +400,7 @@ mod tests {
#[cfg(not(windows))]
#[test]
fn test_cmd_spawn_output() {
let cmd = Command::new("cat").args(["test/api/test.txt"]);
let cmd = Command::new("cat").args(["test/test.txt"]);
let (mut rx, _) = cmd.spawn().unwrap();
tauri::async_runtime::block_on(async move {
@@ -408,7 +421,7 @@ mod tests {
#[cfg(not(windows))]
#[test]
fn test_cmd_spawn_raw_output() {
let cmd = Command::new("cat").args(["test/api/test.txt"]);
let cmd = Command::new("cat").args(["test/test.txt"]);
let (mut rx, _) = cmd.spawn().unwrap();
tauri::async_runtime::block_on(async move {
@@ -430,7 +443,7 @@ mod tests {
#[test]
// test the failure case
fn test_cmd_spawn_fail() {
let cmd = Command::new("cat").args(["test/api/"]);
let cmd = Command::new("cat").args(["test/"]);
let (mut rx, _) = cmd.spawn().unwrap();
tauri::async_runtime::block_on(async move {
@@ -442,7 +455,7 @@ mod tests {
CommandEvent::Stderr(line) => {
assert_eq!(
String::from_utf8(line).unwrap(),
"cat: test/api/: Is a directory"
"cat: test/: Is a directory"
);
}
_ => {}
@@ -455,7 +468,7 @@ mod tests {
#[test]
// test the failure case (raw encoding)
fn test_cmd_spawn_raw_fail() {
let cmd = Command::new("cat").args(["test/api/"]);
let cmd = Command::new("cat").args(["test/"]);
let (mut rx, _) = cmd.spawn().unwrap();
tauri::async_runtime::block_on(async move {
@@ -467,7 +480,7 @@ mod tests {
CommandEvent::Stderr(line) => {
assert_eq!(
String::from_utf8(line).unwrap(),
"cat: test/api/: Is a directory"
"cat: test/: Is a directory"
);
}
_ => {}
@@ -479,7 +492,7 @@ mod tests {
#[cfg(not(windows))]
#[test]
fn test_cmd_output_output() {
let cmd = Command::new("cat").args(["test/api/test.txt"]);
let cmd = Command::new("cat").args(["test/test.txt"]);
let output = tauri::async_runtime::block_on(cmd.output()).unwrap();
assert_eq!(String::from_utf8(output.stderr).unwrap(), "");
@@ -492,13 +505,13 @@ mod tests {
#[cfg(not(windows))]
#[test]
fn test_cmd_output_output_fail() {
let cmd = Command::new("cat").args(["test/api/"]);
let cmd = Command::new("cat").args(["test/"]);
let output = tauri::async_runtime::block_on(cmd.output()).unwrap();
assert_eq!(String::from_utf8(output.stdout).unwrap(), "");
assert_eq!(
String::from_utf8(output.stderr).unwrap(),
"cat: test/api/: Is a directory\n"
"cat: test/: Is a directory\n"
);
}
}
+1
View File
@@ -0,0 +1 @@
This is a test doc!
+1 -1
View File
@@ -4,7 +4,7 @@ Ensure a single instance of your tauri app is running.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Interface with SQL databases through [sqlx](https://github.com/launchbadge/sqlx)
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Simple, persistent key-value store.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+29 -34
View File
@@ -46,7 +46,7 @@ pub fn with_store<R: Runtime, T, F: FnOnce(&mut Store<R>) -> Result<T, Error>>(
if collection.frozen {
return Err(Error::NotFound(path.to_path_buf()));
}
let mut store = StoreBuilder::new(app, path.to_path_buf()).build();
let mut store = StoreBuilder::new(path).build(app);
// ignore loading errors, just use the default
if let Err(err) = store.load() {
warn!(
@@ -205,15 +205,14 @@ impl<R: Runtime> Builder<R> {
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::{StoreBuilder,PluginBuilder};
/// use tauri_plugin_store::{StoreBuilder, Builder};
///
/// let store = StoreBuilder::new("store.bin".parse()?).build();
///
/// let builder = PluginBuilder::default().store(store);
///
/// # Ok(())
/// # }
/// tauri::Builder::default()
/// .setup(|app| {
/// let store = StoreBuilder::new("store.bin").build(app.handle());
/// let builder = Builder::default().store(store);
/// Ok(())
/// });
/// ```
pub fn store(mut self, store: Store<R>) -> Self {
self.stores.insert(store.path.clone(), store);
@@ -225,15 +224,14 @@ impl<R: Runtime> Builder<R> {
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::{StoreBuilder,PluginBuilder};
/// use tauri_plugin_store::{StoreBuilder, Builder};
///
/// let store = StoreBuilder::new("store.bin".parse()?).build();
///
/// let builder = PluginBuilder::default().stores([store]);
///
/// # Ok(())
/// # }
/// tauri::Builder::default()
/// .setup(|app| {
/// let store = StoreBuilder::new("store.bin").build(app.handle());
/// let builder = Builder::default().stores([store]);
/// Ok(())
/// });
/// ```
pub fn stores<T: IntoIterator<Item = Store<R>>>(mut self, stores: T) -> Self {
self.stores = stores
@@ -250,15 +248,14 @@ impl<R: Runtime> Builder<R> {
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::{StoreBuilder,PluginBuilder};
/// use tauri_plugin_store::{StoreBuilder, Builder};
///
/// let store = StoreBuilder::new("store.bin".parse()?).build();
///
/// let builder = PluginBuilder::default().freeze();
///
/// # Ok(())
/// # }
/// tauri::Builder::default()
/// .setup(|app| {
/// let store = StoreBuilder::new("store.bin").build(app.handle());
/// app.handle().plugin(Builder::default().freeze().build());
/// Ok(())
/// });
/// ```
pub fn freeze(mut self) -> Self {
self.frozen = true;
@@ -270,16 +267,14 @@ impl<R: Runtime> Builder<R> {
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::{StoreBuilder,PluginBuilder};
/// use tauri::Wry;
/// use tauri_plugin_store::{StoreBuilder, Builder};
///
/// let store = StoreBuilder::new("store.bin".parse()?).build();
///
/// let plugin = PluginBuilder::default().build::<Wry>();
///
/// # Ok(())
/// # }
/// tauri::Builder::default()
/// .setup(|app| {
/// let store = StoreBuilder::new("store.bin").build(app.handle());
/// app.handle().plugin(Builder::default().build());
/// Ok(())
/// });
/// ```
pub fn build(mut self) -> TauriPlugin<R> {
plugin::Builder::new("store")
+18 -21
View File
@@ -8,7 +8,7 @@ use std::{
collections::HashMap,
fs::{create_dir_all, read, File},
io::Write,
path::PathBuf,
path::{Path, PathBuf},
};
use tauri::{AppHandle, Manager, Runtime};
@@ -30,8 +30,7 @@ fn default_deserialize(
}
/// Builds a [`Store`]
pub struct StoreBuilder<R: Runtime> {
app: AppHandle<R>,
pub struct StoreBuilder {
path: PathBuf,
defaults: Option<HashMap<String, JsonValue>>,
cache: HashMap<String, JsonValue>,
@@ -39,7 +38,7 @@ pub struct StoreBuilder<R: Runtime> {
deserialize: DeserializeFn,
}
impl<R: Runtime> StoreBuilder<R> {
impl StoreBuilder {
/// Creates a new [`StoreBuilder`].
///
/// # Examples
@@ -47,15 +46,14 @@ impl<R: Runtime> StoreBuilder<R> {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::StoreBuilder;
///
/// let builder = StoreBuilder::new("store.bin".parse()?);
/// let builder = StoreBuilder::new("store.bin");
///
/// # Ok(())
/// # }
/// ```
pub fn new(app: AppHandle<R>, path: PathBuf) -> Self {
pub fn new<P: AsRef<Path>>(path: P) -> Self {
Self {
app,
path,
path: path.as_ref().to_path_buf(),
defaults: None,
cache: Default::default(),
serialize: default_serialize,
@@ -75,7 +73,7 @@ impl<R: Runtime> StoreBuilder<R> {
///
/// defaults.insert("foo".to_string(), "bar".into());
///
/// let builder = StoreBuilder::new("store.bin".parse()?)
/// let builder = StoreBuilder::new("store.bin")
/// .defaults(defaults);
///
/// # Ok(())
@@ -93,7 +91,7 @@ impl<R: Runtime> StoreBuilder<R> {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::StoreBuilder;
///
/// let builder = StoreBuilder::new("store.bin".parse()?)
/// let builder = StoreBuilder::new("store.bin")
/// .default("foo".to_string(), "bar".into());
///
/// # Ok(())
@@ -113,7 +111,7 @@ impl<R: Runtime> StoreBuilder<R> {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::StoreBuilder;
///
/// let builder = StoreBuilder::new("store.json".parse()?)
/// let builder = StoreBuilder::new("store.json")
/// .serialize(|cache| serde_json::to_vec(&cache).map_err(Into::into));
///
/// # Ok(())
@@ -130,7 +128,7 @@ impl<R: Runtime> StoreBuilder<R> {
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::StoreBuilder;
///
/// let builder = StoreBuilder::new("store.json".parse()?)
/// let builder = StoreBuilder::new("store.json")
/// .deserialize(|bytes| serde_json::from_slice(&bytes).map_err(Into::into));
///
/// # Ok(())
@@ -144,16 +142,15 @@ impl<R: Runtime> StoreBuilder<R> {
///
/// # Examples
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// use tauri_plugin_store::StoreBuilder;
///
/// let store = StoreBuilder::new("store.bin".parse()?).build();
///
/// # Ok(())
/// # }
pub fn build(self) -> Store<R> {
/// tauri::Builder::default()
/// .setup(|app| {
/// let store = tauri_plugin_store::StoreBuilder::new("store.json").build(app.handle());
/// Ok(())
/// });
/// ```
pub fn build<R: Runtime>(self, app: AppHandle<R>) -> Store<R> {
Store {
app: self.app,
app,
path: self.path,
defaults: self.defaults,
cache: self.cache,
+1 -1
View File
@@ -4,7 +4,7 @@ Store secrets and keys using the [IOTA Stronghold](https://github.com/iotaledger
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+7 -12
View File
@@ -1,20 +1,15 @@
[package]
name = "tauri-plugin-updater"
version = "0.0.0"
edition = "2021"
#edition.workspace = true
#authors.workspace = true
#license.workspace = true
edition.workspace = true
authors.workspace = true
license.workspace = true
[dependencies]
#tauri = { workspace = true, features = ["updater"] }
#serde.workspace = true
#serde_json.workspace = true
#thiserror.workspace = true
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next", features = ["updater", "fs-extract-api"] }
serde = "1"
serde_json = "1"
thiserror = "1"
tauri = { workspace = true, features = ["updater", "fs-extract-api"] }
serde.workspace = true
serde_json.workspace = true
thiserror.workspace = true
tokio = "1"
reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] }
+1 -1
View File
@@ -4,7 +4,7 @@ In-app updates for Tauri applications.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+5 -6
View File
@@ -1,18 +1,17 @@
[package]
name = "app-updater"
version = "0.1.0"
edition = "2021"
#edition.workspace = true
edition.workspace = true
[build-dependencies]
tauri-build = { path = "../../../../../tauri/core/tauri-build", features = [] }
tauri-build.workspace = true
[dependencies]
tauri = { git = "https://github.com/tauri-apps/tauri", branch = "next" }
tauri.workspace = true
serde.workspace = true
serde_json.workspace = true
tauri-plugin-updater = { path = "../.." }
tiny_http = "0.11"
serde = "1"
serde_json = "1"
time = { version = "0.3", features = ["formatting"] }
[features]
+1 -1
View File
@@ -3,5 +3,5 @@
// SPDX-License-Identifier: MIT
fn main() {
tauri_build::build()
tauri_build::build()
}
+39 -39
View File
@@ -7,43 +7,43 @@
use tauri_plugin_updater::UpdaterExt;
fn main() {
let mut context = tauri::generate_context!();
if std::env::var("TARGET").unwrap_or_default() == "nsis" {
// /D sets the default installation directory ($INSTDIR),
// overriding InstallDir and InstallDirRegKey.
// It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces.
// Only absolute paths are supported.
// NOTE: we only need this because this is an integration test and we don't want to install the app in the programs folder
context.config_mut().tauri.updater.windows.installer_args = vec![format!(
"/D={}",
tauri::utils::platform::current_exe()
.unwrap()
.parent()
.unwrap()
.display()
)];
}
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let handle = app.handle();
tauri::async_runtime::spawn(async move {
match handle.updater().check().await {
Ok(update) => {
if let Err(e) = update.download_and_install().await {
println!("{e}");
std::process::exit(1);
}
std::process::exit(0);
}
Err(e) => {
println!("{e}");
std::process::exit(1);
}
}
});
Ok(())
})
.run(context)
.expect("error while running tauri application");
let mut context = tauri::generate_context!();
if std::env::var("TARGET").unwrap_or_default() == "nsis" {
// /D sets the default installation directory ($INSTDIR),
// overriding InstallDir and InstallDirRegKey.
// It must be the last parameter used in the command line and must not contain any quotes, even if the path contains spaces.
// Only absolute paths are supported.
// NOTE: we only need this because this is an integration test and we don't want to install the app in the programs folder
context.config_mut().tauri.updater.windows.installer_args = vec![format!(
"/D={}",
tauri::utils::platform::current_exe()
.unwrap()
.parent()
.unwrap()
.display()
)];
}
tauri::Builder::default()
.plugin(tauri_plugin_updater::Builder::new().build())
.setup(|app| {
let handle = app.handle();
tauri::async_runtime::spawn(async move {
match handle.updater().check().await {
Ok(update) => {
if let Err(e) = update.download_and_install(|_event| {}).await {
println!("{e}");
std::process::exit(1);
}
std::process::exit(0);
}
Err(e) => {
println!("{e}");
std::process::exit(1);
}
}
});
Ok(())
})
.run(context)
.expect("error while running tauri application");
}
@@ -176,7 +176,7 @@ fn update_app() {
let target =
tauri_plugin_updater::target().expect("running updater test in an unsupported platform");
let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let root_dir = manifest_dir.clone();
let root_dir = manifest_dir.join("../../../..");
let mut config = Config {
package: PackageConfig { version: "1.0.0" },
+1 -1
View File
@@ -4,7 +4,7 @@ Upload files from disk to a remote server over HTTP.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+33 -25
View File
@@ -1,30 +1,38 @@
import { invoke } from "@tauri-apps/api/tauri";
import { appWindow } from "tauri-plugin-window-api";
import { invoke, transformCallback } from "@tauri-apps/api/tauri";
interface ProgressPayload {
id: number;
progress: number;
total: number;
}
type ProgressHandler = (progress: number, total: number) => void;
const handlers: Map<number, ProgressHandler> = new Map();
let listening = false;
type ProgressHandler = (progress: ProgressPayload) => void;
async function listenToEventIfNeeded(event: string): Promise<void> {
if (listening) {
return await Promise.resolve();
}
return await appWindow
.listen<ProgressPayload>(event, ({ payload }) => {
const handler = handlers.get(payload.id);
if (handler != null) {
handler(payload.progress, payload.total);
}
})
.then(() => {
listening = true;
// TODO: use channel from @tauri-apps/api on v2
class Channel<T = unknown> {
id: number;
// @ts-expect-error field used by the IPC serializer
private readonly __TAURI_CHANNEL_MARKER__ = true;
#onmessage: (response: T) => void = () => {
// no-op
};
constructor() {
this.id = transformCallback((response: T) => {
this.#onmessage(response);
});
}
set onmessage(handler: (response: T) => void) {
this.#onmessage = handler;
}
get onmessage(): (response: T) => void {
return this.#onmessage;
}
toJSON(): string {
return `__CHANNEL__:${this.id}`;
}
}
async function upload(
@@ -37,17 +45,17 @@ async function upload(
window.crypto.getRandomValues(ids);
const id = ids[0];
const onProgress = new Channel<ProgressPayload>();
if (progressHandler != null) {
handlers.set(id, progressHandler);
onProgress.onmessage = progressHandler;
}
await listenToEventIfNeeded("upload://progress");
await invoke("plugin:upload|upload", {
id,
url,
filePath,
headers: headers ?? {},
onProgress,
});
}
@@ -65,17 +73,17 @@ async function download(
window.crypto.getRandomValues(ids);
const id = ids[0];
const onProgress = new Channel<ProgressPayload>();
if (progressHandler != null) {
handlers.set(id, progressHandler);
onProgress.onmessage = progressHandler;
}
await listenToEventIfNeeded("download://progress");
await invoke("plugin:upload|download", {
id,
url,
filePath,
headers: headers ?? {},
onProgress,
});
}
+1 -2
View File
@@ -28,7 +28,6 @@
"tslib": "^2.5.0"
},
"dependencies": {
"@tauri-apps/api": "^1.2.0",
"tauri-plugin-window-api": "0.0.0"
"@tauri-apps/api": "^1.2.0"
}
}
+15 -28
View File
@@ -5,16 +5,17 @@
use futures_util::TryStreamExt;
use serde::{ser::Serializer, Serialize};
use tauri::{
api::ipc::Channel,
command,
plugin::{Builder as PluginBuilder, TauriPlugin},
Runtime, Window,
Runtime,
};
use tokio::{fs::File, io::AsyncWriteExt};
use tokio_util::codec::{BytesCodec, FramedRead};
use read_progress_stream::ReadProgressStream;
use std::{collections::HashMap, sync::Mutex};
use std::collections::HashMap;
type Result<T> = std::result::Result<T, Error>;
@@ -39,19 +40,17 @@ impl Serialize for Error {
#[derive(Clone, Serialize)]
struct ProgressPayload {
id: u32,
progress: u64,
total: u64,
}
#[command]
async fn download<R: Runtime>(
window: Window<R>,
id: u32,
url: &str,
file_path: &str,
headers: HashMap<String, String>,
) -> Result<u32> {
on_progress: Channel<R>,
) -> Result<()> {
let client = reqwest::Client::new();
let mut request = client.get(url);
@@ -69,33 +68,28 @@ async fn download<R: Runtime>(
while let Some(chunk) = stream.try_next().await? {
file.write_all(&chunk).await?;
let _ = window.emit(
"download://progress",
ProgressPayload {
id,
progress: chunk.len() as u64,
total,
},
);
let _ = on_progress.send(&ProgressPayload {
progress: chunk.len() as u64,
total,
});
}
Ok(id)
Ok(())
}
#[command]
async fn upload<R: Runtime>(
window: Window<R>,
id: u32,
url: &str,
file_path: &str,
headers: HashMap<String, String>,
on_progress: Channel<R>,
) -> Result<serde_json::Value> {
// Read the file
let file = File::open(file_path).await?;
// Create the request and attach the file to the body
let client = reqwest::Client::new();
let mut request = client.post(url).body(file_to_body(id, window, file));
let mut request = client.post(url).body(file_to_body(on_progress, file));
// Loop trought the headers keys and values
// and add them to the request object.
@@ -108,20 +102,13 @@ async fn upload<R: Runtime>(
response.json().await.map_err(Into::into)
}
fn file_to_body<R: Runtime>(id: u32, window: Window<R>, file: File) -> reqwest::Body {
fn file_to_body<R: Runtime>(channel: Channel<R>, file: File) -> reqwest::Body {
let stream = FramedRead::new(file, BytesCodec::new()).map_ok(|r| r.freeze());
let window = Mutex::new(window);
reqwest::Body::wrap_stream(ReadProgressStream::new(
stream,
Box::new(move |progress, total| {
let _ = window.lock().unwrap().emit(
"upload://progress",
ProgressPayload {
id,
progress,
total,
},
);
let _ = channel.send(&ProgressPayload { progress, total });
}),
))
}
+1 -1
View File
@@ -4,7 +4,7 @@
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+1 -1
View File
@@ -4,7 +4,7 @@ Save window positions and sizes and restore them when the app is reopened.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.
+15 -3
View File
@@ -1,5 +1,17 @@
import { invoke } from "@tauri-apps/api/tauri";
import { WindowLabel, getCurrent } from "tauri-plugin-window-api";
interface WindowDef {
label: string;
}
declare global {
interface Window {
__TAURI_METADATA__: {
__windows: WindowDef[];
__currentWindow: WindowDef;
};
}
}
export enum StateFlags {
SIZE = 1 << 0,
@@ -21,7 +33,7 @@ async function saveWindowState(flags: StateFlags) {
/**
* Restore the state for the specified window from disk.
*/
async function restoreState(label: WindowLabel, flags: StateFlags) {
async function restoreState(label: string, flags: StateFlags) {
invoke("plugin:window-state|restore_state", { label, flags });
}
@@ -29,7 +41,7 @@ async function restoreState(label: WindowLabel, flags: StateFlags) {
* Restore the state for the current window from disk.
*/
async function restoreStateCurrent(flags: StateFlags) {
restoreState(getCurrent().label, flags);
restoreState(window.__TAURI_METADATA__.__currentWindow.label, flags);
}
export { restoreState, restoreStateCurrent, saveWindowState };
+1 -2
View File
@@ -28,7 +28,6 @@
"tslib": "^2.5.0"
},
"dependencies": {
"@tauri-apps/api": "^1.2.0",
"tauri-plugin-window-api": "0.0.0"
"@tauri-apps/api": "^1.2.0"
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ Interact with the Tauri window.
## Install
_This plugin requires a Rust version of at least **1.64**_
_This plugin requires a Rust version of at least **1.65**_
There are three general methods of installation that we can recommend.