Skip to content

Commit 077cd5f

Browse files
committed
chore: use nodejs native apis to implement fs.copy in tauri paths for performance
1 parent ba0efb2 commit 077cd5f

File tree

14 files changed

+31815
-66
lines changed

14 files changed

+31815
-66
lines changed

.eslintignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ test/virtualfs.js
66
test/virtualfs.js.map
77
test/virtualfs-debug.js
88
test/virtualfs-debug.js.map
9+
test/thirdparty

dist/phoenix-fs.js

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ const WS_COMMAND = {
125125
WRITE_BIN_FILE: "writeBinFile",
126126
MKDIR: "mkdir",
127127
RENAME: "rename",
128+
COPY: "copy",
128129
UNLINK: "unlink",
129130
WATCH: "watch",
130131
UNWATCH: "unwatch"
@@ -209,6 +210,104 @@ function _getStat(fullPath) {
209210
});
210211
}
211212

213+
/**
214+
* A stat that never rejects
215+
* @param fullPath
216+
* @return {Promise<unknown>}
217+
* @private
218+
*/
219+
function _resolvingStat(fullPath) {
220+
return new Promise((resolve, reject) => {
221+
fs.stat(fullPath)
222+
.then(resolve)
223+
.catch( (err) => {
224+
if(err && err.code === "ENOENT"){
225+
resolve(null);
226+
return;
227+
}
228+
reject(err);
229+
});
230+
});
231+
}
232+
233+
async function _copyFile(srcFile, dst) {
234+
let dstStat = await _resolvingStat(dst);
235+
if(!dstStat){
236+
let parentDir= path.dirname(dst);
237+
let dstFileName= path.basename(dst);
238+
dstStat = await _resolvingStat(parentDir);
239+
if(dstStat && dstStat.isDirectory()){
240+
let dstFilePath = path.join(parentDir, dstFileName);
241+
await fs.copyFile(srcFile, dstFilePath);
242+
return dstFilePath;
243+
} else {
244+
const error = new Error(`_copyFile Cannot create file: ${dst} as parent dir ${parentDir} is file`);
245+
error.code = 'ENOTDIR';
246+
throw error;
247+
}
248+
}
249+
250+
let srcFileName= path.basename(srcFile);
251+
if(dstStat && dstStat.isDirectory()){
252+
let dstFilePath = path.join(dst, srcFileName);
253+
await fs.copyFile(srcFile, dstFilePath);
254+
return dstFilePath;
255+
} else if(dstStat && dstStat.isFile()){
256+
const error = new Error(`_copyFile Destination file already exists: ${dst}`);
257+
error.code = 'EEXIST';
258+
throw error;
259+
} else {
260+
const error = new Error(`_copyFile Cannot copy file, unknown destination: ${srcFile} to ${dst}`);
261+
error.code = 'EIO';
262+
throw error;
263+
}
264+
}
265+
266+
async function _copyFolder(srcFolder, dst) {
267+
let dstStat = await _resolvingStat(dst);
268+
if(dstStat && dstStat.isFile()){
269+
const error = new Error(`Destination file already exists: ${dst}`);
270+
error.code = 'EEXIST';
271+
throw error;
272+
} else if(dstStat && dstStat.isDirectory()){
273+
let destSubFolderPath= path.join(dst, path.basename(srcFolder));
274+
dstStat = await _resolvingStat(destSubFolderPath);
275+
if(dstStat){
276+
const error = new Error(`Destination folder already exists: ${destSubFolderPath}`);
277+
error.code = 'EEXIST';
278+
throw error;
279+
}
280+
await fs.cp(srcFolder, destSubFolderPath, { recursive: true });
281+
return destSubFolderPath;
282+
} else {
283+
await fs.cp(srcFolder, dst, { recursive: true });
284+
return dst;
285+
}
286+
}
287+
288+
async function _copy(ws, metadata) {
289+
const src = metadata.data.src,
290+
dst = metadata.data.dst;
291+
try {
292+
let srcStat = await _resolvingStat(src);
293+
if(!srcStat){
294+
const error = new Error(`Failed to copy ${src} to ${dst}, src does not exist.`);
295+
error.code = 'ENOENT';
296+
_reportError(ws, metadata, error)
297+
return;
298+
}
299+
if (srcStat.isFile()) {
300+
let copiedPath = await _copyFile(src, dst);
301+
_sendResponse(ws, metadata, {copiedPath});
302+
} else if (srcStat.isDirectory()) {
303+
let copiedPath = await _copyFolder(src, dst);
304+
_sendResponse(ws, metadata, {copiedPath});
305+
}
306+
} catch (err) {
307+
_reportError(ws, metadata, err, `Failed to copy file at path ${src} to ${dst}`);
308+
}
309+
}
310+
212311
function _reportError(ws, metadata, err= { }, defaultMessage = "Operation failed! ") {
213312
metadata.error = {
214313
message: err.message || defaultMessage,
@@ -432,6 +531,9 @@ function processWSCommand(ws, metadata, dataBuffer) {
432531
case WS_COMMAND.RENAME:
433532
_rename(ws, metadata);
434533
return;
534+
case WS_COMMAND.COPY:
535+
_copy(ws, metadata);
536+
return;
435537
case WS_COMMAND.UNLINK:
436538
_unlink(ws, metadata);
437539
return;

dist/virtualfs-debug.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18232,6 +18232,7 @@ const $17476582ace0b2bc$var$WS_COMMAND = {
1823218232
WRITE_BIN_FILE: "writeBinFile",
1823318233
MKDIR: "mkdir",
1823418234
RENAME: "rename",
18235+
COPY: "copy",
1823518236
UNLINK: "unlink",
1823618237
WATCH: "watch",
1823718238
UNWATCH: "unwatch"
@@ -18664,6 +18665,18 @@ function $17476582ace0b2bc$var$unlink(path, callback) {
1866418665
});
1866518666
});
1866618667
}
18668+
async function $17476582ace0b2bc$var$copy(src, dst, callback) {
18669+
const srcPlatformPath = $17476582ace0b2bc$require$Utils.getTauriPlatformPath(src);
18670+
const dstPlatformPath = $17476582ace0b2bc$require$Utils.getTauriPlatformPath(dst);
18671+
$17476582ace0b2bc$var$_execCommand($17476582ace0b2bc$var$WS_COMMAND.COPY, {
18672+
src: srcPlatformPath,
18673+
dst: dstPlatformPath
18674+
}).then(({ metadata: metadata })=>{
18675+
callback(null, $17476582ace0b2bc$require$Utils.getTauriVirtualPath(metadata.data.copiedPath));
18676+
}).catch((err)=>{
18677+
callback($17476582ace0b2bc$var$mapNodeTauriErrorMessage(err, src, `Failed to copy: ${src} to ${dst}`));
18678+
});
18679+
}
1866718680
const $17476582ace0b2bc$var$NodeTauriFS = {
1866818681
testNodeWsEndpoint: $17476582ace0b2bc$var$testNodeWsEndpoint,
1866918682
setNodeWSEndpoint: $17476582ace0b2bc$var$setNodeWSEndpoint,
@@ -18678,7 +18691,8 @@ const $17476582ace0b2bc$var$NodeTauriFS = {
1867818691
rename: $17476582ace0b2bc$var$rename,
1867918692
unlink: $17476582ace0b2bc$var$unlink,
1868018693
watchAsync: $17476582ace0b2bc$var$watchAsync,
18681-
unwatchAsync: $17476582ace0b2bc$var$unwatchAsync
18694+
unwatchAsync: $17476582ace0b2bc$var$unwatchAsync,
18695+
copy: $17476582ace0b2bc$var$copy
1868218696
};
1868318697
$17476582ace0b2bc$exports = {
1868418698
NodeTauriFS: $17476582ace0b2bc$var$NodeTauriFS
@@ -19159,6 +19173,19 @@ function $d1f4f8cce920e9b9$var$rename(oldPath, newPath, callback) {
1915919173
if (!$d1f4f8cce920e9b9$require$NodeTauriFS.getNodeWSEndpoint()) throw new Error("Please call fs.setNodeWSEndpoint('ws://your server') before calling this function.");
1916019174
$d1f4f8cce920e9b9$var$preferNodeWs = use;
1916119175
}
19176+
function $d1f4f8cce920e9b9$var$canCopy() {
19177+
// we can only copy if node tari fs is ready as tauri doesn't have folder copy apis.
19178+
return $d1f4f8cce920e9b9$require$NodeTauriFS.isNodeWSReady();
19179+
}
19180+
async function $d1f4f8cce920e9b9$var$copy(src, dst, callback) {
19181+
if (!$d1f4f8cce920e9b9$var$canCopy()) {
19182+
callback(new $d1f4f8cce920e9b9$require$Errors.EIO(`IO error while copying: ${src} to ${dst}, node not ready.`, src));
19183+
return;
19184+
}
19185+
src = globalObject.path.normalize(src);
19186+
dst = globalObject.path.normalize(dst);
19187+
return $d1f4f8cce920e9b9$require$NodeTauriFS.copy(src, dst, callback);
19188+
}
1916219189
const $d1f4f8cce920e9b9$var$TauriFS = {
1916319190
isTauriPath: $d1f4f8cce920e9b9$require$Utils.isTauriPath,
1916419191
isTauriSubPath: $d1f4f8cce920e9b9$require$Utils.isTauriSubPath,
@@ -19174,7 +19201,9 @@ const $d1f4f8cce920e9b9$var$TauriFS = {
1917419201
rename: $d1f4f8cce920e9b9$var$rename,
1917519202
unlink: $d1f4f8cce920e9b9$var$unlink,
1917619203
readFile: $d1f4f8cce920e9b9$var$readFile,
19177-
writeFile: $d1f4f8cce920e9b9$var$writeFile
19204+
writeFile: $d1f4f8cce920e9b9$var$writeFile,
19205+
copy: $d1f4f8cce920e9b9$var$copy,
19206+
canCopy: $d1f4f8cce920e9b9$var$canCopy
1917819207
};
1917919208
$d1f4f8cce920e9b9$exports = {
1918019209
TauriFS: $d1f4f8cce920e9b9$var$TauriFS
@@ -20328,6 +20357,7 @@ const $e3f139c5065f0041$var$fileSystemLib = {
2032820357
// spawn different threads in rust and write tauri handlers which is pretty complex atm. so instead we will
2032920358
// fall back to global copy here and will use node Tauri web socket fs adapter as and when it becomes available.
2033020359
if ($e3f139c5065f0041$require$Mounts.isMountSubPath(src) && $e3f139c5065f0041$require$Mounts.isMountSubPath(dst)) return $e3f139c5065f0041$require$NativeFS.copy(src, dst, callbackInterceptor);
20360+
else if ($e3f139c5065f0041$require$TauriFS.canCopy() && $e3f139c5065f0041$require$TauriFS.isTauriSubPath(src) && $e3f139c5065f0041$require$TauriFS.isTauriSubPath(dst)) return $e3f139c5065f0041$require$TauriFS.copy(src, dst, callbackInterceptor);
2033120361
else return $e3f139c5065f0041$require$globalCopy(src, dst, callbackInterceptor);
2033220362
},
2033320363
showSaveDialog: function(options) {

dist/virtualfs-debug.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)