diff --git a/CHANGELOG.md b/CHANGELOG.md index 11a9e75..091373a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,11 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed +- `deflua` function can now specify guards when using or not using state + ## [v0.2.1] - 2025-05-14 ### Added diff --git a/lib/lua/api.ex b/lib/lua/api.ex index 4310676..160942a 100644 --- a/lib/lua/api.ex +++ b/lib/lua/api.ex @@ -122,7 +122,7 @@ defmodule Lua.API do @doc """ Defines a function that can be exposed in Lua through `Lua.load_api/3` - deflua add_two(number) do + deflua add_two(number) when is_number(number) do number + 2 end @@ -143,6 +143,18 @@ defmodule Lua.API do {[], Lua.set!(lua, [key])} end + ## Using guards + + Since `deflua` uses non-conventional syntax to receive the current state, make sure + you specifiy the `when` clause and guards first, e.g. + + deflua set_int(key, value) when is_integer(value), state do + # Return nothing but modify the state + {[], Lua.set!(lua, [key])} + end + + Specifyiing the `when` cluase and guards last will result in a confusing error message. + ## Variadic functions Technically, all Lua functions are variadic, which means they can receive @@ -164,13 +176,20 @@ defmodule Lua.API do """ defmacro deflua(fa, state, rest) do + name = + case fa do + {:when, _, [{name, _, _} | _]} -> name + {name, _, _} -> name + end + {fa, _acc} = - Macro.prewalk(fa, false, fn - {name, context, args}, false -> {{name, context, args ++ List.wrap(state)}, true} - ast, true -> {ast, true} - end) + Macro.prewalk(fa, :ok, fn + {^name, context, args}, acc -> + {{name, context, args ++ List.wrap(state)}, acc} - {name, _, _} = fa + ast, acc -> + {ast, acc} + end) quote do @lua_function validate_func!( diff --git a/test/lua/api_test.exs b/test/lua/api_test.exs index 3e16f59..3729732 100644 --- a/test/lua/api_test.exs +++ b/test/lua/api_test.exs @@ -299,5 +299,41 @@ defmodule Lua.APITest do assert module.fail(1, 2) == 3 assert module.fail(1, "2") == "rescued" end + + test "deflua functions can have guards" do + assert [{module, _}] = + Code.compile_string(""" + defmodule WithGuards do + use Lua.API + + deflua has_a_guard(a) when is_integer(a) do + a + end + + deflua has_a_guard(a) when is_binary(a) and not is_boolean(a) do + a + end + + deflua has_a_guard(_) do + "not a int" + end + + deflua with_state(a) when is_integer(a), state do + {a, state} + end + + deflua with_state(_), state do + {"not a int", state} + end + end + """) + + assert module.has_a_guard(1) == 1 + assert module.has_a_guard("foo") == "foo" + assert module.has_a_guard(true) == "not a int" + + assert {1, _} = module.with_state(1, Lua.new()) + assert {"not a int", _} = module.with_state(true, Lua.new()) + end end end