diff --git a/CHANGELOG.md b/CHANGELOG.md index 5d33da8..7a623e3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/src/simplifile.gleam b/src/simplifile.gleam index 282e11b..4bdabab 100644 --- a/src/simplifile.gleam +++ b/src/simplifile.gleam @@ -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. /// diff --git a/src/simplifile_erl.erl b/src/simplifile_erl.erl index f554560..6603e65 100644 --- a/src/simplifile_erl.erl +++ b/src/simplifile_erl.erl @@ -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, @@ -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 diff --git a/src/simplifile_js.mjs b/src/simplifile_js.mjs index 827a0df..66ce630 100644 --- a/src/simplifile_js.mjs +++ b/src/simplifile_js.mjs @@ -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 * diff --git a/test/simplifile_test.gleam b/test/simplifile_test.gleam index 1bda19b..6e2d60d 100644 --- a/test/simplifile_test.gleam +++ b/test/simplifile_test.gleam @@ -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() { @@ -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"