-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.