From e583d4e4cb617ec85562469adfe58377c891af63 Mon Sep 17 00:00:00 2001 From: Pascal Le Merrer Date: Mon, 19 Jan 2026 14:13:52 +0100 Subject: [PATCH] Implement file renaming --- jsconfig.json | 8 +++ justfile | 2 + src-tauri/Cargo.lock | 101 ++++++++++++++++++++++++++++ src-tauri/Cargo.toml | 1 + src-tauri/capabilities/default.json | 15 +++-- src-tauri/package-lock.json | 31 +++++++++ src-tauri/package.json | 5 ++ src-tauri/src/lib.rs | 15 +++-- src/jsconfig.json | 0 src/wrapper.js | 44 +++++++++--- todo.js | 87 ++++++++++++++++++++++++ 11 files changed, 291 insertions(+), 18 deletions(-) create mode 100644 jsconfig.json create mode 100644 justfile create mode 100644 src-tauri/package-lock.json create mode 100644 src-tauri/package.json create mode 100644 src/jsconfig.json create mode 100644 todo.js diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..8d445d6 --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "es6", + "checkJs": false + }, + "exclude": ["node_modules"] +} diff --git a/justfile b/justfile new file mode 100644 index 0000000..0b1de1d --- /dev/null +++ b/justfile @@ -0,0 +1,2 @@ +dev: + cargo tauri dev diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index b3a8aff..41e654e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -429,6 +429,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.43" @@ -901,6 +907,7 @@ dependencies = [ "tauri-build", "tauri-plugin-fs", "tauri-plugin-opener", + "tauri-plugin-os", ] [[package]] @@ -1174,6 +1181,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -2022,6 +2039,18 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nodrop" version = "0.1.14" @@ -2152,6 +2181,16 @@ dependencies = [ "objc2-foundation", ] +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + [[package]] name = "objc2-core-text" version = "0.3.2" @@ -2256,8 +2295,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags 2.10.0", + "block2", "objc2", + "objc2-cloud-kit", + "objc2-core-data", "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", "objc2-foundation", ] @@ -2311,6 +2369,22 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "os_info" +version = "3.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4022a17595a00d6a369236fdae483f0de7f0a339960a53118b818238e132224" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + [[package]] name = "pango" version = "0.18.3" @@ -3396,6 +3470,15 @@ dependencies = [ "syn 2.0.114", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -3641,6 +3724,24 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-os" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.17", +] + [[package]] name = "tauri-runtime" version = "2.9.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 77e7f92..3a5aaa1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -23,4 +23,5 @@ tauri-plugin-opener = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-plugin-fs = "2.4.5" +tauri-plugin-os = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index d7ef20b..ef20adc 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -2,7 +2,9 @@ "$schema": "../gen/schemas/desktop-schema.json", "identifier": "default", "description": "Capability for the main window", - "windows": ["main"], + "windows": [ + "main" + ], "permissions": [ "core:default", "opener:default", @@ -16,7 +18,12 @@ "fs:read-dirs", { "identifier": "fs:scope", - "allow": [{ "path": "**/*" }] - } + "allow": [ + { + "path": "**/*" + } + ] + }, + "os:default" ] -} +} \ No newline at end of file diff --git a/src-tauri/package-lock.json b/src-tauri/package-lock.json new file mode 100644 index 0000000..71bdda8 --- /dev/null +++ b/src-tauri/package-lock.json @@ -0,0 +1,31 @@ +{ + "name": "src-tauri", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@tauri-apps/plugin-os": "^2.3.2" + } + }, + "node_modules/@tauri-apps/api": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/@tauri-apps/api/-/api-2.9.1.tgz", + "integrity": "sha512-IGlhP6EivjXHepbBic618GOmiWe4URJiIeZFlB7x3czM0yDHHYviH1Xvoiv4FefdkQtn6v7TuwWCRfOGdnVUGw==", + "license": "Apache-2.0 OR MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/tauri" + } + }, + "node_modules/@tauri-apps/plugin-os": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-os/-/plugin-os-2.3.2.tgz", + "integrity": "sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + } + } +} diff --git a/src-tauri/package.json b/src-tauri/package.json new file mode 100644 index 0000000..5a839e0 --- /dev/null +++ b/src-tauri/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "@tauri-apps/plugin-os": "^2.3.2" + } +} diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index db3989d..d9f9600 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,16 +1,19 @@ // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ -#[tauri::command] -fn get_home_directory() { - println!("I was invoked from JavaScript!"); +#[tauri::command(rename_all = "snake_case")] +fn rename(invoke_message: String) { + // TODO: use a structure with old_name and new_name + println!( + "I was invoked from JavaScript, with this message: {}", + invoke_message + ); } - #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { - tauri::Builder::default() - .invoke_handler(tauri::generate_handler![get_home_directory]) + .plugin(tauri_plugin_os::init()) + .invoke_handler(tauri::generate_handler![rename]) .plugin(tauri_plugin_fs::init()) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/jsconfig.json b/src/jsconfig.json new file mode 100644 index 0000000..e69de29 diff --git a/src/wrapper.js b/src/wrapper.js index 81934eb..29ccf68 100644 --- a/src/wrapper.js +++ b/src/wrapper.js @@ -2,6 +2,10 @@ const app = Elm.Main.init({ node: document.getElementById("elm") }); const path = window.__TAURI__.path; const fs = window.__TAURI__.fs; const { invoke } = window.__TAURI__.core; +const os = window.__TAURI__.os; + + +const separator = os.family() == "windows" ? "\\" : "/"; // Get current directory path app.ports.getCurrentDirectoryPath.subscribe(function () { @@ -16,20 +20,20 @@ app.ports.getCurrentDirectoryPath.subscribe(function () { }); }); -async function getFileMetadata(directory, file) { - if (file.name.startsWith(".")) { +async function getFileMetadata(directory, fileName) { + if (fileName.startsWith(".")) { return null; } - const filePath = directory + "/" + file.name; + const filePath = directory + separator + fileName; return await fs .stat(filePath) .then((metadata) => { const value = { - IsDir: file.isDirectory, + IsDir: metadata.isDirectory, Mode: metadata.mode, ModTime: metadata.mtime.toISOString(), - Name: file.name, + Name: fileName, DirPath: directory, Size: metadata.size, }; @@ -48,7 +52,7 @@ app.ports.getSourceDirectoryContent.subscribe(function (directoryName) { .then((files) => { Promise.all( files.map((file) => { - return getFileMetadata(directoryName, file); + return getFileMetadata(directoryName, file.name); }), ).then((metadata) => { const filteredMetadata = metadata.filter((m) => m != null); @@ -67,7 +71,7 @@ app.ports.getDestinationDirectoryFiles.subscribe(function (directoryName) { .then((files) => { Promise.all( files.map((file) => { - return getFileMetadata(directoryName, file); + return getFileMetadata(directoryName, file.name); }), ).then((metadata) => { const filteredMetadata = metadata.filter((m) => { @@ -88,7 +92,7 @@ app.ports.getDestinationSubdirectories.subscribe(function (directoryName) { .then((files) => { Promise.all( files.map((file) => { - return getFileMetadata(directoryName, file); + return getFileMetadata(directoryName, file.name); }), ).then((metadata) => { const filteredMetadata = metadata.filter((m) => { @@ -102,3 +106,27 @@ app.ports.getDestinationSubdirectories.subscribe(function (directoryName) { app.ports.receiveError.send(msg); }); }); + +// Rename multiple files +app.ports.renameFiles.subscribe(function (renamings) { + console.log(renamings); + // invoke("rename", { changes: renamings }); + // + for(const renaming of renamings) { + fs.rename(renaming.oldName, renaming.newName) + .then(() => { + let lastSeparatorIndex = renaming.newName.lastIndexOf(separator); + let dir = renaming.newName.substring(0, lastSeparatorIndex + 1); + let fileName = renaming.newName.substring(lastSeparatorIndex + 1); + getFileMetadata(dir, fileName).then((fileInfo)=> { + fileInfo.PreviousName = renaming.oldName; + app.ports.filesRenamed.send([fileInfo]); + }); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); + + } +}); diff --git a/todo.js b/todo.js new file mode 100644 index 0000000..444ce70 --- /dev/null +++ b/todo.js @@ -0,0 +1,87 @@ +// Open file chooser to select a source directory +app.ports.selectSourceDirectory.subscribe(function (directoryName, title) { + window.go.main.App.SelectDirectory(directoryName, title) + .then((result) => { + app.ports.receiveSelectedSourceDirectory.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Open file chooser to select a destination directory +app.ports.selectDestinationDirectory.subscribe(function (directoryName, title) { + window.go.main.App.SelectDirectory(directoryName, title) + .then((result) => { + app.ports.receiveSelectedDestinationDirectory.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Moves a list of files to the given directory +app.ports.moveFiles.subscribe(function (params) { + let sourceFiles = params[0]; + let directoryName = params[1]; + console.log("move", params); + window.go.main.App.Move(sourceFiles, directoryName) + .then((result) => { + app.ports.receiveMovedFiles.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Rename multiple files +app.ports.renameFiles.subscribe(function (renamings) { + window.go.main.App.Rename(renamings) + .then((result) => { + app.ports.filesRenamed.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Delete a file +app.ports.removeFile.subscribe(function (filePath) { + window.go.main.App.Remove(filePath) + .then((result) => { + app.ports.fileRemoved.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Open a file using the default app +app.ports.openFile.subscribe(function (filePath) { + window.go.main.App.OpenFile(filePath).catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Create a directory +app.ports.createDirectory.subscribe(function (dirPath) { + window.go.main.App.CreateDirectory(dirPath) + .then((result) => { + app.ports.receiveCreatedDirectory.send(result); + }) + .catch((msg) => { + console.error(msg); + app.ports.receiveError.send(msg); + }); +}); + +// Quit the app +app.ports.quit.subscribe(function () { + window.go.main.App.Exit(); +});