mirror of
https://github.com/tauri-apps/plugins-workspace.git
synced 2026-06-06 13:53:54 +02:00
feat(fs): improved API (#751)
* feat(fs): improved API * fmt * fix unix builds * again * clippy * clippy * fix import in docs examples * fmt, clippy * Update linux.rs * add API for watch * fix with `watcher` feature flag * use baseDir for all commands * do not export close function * fix build * organize and address review comments * fmt * generated files * rename FsFile to FileHandle, move APIs and docs * extend example * extend `Resource` * actually extend it --------- Co-authored-by: FabianLars <fabianlars@fabianlars.de> Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
@@ -9,9 +9,9 @@ rust-version = { workspace = true }
|
||||
links = "tauri-plugin-deep-link"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
rustc-args = [ "--cfg", "docsrs" ]
|
||||
rustdoc-args = [ "--cfg", "docsrs" ]
|
||||
targets = [ "x86_64-linux-android" ]
|
||||
rustc-args = ["--cfg", "docsrs"]
|
||||
rustdoc-args = ["--cfg", "docsrs"]
|
||||
targets = ["x86_64-linux-android"]
|
||||
|
||||
[build-dependencies]
|
||||
serde = { workspace = true }
|
||||
@@ -24,4 +24,4 @@ serde_json = { workspace = true }
|
||||
tauri = { workspace = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = "2"
|
||||
url = { workspace = true }
|
||||
|
||||
@@ -13,8 +13,10 @@ rustdoc-args = [ "--cfg", "docsrs" ]
|
||||
|
||||
[dependencies]
|
||||
serde = { workspace = true }
|
||||
serde_repr = "0.1"
|
||||
tauri = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
url = { workspace = true }
|
||||
anyhow = "1"
|
||||
uuid = { version = "1", features = [ "v4" ] }
|
||||
glob = "0.3"
|
||||
|
||||
+1075
-448
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+700
-294
File diff suppressed because it is too large
Load Diff
+19
-10
@@ -49,17 +49,29 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
||||
PluginBuilder::<R, Option<Config>>::new("fs")
|
||||
.js_init_script(include_str!("api-iife.js").to_string())
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
commands::create,
|
||||
commands::open,
|
||||
commands::copy_file,
|
||||
commands::close,
|
||||
commands::mkdir,
|
||||
commands::read_dir,
|
||||
commands::read,
|
||||
commands::read_file,
|
||||
commands::read_text_file,
|
||||
commands::read_text_file_lines,
|
||||
commands::read_text_file_lines_next,
|
||||
commands::remove,
|
||||
commands::rename,
|
||||
commands::seek,
|
||||
commands::stat,
|
||||
commands::lstat,
|
||||
commands::fstat,
|
||||
commands::truncate,
|
||||
commands::ftruncate,
|
||||
commands::write,
|
||||
commands::write_file,
|
||||
commands::read_dir,
|
||||
commands::copy_file,
|
||||
commands::create_dir,
|
||||
commands::remove_dir,
|
||||
commands::remove_file,
|
||||
commands::rename_file,
|
||||
commands::write_text_file,
|
||||
commands::exists,
|
||||
commands::metadata,
|
||||
#[cfg(feature = "watch")]
|
||||
watcher::watch,
|
||||
#[cfg(feature = "watch")]
|
||||
@@ -75,9 +87,6 @@ pub fn init<R: Runtime>() -> TauriPlugin<R, Option<Config>> {
|
||||
.unwrap_or(&default_scope),
|
||||
)?);
|
||||
|
||||
#[cfg(feature = "watch")]
|
||||
app.manage(watcher::WatcherCollection::default());
|
||||
|
||||
Ok(())
|
||||
})
|
||||
.on_event(|app, event| {
|
||||
|
||||
+64
-34
@@ -5,12 +5,13 @@
|
||||
use notify::{Config, Event, RecommendedWatcher, RecursiveMode, Watcher};
|
||||
use notify_debouncer_mini::{new_debouncer, DebounceEventResult, Debouncer};
|
||||
use serde::Deserialize;
|
||||
use tauri::{command, ipc::Channel, State};
|
||||
|
||||
use crate::Result;
|
||||
use tauri::{
|
||||
ipc::Channel,
|
||||
path::{BaseDirectory, SafePathBuf},
|
||||
AppHandle, Manager, Resource, ResourceId, Runtime,
|
||||
};
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::PathBuf,
|
||||
sync::{
|
||||
mpsc::{channel, Receiver},
|
||||
@@ -20,10 +21,26 @@ use std::{
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
type Id = u32;
|
||||
use crate::commands::{resolve_path, CommandResult};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WatcherCollection(Mutex<HashMap<Id, (WatcherKind, Vec<PathBuf>)>>);
|
||||
struct InnerWatcher {
|
||||
pub kind: WatcherKind,
|
||||
paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
pub struct WatcherResource(Mutex<InnerWatcher>);
|
||||
impl WatcherResource {
|
||||
fn new(kind: WatcherKind, paths: Vec<PathBuf>) -> Self {
|
||||
Self(Mutex::new(InnerWatcher { kind, paths }))
|
||||
}
|
||||
|
||||
fn with_lock<R, F: FnMut(&mut InnerWatcher) -> R>(&self, mut f: F) -> R {
|
||||
let mut watcher = self.0.lock().unwrap();
|
||||
f(&mut watcher)
|
||||
}
|
||||
}
|
||||
|
||||
impl Resource for WatcherResource {}
|
||||
|
||||
enum WatcherKind {
|
||||
Debouncer(Debouncer<RecommendedWatcher>),
|
||||
@@ -55,63 +72,76 @@ fn watch_debounced(on_event: Channel, rx: Receiver<DebounceEventResult>) {
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct WatchOptions {
|
||||
delay_ms: Option<u64>,
|
||||
dir: Option<BaseDirectory>,
|
||||
recursive: bool,
|
||||
delay_ms: Option<u64>,
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn watch(
|
||||
watchers: State<'_, WatcherCollection>,
|
||||
id: Id,
|
||||
paths: Vec<PathBuf>,
|
||||
#[tauri::command]
|
||||
pub async fn watch<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
paths: Vec<SafePathBuf>,
|
||||
options: WatchOptions,
|
||||
on_event: Channel,
|
||||
) -> Result<()> {
|
||||
) -> CommandResult<ResourceId> {
|
||||
let mut resolved_paths = Vec::with_capacity(paths.capacity());
|
||||
for path in paths {
|
||||
resolved_paths.push(resolve_path(&app, path, options.dir)?);
|
||||
}
|
||||
|
||||
let mode = if options.recursive {
|
||||
RecursiveMode::Recursive
|
||||
} else {
|
||||
RecursiveMode::NonRecursive
|
||||
};
|
||||
|
||||
let watcher = if let Some(delay) = options.delay_ms {
|
||||
let kind = if let Some(delay) = options.delay_ms {
|
||||
let (tx, rx) = channel();
|
||||
let mut debouncer = new_debouncer(Duration::from_millis(delay), tx)?;
|
||||
let watcher = debouncer.watcher();
|
||||
for path in &paths {
|
||||
watcher.watch(path, mode)?;
|
||||
for path in &resolved_paths {
|
||||
watcher.watch(path.as_ref(), mode)?;
|
||||
}
|
||||
watch_debounced(on_event, rx);
|
||||
WatcherKind::Debouncer(debouncer)
|
||||
} else {
|
||||
let (tx, rx) = channel();
|
||||
let mut watcher = RecommendedWatcher::new(tx, Config::default())?;
|
||||
for path in &paths {
|
||||
watcher.watch(path, mode)?;
|
||||
for path in &resolved_paths {
|
||||
watcher.watch(path.as_ref(), mode)?;
|
||||
}
|
||||
watch_raw(on_event, rx);
|
||||
WatcherKind::Watcher(watcher)
|
||||
};
|
||||
|
||||
watchers.0.lock().unwrap().insert(id, (watcher, paths));
|
||||
let rid = app
|
||||
.resources_table()
|
||||
.add(WatcherResource::new(kind, resolved_paths));
|
||||
|
||||
Ok(())
|
||||
Ok(rid)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub async fn unwatch(watchers: State<'_, WatcherCollection>, id: Id) -> Result<()> {
|
||||
if let Some((watcher, paths)) = watchers.0.lock().unwrap().remove(&id) {
|
||||
match watcher {
|
||||
WatcherKind::Debouncer(mut debouncer) => {
|
||||
for path in paths {
|
||||
debouncer.watcher().unwatch(&path)?
|
||||
#[tauri::command]
|
||||
pub async fn unwatch<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> CommandResult<()> {
|
||||
let watcher = app.resources_table().take::<WatcherResource>(rid)?;
|
||||
WatcherResource::with_lock(&watcher, |watcher| {
|
||||
match &mut watcher.kind {
|
||||
WatcherKind::Debouncer(ref mut debouncer) => {
|
||||
for path in &watcher.paths {
|
||||
debouncer.watcher().unwatch(path.as_ref()).map_err(|e| {
|
||||
format!("failed to unwatch path: {} with error: {e}", path.display())
|
||||
})?
|
||||
}
|
||||
}
|
||||
WatcherKind::Watcher(mut watcher) => {
|
||||
for path in paths {
|
||||
watcher.unwatch(&path)?
|
||||
WatcherKind::Watcher(ref mut w) => {
|
||||
for path in &watcher.paths {
|
||||
w.unwatch(path.as_ref()).map_err(|e| {
|
||||
format!("failed to unwatch path: {} with error: {e}", path.display())
|
||||
})?
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ tauri-plugin-fs = { path = "../fs", version = "2.0.0-alpha.5" }
|
||||
glob = "0.3"
|
||||
http = "0.2"
|
||||
reqwest = { version = "0.11", default-features = false }
|
||||
url = "2.4"
|
||||
url = { workspace = true }
|
||||
data-url = "0.3"
|
||||
|
||||
[features]
|
||||
|
||||
@@ -119,7 +119,7 @@ unsafe extern "system" fn single_instance_window_proc<R: Runtime>(
|
||||
let data = CStr::from_ptr((*cds_ptr).lpData as _).to_string_lossy();
|
||||
let mut s = data.split('|');
|
||||
let cwd = s.next().unwrap();
|
||||
let args = s.into_iter().map(|s| s.to_string()).collect();
|
||||
let args = s.map(|s| s.to_string()).collect();
|
||||
callback(app_handle, args, cwd.to_string());
|
||||
}
|
||||
1
|
||||
|
||||
@@ -18,7 +18,7 @@ serde_json = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
tokio = "1"
|
||||
reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] }
|
||||
url = "2"
|
||||
url = { workspace = true }
|
||||
http = "0.2"
|
||||
dirs-next = "2"
|
||||
minisign-verify = "0.2"
|
||||
|
||||
Reference in New Issue
Block a user