Modern and ergonomic error handling for Lua, inspired by Rust's Result
Find a file
NTBBloodbath 4ed2a0189c
All checks were successful
ci/crow/tag/release Pipeline was successful
docs: update README.md
2025-08-22 15:15:43 -04:00
.woodpecker ci: add luarocks workflow (hope it doesn't blow up) 2025-08-22 13:26:30 -04:00
examples refactor!: rename Result.try(fn) to Result.safe(fn) 2025-06-18 08:45:03 -04:00
lua/fallo feat: add JSON serialization for structured errors 2025-07-06 04:14:58 -04:00
test feat: add JSON serialization for structured errors 2025-07-06 04:14:58 -04:00
.busted ci: add test workflow 2025-08-21 11:53:44 -04:00
.envrc Initial commit 2025-06-14 06:18:01 -04:00
.gitignore feat: enable code coverage through luacov 2025-06-14 12:17:24 -04:00
.stylua.toml Initial commit 2025-06-14 06:18:01 -04:00
fallo-scm-1.rockspec feat: add JSON serialization for structured errors 2025-07-06 04:14:58 -04:00
flake.lock Initial commit 2025-06-14 06:18:01 -04:00
flake.nix feat: add JSON serialization for structured errors 2025-07-06 04:14:58 -04:00
generate_rockspec.lua ci: add luarocks workflow (hope it doesn't blow up) 2025-08-22 13:26:30 -04:00
LICENSE Initial commit 2025-06-14 06:18:01 -04:00
README.md docs: update README.md 2025-08-22 15:15:43 -04:00

Fallo

Lua Version LuaRocks License

Fallo (failure in Spanish) is a modern, ergonomic error handling library for Lua with support for structured errors, highly inspired by Rust's Result.

It provides comprehensive error propagation, transformation, and interoperability with Lua's native error system.

Features

  • Structured errors with metadata and stack traces.
  • Method chaining for complex workflows.
  • Seamless interoperability with Lua's error system.
  • Automatic error propagation with Result.safe.
  • Side effect handlers for logging and metrics.
  • LuaCATS annotations for safe typing editor support.
  • Zero dependencies self-contained single-file library.

Installation

Luarocks

luarocks install fallo

Manual

As fallo is a self-contained single-file library, you can easily download fallo, place it in your project directory and import it using:

local Result = require("fallo")

Important

Starting from version 2.1.0, fallo uses the lua-cjson library by default for structured error serialization. However, this is handled gracefully, so if you embed the library directly in your project or won't be using serialization, you won't encounter any issues and will maintain the behavior of previous versions, though using serialization is recommended.

Usage

Basic Usage

Creating Results

local Result = require("fallo")

-- Successful result
local success = Result.ok(42)

-- Error result
local failure = Result.err("File not found")

-- Structured error
local structured = Result.err({
  code = 404,
  message = "User not found",
  context = {user_id = "abc123"}
})

Handling Results

-- Basic inspection
if success:is_ok() then
  print("Value:", success:unwrap())
end

if failure:is_err() then
  print("Error:", failure.error)
end

-- Safe unwrapping
local value = success:unwrap_or(100)
local computed = failure:unwrap_or_else(function(err)
  return "Default: " .. err
end)

-- Chained operations
Result.ok(5)
  :map(function(x) return x * 2 end) -- 10
  :and_then(function(x) return Result.ok(x + 1) end) -- 11
  :inspect(print) -- Prints 11
Error Propagation

Automatic Propagation

local config = Result.safe(function()
  local raw = read_file("config.json"):unwrap()
  return parse_json(raw):unwrap()
end)

config:inspect_err(function(e)
  print("Failed to load config:", e.message)
end)

Traditional Lua Integration

-- Convert to Lua's error system
local function legacy_api()
  return Result.ok(42):to_lua_error()
end

-- Convert from Lua's assert pattern
local function modern_wrapper()
  local success, data = legacy_api()
  return Result.from_assert(success, data)
end

Note

Check out the examples directory for usage examples.

Core API

Configuration

Option Description Default
traceback Whether to add error stack traces true
json JSON module to be used for serialization or nil lua-cjson
Example
local Result = require("fallo")

Result.config = {
  traceback = true,
  json = vim.json, -- nil to disable JSON serialization
}

Result Creation

Method Description Example
Result.ok(value) Creates successful result Result.ok(42)
Result.err(error) Creates error result Result.err("failed"), Result.err({ oh = "no" })
Result.wrap(fn, ...) Wraps function call Result.wrap(os.remove, "temp.txt")
Result.wrap_fn(fn) Creates safe function safe_remove = Result.wrap_fn(remove_tmpfiles)
Result.safe(fn) Protected execution context Result.safe(may_fail)
Result.serialize(err) Serialize a structured error Result.serialize(err)
Result.deserialize(err) Deserialize an error Result.deserialize(err)
:with_traceback() Add error traceback to result res:with_traceback()

Result Handling

Method Description Example
:unwrap() Returns value or throws error value = res:unwrap()
:unwrap_or(default) Returns value or default value = res:unwrap_or(0)
:unwrap_or_else(fn) Returns value or computes default value = res:unwrap_or_else(error_handler)
:expect(message) Unwraps with custom error message res:expect("Should have value")
:try() Propagates the error if Result is not Ok res:try()
:is_ok() Checks if result is successful if res:is_ok() then ... end
:is_err() Checks if result is error if res:is_err() then ... end

Transformations

Method Description Example
:map(fn) Transforms success value res:map(tostring)
:map_err(fn) Transforms error value res:map_err(enrich_error)
:and_then(fn) Chains result-returning functions res:and_then(validate)
:or_else(fn) Recovers from errors res:or_else(fallback)
:inspect(fn) Side effect on success res:inspect(print)
:inspect_err(fn) Side effect on error res:inspect_or(log_error)
:match(patterns) Pattern matching res:match({ok=success_fn, err=error_fn})

Contribute

  1. Fork it (https://codeberg.org/amartin/fallo/fork)
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am "feat: add some feature"). Please use conventional commits.
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request.

Important

Before commit your changes, make sure to format the code by running stylua {lua,test}/**/*.lua, and run busted to make sure all tests works correctly.

TODO

  • Allow error propagation
  • Coroutine-based async error handling (currently only through Result.safe(fn))
  • Expose configuration options (e.g. enable/disable stack traces)
  • Improve structured errors through metatables (perhaps?)

License

Fallo is licensed under LGPL-3.0+.