-module(glua).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/glua.gleam").
-export([exec/2, run/2, then/2, success/1, failure/1, 'try'/2, map/2, fold/2, nil/0, string/1, bool/1, guard/3, int/1, format_error/1, float/1, table/1, table_list/1, table_list_decoder/1, function_decoder/0, userdata/1, function/1, dereference/2, returning/2, returning_multi/2, new/0, gc/1, get/1, get_private/3, set/2, sandbox/2, set_api/2, set_lua_paths/1, set_private/3, delete_private/2, load/1, load_file/1, eval/1, eval_chunk/1, eval_file/1, call_function/2, call_function_by_name/2, index/2, new_index/3, error/1, error_with_level/2, new_sandboxed/1]).
-export_type([lua/0, error/1, lua_compile_error/0, lua_compile_error_kind/0, lua_runtime_exception_kind/0, action/2, chunk/0, value/0, never/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
?MODULEDOC(
" A library to embed Lua in Gleam programs.\n"
"\n"
" Gleam wrapper around [Luerl](https://github.com/rvirding/luerl).\n"
).
-type lua() :: any().
-type error(DQP) :: {lua_compile_failure, list(lua_compile_error())} |
{lua_runtime_exception, lua_runtime_exception_kind(), lua()} |
{key_not_found, list(binary())} |
{file_not_found, binary()} |
{unexpected_result_type, list(gleam@dynamic@decode:decode_error())} |
{custom_error, DQP} |
{unknown_error, gleam@dynamic:dynamic_()}.
-type lua_compile_error() :: {lua_compile_error,
integer(),
lua_compile_error_kind(),
binary()}.
-type lua_compile_error_kind() :: parse | tokenize.
-type lua_runtime_exception_kind() :: {illegal_index, binary(), binary()} |
{error_call, binary(), gleam@option:option(integer())} |
{undefined_function, binary()} |
{undefined_method, binary(), binary()} |
{bad_arith, binary(), list(binary())} |
{badarg, binary(), list(gleam@dynamic:dynamic_())} |
{assert_error, binary()} |
unknown_exception.
-opaque action(DQQ, DQR) :: {action,
fun((lua()) -> {ok, {lua(), DQQ}} | {error, error(DQR)})}.
-type chunk() :: any().
-type value() :: any().
-type never() :: any().
-file("src/glua.gleam", 325).
?DOC(
" Runs an `Action` within a Lua environment and returns both the result\n"
" and the updated Lua state in case of no errors.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let state = glua.new()\n"
" let assert Ok(#(new_state, Nil)) =\n"
" glua.exec(state, glua.set([\"my_value\"], glua.string(\"Hello!\")))\n"
"\n"
" glua.exec(\n"
" state:,\n"
" action: glua.eval(code: \"return my_value\") |> glua.returning_multi(decode.string)\n"
" )\n"
"\n"
" // -> Ok(#(_state, [\"Hello!\"]))\n"
" ```\n"
).
-spec exec(lua(), action(DRC, DRD)) -> {ok, {lua(), DRC}} | {error, error(DRD)}.
exec(Lua, Action) ->
(erlang:element(2, Action))(Lua).
-file("src/glua.gleam", 301).
?DOC(
" Runs an `Action` within a Lua environment.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let state = glua.new()\n"
"\n"
" glua.eval(code: \"return 'Hello from Lua!'\")\n"
" |> glua.returning_multi(using: decode.string)\n"
" |> glua.run(state, _)\n"
" // -> Ok(\"Hello from Lua!\")\n"
" ```\n"
).
-spec run(lua(), action(DQV, DQW)) -> {ok, DQV} | {error, error(DQW)}.
run(Lua, Action) ->
_pipe = exec(Lua, Action),
gleam@result:map(_pipe, fun gleam@pair:second/1).
-file("src/glua.gleam", 359).
?DOC(
" Composes two `Action`s into a single one, by executing the first one and passing its return value\n"
" to a function that returns another `Action`.\n"
"\n"
" If the first `Action` returns an `Error` when executed, then the function is not called\n"
" and the error is returned.\n"
"\n"
" This function is the most common way to chain together multiple `Action`s.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let my_value = 1\n"
" let assert Ok(#(_state, ret)) = glua.run(glua.new(), {\n"
" use _ <- glua.then(glua.set(keys: [\"my_value\"], value: glua.int(my_value)))\n"
" glua.get(keys: [\"my_value\"]) |> glua.returning(decode.int)\n"
" })\n"
"\n"
" assert ret == my_value\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use ret <- glua.then(glua.eval_file(path: \"./my_file.lua\"))\n"
" glua.call_function_by_name(path: [\"table\", \"pack\"], args: ret)\n"
" })\n"
" // -> Error(glua.FileNotFound(\"./my_file.lua\"))\n"
" ```\n"
).
-spec then(action(DRJ, DRK), fun((DRJ) -> action(DRN, DRK))) -> action(DRN, DRK).
then(Action, Next) ->
{action,
fun(State) ->
gleam@result:'try'(
(erlang:element(2, Action))(State),
fun(_use0) ->
{New, Ret} = _use0,
(erlang:element(2, Next(Ret)))(New)
end
)
end}.
-file("src/glua.gleam", 429).
?DOC(
" Creates an `Action` that always succeeds and returns `value`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), glua.success(\"my value\"))\n"
" // -> Ok(\"my_value\")\n"
" ```\n"
).
-spec success(DSH) -> action(DSH, any()).
success(Value) ->
{action, fun(State) -> {ok, {State, Value}} end}.
-file("src/glua.gleam", 445).
?DOC(
" Creates an `Action` that always fails with `glua.CustomError(error)`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(\n"
" glua.new(),\n"
" glua.failure(\"incorrect number of return values\")\n"
" )\n"
" // -> Error(glua.CustomError(\"incorrect number of return values\"))\n"
" ```\n"
).
-spec failure(DSL) -> action(any(), DSL).
failure(Error) ->
{action, fun(_) -> {error, {custom_error, Error}} end}.
-file("src/glua.gleam", 388).
?DOC(
" Tries to update the return value of an `Action` by passing it to a function\n"
" that yields a result.\n"
"\n"
" This is a shorthand for writing a case with `glua.then`:\n"
"\n"
" ```gleam\n"
" use fun <- glua.then(glua.get([\"string\", \"reverse\"]))\n"
" glua.call_function(fun:, args: [glua.string(\"Hello\")])\n"
" |> glua.try(list.first)\n"
" |> glua.returning(decode.string)\n"
" ```\n"
"\n"
" as opposed to this:\n"
"\n"
" ```gleam\n"
" use fun <- glua.then(glua.get([\"string\", \"reverse\"]))\n"
" use return <- glua.then(glua.call_function(fun, [glua.string(\"Hello\")]))\n"
" case return {\n"
" [first] -> glua.dereference(ref: first, using: decode.string)\n"
" _ -> glua.failure(Nil)\n"
" }\n"
" ```\n"
).
-spec 'try'(action(DRS, DRT), fun((DRS) -> {ok, DRW} | {error, DRT})) -> action(DRW, DRT).
'try'(Action, Fun) ->
then(Action, fun(Ret) -> case Fun(Ret) of
{ok, X} ->
success(X);
{error, E} ->
failure(E)
end end).
-file("src/glua.gleam", 500).
?DOC(
" Transforms the return value of an `Action` with the provided function.\n"
"\n"
" If the `Action` returns an `Error` when executed then the function is not called and the\n"
" error is returned.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.get(keys: [\"_VERSION\"])\n"
" |> glua.returning(using: decode.string)\n"
" |> glua.map(fn(version) {\n"
" \"glua supports \" <> version\n"
" })\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok(\"glua supports Lua 5.3\")\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use n <- glua.map(glua.get(keys: [\"my_number\"]))\n"
" n * 2\n"
" })\n"
" // -> Error(glua.KeyNotFound([\"my_number\"]))\n"
" ```\n"
).
-spec map(action(DSX, DSY), fun((DSX) -> DTB)) -> action(DTB, DSY).
map(Action, Fun) ->
{action, fun(State) -> _pipe = (erlang:element(2, Action))(State),
gleam@result:map(
_pipe,
fun(_capture) -> gleam@pair:map_second(_capture, Fun) end
) end}.
-file("src/glua.gleam", 521).
?DOC(
" Maps a list of elements into a list of `Action`s by calling a function in each element and then flattens\n"
" all the `Action`s into a single one.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let numbers = [9, 16, 25]\n"
" let keys = [\"math\", \"sqrt\"]\n"
" glua.run(glua.new(), glua.fold(numbers, fn(n) {\n"
" glua.call_function_by_name(keys:, args: [glua.int(n)])\n"
" |> glua.try(list.first)\n"
" |> glua.returning(using: decode.float)\n"
" }))\n"
" // -> Ok([3.0, 4.0, 5.0])\n"
" ```\n"
).
-spec fold(list(DTE), fun((DTE) -> action(DTG, DTH))) -> action(list(DTG), DTH).
fold(List, Fun) ->
{action,
fun(State) ->
_pipe@1 = gleam@list:try_fold(
List,
{State, []},
fun(Acc, E) ->
{State@1, Results} = Acc,
_pipe = (erlang:element(2, Fun(E)))(State@1),
gleam@result:map(
_pipe,
fun(_capture) ->
gleam@pair:map_second(
_capture,
fun(Ret) -> [Ret | Results] end
)
end
)
end
),
gleam@result:map(
_pipe@1,
fun(_capture@1) ->
gleam@pair:map_second(_capture@1, fun lists:reverse/1)
end
)
end}.
-file("src/glua.gleam", 541).
-spec nil() -> value().
nil() ->
glua_ffi:coerce_nil().
-file("src/glua.gleam", 544).
-spec string(binary()) -> value().
string(V) ->
glua_ffi:coerce(V).
-file("src/glua.gleam", 208).
-spec format_decode_error(gleam@dynamic@decode:decode_error()) -> binary().
format_decode_error(Error) ->
Base = <<<<<<"Expected "/utf8, (erlang:element(2, Error))/binary>>/binary,
", but found "/utf8>>/binary,
(erlang:element(3, Error))/binary>>,
case erlang:element(4, Error) of
[] ->
Base;
Path ->
<<<>/binary,
(gleam@string:join(Path, <<"."/utf8>>))/binary>>
end.
-file("src/glua.gleam", 547).
-spec bool(boolean()) -> value().
bool(V) ->
glua_ffi:coerce(V).
-file("src/glua.gleam", 413).
?DOC(
" Runs a callback function if the given bool is `False`, otherwise return a failing `Action`\n"
" using the provided value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use ret <- glua.then(glua.eval(code: \"local a = 1\"))\n"
" use <- glua.guard(when: ret == [], return: \"expected at least one value from Lua\")\n"
"\n"
" glua.fold(ret, glua.dereference(_, using: decode.int))\n"
" })\n"
" // -> Error(glua.CustomError(\"expected at least one value from Lua\"))\n"
" ```\n"
).
-spec guard(boolean(), DSB, fun(() -> action(DSC, DSB))) -> action(DSC, DSB).
guard(Requirement, Consequence, Alternative) ->
gleam@bool:guard(Requirement, failure(Consequence), Alternative).
-file("src/glua.gleam", 550).
-spec int(integer()) -> value().
int(V) ->
glua_ffi:coerce(V).
-file("src/glua.gleam", 147).
-spec format_compile_error(lua_compile_error()) -> binary().
format_compile_error(Error) ->
Kind = case erlang:element(3, Error) of
parse ->
<<"parse"/utf8>>;
tokenize ->
<<"tokenize"/utf8>>
end,
<<<<<<<<<<"Failed to "/utf8, Kind/binary>>/binary, ": error on line "/utf8>>/binary,
(erlang:integer_to_binary(erlang:element(2, Error)))/binary>>/binary,
": "/utf8>>/binary,
(erlang:element(4, Error))/binary>>.
-file("src/glua.gleam", 161).
-spec format_exception(lua_runtime_exception_kind()) -> binary().
format_exception(Exception) ->
case Exception of
{illegal_index, Index, Value} ->
<<<<<<<<<<<<<<"Invalid index "/utf8, "\""/utf8>>/binary,
Index/binary>>/binary,
"\""/utf8>>/binary,
" at object "/utf8>>/binary,
"\""/utf8>>/binary,
Value/binary>>/binary,
"\""/utf8>>;
{error_call, Msg, Level} ->
Base = <<"Error call: "/utf8, Msg/binary>>,
case Level of
{some, Level@1} ->
<<<>/binary,
(erlang:integer_to_binary(Level@1))/binary>>;
none ->
Base
end;
{undefined_function, Fun} ->
<<"Undefined function: "/utf8, Fun/binary>>;
{undefined_method, Obj, Method} ->
<<<<<<<<<<<<<<"Undefined method "/utf8, "\""/utf8>>/binary,
Method/binary>>/binary,
"\""/utf8>>/binary,
" for object: "/utf8>>/binary,
"\""/utf8>>/binary,
Obj/binary>>/binary,
"\""/utf8>>;
{bad_arith, Operator, Args} ->
<<"Bad arithmetic expression: "/utf8,
(gleam@string:join(
Args,
<<<<" "/utf8, Operator/binary>>/binary, " "/utf8>>
))/binary>>;
{badarg, Function, Args@1} ->
<<<<<<"Bad argument "/utf8,
(gleam@string:join(
gleam@list:map(Args@1, fun luerl_lib:format_value/1),
<<", "/utf8>>
))/binary>>/binary,
" for function "/utf8>>/binary,
Function/binary>>;
{assert_error, Msg@1} ->
<<"Assertion failed with message: "/utf8, Msg@1/binary>>;
unknown_exception ->
<<"Unknown exception"/utf8>>
end.
-file("src/glua.gleam", 121).
?DOC(
" Turns a `glua.Error` value into a human-readable string\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Error(e) = glua.run(glua.new(), glua.eval(\n"
" code: \"if true end\",\n"
" ))\n"
"\n"
" glua.format_error(e)\n"
" // -> \"Lua compile error: \\n\\nFailed to parse: error on line 1: syntax error before: 'end'\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Error(e) = glua.run(glua.new(), glua.eval(\n"
" code: \"local a = 1; local b = true; return a + b\",\n"
" ))\n"
"\n"
" glua.format_error(e)\n"
" // -> \"Lua runtime exception: Bad arithmetic expression: 1 + true\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Error(e) = glua.run(glua.new(), glua.get(\n"
" keys: [\"a_value\"],\n"
" ))\n"
" \n"
" glua.format_error(e)\n"
" // -> \"Key \\\"a_value\\\" not found\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Error(e) = glua.run(glua.new(), glua.eval_file(\n"
" path: \"my_lua_file.lua\",\n"
" ))\n"
"\n"
" glua.format_error(e)\n"
" // -> \"Lua source file \\\"my_lua_file.lua\\\" not found\"\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Error(e) = glua.run(glua.new(), {\n"
" use ret <- glua.then(glua.eval(\n"
" code: \"return 1 + 1\",\n"
" ))\n"
" use ref <- glua.try(list.first(ret))\n"
" \n"
" glua.dereference(ref:, using: decode.string)\n"
" })\n"
"\n"
" glua.format_error(e)\n"
" // -> \"Expected String, but found Int\"\n"
" ```\n"
).
-spec format_error(error(any())) -> binary().
format_error(Error) ->
case Error of
{lua_compile_failure, Errors} ->
<<<<"Lua compile error: "/utf8, "\n\n"/utf8>>/binary,
(gleam@string:join(
gleam@list:map(Errors, fun format_compile_error/1),
<<"\n"/utf8>>
))/binary>>;
{lua_runtime_exception, Exception, State} ->
Base = <<"Lua runtime exception: "/utf8,
(format_exception(Exception))/binary>>,
Stacktrace = glua_ffi:get_stacktrace(State),
case Stacktrace of
<<""/utf8>> ->
Base;
Stacktrace@1 ->
<<<>/binary, Stacktrace@1/binary>>
end;
{key_not_found, Path} ->
<<<<<<<<"Key "/utf8, "\""/utf8>>/binary,
(gleam@string:join(Path, <<"."/utf8>>))/binary>>/binary,
"\""/utf8>>/binary,
" not found"/utf8>>;
{file_not_found, Path@1} ->
<<<<<<<<"Lua source file "/utf8, "\""/utf8>>/binary, Path@1/binary>>/binary,
"\""/utf8>>/binary,
" not found"/utf8>>;
{unexpected_result_type, Decode_errors} ->
_pipe = gleam@list:map(Decode_errors, fun format_decode_error/1),
gleam@string:join(_pipe, <<"\n"/utf8>>);
{custom_error, Error@1} ->
gleam@string:inspect(Error@1);
{unknown_error, Error@2} ->
<<"Unknown error: "/utf8, (luerl_lib:format_error(Error@2))/binary>>
end.
-file("src/glua.gleam", 553).
-spec float(float()) -> value().
float(V) ->
glua_ffi:coerce(V).
-file("src/glua.gleam", 555).
-spec table(list({value(), value()})) -> action(value(), any()).
table(Values) ->
{action,
fun(State) ->
{ok,
begin
_pipe = luerl_heap:alloc_table(Values, State),
gleam@pair:swap(_pipe)
end}
end}.
-file("src/glua.gleam", 560).
-spec table_list(list(value())) -> action(value(), any()).
table_list(Values) ->
{_, Values@1} = gleam@list:map_fold(
Values,
1,
fun(Acc, Val) -> {Acc + 1, {glua_ffi:coerce(Acc), Val}} end
),
table(Values@1).
-file("src/glua.gleam", 597).
-spec list_loop(gleam@dict:dict(integer(), DUA), list(DUA), integer()) -> list(DUA).
list_loop(Dict, Acc, Idx) ->
case gleam_stdlib:map_get(Dict, Idx) of
{ok, It} ->
list_loop(Dict, [It | Acc], Idx + 1);
{error, nil} ->
lists:reverse(Acc)
end.
-file("src/glua.gleam", 591).
?DOC(
" A decoder for list-style Lua tables.\n"
" This decoder works similarly to ipairs in the sense that it stops\n"
" when there is a gap in the table list.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.eval(\"return { 1, 2, 3 }\")\n"
" |> glua.try(list.first)\n"
" |> glua.returning(glua.table_list_decoder(decode.int))\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok([1, 2, 3])\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.eval(\"return { [1] = 'a', [2] = 'b', [4] = 'd'}\")\n"
" |> glua.try(list.first)\n"
" |> glua.returning(glua.table_list_decoder(decode.string))\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok([\"a\", \"b\"])\n"
" ```\n"
).
-spec table_list_decoder(gleam@dynamic@decode:decoder(DTW)) -> gleam@dynamic@decode:decoder(list(DTW)).
table_list_decoder(Decoder) ->
_pipe = gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_int/1},
Decoder
),
gleam@dynamic@decode:map(
_pipe,
fun(_capture) -> list_loop(_capture, [], 1) end
).
-file("src/glua.gleam", 626).
-spec function_decoder() -> gleam@dynamic@decode:decoder(fun((list(value())) -> action(list(value()), any()))).
function_decoder() ->
gleam@dynamic@decode:new_primitive_decoder(
<<"LuaFunction"/utf8>>,
fun glua_ffi:decode_fun/1
).
-file("src/glua.gleam", 689).
?DOC(
" Encodes any Gleam value as a reference that can be passed to a Lua program.\n"
"\n"
" Deferencing a userdata value inside Lua code will cause a Lua exception.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" pub type User {\n"
" User(name: String, is_admin: Bool)\n"
" }\n"
"\n"
" let user_decoder = {\n"
" use name <- decode.field(1, decode.string)\n"
" use is_admin <- decode.field(2, decode.bool)\n"
" decode.success(User(name:, is_admin:))\n"
" }\n"
"\n"
" glua.run(glua.new(), {\n"
" use userdata <- glua.then(userdata(User(\"Jhon Doe\", False)))\n"
" use _ <- glua.then(glua.set(\n"
" keys: [\"a_user\"],\n"
" value: userdata\n"
" ))\n"
" \n"
" glua.eval(code: \"return a_user\")\n"
" |> glua.try(list.first)\n"
" |> glua.returning(using: user_decoder)\n"
" })\n"
" // -> Ok(User(\"Jhon Doe\", False))\n"
" ```\n"
"\n"
" ```gleam\n"
" pub type Person {\n"
" Person(name: String, email: String)\n"
" }\n"
"\n"
" let assert Error(glua.LuaRuntimeException(glua.IllegalIndex(_), _)) =\n"
" glua.run(glua.new(), {\n"
" use userdata <- glua.then(glua.userdata(\n"
" Person(name: \"Lucy\", email: \"lucy@example.com\")\n"
" ))\n"
" use _ <- glua.then(glua.set(\n"
" keys: [\"lucy\"],\n"
" value: userdata\n"
" ))\n"
"\n"
" glua.eval(code: \"return lucy.email\")\n"
" })\n"
" ```\n"
).
-spec userdata(any()) -> action(value(), any()).
userdata(V) ->
{action,
fun(State) ->
{ok,
begin
_pipe = luerl_heap:alloc_userdata(V, State),
gleam@pair:swap(_pipe)
end}
end}.
-file("src/glua.gleam", 610).
?DOC(
" Encodes a Gleam function into a Lua function.\n"
"\n"
" > **Note**: The function to be encoded has to return an `Action` with a `Never` type\n"
" > as the `error` parameter, meaning that the function cannot invoke `glua.failure` in its body.\n"
" > If you want to return an error inside that function, you should use `glua.error` or `glua.error_with_code`,\n"
" > both of which will call the Lua `error` function.\n"
).
-spec function(fun((list(value())) -> action(list(value()), never()))) -> value().
function(F) ->
glua_ffi:wrap_fun(F).
-file("src/glua.gleam", 727).
?DOC(
" Converts a reference to a Lua value into type-safe Gleam data using the provided decoder.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use ref <- glua.then(\n"
" glua.eval(code: \"return 'Hello from Lua!'\")\n"
" |> glua.try(list.first)\n"
" )\n"
"\n"
" glua.dereference(ref:, using: decode.string)\n"
" })\n"
" // -> Ok(\"Hello from Lua!\")\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Ok(#(state, [ref1, ref2])) = glua.exec(\n"
" glua.new(),\n"
" glua.eval(code: \"return 1, true\")\n"
" )\n"
"\n"
" let assert Ok(1) =\n"
" glua.run(state, glua.dereference(ref: ref1, using: decode.int))\n"
" let assert Ok(True) =\n"
" glua.run(state, glua.dereference(ref: ref2, using: decode.bool))\n"
" ```\n"
).
-spec dereference(value(), gleam@dynamic@decode:decoder(DVK)) -> action(DVK, any()).
dereference(Ref, Decoder) ->
{action,
fun(State) ->
gleam@result:map(
begin
_pipe = glua_ffi:dereference(State, Ref),
_pipe@1 = gleam@dynamic@decode:run(_pipe, Decoder),
gleam@result:map_error(
_pipe@1,
fun(Field@0) -> {unexpected_result_type, Field@0} end
)
end,
fun(Ret) -> {State, Ret} end
)
end}.
-file("src/glua.gleam", 761).
?DOC(
" Transforms an `Action` that returns a reference to a Lua value into an `Action` that returns\n"
" a typed Gleam value.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let decoder =\n"
" decode.dict(decode.string, decode.int)\n"
" |> decode.map(dict.to_list)\n"
"\n"
" glua.eval(code: \"return { a = 1, b = 2 }\")\n"
" |> glua.try(apply: list.first)\n"
" |> glua.returning(using: decoder)\n"
" |> glua.run(glua.new(), _)\n"
"\n"
" // -> Ok([#(\"a\", 1), #(\"b\", 2)])\n"
" ```\n"
).
-spec returning(action(value(), DVP), gleam@dynamic@decode:decoder(DVS)) -> action(DVS, DVP).
returning(Act, Decoder) ->
then(Act, fun(Ref) -> dereference(Ref, Decoder) end).
-file("src/glua.gleam", 771).
?DOC(
" Same as `glua.returning`, but works on an `Action` that returns multiple references to Lua values\n"
" instead of a single one.\n"
).
-spec returning_multi(
action(list(value()), DVX),
gleam@dynamic@decode:decoder(DWA)
) -> action(list(DWA), DVX).
returning_multi(Act, Decoder) ->
then(
Act,
fun(Refs) ->
fold(Refs, fun(_capture) -> dereference(_capture, Decoder) end)
end
).
-file("src/glua.gleam", 781).
?DOC(" Creates a new Lua VM instance\n").
-spec new() -> lua().
new() ->
luerl:init().
-file("src/glua.gleam", 844).
?DOC(" Runs the garbage collector on the given Lua state.\n").
-spec gc(lua()) -> lua().
gc(Lua) ->
luerl:gc(Lua).
-file("src/glua.gleam", 874).
?DOC(
" Gets a value in the Lua environment.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.get(keys: [\"_VERSION\"])\n"
" |> glua.returning(using: decode.string)\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok(\"Lua 5.3\")\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use _ <- glua.then(glua.set(\n"
" keys: [\"my_table\", \"my_value\"],\n"
" value: glua.bool(True)\n"
" ))\n"
"\n"
" glua.get(keys: [\"my_table\", \"my_value\"]))\n"
" |> glua.returning(using: decode.bool)\n"
" })\n"
" // -> Ok(True)\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), glua.get(keys: [\"non_existent\"]))\n"
" // -> Error(glua.KeyNotFound([\"non_existent\"]))\n"
" ```\n"
).
-spec get(list(binary())) -> action(value(), any()).
get(Keys) ->
{action,
fun(State) ->
gleam@result:map(
glua_ffi:get_table_keys(State, Keys),
fun(Ret) -> {State, Ret} end
)
end}.
-file("src/glua.gleam", 893).
?DOC(
" Gets a private value that is not exposed to the Lua runtime.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" assert glua.new()\n"
" |> glua.set_private(\"private_value\", \"secret_value\")\n"
" |> glua.get_private(\"private_value\", decode.string)\n"
" == Ok(\"secret_value\")\n"
" ```\n"
).
-spec get_private(lua(), binary(), gleam@dynamic@decode:decoder(DXD)) -> {ok,
DXD} |
{error, error(any())}.
get_private(Lua, Key, Decoder) ->
gleam@result:'try'(
glua_ffi:get_private(Lua, Key),
fun(Value) -> _pipe = gleam@dynamic@decode:run(Value, Decoder),
gleam@result:map_error(
_pipe,
fun(Field@0) -> {unexpected_result_type, Field@0} end
) end
).
-file("src/glua.gleam", 942).
?DOC(
" Sets a value in the Lua environment.\n"
"\n"
" All nested keys will be created as intermediate tables.\n"
"\n"
" If successfull, this function will return the updated Lua state\n"
" and the setted value will be available in Lua scripts.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use _ <- glua.then(glua.set(\n"
" keys: [\"my_number\"],\n"
" value: glua.int(10)\n"
" ))\n"
"\n"
" glua.get(keys: [\"my_number\"])\n"
" |> glua.returning(using: decode.int)\n"
" })\n"
" // -> Ok(10)\n"
" ```\n"
"\n"
" ```gleam\n"
" let emails = [\"jhondoe@example.com\", \"lucy@example.com\"]\n"
" let assert Ok(results) = glua.run(glua.new(), {\n"
" use encoded <- glua.then(glua.table(\n"
" list.index_map(emails, fn(email, i) { #(glua.int(i + 1), glua.string(email)) })\n"
" ))\n"
" use _ <- glua.then(glua.set([\"info\", \"emails\"], encoded))\n"
"\n"
" glua.eval(code: \"return info.emails\"))\n"
" |> glua.try(list.first)\n"
" |> glua.returning(using: glua.table_list_decoder(decode.string))\n"
" })\n"
"\n"
" assert results == emails\n"
" ```\n"
).
-spec set(list(binary()), value()) -> action(nil, any()).
set(Keys, Val) ->
{action,
fun(State) ->
gleam@result:'try'(
gleam@list:try_fold(
Keys,
{State, []},
fun(Acc, Key) ->
{State@1, Keys@1} = Acc,
Keys@2 = lists:append(Keys@1, [Key]),
case glua_ffi:get_table_keys(State@1, Keys@2) of
{ok, _} ->
{ok, {State@1, Keys@2}};
{error, {key_not_found, _}} ->
{Tbl, New} = luerl_heap:alloc_table([], State@1),
gleam@result:map(
glua_ffi:set_table_keys(New, Keys@2, Tbl),
fun(New@1) -> {New@1, Keys@2} end
);
{error, E} ->
{error, E}
end
end
),
fun(_use0) ->
{New@2, Keys@3} = _use0,
_pipe = glua_ffi:set_table_keys(New@2, Keys@3, Val),
gleam@result:map(_pipe, fun(State@2) -> {State@2, nil} end)
end
)
end}.
-file("src/glua.gleam", 832).
?DOC(
" Swaps out the value at `keys` with a function that causes a Lua error when called.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let assert Ok(state) = glua.new() |> glua.sandbox([\"os\"], [\"execute\"])\n"
" let assert Error(glua.LuaRuntimeException(exception, _)) =\n"
" glua.run(state, glua.eval(\n"
" code: \"os.execute(\\\"rm -f important_file\\\"); return 0\",\n"
" ))\n"
"\n"
" // 'important_file' was not deleted\n"
" assert exception == glua.ErrorCall([\"os.execute is sandboxed\"])\n"
" ```\n"
).
-spec sandbox(lua(), list(binary())) -> {ok, lua()} | {error, error(any())}.
sandbox(Lua, Keys) ->
Msg = <<(gleam@string:join(Keys, <<"."/utf8>>))/binary,
" is sandboxed"/utf8>>,
_pipe = (erlang:element(
2,
set([<<"_G"/utf8>> | Keys], glua_ffi:sandbox_fun(Msg))
))(Lua),
gleam@result:map(_pipe, fun gleam@pair:first/1).
-file("src/glua.gleam", 980).
?DOC(" Sets a group of values under a particular table in the Lua environment.\n").
-spec set_api(list(binary()), list({binary(), value()})) -> action(nil, any()).
set_api(Keys, Values) ->
then(
fold(
Values,
fun(Pair) ->
set(
lists:append(Keys, [erlang:element(1, Pair)]),
erlang:element(2, Pair)
)
end
),
fun(_) -> success(nil) end
).
-file("src/glua.gleam", 1012).
?DOC(
" Sets the paths where the Lua runtime will look when requiring other Lua files.\n"
"\n"
" > **Warning**: This function will not work properly if `[\"package\"]` or `[\"require\"]` are sandboxed\n"
" > in the provided Lua state. If you constructed the Lua state using `glua.new_sandboxed`,\n"
" > remember to allow the required values by passing `[[\"package\"], [\"require\"]]` to `glua.new_sandboxed`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let my_scripts_paths = [\"app/scripts/lua/?.lua\"]\n"
" glua.run(glua.new(), {\n"
" use _ <- glua.then(glua.set_lua_paths(paths: my_scripts_paths))\n"
"\n"
" glua.eval(\n"
" code: \"local my_math = require 'my_script'; return my_math.square(3)\"\n"
" )\n"
" |> glua.try(list.first)\n"
" |> glua.returning(decode.int)\n"
" })\n"
" // -> Ok(9)\n"
" ```\n"
).
-spec set_lua_paths(list(binary())) -> action(nil, any()).
set_lua_paths(Paths) ->
Paths@1 = begin
_pipe = gleam@string:join(Paths, <<";"/utf8>>),
glua_ffi:coerce(_pipe)
end,
set([<<"package"/utf8>>, <<"path"/utf8>>], Paths@1).
-file("src/glua.gleam", 975).
?DOC(
" Sets a value that is not exposed to the Lua runtime and can only be accessed from Gleam.\n"
"\n"
" ## Examples\n"
" ```gleam\n"
" assert glua.new()\n"
" |> glua.set(\"secret_value\", \"private_value\")\n"
" |> glua.get(\"secret_value\", decode.string)\n"
" == Ok(\"secret_value\")\n"
" ```\n"
).
-spec set_private(lua(), binary(), any()) -> lua().
set_private(Lua, Key, Value) ->
luerl:put_private(Key, Value, Lua).
-file("src/glua.gleam", 1035).
?DOC(
" Remove a private value that is not exposed to the Lua runtime. \n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" let lua = glua.set_private(glua.new(), \"my_value\", \"will_be_removed\")\n"
" assert glua.get(lua, \"my_value\", decode.string) == Ok(\"will_be_removed\")\n"
"\n"
" assert glua.delete_private(lua, \"my_value\")\n"
" |> glua.get(\"my_value\", decode.string)\n"
" == Error(glua.KeyNotFound([\"my_value\"]))\n"
" ```\n"
).
-spec delete_private(lua(), binary()) -> lua().
delete_private(Lua, Key) ->
luerl:delete_private(Key, Lua).
-file("src/glua.gleam", 1045).
?DOC(
" Parses a string of Lua code and returns it as a compiled chunk.\n"
"\n"
" To eval the returned chunk, use `glua.eval_chunk`.\n"
).
-spec load(binary()) -> action(chunk(), any()).
load(Code) ->
{action, fun(_capture) -> glua_ffi:load(_capture, Code) end}.
-file("src/glua.gleam", 1055).
?DOC(
" Parses a Lua source file and returns it as a compiled chunk.\n"
"\n"
" To eval the returned chunk, use `glua.eval_chunk`.\n"
).
-spec load_file(binary()) -> action(chunk(), any()).
load_file(Path) ->
{action, fun(_capture) -> glua_ffi:load_file(_capture, Path) end}.
-file("src/glua.gleam", 1095).
?DOC(
" Evaluates a string of Lua code.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.eval(code: \"return 1 + 2\")\n"
" |> glua.returning_multi(using: decode.int)\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok([3])\n"
" ```\n"
"\n"
" ```gleam\n"
" let assert Ok(#(state, [ref1, ref2])) = glua.exec(glua.new(), glua.eval(\n"
" code: \"return 'hello, world!', 10\",\n"
" ))\n"
"\n"
" let assert Ok(\"hello world\") =\n"
" glua.run(state, glua.dereference(ref: ref1, using: decode.string))\n"
" let assert Ok(10) =\n"
" glua.run(state, glua.dereference(ref: ref2, using: decode.int))\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), glua.eval(code: \"return 1 * \"))\n"
" // -> Error(glua.LuaCompileFailure(\n"
" [glua.LuaCompileError(1, Parse, \"syntax error before: \")]\n"
" ))\n"
" ```\n"
"\n"
" > **Note**: If you are evaluating the same piece of code multiple times,\n"
" > instead of calling `glua.eval` repeatly it is recommended to first convert\n"
" > the code to a chunk by passing it to `glua.load`, and then\n"
" > evaluate that chunk using `glua.eval_chunk`.\n"
).
-spec eval(binary()) -> action(list(value()), any()).
eval(Code) ->
{action, fun(_capture) -> glua_ffi:eval(_capture, Code) end}.
-file("src/glua.gleam", 1113).
?DOC(
" Evaluates a compiled chunk of Lua code.\n"
"\n"
" ## Examples\n"
" \n"
" ```gleam\n"
" glua.load(code: \"return 'hello, world!'\")\n"
" |> glua.then(glua.eval_chunk)\n"
" |> glua.returning_multi(using: decode.string)\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok([\"hello, world!\"])\n"
" ```\n"
).
-spec eval_chunk(chunk()) -> action(list(value()), any()).
eval_chunk(Chunk) ->
{action, fun(_capture) -> glua_ffi:eval_chunk(_capture, Chunk) end}.
-file("src/glua.gleam", 1142).
?DOC(
" Evaluates a Lua source file.\n"
"\n"
" ## Examples\n"
" \n"
" ```gleam\n"
" glua.eval_file(\n"
" path: \"path/to/hello.lua\",\n"
" )\n"
" |> glua.returning_multi(using: decode.string)\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok([\"hello, world!\"])\n"
" ```\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), glua.eval_file(\n"
" path: \"path/to/non/existent/file\",\n"
" ))\n"
" // -> Error(glua.FileNotFound([\"path/to/non/existent/file\"]))\n"
" ```\n"
).
-spec eval_file(binary()) -> action(list(value()), any()).
eval_file(Path) ->
{action, fun(_capture) -> glua_ffi:eval_file(_capture, Path) end}.
-file("src/glua.gleam", 1191).
?DOC(
" Calls a Lua function by reference.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" use fun <- glua.then(\n"
" glua.eval(code: \"return math.sqrt\") |> glua.try(list.first)\n"
" )\n"
"\n"
" glua.call_function(\n"
" ref: fun,\n"
" args: [glua.int(81)],\n"
" )\n"
" |> glua.returning_multi(using: decode.float)\n"
" })\n"
" // -> Ok([9.0])\n"
" ```\n"
"\n"
" ```gleam\n"
" let code = \"function fib(n)\n"
" if n <= 1 then\n"
" return n\n"
" else\n"
" return fib(n - 1) + fib(n - 2)\n"
" end\n"
" end\n"
"\n"
" return fib\n"
" \"\n"
"\n"
" glua.run(glua.new(), {\n"
" use fun <- glua.then(glua.eval(code:) |> glua.try(list.first))\n"
"\n"
" glua.call_function(\n"
" ref: fun,\n"
" args: [glua.int(10)],\n"
" )\n"
" |> glua.returning_multi(using: decode.int)\n"
" })\n"
" // -> Ok([55])\n"
" ```\n"
).
-spec call_function(value(), list(value())) -> action(list(value()), any()).
call_function(Fun, Args) ->
{action, fun(_capture) -> glua_ffi:call_function(_capture, Fun, Args) end}.
-file("src/glua.gleam", 1220).
?DOC(
" Gets a reference to the function at `keys`, then inmediatly calls it with the provided `args`.\n"
"\n"
" This is a shorthand for `glua.get` followed by `glua.call_function`.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.call_function_by_name(\n"
" keys: [\"string\", \"upper\"],\n"
" args: [glua.string(\"hello from Gleam!\")]\n"
" ))\n"
" |> glua.returning_multi(using: decode.string)\n"
" |> glua.run(glua.new(), _)\n"
" // -> Ok(\"HELLO FROM GLEAM!\")\n"
" ```\n"
).
-spec call_function_by_name(list(binary()), list(value())) -> action(list(value()), any()).
call_function_by_name(Keys, Args) ->
then(get(Keys), fun(Fun) -> call_function(Fun, Args) end).
-file("src/glua.gleam", 1248).
?DOC(
" Gets the value at `keys` under the provided table.\n"
"\n"
" This might trigger the `__index` metamethod.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" let fun = fn(_) {\n"
" glua.success([glua.string(\"fixed value\")])\n"
" }\n"
"\n"
" use tbl <- glua.then(glua.table([]))\n"
" use mt <- glua.then(glua.table([#(glua.string(\"__index\"), glua.function(fun))]))\n"
" use _ <- glua.then(glua.call_function(lib.set_metatable(), [tbl, mt]))\n"
"\n"
" glua.index(tbl, glua.string(\"a_key\")) |> glua.returning(decode.string)\n"
" })\n"
" // -> Ok(\"fixed value\")\n"
" ```\n"
).
-spec index(value(), value()) -> action(value(), any()).
index(Ref, Key) ->
{action, fun(_capture) -> glua_ffi:get_table_key(_capture, Ref, Key) end}.
-file("src/glua.gleam", 1282).
?DOC(
" Sets `value` under `key` of the provided table.\n"
"\n"
" This might trigger the `__newindex` metamethod.\n"
"\n"
" ## Examples\n"
"\n"
" ```gleam\n"
" glua.run(glua.new(), {\n"
" let fun = fn(_) {\n"
" glua.error(\"this is a read-only table\")\n"
" }\n"
"\n"
" use tbl <- glua.then(glua.table([]))\n"
" use mt <- glua.then(glua.table([#(glua.string(\"__newindex\"), glua.function(fun))]))\n"
" use _ <- glua.then(glua.call_function(lib.set_metatable(), [tbl, mt]))\n"
"\n"
" glua.new_index(tbl, glua.string(\"my_new_key\"), glua.string(\"my_new_value\"))\n"
" })\n"
" // -> Error(glua.LuaRuntimeException(\n"
" exception: glua.ErrorCall(\"this is a read-only table\", option.None),\n"
" state: _\n"
" ))\n"
" ```\n"
).
-spec new_index(value(), value(), value()) -> action(nil, any()).
new_index(Ref, Key, Val) ->
{action,
fun(_capture) -> glua_ffi:set_table_key(_capture, Ref, Key, Val) end}.
-file("src/glua.gleam", 457).
?DOC(" Invokes the Lua `error` function with the provided message.\n").
-spec error(binary()) -> action(any(), any()).
error(Message) ->
{action,
fun(State) ->
case (erlang:element(
2,
call_function(
glua_stdlib_ffi:error(),
[glua_ffi:coerce(Message)]
)
))(State) of
{ok, _} ->
erlang:error(#{gleam_error => panic,
message => <<"The error function returned a non error which should never happen! Please report this issue to the git repository."/utf8>>,
file => <>,
module => <<"glua"/utf8>>,
function => <<"error"/utf8>>,
line => 460});
{error, E} ->
{error, E}
end
end}.
-file("src/glua.gleam", 466).
?DOC(" Invokes the Lua `error` function with the provided message and level.\n").
-spec error_with_level(binary(), integer()) -> action(any(), any()).
error_with_level(Message, Level) ->
{action,
fun(State) ->
case (erlang:element(
2,
call_function(
glua_stdlib_ffi:error(),
[glua_ffi:coerce(Message), glua_ffi:coerce(Level)]
)
))(State) of
{ok, _} ->
erlang:error(#{gleam_error => panic,
message => <<"The error function returned a non error which should never happen! Please report this issue to the git repository."/utf8>>,
file => <>,
module => <<"glua"/utf8>>,
function => <<"error_with_level"/utf8>>,
line => 471});
{error, E} ->
{error, E}
end
end}.
-file("src/glua.gleam", 808).
?DOC(
" Creates a new Lua VM instance with sensible modules and functions sandboxed.\n"
"\n"
" Check `glua.default_sandbox` to see what modules and functions will be sandboxed.\n"
"\n"
" This function accepts a list of paths to Lua values that will be excluded from being sandboxed,\n"
" so needed modules or functions can be enabled while keeping sandboxed the rest.\n"
" In case you want to sandbox more Lua values, pass to `glua.sandbox` the returned Lua state.\n"
).
-spec new_sandboxed(list(list(binary()))) -> {ok, lua()} | {error, error(any())}.
new_sandboxed(Excluded) ->
_pipe = erlang:'--'(
[[<<"io"/utf8>>],
[<<"file"/utf8>>],
[<<"os"/utf8>>, <<"execute"/utf8>>],
[<<"os"/utf8>>, <<"exit"/utf8>>],
[<<"os"/utf8>>, <<"getenv"/utf8>>],
[<<"os"/utf8>>, <<"remove"/utf8>>],
[<<"os"/utf8>>, <<"rename"/utf8>>],
[<<"os"/utf8>>, <<"tmpname"/utf8>>],
[<<"package"/utf8>>],
[<<"load"/utf8>>],
[<<"loadfile"/utf8>>],
[<<"require"/utf8>>],
[<<"dofile"/utf8>>],
[<<"loadstring"/utf8>>]],
Excluded
),
gleam@list:try_fold(_pipe, luerl:init(), fun sandbox/2).