Implement file renaming

This commit is contained in:
Pascal Le Merrer 2026-01-19 14:13:52 +01:00
parent 18d8003f4a
commit e583d4e4cb
11 changed files with 291 additions and 18 deletions

8
jsconfig.json Normal file
View file

@ -0,0 +1,8 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"checkJs": false
},
"exclude": ["node_modules"]
}

2
justfile Normal file
View file

@ -0,0 +1,2 @@
dev:
cargo tauri dev

101
src-tauri/Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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"
]
}

31
src-tauri/package-lock.json generated Normal file
View file

@ -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"
}
}
}
}

5
src-tauri/package.json Normal file
View file

@ -0,0 +1,5 @@
{
"dependencies": {
"@tauri-apps/plugin-os": "^2.3.2"
}
}

View file

@ -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");

0
src/jsconfig.json Normal file
View file

View file

@ -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);
});
}
});

87
todo.js Normal file
View file

@ -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();
});