Packages

Generate Gleam types, JSON codecs, and a typed XRPC client from atproto lexicon files.

Current section

Files

Jump to
atproto_codegen src atproto_codegen@plugin.erl
Raw

src/atproto_codegen@plugin.erl

-module(atproto_codegen@plugin).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto_codegen/plugin.gleam").
-export([run/4, assemble/2]).
-export_type([output_file/0, codegen_error/0, plugin_context/0, plugin/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 output_file() :: {output_file, binary(), list(binary())} |
{whole_file, binary(), binary()}.
-type codegen_error() :: {unknown_dependency, binary(), binary()} |
{unresolved_order, list(binary())} |
{mismatched_sections, binary()} |
{conflicting_whole_file, binary()} |
{plugin_failure, binary(), binary()}.
-type plugin_context() :: {plugin_context,
atproto_codegen@config:config(),
list(atproto_lexicon@ast:lexicon_doc()),
list(atproto_codegen@lower:flat_lexicon()),
gleam@dict:dict(binary(), list(output_file()))}.
-type plugin() :: {plugin,
binary(),
list(binary()),
fun((plugin_context()) -> {ok, list(output_file())} |
{error, codegen_error()})}.
-file("src/atproto_codegen/plugin.gleam", 110).
?DOC(false).
-spec place(list(plugin()), list(plugin())) -> {ok, list(plugin())} |
{error, codegen_error()}.
place(Remaining, Placed) ->
case Remaining of
[] ->
{ok, lists:reverse(Placed)};
_ ->
Placed_names = gleam@list:map(
Placed,
fun(P) -> erlang:element(2, P) end
),
{Ready, Rest} = gleam@list:partition(
Remaining,
fun(P@1) ->
gleam@list:all(
erlang:element(3, P@1),
fun(_capture) ->
gleam@list:contains(Placed_names, _capture)
end
)
end
),
case Ready of
[] ->
{error,
{unresolved_order,
gleam@list:map(
Remaining,
fun(P@2) -> erlang:element(2, P@2) end
)}};
_ ->
place(Rest, lists:append(lists:reverse(Ready), Placed))
end
end.
-file("src/atproto_codegen/plugin.gleam", 94).
?DOC(false).
-spec check_known_deps(list(plugin()), list(binary())) -> {ok, nil} |
{error, codegen_error()}.
check_known_deps(Plugins, Names) ->
_pipe = Plugins,
gleam@list:try_each(
_pipe,
fun(Plugin) -> _pipe@1 = erlang:element(3, Plugin),
gleam@list:try_each(
_pipe@1,
fun(Dep) -> case gleam@list:contains(Names, Dep) of
true ->
{ok, nil};
false ->
{error,
{unknown_dependency,
erlang:element(2, Plugin),
Dep}}
end end
) end
).
-file("src/atproto_codegen/plugin.gleam", 88).
?DOC(false).
-spec resolve_order(list(plugin())) -> {ok, list(plugin())} |
{error, codegen_error()}.
resolve_order(Plugins) ->
Names = gleam@list:map(Plugins, fun(P) -> erlang:element(2, P) end),
gleam@result:'try'(
check_known_deps(Plugins, Names),
fun(_) -> place(Plugins, []) end
).
-file("src/atproto_codegen/plugin.gleam", 74).
?DOC(false).
-spec run(
list(plugin()),
atproto_codegen@config:config(),
list(atproto_lexicon@ast:lexicon_doc()),
list(atproto_codegen@lower:flat_lexicon())
) -> {ok, gleam@dict:dict(binary(), list(output_file()))} |
{error, codegen_error()}.
run(Plugins, Config, Docs, Lexicons) ->
gleam@result:'try'(
resolve_order(Plugins),
fun(Order) ->
gleam@list:try_fold(
Order,
maps:new(),
fun(Acc, Plugin) ->
Context = {plugin_context, Config, Docs, Lexicons, Acc},
gleam@result:'try'(
(erlang:element(4, Plugin))(Context),
fun(Outputs) ->
{ok,
gleam@dict:insert(
Acc,
erlang:element(2, Plugin),
Outputs
)}
end
)
end
)
end
).
-file("src/atproto_codegen/plugin.gleam", 227).
?DOC(false).
-spec zip_flat(list(list(binary()))) -> list(binary()).
zip_flat(Lists) ->
case gleam@list:any(Lists, fun(L) -> L =:= [] end) of
true ->
[];
false ->
Heads = gleam@list:filter_map(Lists, fun gleam@list:first/1),
Tails = gleam@list:map(
Lists,
fun(L@1) -> gleam@list:drop(L@1, 1) end
),
lists:append(Heads, zip_flat(Tails))
end.
-file("src/atproto_codegen/plugin.gleam", 210).
?DOC(false).
-spec interleave(binary(), list(list(binary()))) -> {ok, list(binary())} |
{error, codegen_error()}.
interleave(Path, Sections) ->
Non_empty = gleam@list:filter(Sections, fun(S) -> S /= [] end),
case Non_empty of
[] ->
{ok, []};
[First | Rest] ->
Width = erlang:length(First),
case gleam@list:all(
Rest,
fun(S@1) -> erlang:length(S@1) =:= Width end
) of
false ->
{error, {mismatched_sections, Path}};
true ->
{ok, zip_flat(Non_empty)}
end
end.
-file("src/atproto_codegen/plugin.gleam", 149).
?DOC(false).
-spec file_path(output_file()) -> binary().
file_path(File) ->
case File of
{output_file, Path, _} ->
Path;
{whole_file, Path@1, _} ->
Path@1
end.
-file("src/atproto_codegen/plugin.gleam", 192).
?DOC(false).
-spec assemble_parts(
gleam@dict:dict(binary(), list(output_file())),
list(binary()),
binary()
) -> {ok, {binary(), binary()}} | {error, codegen_error()}.
assemble_parts(Outputs, Order, Path) ->
gleam@result:'try'(
gleam@list:try_map(
Order,
fun(Name) ->
Files = begin
_pipe = gleam_stdlib:map_get(Outputs, Name),
gleam@result:unwrap(_pipe, [])
end,
case gleam@list:find(
Files,
fun(File) -> file_path(File) =:= Path end
) of
{ok, {output_file, _, Parts}} ->
{ok, Parts};
{ok, {whole_file, _, _}} ->
{ok, []};
{error, nil} ->
{ok, []}
end
end
),
fun(Sections) ->
gleam@result:'try'(
interleave(Path, Sections),
fun(Merged) ->
{ok, {Path, gleam@string:join(Merged, <<"\n\n"/utf8>>)}}
end
)
end
).
-file("src/atproto_codegen/plugin.gleam", 156).
?DOC(false).
-spec assemble_path(
gleam@dict:dict(binary(), list(output_file())),
list(binary()),
binary()
) -> {ok, {binary(), binary()}} | {error, codegen_error()}.
assemble_path(Outputs, Order, Path) ->
Entries = gleam@list:flat_map(
Order,
fun(Name) -> _pipe = gleam_stdlib:map_get(Outputs, Name),
_pipe@1 = gleam@result:unwrap(_pipe, []),
gleam@list:filter(
_pipe@1,
fun(File) -> file_path(File) =:= Path end
) end
),
Wholes = gleam@list:filter_map(Entries, fun(File@1) -> case File@1 of
{whole_file, _, Content} ->
{ok, Content};
{output_file, _, _} ->
{error, nil}
end end),
Has_parts = gleam@list:any(Entries, fun(File@2) -> case File@2 of
{output_file, _, _} ->
true;
{whole_file, _, _} ->
false
end end),
case Wholes of
[] ->
assemble_parts(Outputs, Order, Path);
[Content@1] ->
case Has_parts of
true ->
{error, {conflicting_whole_file, Path}};
false ->
{ok, {Path, Content@1}}
end;
_ ->
{error, {conflicting_whole_file, Path}}
end.
-file("src/atproto_codegen/plugin.gleam", 135).
?DOC(false).
-spec assemble(gleam@dict:dict(binary(), list(output_file())), list(binary())) -> {ok,
list({binary(), binary()})} |
{error, codegen_error()}.
assemble(Outputs, Order) ->
_pipe = Order,
_pipe@3 = gleam@list:flat_map(
_pipe,
fun(Name) -> _pipe@1 = gleam_stdlib:map_get(Outputs, Name),
_pipe@2 = gleam@result:unwrap(_pipe@1, []),
gleam@list:map(_pipe@2, fun file_path/1) end
),
_pipe@4 = gleam@list:unique(_pipe@3),
gleam@list:try_map(
_pipe@4,
fun(_capture) -> assemble_path(Outputs, Order, _capture) end
).