Files
clapper/src/fileOps.js
2021-05-02 20:25:15 +02:00

228 lines
6.6 KiB
JavaScript

const { Gio, GLib } = imports.gi;
const ByteArray = imports.byteArray;
const Debug = imports.src.debug;
const Misc = imports.src.misc;
const { debug } = Debug;
/* FIXME: Use Gio._LocalFilePrototype once we are safe to assume
* that GJS with https://gitlab.gnome.org/GNOME/gjs/-/commit/ec9385b8 is used. */
const LocalFilePrototype = Gio.File.new_for_path('/').constructor.prototype;
Gio._promisify(LocalFilePrototype, 'load_bytes_async', 'load_bytes_finish');
Gio._promisify(LocalFilePrototype, 'make_directory_async', 'make_directory_finish');
Gio._promisify(LocalFilePrototype, 'replace_contents_bytes_async', 'replace_contents_finish');
Gio._promisify(LocalFilePrototype, 'query_info_async', 'query_info_finish');
Gio._promisify(LocalFilePrototype, 'enumerate_children_async', 'enumerate_children_finish');
Gio._promisify(Gio.FileEnumerator.prototype, 'close_async', 'close_finish');
Gio._promisify(Gio.FileEnumerator.prototype, 'next_files_async', 'next_files_finish');
function createCacheDirPromise()
{
const dir = Gio.File.new_for_path(
GLib.get_user_cache_dir() + '/' + Misc.appId
);
return createDirPromise(dir);
}
function createTempDirPromise()
{
const dir = Gio.File.new_for_path(
GLib.get_tmp_dir() + '/' + Misc.appId
);
return createDirPromise(dir);
}
/* Creates dir and resolves with it */
function createDirPromise(dir)
{
return new Promise((resolve, reject) => {
if(dir.query_exists(null))
return resolve(dir);
dir.make_directory_async(
GLib.PRIORITY_DEFAULT,
null
)
.then(success => {
if(success)
return resolve(dir);
reject(new Error(`could not create dir: ${dir.get_path()}`));
})
.catch(err => reject(err));
});
}
/* Simple save data to GioFile */
function saveFileSimplePromise(file, data)
{
return file.replace_contents_bytes_async(
GLib.Bytes.new_take(data),
null,
false,
Gio.FileCreateFlags.NONE,
null
);
}
/* Saves file in optional subdirectory and resolves with it */
function saveFilePromise(place, subdirName, fileName, data)
{
return new Promise(async (resolve, reject) => {
let folderPath = GLib[`get_${place}_dir`]() + '/' + Misc.appId;
if(subdirName)
folderPath += `/${subdirName}`;
const destDir = Gio.File.new_for_path(folderPath);
const destPath = folderPath + '/' + fileName;
debug(`saving file: ${destPath}`);
const checkFolders = (subdirName)
? [destDir.get_parent(), destDir]
: [destDir];
for(let dir of checkFolders) {
const createdDir = await createDirPromise(dir).catch(debug);
if(!createdDir)
return reject(new Error(`could not create dir: ${dir.get_path()}`));
}
const destFile = destDir.get_child(fileName);
saveFileSimplePromise(destFile, data)
.then(() => {
debug(`saved file: ${destPath}`);
resolve(destFile);
})
.catch(err => reject(err));
});
}
function getFileContentsPromise(place, subdirName, fileName)
{
return new Promise((resolve, reject) => {
let destPath = GLib[`get_${place}_dir`]() + '/' + Misc.appId;
if(subdirName)
destPath += `/${subdirName}`;
destPath += `/${fileName}`;
const file = Gio.File.new_for_path(destPath);
debug(`reading data from: ${destPath}`);
if(!file.query_exists(null)) {
debug(`no such file: ${file.get_path()}`);
return resolve(null);
}
file.load_bytes_async(null)
.then(result => {
const data = result[0].get_data();
if(!data || !data.length)
return reject(new Error('source file is empty'));
debug(`read data from: ${destPath}`);
if(data instanceof Uint8Array)
resolve(ByteArray.toString(data));
else
resolve(data);
})
.catch(err => reject(err));
});
}
function _getDirUrisPromise(dir, isDeep)
{
return new Promise(async (resolve, reject) => {
const enumerator = await dir.enumerate_children_async(
'standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT,
null
).catch(debug);
if(!enumerator)
return reject(new Error('could not create file enumerator'));
const dirPath = dir.get_path();
const arr = [];
debug(`enumerating files in dir: ${dirPath}`);
while(true) {
const infos = await enumerator.next_files_async(
1,
GLib.PRIORITY_DEFAULT,
null
).catch(debug);
if(!infos || !infos.length)
break;
const fileUri = dir.get_uri() + '/' + infos[0].get_name();
if(infos[0].get_file_type() !== Gio.FileType.DIRECTORY) {
arr.push(fileUri);
continue;
}
if(!isDeep)
continue;
const subDir = Misc.getFileFromLocalUri(fileUri);
const subDirUris = await _getDirUrisPromise(subDir, isDeep).catch(debug);
if(subDirUris && subDirUris.length)
arr.push(...subDirUris);
}
const isClosed = await enumerator.close_async(
GLib.PRIORITY_DEFAULT,
null
).catch(debug);
if(isClosed)
debug(`closed enumerator for dir: ${dirPath}`);
else
debug(new Error(`could not close file enumerator for dir: ${dirPath}`));
resolve(arr);
});
}
/* Either GioFile or URI for dir arg */
function getDirFilesUrisPromise(dir, isDeep)
{
return new Promise(async (resolve, reject) => {
if(!dir.get_path)
dir = Misc.getFileFromLocalUri(dir);
if(!dir)
return reject(new Error('invalid directory'));
const fileInfo = await dir.query_info_async(
'standard::type',
Gio.FileQueryInfoFlags.NONE,
GLib.PRIORITY_DEFAULT,
null
).catch(debug);
if(!fileInfo)
return reject(new Error('no file type info'));
if(fileInfo.get_file_type() !== Gio.FileType.DIRECTORY)
return resolve([dir.get_uri()]);
const arr = await _getDirUrisPromise(dir, isDeep).catch(debug);
if(!arr || !arr.length)
return reject(new Error('enumerated files list is empty'));
resolve(arr.sort());
});
}