-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
10afa9e
commit 7048e95
Showing
3 changed files
with
165 additions
and
78 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
{ | ||
"compilerOptions": { | ||
"checkJs": true, | ||
"rootDirs": ["src", "build/dev/javascript/simplifile"], | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,51 +1,77 @@ | ||
%%% -------------------------------------------------- | ||
%%% @author Benjamin Peinhardt <[email protected]> | ||
%%% @doc Erlang ffi glue code for the simplifile Gleam package. | ||
%%% @end | ||
%%% -------------------------------------------------- | ||
|
||
-module(simplifile_erl). | ||
|
||
%% API | ||
-export([ | ||
read_file/1, | ||
append_file/2, write_file/2, delete_file/1, delete_directory/1, recursive_delete/1, | ||
list_directory/1, make_directory/1, is_file/1, create_dir_all/1, rename_file/2, set_permissions/2 | ||
append_file/2, | ||
write_file/2, | ||
delete_file/1, | ||
delete_directory/1, | ||
recursive_delete/1, | ||
list_directory/1, | ||
make_directory/1, | ||
is_file/1, | ||
create_dir_all/1, | ||
rename_file/2, | ||
set_permissions/2 | ||
]). | ||
|
||
%% A macro for checking whether the error returned is one of the atoms for a posixe error. | ||
-define(is_posix_error(Error), | ||
Error =:= eacces orelse Error =:= eagain orelse Error =:= ebadf orelse | ||
Error =:= ebadmsg orelse Error =:= ebusy orelse Error =:= edeadlk orelse | ||
Error =:= edeadlock orelse Error =:= edquot orelse Error =:= eexist orelse | ||
Error =:= efault orelse Error =:= efbig orelse Error =:= eftype orelse | ||
Error =:= eintr orelse Error =:= einval orelse Error =:= eio orelse | ||
Error =:= eisdir orelse Error =:= eloop orelse Error =:= emfile orelse | ||
Error =:= emlink orelse Error =:= emultihop orelse Error =:= enametoolong orelse | ||
Error =:= enfile orelse Error =:= enobufs orelse Error =:= enodev orelse | ||
Error =:= enolck orelse Error =:= enolink orelse Error =:= enoent orelse | ||
Error =:= enomem orelse Error =:= enospc orelse Error =:= enosr orelse | ||
Error =:= enostr orelse Error =:= enosys orelse Error =:= enotblk orelse | ||
Error =:= enotdir orelse Error =:= enotsup orelse Error =:= enxio orelse | ||
Error =:= eopnotsupp orelse Error =:= eoverflow orelse Error =:= eperm orelse | ||
Error =:= epipe orelse Error =:= erange orelse Error =:= erofs orelse | ||
Error =:= espipe orelse Error =:= esrch orelse Error =:= estale orelse | ||
Error =:= etxtbsy orelse Error =:= exdev | ||
Error =:= ebadmsg orelse Error =:= ebusy orelse Error =:= edeadlk orelse | ||
Error =:= edeadlock orelse Error =:= edquot orelse Error =:= eexist orelse | ||
Error =:= efault orelse Error =:= efbig orelse Error =:= eftype orelse | ||
Error =:= eintr orelse Error =:= einval orelse Error =:= eio orelse | ||
Error =:= eisdir orelse Error =:= eloop orelse Error =:= emfile orelse | ||
Error =:= emlink orelse Error =:= emultihop orelse Error =:= enametoolong orelse | ||
Error =:= enfile orelse Error =:= enobufs orelse Error =:= enodev orelse | ||
Error =:= enolck orelse Error =:= enolink orelse Error =:= enoent orelse | ||
Error =:= enomem orelse Error =:= enospc orelse Error =:= enosr orelse | ||
Error =:= enostr orelse Error =:= enosys orelse Error =:= enotblk orelse | ||
Error =:= enotdir orelse Error =:= enotsup orelse Error =:= enxio orelse | ||
Error =:= eopnotsupp orelse Error =:= eoverflow orelse Error =:= eperm orelse | ||
Error =:= epipe orelse Error =:= erange orelse Error =:= erofs orelse | ||
Error =:= espipe orelse Error =:= esrch orelse Error =:= estale orelse | ||
Error =:= etxtbsy orelse Error =:= exdev | ||
). | ||
|
||
%% Only return the error if it's a posix error. Also converts ok to {ok, nil}, | ||
%% as gleam doesn't have atoms. | ||
posix_result(Result) -> | ||
case Result of | ||
ok -> {ok, nil}; | ||
{ok, Value} -> {ok, Value}; | ||
{error, Reason} when ?is_posix_error(Reason) -> {error, Reason} | ||
end. | ||
|
||
%% Read the binary contents of a file | ||
read_file(Filename) -> | ||
posix_result(file:read_file(Filename)). | ||
|
||
%% Write bytes to a file | ||
write_file(Contents, Filename) -> | ||
posix_result(file:write_file(Filename, Contents)). | ||
|
||
%% Append bytes to a file | ||
append_file(Contents, Filename) -> | ||
posix_result(file:write_file(Filename, Contents, [append])). | ||
|
||
%% Delete the file at the given path | ||
delete_file(Filename) -> | ||
posix_result(file:delete(Filename)). | ||
|
||
%% Create a directory at the given path. Missing parent directories are not created. | ||
make_directory(Dir) -> | ||
posix_result(file:make_dir(Dir)). | ||
|
||
%% List the contents of a directory | ||
list_directory(Dir) -> | ||
case file:list_dir(Dir) of | ||
{ok, Filenames} -> | ||
|
@@ -54,20 +80,26 @@ list_directory(Dir) -> | |
{error, Reason} | ||
end. | ||
|
||
%% Delete a directory at the given path. Directory must be empty. | ||
delete_directory(Dir) -> | ||
posix_result(file:del_dir(Dir)). | ||
|
||
%% Deletes a file/directory and everything in it. | ||
recursive_delete(Dir) -> | ||
posix_result(file:del_dir_r(Dir)). | ||
|
||
%% Checks whether a given file exists and is a file (as opposed to a directory) | ||
is_file(Filename) -> | ||
not (file:read_file_info(Filename) == {error, enoent}) and not filelib: is_dir(Filename). | ||
not (file:read_file_info(Filename) == {error, enoent}) and not filelib:is_dir(Filename). | ||
|
||
%% Creates the entire path for a given directory to exist | ||
create_dir_all(Filename) -> | ||
posix_result(filelib:ensure_dir(Filename)). | ||
|
||
%% Move file from one path to another | ||
rename_file(Source, Destination) -> | ||
posix_result(file:rename(Source, Destination)). | ||
|
||
%% Set the permissions for the given file. | ||
set_permissions(Filename, Permissions) -> | ||
posix_result(file:change_mode(Filename, Permissions)). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,111 +1,160 @@ | ||
// @ts-check | ||
|
||
import fs from "node:fs" | ||
import path from "node:path" | ||
import { BitArray, Ok, Error as GError, toList} from "./gleam.mjs"; | ||
|
||
/** | ||
* Read the contents of a file as a BitArray | ||
* | ||
* @param {string} filepath | ||
* @returns {Ok | GError} A Result with the BitArray of the file contents | ||
*/ | ||
export function readBits(filepath) { | ||
try { | ||
return gleamResult(() => { | ||
const contents = fs.readFileSync(path.normalize(filepath)) | ||
return new Ok(new BitArray(new Uint8Array(contents))) | ||
} catch(e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return new BitArray(new Uint8Array(contents)) | ||
}) | ||
} | ||
|
||
/** | ||
* Write the given BitArray to a file | ||
* | ||
* @param {BitArray} contents | ||
* @param {string} filepath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function writeBits(contents, filepath) { | ||
try { | ||
fs.writeFileSync(path.normalize(filepath), contents.buffer) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => fs.writeFileSync(path.normalize(filepath), contents.buffer)) | ||
} | ||
|
||
/** | ||
* Append the given BitArray to a file | ||
* | ||
* @param {BitArray} contents | ||
* @param {string} filepath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function appendBits(contents, filepath) { | ||
try { | ||
fs.appendFileSync(path.normalize(filepath), contents.buffer) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
} | ||
|
||
function stringifyError(e) { | ||
return e.code | ||
return gleamResult(() => fs.appendFileSync(path.normalize(filepath), contents.buffer)) | ||
} | ||
|
||
/** | ||
* Check whether a file exists at the given path | ||
* | ||
* @param {string} filepath | ||
* @returns {boolean} | ||
*/ | ||
export function isFile(filepath) { | ||
let fp = path.normalize(filepath) | ||
return fs.existsSync(fp) && fs.lstatSync(fp).isFile(); | ||
} | ||
|
||
/** | ||
* Check whether a directory exists at the given path | ||
* | ||
* @param {string} filepath | ||
* @returns {boolean} | ||
*/ | ||
export function isDirectory(filepath) { | ||
let fp = path.normalize(filepath) | ||
return fs.existsSync(fp) && fs.lstatSync(fp).isDirectory(); | ||
} | ||
|
||
/** | ||
* Create a directory at the given filepath | ||
* | ||
* @param {string} filepath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function makeDirectory(filepath) { | ||
try { | ||
fs.mkdirSync(path.normalize(filepath)) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => fs.mkdirSync(path.normalize(filepath))) | ||
} | ||
|
||
/** | ||
* Recursively create a directory structure from the given path. | ||
* | ||
* @param {string} filepath | ||
* @returns | ||
*/ | ||
export function createDirAll(filepath) { | ||
try { | ||
fs.mkdirSync(filepath, { recursive: true }) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => { | ||
fs.mkdirSync(path.normalize(filepath), { recursive: true }) | ||
}) | ||
} | ||
|
||
/** | ||
* Recursively delete a directory or delete a file at the given path. | ||
* | ||
* @param {string} fileOrDirPath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function deleteFileOrDirRecursive(fileOrDirPath) { | ||
try { | ||
return gleamResult(() => { | ||
if (isDirectory(fileOrDirPath)) { | ||
fs.rmSync(path.normalize(fileOrDirPath), { recursive: true }) | ||
} else { | ||
fs.unlinkSync(path.normalize(fileOrDirPath)) | ||
} | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
}) | ||
} | ||
|
||
/** | ||
* List the contents of a directory. | ||
* | ||
* @param {string} filepath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function listContents(filepath) { | ||
try { | ||
const stuff = toList(fs.readdirSync(path.normalize(filepath))) | ||
return new Ok(stuff) | ||
} catch(e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => toList(fs.readdirSync(path.normalize(filepath)))) | ||
} | ||
|
||
/** | ||
* Copy a file to a new path. | ||
* | ||
* @param {string} srcpath | ||
* @param {string} destpath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function copyFile(srcpath, destpath) { | ||
try { | ||
fs.copyFileSync(path.normalize(srcpath), path.normalize(destpath)) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => fs.copyFileSync(path.normalize(srcpath), path.normalize(destpath))) | ||
} | ||
|
||
/** | ||
* Move a file to the new path. | ||
* | ||
* @param {string} srcpath | ||
* @param {string} destpath | ||
* @returns {Ok | GError} | ||
*/ | ||
export function renameFile(srcpath, destpath) { | ||
try { | ||
fs.renameSync(path.normalize(srcpath), path.normalize(destpath)) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
} | ||
return gleamResult(() => fs.renameSync(path.normalize(srcpath), path.normalize(destpath))) | ||
} | ||
|
||
/** | ||
* Set the file permissions. Octal number should be in base 8. | ||
* | ||
* @param {string} filepath | ||
* @param {number} octalNumber | ||
* @returns {Ok | GError} | ||
*/ | ||
export function setPermissions(filepath, octalNumber) { | ||
return gleamResult(() => fs.chmodSync(path.normalize(filepath), octalNumber)) | ||
} | ||
|
||
/** | ||
* Perform some operation and return a Gleam `Result(a, String)` | ||
* where `a` is the type returned by the operation and the `String` | ||
* is the error code. | ||
* | ||
* @param {function():any} op | ||
* @returns {Ok | GError} | ||
*/ | ||
function gleamResult(op) { | ||
try { | ||
fs.chmodSync(path.normalize(filepath), octalNumber) | ||
return new Ok(undefined) | ||
} catch (e) { | ||
return new GError(stringifyError(e)) | ||
const val = op() | ||
return new Ok(val) | ||
} catch(e) { | ||
return new GError(e.code) | ||
} | ||
} |