Current section
Files
Jump to
Current section
Files
src/libero@source.erl
-module(libero@source).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/libero/source.gleam").
-export([walk_directory/1, derive_module_path/1, parse_module/1, build_type_import_map/1, build_type_alias_originals/1, build_alias_resolution_map/1]).
-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(
" Source-file helpers for seed-driven type discovery.\n"
"\n"
" Frameworks such as Rally decide which types cross the boundary and pass\n"
" those seeds to Libero.\n"
).
-file("src/libero/source.gleam", 59).
-spec visit_file(list(binary()), binary(), binary()) -> list(binary()).
visit_file(Acc, Entry, Child) ->
gleam@bool:guard(
not gleam_stdlib:string_ends_with(Entry, <<".gleam"/utf8>>),
Acc,
fun() -> [Child | Acc] end
).
-file("src/libero/source.gleam", 49).
-spec visit_subdirectory(list(binary()), binary(), binary()) -> {ok,
list(binary())} |
{error, libero@gen_error:gen_error()}.
visit_subdirectory(Acc, Entry, Child) ->
gleam@bool:guard(
Entry =:= <<"generated"/utf8>>,
{ok, Acc},
fun() ->
gleam@result:'try'(
walk_directory(Child),
fun(Nested) -> {ok, lists:append(Nested, Acc)} end
)
end
).
-file("src/libero/source.gleam", 34).
-spec visit_entry(list(binary()), binary(), binary()) -> {ok, list(binary())} |
{error, libero@gen_error:gen_error()}.
visit_entry(Acc, Parent, Entry) ->
Child = <<<<Parent/binary, "/"/utf8>>/binary, Entry/binary>>,
Is_symlink = begin
_pipe = simplifile_erl:is_symlink(Child),
gleam@result:unwrap(_pipe, false)
end,
Is_dir = gleam@result:unwrap(simplifile_erl:is_directory(Child), false),
gleam@bool:guard(
Is_symlink andalso Is_dir,
{ok, Acc},
fun() -> case Is_dir of
true ->
visit_subdirectory(Acc, Entry, Child);
false ->
{ok, visit_file(Acc, Entry, Child)}
end end
).
-file("src/libero/source.gleam", 21).
?DOC(
" Recursively walk a directory, returning every `.gleam` file found.\n"
" Skips any subdirectory named `generated`.\n"
).
-spec walk_directory(binary()) -> {ok, list(binary())} |
{error, libero@gen_error:gen_error()}.
walk_directory(Path) ->
gleam@result:'try'(
begin
_pipe = simplifile_erl:read_directory(Path),
gleam@result:map_error(
_pipe,
fun(Cause) -> {cannot_read_dir, Path, Cause} end
)
end,
fun(Entries) ->
gleam@result:map(
gleam@list:try_fold(
Entries,
[],
fun(Acc, Entry) -> visit_entry(Acc, Path, Entry) end
),
fun(Files) ->
gleam@list:sort(Files, fun gleam@string:compare/2)
end
)
end
).
-file("src/libero/source.gleam", 71).
?DOC(
" Derive the Gleam module path from a file path by finding the last\n"
" occurrence of `/src/` and taking everything after it, then stripping\n"
" the `.gleam` extension.\n"
).
-spec derive_module_path(binary()) -> binary().
derive_module_path(File_path) ->
Without_extension = case gleam_stdlib:string_ends_with(
File_path,
<<".gleam"/utf8>>
) of
true ->
gleam@string:slice(
File_path,
0,
string:length(File_path) - string:length(<<".gleam"/utf8>>)
);
false ->
File_path
end,
case gleam@string:split(Without_extension, <<"/src/"/utf8>>) of
[_] ->
Without_extension;
Parts ->
_pipe = gleam@list:last(Parts),
gleam@result:unwrap(_pipe, Without_extension)
end.
-file("src/libero/source.gleam", 89).
?DOC(
" Read a `.gleam` file and parse it via `glance`, surfacing both I/O and\n"
" parser failures as `GenError` variants tagged with the file path.\n"
).
-spec parse_module(binary()) -> {ok, glance:module_()} |
{error, libero@gen_error:gen_error()}.
parse_module(File_path) ->
gleam@result:'try'(
begin
_pipe = simplifile:read(File_path),
gleam@result:map_error(
_pipe,
fun(Cause) -> {cannot_read_file, File_path, Cause} end
)
end,
fun(Content) -> _pipe@1 = glance:module(Content),
gleam@result:map_error(
_pipe@1,
fun(Cause@1) -> {parse_failed, File_path, Cause@1} end
) end
).
-file("src/libero/source.gleam", 102).
?DOC(
" Build a map from unqualified type names to the full module path of their\n"
" import.\n"
).
-spec build_type_import_map(list(glance:definition(glance:import()))) -> gleam@dict:dict(binary(), binary()).
build_type_import_map(Imports) ->
gleam@list:fold(
Imports,
maps:new(),
fun(Acc, Def) ->
{definition, _, Imp} = Def,
gleam@list:fold(
erlang:element(5, Imp),
Acc,
fun(Inner_acc, Uq) ->
Key = case erlang:element(3, Uq) of
{some, Alias} ->
Alias;
none ->
erlang:element(2, Uq)
end,
gleam@dict:insert(Inner_acc, Key, erlang:element(3, Imp))
end
)
end
).
-file("src/libero/source.gleam", 119).
?DOC(
" Map locally-bound type names back to their original names from the source\n"
" module. Only populated when an import uses `type X as Y`.\n"
).
-spec build_type_alias_originals(list(glance:definition(glance:import()))) -> gleam@dict:dict(binary(), binary()).
build_type_alias_originals(Imports) ->
gleam@list:fold(
Imports,
maps:new(),
fun(Acc, Def) ->
{definition, _, Imp} = Def,
gleam@list:fold(
erlang:element(5, Imp),
Acc,
fun(Inner_acc, Uq) -> case erlang:element(3, Uq) of
{some, Alias} ->
gleam@dict:insert(
Inner_acc,
Alias,
erlang:element(2, Uq)
);
none ->
Inner_acc
end end
)
end
).
-file("src/libero/source.gleam", 135).
?DOC(
" Build a map from import aliases and bare module names to the full module\n"
" path.\n"
).
-spec build_alias_resolution_map(list(glance:definition(glance:import()))) -> gleam@dict:dict(binary(), binary()).
build_alias_resolution_map(Imports) ->
gleam@list:fold(
Imports,
maps:new(),
fun(Acc, Def) ->
{definition, _, Imp} = Def,
Last_seg = libero@field_type:last_segment(erlang:element(3, Imp)),
Alias = case erlang:element(4, Imp) of
{some, {named, Name}} ->
Name;
_ ->
Last_seg
end,
gleam@dict:insert(Acc, Alias, erlang:element(3, Imp))
end
).