Current section

Files

Jump to
simplifile src simplifile.gleam
Raw

src/simplifile.gleam

import gleam/bit_string
import gleam/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 or directory at a given path. Performs a recursive
/// delete on a directory.
/// ## Example
/// ```gleam
/// let assert Ok(Nil) = delete(file_at: "./delete_me.txt")
/// ```
///
pub fn delete(file_or_dir_at path: String) -> Result(Nil, FileError) {
do_delete(path)
|> 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)
}
/// Create a directory at the provided filepath. Returns an error if
/// the directory already exists.
///
/// ## Example
/// ```gleam
/// create_directory("./test")
/// ```
pub fn create_directory(filepath: String) -> Result(Nil, FileError) {
do_make_directory(filepath)
|> cast_error
}
/// 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)
|> cast_error
}
/// Returns `True` if there is a file or directory at the given path, false otherwise
///
pub fn is_file(filepath: String) -> Bool {
do_is_file(filepath)
}
/// Creates an empty file at the given filepath. Returns an `Error(Eexist)`
/// if the file already exists.
///
pub fn create_file(at filepath: String) -> Result(Nil, FileError) {
case
filepath
|> is_file || filepath
|> is_directory
{
True -> Error(Eexist)
False -> write_bits(<<>>, to: filepath)
}
}
/// Recursively creates necessary directories for a given directory
/// path. Note that if you pass a path that "looks like" a file, i.e.
/// `./a/b.txt`, a folder named `b.txt` will be created, so be sure
/// to pass only the path to the required directory.
pub fn create_dir_all(dirpath: String) -> Result(Nil, FileError) {
let path = case
dirpath
|> string.ends_with("/")
{
True -> dirpath
False -> dirpath <> "/"
}
do_create_dir_all(path)
|> cast_error
}
@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, "./simplifile_js.mjs", "deleteFileOrDirRecursive")
fn do_delete(file_or_dir_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, "./simplifile_js.mjs", "readBits")
fn do_read_bits(from: String) -> Result(BitString, String)
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "writeBits")
fn do_write_bits(content: BitString, to filepath: String) -> Result(Nil, String)
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "appendBits")
fn do_append_bits(
content: BitString,
to filepath: String,
) -> Result(Nil, String)
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "isDirectory")
fn do_is_directory(filepath: String) -> Bool
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "makeDirectory")
fn do_make_directory(filepath: String) -> Result(Nil, String)
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "listContents")
fn do_list_contents(directory_path: String) -> Result(List(String), String)
@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, "simplifile_erl", "append_file")
fn do_append_bits(
content: BitString,
to filepath: String,
) -> Result(Nil, FileError)
@target(erlang)
@external(erlang, "simplifile_erl", "write_file")
fn do_write_bits(
content: BitString,
to filepath: String,
) -> Result(Nil, FileError)
@target(erlang)
@external(erlang, "simplifile_erl", "read_file")
fn do_read_bits(from: String) -> Result(BitString, FileError)
@target(erlang)
@external(erlang, "simplifile_erl", "recursive_delete")
fn do_delete(file_or_dir_at: 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, "simplifile_erl", "make_directory")
fn do_make_directory(directory: String) -> Result(Nil, FileError)
@target(erlang)
@external(erlang, "simplifile_erl", "list_directory")
fn do_list_contents(directory: String) -> Result(List(String), FileError)
@external(erlang, "simplifile_erl", "is_file")
@external(javascript, "./simplifile_js.mjs", "isFile")
fn do_is_file(filepath: String) -> Bool
@target(javascript)
@external(javascript, "./simplifile_js.mjs", "createDirAll")
fn do_create_dir_all(dirpath: String) -> Result(Nil, String)
@target(erlang)
@external(erlang, "simplifile_erl", "create_dir_all")
fn do_create_dir_all(dirpath: String) -> Result(Nil, FileError)