diff --git a/plugins/fs/build.rs b/plugins/fs/build.rs index e45af528c..3909b1c0a 100644 --- a/plugins/fs/build.rs +++ b/plugins/fs/build.rs @@ -102,6 +102,7 @@ const COMMANDS: &[(&str, &[&str])] = &[ ("exists", &[]), ("watch", &[]), ("unwatch", &[]), + ("size", &[]), ]; fn main() { diff --git a/plugins/fs/guest-js/index.ts b/plugins/fs/guest-js/index.ts index 78a5d5fec..8af720a47 100644 --- a/plugins/fs/guest-js/index.ts +++ b/plugins/fs/guest-js/index.ts @@ -1322,6 +1322,28 @@ async function watchImmediate( } } +/** + * Get the size of a file or directory. + * @example + * ```typescript + * import { size, BaseDirectory } from '@tauri-apps/plugin-fs'; + * // Get the size of the `$APPDATA/tauri` directory. + * const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData }); + * console.log(dirSize); // 1024 + * ``` + * + * @since 2.0.0 + */ +async function size(path: string | URL): Promise { + if (path instanceof URL && path.protocol !== 'file:') { + throw new TypeError('Must be a file URL.') + } + + return await invoke('plugin:fs|size', { + path: path instanceof URL ? path.toString() : path, + }) +} + export type { CreateOptions, OpenOptions, @@ -1369,5 +1391,6 @@ export { writeTextFile, exists, watch, - watchImmediate + watchImmediate, + size } diff --git a/plugins/fs/permissions/autogenerated/commands/size.toml b/plugins/fs/permissions/autogenerated/commands/size.toml new file mode 100644 index 000000000..8a0ea55cc --- /dev/null +++ b/plugins/fs/permissions/autogenerated/commands/size.toml @@ -0,0 +1,13 @@ +# Automatically generated - DO NOT EDIT! + +"$schema" = "../../schemas/schema.json" + +[[permission]] +identifier = "allow-size" +description = "Enables the size command without any pre-configured scope." +commands.allow = ["size"] + +[[permission]] +identifier = "deny-size" +description = "Denies the size command without any pre-configured scope." +commands.deny = ["size"] diff --git a/plugins/fs/permissions/autogenerated/reference.md b/plugins/fs/permissions/autogenerated/reference.md index 05d14475b..3cec32ed8 100644 --- a/plugins/fs/permissions/autogenerated/reference.md +++ b/plugins/fs/permissions/autogenerated/reference.md @@ -3410,6 +3410,32 @@ Denies the seek command without any pre-configured scope. +`fs:allow-size` + + + + +Enables the size command without any pre-configured scope. + + + + + + + +`fs:deny-size` + + + + +Denies the size command without any pre-configured scope. + + + + + + + `fs:allow-stat` diff --git a/plugins/fs/permissions/read-meta.toml b/plugins/fs/permissions/read-meta.toml index 09c731ad6..83024b0c2 100644 --- a/plugins/fs/permissions/read-meta.toml +++ b/plugins/fs/permissions/read-meta.toml @@ -3,4 +3,4 @@ [[permission]] identifier = "read-meta" description = "This enables all index or metadata related commands without any pre-configured accessible paths." -commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists"] +commands.allow = ["read_dir", "stat", "lstat", "fstat", "exists", "size"] diff --git a/plugins/fs/permissions/schemas/schema.json b/plugins/fs/permissions/schemas/schema.json index 275e44d19..2c13d5c61 100644 --- a/plugins/fs/permissions/schemas/schema.json +++ b/plugins/fs/permissions/schemas/schema.json @@ -1589,6 +1589,16 @@ "type": "string", "const": "deny-seek" }, + { + "description": "Enables the size command without any pre-configured scope.", + "type": "string", + "const": "allow-size" + }, + { + "description": "Denies the size command without any pre-configured scope.", + "type": "string", + "const": "deny-size" + }, { "description": "Enables the stat command without any pre-configured scope.", "type": "string", diff --git a/plugins/fs/src/commands.rs b/plugins/fs/src/commands.rs index 85c866c7d..2088236f8 100644 --- a/plugins/fs/src/commands.rs +++ b/plugins/fs/src/commands.rs @@ -3,6 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: MIT +use anyhow::Ok; use serde::{Deserialize, Serialize, Serializer}; use serde_repr::{Deserialize_repr, Serialize_repr}; use tauri::{ @@ -897,6 +898,55 @@ pub fn exists( Ok(resolved_path.exists()) } +#[tauri::command] +pub async fn size( + webview: Webview, + global_scope: GlobalScope, + command_scope: CommandScope, + path: SafeFilePath, + options: Option, +) -> CommandResult { + let resolved_path = resolve_path( + &webview, + &global_scope, + &command_scope, + path, + options.as_ref().and_then(|o| o.base_dir), + )?; + + let metadata = resolved_path.metadata()?; + + if metadata.is_file() { + Ok(metadata.len()) + } else { + let size = get_dir_size(resolved_path).map_err(|e| { + format!( + "failed to get size at path: {} with error: {e}", + resolved_path.display() + ) + })?; + + Ok(size) + } +} + +fn get_dir_size(path: PathBuf) -> CommandResult { + let mut size = 0; + + for entry in std::fs::read_dir(path)? { + let entry = entry?; + let metadata = entry.metadata()?; + + if metadata.is_file() { + size += metadata.len(); + } else if metadata.is_dir() { + size += get_dir_size(entry.path())?; + } + } + + Ok(size) +} + #[cfg(not(target_os = "android"))] pub fn resolve_file( webview: &Webview,