-module(lightspeed@data@repository).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/data/repository.gleam").
-export([tenant_scope/3, system_scope/1, new/1, seed/2, adapter/1, adapter_label/1, all/1, list_by_tenant/3, fetch_by_id/3, upsert/3, delete_by_id/3, role_label/1, scope_label/1, error_label/1]).
-export_type([adapter/0, role/0, scope/0, record/0, access_error/0, repository/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(" Scoped data-access patterns with Gleam/Elixir interoperability adapters.\n").
-type adapter() :: gleam_in_memory |
{elixir_bridge, binary()} |
{mixed_bridge, binary(), binary()}.
-type role() :: viewer | editor | tenant_admin.
-type scope() :: {tenant_scope, binary(), binary(), role()} |
{system_scope, binary()}.
-type record() :: {record, binary(), binary(), binary(), binary(), binary()}.
-type access_error() :: {not_found, binary()} |
{forbidden, binary(), binary(), binary()}.
-opaque repository() :: {repository, adapter(), list(record())}.
-file("src/lightspeed/data/repository.gleam", 48).
?DOC(" Build a tenant-scoped actor identity.\n").
-spec tenant_scope(binary(), binary(), role()) -> scope().
tenant_scope(Actor_id, Tenant_id, Role) ->
{tenant_scope, Actor_id, Tenant_id, Role}.
-file("src/lightspeed/data/repository.gleam", 53).
?DOC(" Build a system-scoped actor identity.\n").
-spec system_scope(binary()) -> scope().
system_scope(Actor_id) ->
{system_scope, Actor_id}.
-file("src/lightspeed/data/repository.gleam", 58).
?DOC(" Build an empty repository for one adapter profile.\n").
-spec new(adapter()) -> repository().
new(Adapter) ->
{repository, Adapter, []}.
-file("src/lightspeed/data/repository.gleam", 63).
?DOC(" Build repository with seeded records.\n").
-spec seed(adapter(), list(record())) -> repository().
seed(Adapter, Rows) ->
{repository, Adapter, lists:reverse(Rows)}.
-file("src/lightspeed/data/repository.gleam", 68).
?DOC(" Repository adapter profile.\n").
-spec adapter(repository()) -> adapter().
adapter(Repository) ->
erlang:element(2, Repository).
-file("src/lightspeed/data/repository.gleam", 73).
?DOC(" Stable adapter label for fixtures and migration signatures.\n").
-spec adapter_label(adapter()) -> binary().
adapter_label(Adapter) ->
case Adapter of
gleam_in_memory ->
<<"gleam_in_memory"/utf8>>;
{elixir_bridge, Module} ->
<<"elixir_bridge:"/utf8, Module/binary>>;
{mixed_bridge, Read_module, Write_module} ->
<<<<<<"mixed_bridge:"/utf8, Read_module/binary>>/binary, ":"/utf8>>/binary,
Write_module/binary>>
end.
-file("src/lightspeed/data/repository.gleam", 83).
?DOC(" All rows in stable order.\n").
-spec all(repository()) -> list(record()).
all(Repository) ->
lists:reverse(erlang:element(3, Repository)).
-file("src/lightspeed/data/repository.gleam", 179).
-spec forbidden(scope(), binary(), binary()) -> access_error().
forbidden(Scope, Action, Tenant_id) ->
case Scope of
{tenant_scope, Actor_id, _, _} ->
{forbidden, Actor_id, Action, Tenant_id};
{system_scope, Actor_id@1} ->
{forbidden, Actor_id@1, Action, Tenant_id}
end.
-file("src/lightspeed/data/repository.gleam", 244).
-spec filter_tenant(list(record()), binary(), list(record())) -> list(record()).
filter_tenant(Rows, Tenant_id, Rows_rev) ->
case Rows of
[] ->
lists:reverse(Rows_rev);
[Row | Rest] ->
case erlang:element(3, Row) =:= Tenant_id of
true ->
filter_tenant(Rest, Tenant_id, [Row | Rows_rev]);
false ->
filter_tenant(Rest, Tenant_id, Rows_rev)
end
end.
-file("src/lightspeed/data/repository.gleam", 188).
-spec can_list_tenant(scope(), binary()) -> boolean().
can_list_tenant(Scope, Tenant_id) ->
case Scope of
{system_scope, _} ->
true;
{tenant_scope, _, Scope_tenant, _} ->
Scope_tenant =:= Tenant_id
end.
-file("src/lightspeed/data/repository.gleam", 88).
?DOC(" List rows by tenant with scope validation.\n").
-spec list_by_tenant(repository(), scope(), binary()) -> {ok, list(record())} |
{error, access_error()}.
list_by_tenant(Repository, Scope, Tenant_id) ->
case can_list_tenant(Scope, Tenant_id) of
true ->
{ok, filter_tenant(all(Repository), Tenant_id, [])};
false ->
{error, forbidden(Scope, <<"list"/utf8>>, Tenant_id)}
end.
-file("src/lightspeed/data/repository.gleam", 195).
-spec can_read(scope(), binary()) -> boolean().
can_read(Scope, Tenant_id) ->
case Scope of
{system_scope, _} ->
true;
{tenant_scope, _, Scope_tenant, _} ->
Scope_tenant =:= Tenant_id
end.
-file("src/lightspeed/data/repository.gleam", 233).
-spec lookup(list(record()), binary()) -> {ok, record()} | {error, nil}.
lookup(Rows, Id) ->
case Rows of
[] ->
{error, nil};
[Row | Rest] ->
case erlang:element(2, Row) =:= Id of
true ->
{ok, Row};
false ->
lookup(Rest, Id)
end
end.
-file("src/lightspeed/data/repository.gleam", 100).
?DOC(" Fetch one row by id with scope validation.\n").
-spec fetch_by_id(repository(), scope(), binary()) -> {ok, record()} |
{error, access_error()}.
fetch_by_id(Repository, Scope, Id) ->
case lookup(all(Repository), Id) of
{error, nil} ->
{error, {not_found, Id}};
{ok, Row} ->
case can_read(Scope, erlang:element(3, Row)) of
true ->
{ok, Row};
false ->
{error,
forbidden(
Scope,
<<"read"/utf8>>,
erlang:element(3, Row)
)}
end
end.
-file("src/lightspeed/data/repository.gleam", 222).
-spec remove_id(list(record()), binary()) -> list(record()).
remove_id(Rows_rev, Id) ->
case Rows_rev of
[] ->
[];
[Row | Rest] ->
case erlang:element(2, Row) =:= Id of
true ->
remove_id(Rest, Id);
false ->
[Row | remove_id(Rest, Id)]
end
end.
-file("src/lightspeed/data/repository.gleam", 218).
-spec upsert_row(list(record()), record()) -> list(record()).
upsert_row(Rows_rev, Next) ->
[Next | remove_id(Rows_rev, erlang:element(2, Next))].
-file("src/lightspeed/data/repository.gleam", 210).
-spec role_can_write(role()) -> boolean().
role_can_write(Role) ->
case Role of
viewer ->
false;
editor ->
true;
tenant_admin ->
true
end.
-file("src/lightspeed/data/repository.gleam", 202).
-spec can_write(scope(), binary()) -> boolean().
can_write(Scope, Tenant_id) ->
case Scope of
{system_scope, _} ->
true;
{tenant_scope, _, Scope_tenant, Role} ->
(Scope_tenant =:= Tenant_id) andalso role_can_write(Role)
end.
-file("src/lightspeed/data/repository.gleam", 116).
?DOC(" Upsert one row with scope validation.\n").
-spec upsert(repository(), scope(), record()) -> {ok, repository()} |
{error, access_error()}.
upsert(Repository, Scope, Row) ->
case can_write(Scope, erlang:element(3, Row)) of
false ->
{error, forbidden(Scope, <<"write"/utf8>>, erlang:element(3, Row))};
true ->
{ok,
{repository,
erlang:element(2, Repository),
upsert_row(erlang:element(3, Repository), Row)}}
end.
-file("src/lightspeed/data/repository.gleam", 131).
?DOC(" Delete one row by id with scope validation.\n").
-spec delete_by_id(repository(), scope(), binary()) -> {ok, repository()} |
{error, access_error()}.
delete_by_id(Repository, Scope, Id) ->
case lookup(all(Repository), Id) of
{error, nil} ->
{error, {not_found, Id}};
{ok, Row} ->
case can_write(Scope, erlang:element(3, Row)) of
false ->
{error,
forbidden(
Scope,
<<"delete"/utf8>>,
erlang:element(3, Row)
)};
true ->
{ok,
{repository,
erlang:element(2, Repository),
remove_id(erlang:element(3, Repository), Id)}}
end
end.
-file("src/lightspeed/data/repository.gleam", 153).
?DOC(" Stable role label.\n").
-spec role_label(role()) -> binary().
role_label(Role) ->
case Role of
viewer ->
<<"viewer"/utf8>>;
editor ->
<<"editor"/utf8>>;
tenant_admin ->
<<"tenant_admin"/utf8>>
end.
-file("src/lightspeed/data/repository.gleam", 162).
?DOC(" Stable scope label.\n").
-spec scope_label(scope()) -> binary().
scope_label(Scope) ->
case Scope of
{tenant_scope, Actor_id, Tenant_id, Role} ->
<<<<<<<<<<"tenant_scope:"/utf8, Actor_id/binary>>/binary, ":"/utf8>>/binary,
Tenant_id/binary>>/binary,
":"/utf8>>/binary,
(role_label(Role))/binary>>;
{system_scope, Actor_id@1} ->
<<"system_scope:"/utf8, Actor_id@1/binary>>
end.
-file("src/lightspeed/data/repository.gleam", 171).
?DOC(" Stable error label.\n").
-spec error_label(access_error()) -> binary().
error_label(Error) ->
case Error of
{not_found, Id} ->
<<"not_found:"/utf8, Id/binary>>;
{forbidden, Actor_id, Action, Tenant_id} ->
<<<<<<<<<<"forbidden:"/utf8, Actor_id/binary>>/binary, ":"/utf8>>/binary,
Action/binary>>/binary,
":"/utf8>>/binary,
Tenant_id/binary>>
end.