import gleam/bit_string @target(javascript) import gleam/result /// This type represents all of the reasons for why a file system operation could fail. /// /// Most of these reasons are POSIX errors, which come from the operating system /// and start with E. Others have been added to represent other issues that may /// arise specific to this library. /// pub type FileError { /// Permission denied. Eacces /// Resource temporarily unavailable. Eagain /// Bad file number Ebadf /// Bad message. Ebadmsg /// File busy. Ebusy /// Resource deadlock avoided. Edeadlk /// On most architectures, same as `Edeadlk`. On some architectures, it /// means "File locking deadlock error." Edeadlock /// Disk quota exceeded. Edquot /// File already exists. Eexist /// Bad address in system call argument. Efault /// File too large. Efbig /// Inappropriate file type or format. Usually caused by trying to set the /// "sticky bit" on a regular file (not a directory). Eftype /// Interrupted system call. Eintr /// Invalid argument. Einval /// I/O error. Eio /// Illegal operation on a directory. Eisdir /// Too many levels of symbolic links. Eloop /// Too many open files. Emfile /// Too many links. Emlink /// Multihop attempted. Emultihop /// Filename too long Enametoolong /// File table overflow Enfile /// No buffer space available. Enobufs /// No such device. Enodev /// No locks available. Enolck /// Link has been severed. Enolink /// No such file or directory. Enoent /// Not enough memory. Enomem /// No space left on device. Enospc /// No STREAM resources. Enosr /// Not a STREAM. Enostr /// Function not implemented. Enosys /// Block device required. Enotblk /// Not a directory. Enotdir /// Operation not supported. Enotsup /// No such device or address. Enxio /// Operation not supported on socket. Eopnotsupp /// Value too large to be stored in data type. Eoverflow /// Not owner. Eperm /// Broken pipe. Epipe /// Result too large. Erange /// Read-only file system. Erofs /// Invalid seek. Espipe /// No such process. Esrch /// Stale remote file handle. Estale /// Text file busy. Etxtbsy /// Cross-domain link. Exdev /// File was requested to be read as UTF-8, but is not UTF-8 encoded. NotUtf8 /// Any error not accounted for by this type Unknown } /// Read a files contents as a string /// ## Example /// ```gleam /// let assert Ok(records) = read(from: "./users.csv") /// ``` /// pub fn read(from filepath: String) -> Result(String, FileError) { do_read(filepath) |> cast_error } /// Write a string to a file at the given path /// ## Example /// ```gleam /// let assert Ok(Nil) = write("Hello, World!", to: "./hello_world.txt") /// ``` /// pub fn write(contents: String, to filepath: String) -> Result(Nil, FileError) { do_write(contents, to: filepath) |> cast_error } /// Delete a file at a given filepath /// ## Example /// ```gleam /// let assert Ok(Nil) = delete(file_at: "./delete_me.txt") /// ``` /// pub fn delete(file_at filepath: String) -> Result(Nil, FileError) { do_delete(filepath) |> cast_error } /// Append a string to the contents of a file at the given path /// ## Example /// ```gleam /// let assert Ok(Nil) = append("more text", to: "./needs_more_text.txt") /// ``` /// pub fn append(contents: String, to filepath: String) -> Result(Nil, FileError) { do_append(contents, to: filepath) |> cast_error } /// Read a files contents as a bitstring /// ## Example /// ```gleam /// let assert Ok(records) = read_bits(from: "./users.csv") /// ``` /// pub fn read_bits(from filepath: String) -> Result(BitString, FileError) { do_read_bits(filepath) |> cast_error } /// Write a bitstring to a file at the given path /// ## Example /// ```gleam /// let assert Ok(Nil) = write_bits(<<"Hello, World!":utf8>>, to: "./hello_world.txt") /// ``` /// pub fn write_bits( bits: BitString, to filepath: String, ) -> Result(Nil, FileError) { do_write_bits(bits, filepath) |> cast_error } /// Append a bitstring to the contents of a file at the given path /// ## Example /// ```gleam /// let assert Ok(Nil) = append_bits(<<"more text":utf8>>, to: "./needs_more_text.txt") /// ``` /// pub fn append_bits( bits: BitString, to filepath: String, ) -> Result(Nil, FileError) { do_append_bits(bits, filepath) |> cast_error } /// Checks if the provided filepath is a directory /// ## Example /// ```gleam /// let assert True = is_directory("./test") /// ``` pub fn is_directory(filepath: String) -> Bool { do_is_directory(filepath) } /// Make a directory at the provided filepath /// /// ## Example /// ```gleam /// make_directory("./test") /// ``` pub fn make_directory(filepath: String) -> Result(Nil, FileError) { do_make_directory(filepath) } /// Delete a directory at the provided filepath /// /// ## Example /// ```gleam /// delete_directory("./test") /// ``` pub fn delete_directory(filepath: String) -> Result(Nil, FileError) { do_delete_directory(filepath) } /// Lists the contents of a directory. /// The list contains directory and file names, and is not recursive. /// /// ## Example /// ```gleam /// let assert Ok(files_and_folders) = list_contents(of: "./Folder1") /// ``` /// pub fn list_contents(of directory: String) -> Result(List(String), FileError) { do_list_contents(directory) } @target(javascript) fn do_read(from filepath: String) -> Result(String, String) { case do_read_bits(filepath) { Ok(bit_str) -> { case bit_string.to_string(bit_str) { Ok(str) -> Ok(str) _ -> Error("NOTUTF8") } } Error(e) -> Error(e) } } @target(javascript) fn do_write(content: String, to filepath: String) -> Result(Nil, String) { content |> bit_string.from_string |> do_write_bits(to: filepath) } @target(javascript) @external(javascript, "./file.mjs", "deleteFile") fn do_delete(file_at: String) -> Result(Nil, String) @target(javascript) fn do_append(content: String, to filepath: String) -> Result(Nil, String) { content |> bit_string.from_string |> do_append_bits(to: filepath) } @target(javascript) @external(javascript, "./file.mjs", "readBits") fn do_read_bits(from: String) -> Result(BitString, String) @target(javascript) @external(javascript, "./file.mjs", "writeBits") fn do_write_bits(content: BitString, to filepath: String) -> Result(Nil, String) @target(javascript) @external(javascript, "./file.mjs", "appendBits") fn do_append_bits( content: BitString, to filepath: String, ) -> Result(Nil, String) @target(javascript) @external(javascript, "./file.mjs", "isDirectory") fn do_is_directory(filepath: String) -> Bool @target(javascript) @external(javascript, "./file.mjs", "makeDirectory") fn do_make_directory(filepath: String) -> Result(Nil, FileError) @target(javascript) @external(javascript, "./file.mjs", "deleteDirectory") fn do_delete_directory(filepath: String) -> Result(Nil, FileError) @target(javascript) @external(javascript, "./file.mjs", "listContents") fn do_list_contents(directory_path: String) -> Result(List(String), FileError) @target(javascript) fn cast_error(input: Result(a, String)) -> Result(a, FileError) { result.map_error( input, fn(e) { case e { "EACCES" -> Eacces "EAGAIN" -> Eagain "EBADF" -> Ebadf "EBADMSG" -> Ebadmsg "EBUSY" -> Ebusy "EDEADLK" -> Edeadlk "EDEADLOCK" -> Edeadlock "EDQUOT" -> Edquot "EEXIST" -> Eexist "EFAULT" -> Efault "EFBIG" -> Efbig "EFTYPE" -> Eftype "EINTR" -> Eintr "EINVAL" -> Einval "EIO" -> Eio "EISDIR" -> Eisdir "ELOOP" -> Eloop "EMFILE" -> Emfile "EMLINK" -> Emlink "EMULTIHOP" -> Emultihop "ENAMETOOLONG" -> Enametoolong "ENFILE" -> Enfile "ENOBUFS" -> Enobufs "ENODEV" -> Enodev "ENOLCK" -> Enolck "ENOLINK" -> Enolink "ENOENT" -> Enoent "ENOMEM" -> Enomem "ENOSPC" -> Enospc "ENOSR" -> Enosr "ENOSTR" -> Enostr "ENOSYS" -> Enosys "ENOBLK" -> Enotblk "ENODIR" -> Enotdir "ENOTSUP" -> Enotsup "ENXIO" -> Enxio "EOPNOTSUPP" -> Eopnotsupp "EOVERFLOW" -> Eoverflow "EPERM" -> Eperm "EPIPE" -> Epipe "ERANGE" -> Erange "EROFS" -> Erofs "ESPIPE" -> Espipe "ESRCH" -> Esrch "ESTALE" -> Estale "ETXTBSY" -> Etxtbsy "EXDEV" -> Exdev "NOTUTF8" -> NotUtf8 _ -> Unknown } }, ) } @target(erlang) @external(erlang, "gleam_erlang_ffi", "append_file") fn do_append_bits( content: BitString, to filepath: String, ) -> Result(Nil, FileError) @target(erlang) @external(erlang, "gleam_erlang_ffi", "write_file") fn do_write_bits( content: BitString, to filepath: String, ) -> Result(Nil, FileError) @target(erlang) @external(erlang, "gleam_erlang_ffi", "read_file") fn do_read_bits(from: String) -> Result(BitString, FileError) @target(erlang) @external(erlang, "gleam_erlang_ffi", "delete_file") fn do_delete(filepath: String) -> Result(Nil, FileError) @target(erlang) fn do_append(content: String, to filepath: String) -> Result(Nil, FileError) { content |> bit_string.from_string |> do_append_bits(filepath) } @target(erlang) fn do_write(content: String, to filepath: String) -> Result(Nil, FileError) { content |> bit_string.from_string |> do_write_bits(filepath) } @target(erlang) fn do_read(from filepath: String) -> Result(String, FileError) { case do_read_bits(filepath) { Ok(bit_str) -> { case bit_string.to_string(bit_str) { Ok(str) -> Ok(str) _ -> Error(NotUtf8) } } Error(e) -> Error(e) } } @target(erlang) fn cast_error(input: Result(a, FileError)) -> Result(a, FileError) { input } @target(erlang) @external(erlang, "filelib", "is_dir") fn do_is_directory(path: String) -> Bool @target(erlang) @external(erlang, "gleam_erlang_ffi", "make_directory") fn do_make_directory(directory: String) -> Result(Nil, FileError) @target(erlang) @external(erlang, "gleam_erlang_ffi", "delete_directory") fn do_delete_directory(directory: String) -> Result(Nil, FileError) @target(erlang) @external(erlang, "gleam_erlang_ffi", "list_directory") fn do_list_contents(directory: String) -> Result(List(String), FileError)