Current section
Files
Jump to
Current section
Files
src/graded@internal@effects.erl
-module(graded@internal@effects).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/graded/internal/effects.gleam").
-export([lookup_type_field/3, with_type_fields/2, with_externals/2, lookup/2, lookup_effects/2, lookup_param_bounds/2, format_effect_set/1, parse_path_dependencies/1, load_spec_effects_from_file/1, load_spec_effects/1, with_inferred/2, with_inferred_params/2, parse_semver/1, compare_semver/2, semver_lte/2, pick_best_version/2, load_knowledge_base/1, empty_knowledge_base/0]).
-export_type([effect_lookup/0, knowledge_base/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(false).
-type effect_lookup() :: {known, graded@internal@types:effect_set()} | unknown.
-type knowledge_base() :: {knowledge_base,
gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound())),
gleam@dict:dict({binary(), binary()}, graded@internal@types:effect_set()),
gleam@set:set(binary())}.
-file("src/graded/internal/effects.gleam", 61).
?DOC(false).
-spec lookup_type_field(knowledge_base(), binary(), binary()) -> effect_lookup().
lookup_type_field(Knowledge_base, Type_name, Field) ->
case gleam_stdlib:map_get(
erlang:element(4, Knowledge_base),
{Type_name, Field}
) of
{ok, Effect_set} ->
{known, Effect_set};
{error, nil} ->
unknown
end.
-file("src/graded/internal/effects.gleam", 73).
?DOC(false).
-spec with_type_fields(
knowledge_base(),
list(graded@internal@types:type_field_annotation())
) -> knowledge_base().
with_type_fields(Knowledge_base, Type_fields) ->
Merged = gleam@list:fold(
Type_fields,
erlang:element(4, Knowledge_base),
fun(Accumulator, Type_field) ->
gleam@dict:insert(
Accumulator,
{erlang:element(3, Type_field), erlang:element(4, Type_field)},
erlang:element(5, Type_field)
)
end
),
{knowledge_base,
erlang:element(2, Knowledge_base),
erlang:element(3, Knowledge_base),
Merged,
erlang:element(5, Knowledge_base)}.
-file("src/graded/internal/effects.gleam", 95).
?DOC(false).
-spec with_externals(
knowledge_base(),
list(graded@internal@types:external_annotation())
) -> knowledge_base().
with_externals(Knowledge_base, Externals) ->
{Effect_map@1, Pure_set@1} = gleam@list:fold(
Externals,
{erlang:element(2, Knowledge_base), erlang:element(5, Knowledge_base)},
fun(Accumulator, External_annotation) ->
{Effect_map, Pure_set} = Accumulator,
case erlang:element(3, External_annotation) of
module_external ->
{Effect_map,
gleam@set:insert(
Pure_set,
erlang:element(2, External_annotation)
)};
{function_external, Function} ->
{gleam@dict:insert(
Effect_map,
{qualified_name,
erlang:element(2, External_annotation),
Function},
erlang:element(4, External_annotation)
),
Pure_set}
end
end
),
{knowledge_base,
Effect_map@1,
erlang:element(3, Knowledge_base),
erlang:element(4, Knowledge_base),
Pure_set@1}.
-file("src/graded/internal/effects.gleam", 129).
?DOC(false).
-spec lookup(knowledge_base(), graded@internal@types:qualified_name()) -> effect_lookup().
lookup(Knowledge_base, Name) ->
case gleam_stdlib:map_get(erlang:element(2, Knowledge_base), Name) of
{ok, Effect_set} ->
{known, Effect_set};
{error, nil} ->
case gleam@set:contains(
erlang:element(5, Knowledge_base),
erlang:element(2, Name)
) of
true ->
{known, graded@internal@types:empty()};
false ->
unknown
end
end.
-file("src/graded/internal/effects.gleam", 144).
?DOC(false).
-spec lookup_effects(knowledge_base(), graded@internal@types:qualified_name()) -> graded@internal@types:effect_set().
lookup_effects(Knowledge_base, Name) ->
case lookup(Knowledge_base, Name) of
{known, Effect_set} ->
Effect_set;
unknown ->
graded@internal@types:from_labels([<<"Unknown"/utf8>>])
end.
-file("src/graded/internal/effects.gleam", 157).
?DOC(false).
-spec lookup_param_bounds(
knowledge_base(),
graded@internal@types:qualified_name()
) -> list(graded@internal@types:param_bound()).
lookup_param_bounds(Knowledge_base, Name) ->
case gleam_stdlib:map_get(erlang:element(3, Knowledge_base), Name) of
{ok, Bounds} ->
Bounds;
{error, nil} ->
[]
end.
-file("src/graded/internal/effects.gleam", 168).
?DOC(false).
-spec format_effect_set(graded@internal@types:effect_set()) -> binary().
format_effect_set(Effect_set) ->
case Effect_set of
wildcard ->
<<"[_]"/utf8>>;
{specific, Labels} ->
case begin
_pipe = gleam@set:to_list(Labels),
gleam@list:sort(_pipe, fun gleam@string:compare/2)
end of
[] ->
<<"[]"/utf8>>;
Sorted ->
<<<<"["/utf8,
(gleam@string:join(Sorted, <<", "/utf8>>))/binary>>/binary,
"]"/utf8>>
end;
{polymorphic, Labels@1, Variables} ->
Sorted_labels = begin
_pipe@1 = gleam@set:to_list(Labels@1),
gleam@list:sort(_pipe@1, fun gleam@string:compare/2)
end,
Sorted_variables = begin
_pipe@2 = gleam@set:to_list(Variables),
gleam@list:sort(_pipe@2, fun gleam@string:compare/2)
end,
<<<<"["/utf8,
(gleam@string:join(
lists:append(Sorted_labels, Sorted_variables),
<<", "/utf8>>
))/binary>>/binary,
"]"/utf8>>
end.
-file("src/graded/internal/effects.gleam", 188).
?DOC(false).
-spec parse_path_dependencies(binary()) -> list({binary(), binary()}).
parse_path_dependencies(Gleam_toml_path) ->
Parsed = begin
gleam@result:'try'(
begin
_pipe = simplifile:read(Gleam_toml_path),
gleam@result:map_error(_pipe, fun(_) -> nil end)
end,
fun(Content) ->
gleam@result:'try'(
begin
_pipe@1 = tom:parse(Content),
gleam@result:map_error(_pipe@1, fun(_) -> nil end)
end,
fun(Toml) ->
gleam@result:'try'(
begin
_pipe@2 = tom:get_table(
Toml,
[<<"dependencies"/utf8>>]
),
gleam@result:map_error(
_pipe@2,
fun(_) -> nil end
)
end,
fun(Deps) ->
{ok,
gleam@dict:fold(
Deps,
[],
fun(Acc, Name, Value) -> case Value of
{inline_table, Table} ->
case tom:get_string(
Table,
[<<"path"/utf8>>]
) of
{ok, Path} ->
[{Name, Path} | Acc];
{error, _} ->
Acc
end;
_ ->
Acc
end end
)}
end
)
end
)
end
)
end,
gleam@result:unwrap(Parsed, []).
-file("src/graded/internal/effects.gleam", 238).
?DOC(false).
-spec fold_spec_effects(list(graded@internal@types:effect_annotation())) -> gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()).
fold_spec_effects(Annotations) ->
gleam@list:fold(
Annotations,
maps:new(),
fun(Acc, Ann) -> case erlang:element(2, Ann) of
effects ->
case graded@internal@annotation:split_qualified_name(
erlang:element(3, Ann)
) of
{ok, {Module, Function}} ->
gleam@dict:insert(
Acc,
{qualified_name, Module, Function},
erlang:element(5, Ann)
);
{error, _} ->
Acc
end;
check ->
Acc
end end
).
-file("src/graded/internal/effects.gleam", 232).
?DOC(false).
-spec load_spec_effects_from_file(graded@internal@types:graded_file()) -> gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()).
load_spec_effects_from_file(File) ->
fold_spec_effects(graded@internal@annotation:extract_annotations(File)).
-file("src/graded/internal/effects.gleam", 254).
?DOC(false).
-spec read_spec_annotations(binary()) -> {ok,
list(graded@internal@types:effect_annotation())} |
{error, nil}.
read_spec_annotations(Spec_path) ->
gleam@result:'try'(
begin
_pipe = simplifile:read(Spec_path),
gleam@result:replace_error(_pipe, nil)
end,
fun(Content) ->
gleam@result:'try'(
begin
_pipe@1 = graded@internal@annotation:parse_file(Content),
gleam@result:replace_error(_pipe@1, nil)
end,
fun(File) ->
{ok, graded@internal@annotation:extract_annotations(File)}
end
)
end
).
-file("src/graded/internal/effects.gleam", 222).
?DOC(false).
-spec load_spec_effects(binary()) -> gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()).
load_spec_effects(Spec_path) ->
case read_spec_annotations(Spec_path) of
{error, _} ->
maps:new();
{ok, Annotations} ->
fold_spec_effects(Annotations)
end.
-file("src/graded/internal/effects.gleam", 268).
?DOC(false).
-spec with_inferred(
knowledge_base(),
gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set())
) -> knowledge_base().
with_inferred(Knowledge_base, Inferred) ->
Merged = maps:merge(Inferred, erlang:element(2, Knowledge_base)),
{knowledge_base,
Merged,
erlang:element(3, Knowledge_base),
erlang:element(4, Knowledge_base),
erlang:element(5, Knowledge_base)}.
-file("src/graded/internal/effects.gleam", 280).
?DOC(false).
-spec with_inferred_params(
knowledge_base(),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))
) -> knowledge_base().
with_inferred_params(Knowledge_base, Inferred) ->
Merged = maps:merge(Inferred, erlang:element(3, Knowledge_base)),
{knowledge_base,
erlang:element(2, Knowledge_base),
Merged,
erlang:element(4, Knowledge_base),
erlang:element(5, Knowledge_base)}.
-file("src/graded/internal/effects.gleam", 323).
?DOC(false).
-spec fold_qualified_annotation(
{gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))},
graded@internal@types:effect_annotation()
) -> {gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))}.
fold_qualified_annotation(Accumulator, Ann) ->
{Effect_map, Param_map} = Accumulator,
case graded@internal@annotation:split_qualified_name(erlang:element(3, Ann)) of
{error, _} ->
Accumulator;
{ok, {Module, Function}} ->
Qualified_name = {qualified_name, Module, Function},
New_effect_map = case erlang:element(2, Ann) of
effects ->
gleam@dict:insert(
Effect_map,
Qualified_name,
erlang:element(5, Ann)
);
check ->
Effect_map
end,
New_param_map = case erlang:element(4, Ann) of
[] ->
Param_map;
Params ->
gleam@dict:insert(Param_map, Qualified_name, Params)
end,
{New_effect_map, New_param_map}
end.
-file("src/graded/internal/effects.gleam", 313).
?DOC(false).
-spec load_spec_into_maps(
{gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))},
binary()
) -> {gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))}.
load_spec_into_maps(Maps, Spec_path) ->
case read_spec_annotations(Spec_path) of
{error, _} ->
Maps;
{ok, Annotations} ->
gleam@list:fold(Annotations, Maps, fun fold_qualified_annotation/2)
end.
-file("src/graded/internal/effects.gleam", 296).
?DOC(false).
-spec load_dependency_effects(binary()) -> {gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@dict:dict(graded@internal@types:qualified_name(), list(graded@internal@types:param_bound()))}.
load_dependency_effects(Packages_directory) ->
Entries = case simplifile_erl:read_directory(Packages_directory) of
{ok, Found} ->
Found;
{error, _} ->
[]
end,
gleam@list:fold(
Entries,
{maps:new(), maps:new()},
fun(Maps, Package_name) ->
Dep_root = <<<<Packages_directory/binary, "/"/utf8>>/binary,
Package_name/binary>>,
Spec_file = case graded@internal@config:read(
<<Dep_root/binary, "/gleam.toml"/utf8>>
) of
{ok, Cfg} ->
erlang:element(3, Cfg);
{error, _} ->
graded@internal@config:default_spec_file(Package_name)
end,
load_spec_into_maps(
Maps,
<<<<Dep_root/binary, "/"/utf8>>/binary, Spec_file/binary>>
)
end
).
-file("src/graded/internal/effects.gleam", 351).
?DOC(false).
-spec find_catalog_directory() -> binary().
find_catalog_directory() ->
Installed = <<"build/packages/graded/priv/catalog"/utf8>>,
case simplifile_erl:is_directory(Installed) of
{ok, true} ->
Installed;
_ ->
<<"priv/catalog"/utf8>>
end.
-file("src/graded/internal/effects.gleam", 460).
?DOC(false).
-spec parse_semver(binary()) -> {integer(), integer(), integer()}.
parse_semver(Version) ->
case gleam@string:split(Version, <<"."/utf8>>) of
[Major, Minor, Patch] ->
{begin
_pipe = gleam_stdlib:parse_int(Major),
gleam@result:unwrap(_pipe, 0)
end,
begin
_pipe@1 = gleam_stdlib:parse_int(Minor),
gleam@result:unwrap(_pipe@1, 0)
end,
begin
_pipe@2 = gleam_stdlib:parse_int(Patch),
gleam@result:unwrap(_pipe@2, 0)
end};
[Major@1, Minor@1] ->
{begin
_pipe@3 = gleam_stdlib:parse_int(Major@1),
gleam@result:unwrap(_pipe@3, 0)
end,
begin
_pipe@4 = gleam_stdlib:parse_int(Minor@1),
gleam@result:unwrap(_pipe@4, 0)
end,
0};
_ ->
{0, 0, 0}
end.
-file("src/graded/internal/effects.gleam", 480).
?DOC(false).
-spec compare_semver(
{integer(), integer(), integer()},
{integer(), integer(), integer()}
) -> gleam@order:order().
compare_semver(Left, Right) ->
case gleam@int:compare(erlang:element(1, Left), erlang:element(1, Right)) of
eq ->
case gleam@int:compare(
erlang:element(2, Left),
erlang:element(2, Right)
) of
eq ->
gleam@int:compare(
erlang:element(3, Left),
erlang:element(3, Right)
);
Other ->
Other
end;
Other@1 ->
Other@1
end.
-file("src/graded/internal/effects.gleam", 476).
?DOC(false).
-spec semver_lte(
{integer(), integer(), integer()},
{integer(), integer(), integer()}
) -> boolean().
semver_lte(Left, Right) ->
compare_semver(Left, Right) /= gt.
-file("src/graded/internal/effects.gleam", 439).
?DOC(false).
-spec pick_best_version(
list({{integer(), integer(), integer()}, binary()}),
{integer(), integer(), integer()}
) -> {ok, binary()} | {error, nil}.
pick_best_version(Versions, Installed) ->
Eligible = begin
_pipe = gleam@list:filter(
Versions,
fun(Version) ->
semver_lte(erlang:element(1, Version), Installed)
end
),
gleam@list:sort(
_pipe,
fun(Left, Right) ->
compare_semver(
erlang:element(1, Right),
erlang:element(1, Left)
)
end
)
end,
case Eligible of
[Best | _] ->
{ok, erlang:element(2, Best)};
[] ->
case gleam@list:sort(
Versions,
fun(Left@1, Right@1) ->
compare_semver(
erlang:element(1, Right@1),
erlang:element(1, Left@1)
)
end
) of
[Best@1 | _] ->
{ok, erlang:element(2, Best@1)};
[] ->
{error, nil}
end
end.
-file("src/graded/internal/effects.gleam", 396).
?DOC(false).
-spec resolve_catalog_files(list(binary()), gleam@dict:dict(binary(), binary())) -> list(binary()).
resolve_catalog_files(Catalog_files, Installed_versions) ->
Parsed = gleam@list:filter_map(
Catalog_files,
fun(Path) ->
Filename = begin
_pipe = Path,
_pipe@1 = gleam@string:split(_pipe, <<"/"/utf8>>),
_pipe@2 = gleam@list:last(_pipe@1),
_pipe@3 = gleam@result:unwrap(_pipe@2, <<""/utf8>>),
gleam@string:replace(_pipe@3, <<".graded"/utf8>>, <<""/utf8>>)
end,
case gleam@string:split(Filename, <<"@"/utf8>>) of
[Package, Version] ->
{ok, {Package, parse_semver(Version), Path}};
_ ->
{error, nil}
end
end
),
Grouped = gleam@list:fold(
Parsed,
maps:new(),
fun(Accumulator, Entry) ->
{Package@1, Version@1, Path@1} = Entry,
Existing = begin
_pipe@4 = gleam_stdlib:map_get(Accumulator, Package@1),
gleam@result:unwrap(_pipe@4, [])
end,
gleam@dict:insert(
Accumulator,
Package@1,
[{Version@1, Path@1} | Existing]
)
end
),
gleam@dict:fold(
Grouped,
[],
fun(Selected, Package@2, Versions) ->
case gleam_stdlib:map_get(Installed_versions, Package@2) of
{error, nil} ->
Selected;
{ok, Installed_str} ->
Installed = parse_semver(Installed_str),
Best = pick_best_version(Versions, Installed),
case Best of
{ok, Path@2} ->
[Path@2 | Selected];
{error, nil} ->
Selected
end
end
end
).
-file("src/graded/internal/effects.gleam", 494).
?DOC(false).
-spec parse_manifest_versions(binary()) -> gleam@dict:dict(binary(), binary()).
parse_manifest_versions(Manifest_path) ->
Parsed = begin
gleam@result:'try'(
begin
_pipe = simplifile:read(Manifest_path),
gleam@result:map_error(_pipe, fun(_) -> nil end)
end,
fun(Content) ->
gleam@result:'try'(
begin
_pipe@1 = tom:parse(Content),
gleam@result:map_error(_pipe@1, fun(_) -> nil end)
end,
fun(Toml) ->
gleam@result:'try'(
begin
_pipe@2 = tom:get_array(
Toml,
[<<"packages"/utf8>>]
),
gleam@result:map_error(
_pipe@2,
fun(_) -> nil end
)
end,
fun(Packages) ->
{ok,
gleam@list:fold(
Packages,
maps:new(),
fun(Accumulator, Package) ->
case Package of
{inline_table, Table} ->
case {tom:get_string(
Table,
[<<"name"/utf8>>]
),
tom:get_string(
Table,
[<<"version"/utf8>>]
)} of
{{ok, Name},
{ok, Version}} ->
gleam@dict:insert(
Accumulator,
Name,
Version
);
{_, _} ->
Accumulator
end;
_ ->
Accumulator
end
end
)}
end
)
end
)
end
)
end,
gleam@result:unwrap(Parsed, maps:new()).
-file("src/graded/internal/effects.gleam", 359).
?DOC(false).
-spec load_catalog(binary(), binary()) -> {gleam@dict:dict(graded@internal@types:qualified_name(), graded@internal@types:effect_set()),
gleam@set:set(binary())}.
load_catalog(Catalog_dir, Manifest_path) ->
Installed_versions = parse_manifest_versions(Manifest_path),
Catalog_files = case simplifile:get_files(Catalog_dir) of
{ok, Files} ->
gleam@list:filter(
Files,
fun(File) ->
gleam_stdlib:string_ends_with(File, <<".graded"/utf8>>)
end
);
{error, _} ->
[]
end,
Selected = resolve_catalog_files(Catalog_files, Installed_versions),
All_externals = gleam@list:flat_map(
Selected,
fun(File_path) -> case simplifile:read(File_path) of
{error, _} ->
[];
{ok, Content} ->
case graded@internal@annotation:parse_file(Content) of
{error, _} ->
[];
{ok, Graded_file} ->
graded@internal@annotation:extract_externals(
Graded_file
)
end
end end
),
Empty_kb = {knowledge_base,
maps:new(),
maps:new(),
maps:new(),
gleam@set:new()},
Merged = with_externals(Empty_kb, All_externals),
{erlang:element(2, Merged), erlang:element(5, Merged)}.
-file("src/graded/internal/effects.gleam", 36).
?DOC(false).
-spec load_knowledge_base(binary()) -> knowledge_base().
load_knowledge_base(Packages_directory) ->
{Dep_effects, Dep_params} = load_dependency_effects(Packages_directory),
Catalog_dir = find_catalog_directory(),
{Cat_effects, Cat_pure} = load_catalog(
Catalog_dir,
<<"manifest.toml"/utf8>>
),
{knowledge_base,
maps:merge(Dep_effects, Cat_effects),
Dep_params,
maps:new(),
Cat_pure}.
-file("src/graded/internal/effects.gleam", 49).
?DOC(false).
-spec empty_knowledge_base() -> knowledge_base().
empty_knowledge_base() ->
Catalog_dir = find_catalog_directory(),
{Cat_effects, Cat_pure} = load_catalog(
Catalog_dir,
<<"manifest.toml"/utf8>>
),
{knowledge_base, Cat_effects, maps:new(), maps:new(), Cat_pure}.