Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

## v2.4.0 - 7 March 2026
- Use `raw` option for write and append operations on Erlang
- Add `delete_file` function for specifically deleting a file (uses `raw` on Erlang as well)

## v2.3.2 - 26 December 2025
- Fix bug where unknown errors were not properly converted to the Unknown variant in Erlang ffi.

Expand Down
2 changes: 1 addition & 1 deletion gleam.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name = "simplifile"
version = "2.3.2"
version = "2.4.0"
description = "Basic file operations that work on all targets"

licences = ["Apache-2.0"]
Expand Down
7 changes: 7 additions & 0 deletions src/simplifile.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,13 @@ pub fn write(
@external(javascript, "./simplifile_js.mjs", "delete_")
pub fn delete(file_or_dir_at path: String) -> Result(Nil, FileError)

/// Delete a file.
/// On Erlang, if you're specifically deleting a single file, this function
/// should be more efficient than `delete` because it can pass the raw option.
@external(erlang, "simplifile_erl", "delete_file")
@external(javascript, "./simplifile_js.mjs", "deleteFile")
pub fn delete_file(at path: String) -> Result(Nil, FileError)

/// Delete all files/directories specified in a list of paths.
/// Recursively deletes provided directories.
/// Does not return an error if one or more of the provided paths
Expand Down
6 changes: 3 additions & 3 deletions src/simplifile_erl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,20 @@ read_bits(Filename) ->
%% Write bytes to a file
write_bits(Filename, Contents) ->
case bit_size(Contents) rem 8 of
0 -> posix_result(file:write_file(Filename, Contents));
0 -> posix_result(file:write_file(Filename, Contents, [raw]));
_ -> {error, einval}
end.

%% Append bytes to a file
append_bits(Filename, Contents) ->
case bit_size(Contents) rem 8 of
0 -> posix_result(file:write_file(Filename, Contents, [append]));
0 -> posix_result(file:write_file(Filename, Contents, [append, raw]));
_ -> {error, einval}
end.

%% Delete the file at the given path
delete_file(Filename) ->
posix_result(file:delete(Filename)).
posix_result(file:delete(Filename, [raw])).

%% Create a directory at the given path. Missing parent directories are not created.
create_directory(Dir) ->
Expand Down
12 changes: 12 additions & 0 deletions src/simplifile_js.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,18 @@ export function delete_(fileOrDirPath) {
});
}

/**
* Specifically delete a single file.
*
* @param {string} filePath
* @returns {Ok | GError}
*/
export function deleteFile(filePath) {
return gleamResult(() => {
fs.unlinkSync(path.normalize(filePath));
})
}

/**
* List the contents of a directory.
*
Expand Down
35 changes: 30 additions & 5 deletions test/simplifile_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ import simplifile.{
Esrch, Estale, Etxtbsy, Exdev, Execute, File, FilePermissions, NotUtf8, Read,
Unknown, Write, append, append_bits, copy, copy_directory, copy_file,
create_directory, create_directory_all, create_file, create_link,
create_symlink, delete, delete_all, file_info, file_info_permissions,
file_info_permissions_octal, file_info_type, file_permissions_to_octal,
get_files, is_directory, is_file, is_symlink, link_info, read, read_bits,
read_directory, rename, set_permissions, set_permissions_octal, write,
write_bits,
create_symlink, delete, delete_all, delete_file, file_info,
file_info_permissions, file_info_permissions_octal, file_info_type,
file_permissions_to_octal, get_files, is_directory, is_file, is_symlink,
link_info, read, read_bits, read_directory, rename, set_permissions,
set_permissions_octal, write, write_bits,
}

pub fn main() {
Expand Down Expand Up @@ -357,6 +357,31 @@ pub fn delete_test() {
let assert Ok(False) = is_file("./tmp/" <> the_target)
}

pub fn delete_file_test() {
// Basic delete
let assert Ok(_) = write("Hello", to: "./tmp/existing_file_2.txt")
let assert Ok(_) = delete_file("./tmp/existing_file_2.txt")
let assert Error(Enoent) = read("./tmp/existing_file_2.txt")

// Deleting a file that doesn't exist throws an error
let assert Error(Enoent) = delete_file("./idontexist")

// Delete a symlink doesn't delete the target
let the_target = "target_of_created_symlink_2"
let the_symlink = "./tmp/created_symlink_2"
let assert Ok(_) =
""
|> write(to: "./tmp/" <> the_target)
let assert Ok(_) = create_symlink(the_target, the_symlink)
let assert Ok(True) = is_file("./tmp/" <> the_target)
let assert Ok(True) = is_symlink(the_symlink)
let assert Ok(_) = delete_file(the_symlink)
let assert Ok(False) = is_symlink(the_symlink)
let assert Ok(True) = is_file("./tmp/" <> the_target)
let assert Ok(_) = delete_file(at: "./tmp/" <> the_target)
let assert Ok(False) = is_file("./tmp/" <> the_target)
}

pub fn delete_all_test() {
let files =
list.map([1, 2, 3, 4, 5], fn(item) {
Expand Down