fio
"Complete", safe, ergonomic file operations for all Gleam targets.
A single import for everything you need: read, write, copy, delete, symlinks, permissions, paths, atomic writes, file handles, and more. It comes with rich error types and cross-platform consistency.
All functionality is available via import fio (no need for submodule imports).
Features
- Unified API: One
import fiofor all file operations. No juggling multiple packages. - Cross-platform: Works identically on Erlang, Node.js, Deno, and Bun.
- Rich errors: POSIX-style error codes plus semantic types like
NotUtf8(path). Pattern match precisely. - Atomic writes:
write_atomic/write_bits_atomicguarantee readers never see partial content. Temporary files are cleaned up even if the rename fails. - Atomic helper:
fio.atomic(path, fn(tmp_path) { ... })makes temporary-file writes explicit and ergonomic. The temp file lands in the same directory as the target and uses the.__fio_tmp_*prefix, consistent withwrite_atomic. - Streaming:
read_fold,stream,stream_bytes, andhandle.fold_chunkslet you process files chunk by chunk without loading them fully into memory. - Context helpers:
with_openedandwith_writerprovide resource-safe file-handle callbacks. - High-level helpers:
ensure_file,copy_if_newer,write_new,write_if_changed,read_lines(normalises\r\nand\n),write_lines, andfio/jsonfor common workflows. - Error UX:
fio.explain(error)returns a CLI-friendly description string. - Type-safe permissions:
FilePermissionswithSet(Permission), not magic integers. - Path operations:
join,split,expand,safe_relative, and more, built in. - Symlinks and hard links: Create, detect, read link targets.
- Symlink loop safety: Recursive operations track
(dev, inode)pairs. Circular symlinks are listed but never descended into. On Windows, whereinodemay be zero, the full path string is used as a fallback key. - FFI safety: Erlang bindings map hash algorithm strings with a closed set, preventing atom table exhaustion.
- Touch: Create files or update timestamps, like Unix
touch. - Idempotent deletes:
delete_allsucceeds silently on non-existent paths. - Observability:
fio/observerprovides structured event sinks and transparent wrappers to instrument any fio call without restructuring your code.
Installation
gleam add fioQuick Start
import fio
import fio/error
pub fn main() {
// Write and read
let assert Ok(_) = fio.write("hello.txt", "Ciao, mondo!")
let assert Ok(content) = fio.read("hello.txt")
// content == "Ciao, mondo!"
// Graceful error handling
case fio.read("missing.txt") {
Ok(text) -> use_text(text)
Error(error.Enoent) -> use_defaults()
Error(e) -> panic as { "Error: " <> error.describe(e) }
}
// Path safety (via the same `fio` facade)
let safe = fio.safe_relative("../../../etc/passwd")
// safe == Error(Nil) -- blocked!
}Documentation
A full reference for fio is available in the documentation: