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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Changelog

## Unreleased
- Add `create_link` function for hard-linking

## v2.2.1 - 25 March 2025
- Remove use of deprecated field in JavaScript ffi
Expand Down
15 changes: 15 additions & 0 deletions src/simplifile.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,21 @@ pub fn create_symlink(
from symlink: String,
) -> Result(Nil, FileError)

/// Create a "hard link" called symlink pointing to target.
/// This does not have the same relative pathing footgun as
/// `create_symlink`.
///
/// ## Example
/// ```gleam
/// create_link("../target", "./link")
/// ```
@external(erlang, "simplifile_erl", "create_link")
@external(javascript, "./simplifile_js.mjs", "createLink")
pub fn create_link(
to target: String,
from link: String,
) -> Result(Nil, FileError)

/// Lists the contents of a directory.
/// The list contains directory and file names, and is not recursive.
///
Expand Down
5 changes: 5 additions & 0 deletions src/simplifile_erl.erl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
create_directory/1,
create_dir_all/1,
delete_file/1,
create_link/2,
create_symlink/2,
delete/1,
delete_directory/1,
Expand Down Expand Up @@ -115,6 +116,10 @@ create_directory(Dir) ->
create_symlink(Existing, New) ->
posix_result(file:make_symlink(Existing, New)).

%% Create a "hard link" New to the file or directory Existing.
create_link(Existing, New) ->
posix_result(file:make_link(Existing, New)).

%% List the contents of a directory
read_directory(Dir) ->
case file:list_dir(Dir) of
Expand Down
11 changes: 11 additions & 0 deletions src/simplifile_js.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,17 @@ export function createSymlink(target, path) {
return gleamResult(() => fs.symlinkSync(target, path));
}

/**
* Create the "hard link" called path pointing to the target
*
* @param {string} target
* @param {sting} path
* returns {ok | GError}
*/
export function createLink(target, path) {
return gleamResult(() => fs.linkSync(target, path));
}

/**
* Create a directory at the given filepath
*
Expand Down
31 changes: 26 additions & 5 deletions test/simplifile_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import simplifile.{
Eopnotsupp, Eoverflow, Eperm, Epipe, Erange, Erofs, Espipe, 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_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_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,
}

pub fn main() {
Expand Down Expand Up @@ -98,6 +98,27 @@ pub fn make_symlink_test() {
let assert Ok(_) = delete("./tmp/" <> the_target)
}

pub fn make_link_test() {
let the_target = "target_of_created_link"
let the_link = "./tmp/created_link"

// File does not exist yet
let assert Error(_) = create_link(the_target, the_link)

let assert Ok(_) =
"some txt"
|> write(to: the_target)

let assert Ok(_) = create_link(the_target, the_link)
// Link already exists
let assert Error(_) = create_link(the_target, the_link)

let assert Ok("some txt") = read(the_link)

let assert Ok(_) = delete(the_target)
let assert Ok(_) = delete(the_link)
}

pub fn read_directory_test() {
// Test setup
let test_dir = "./tmp/test_dir"
Expand Down