Current section
Files
Jump to
Current section
Files
src/atproto_codegen.erl
-module(atproto_codegen).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto_codegen.gleam").
-export([main/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(
" Lexicon -> Gleam codegen. Reads `<lexicons_dir>/**/*.json` and writes\n"
" typed modules into `<out_dir>`, importable as `<out_module>/...`.\n"
"\n"
" Pipeline: read lexicon files, decode via `atproto_lexicon/decoding` into\n"
" `ast.LexiconDoc`, `codegen/lower` flattens those into the view plugins\n"
" consume, `codegen/plugin` runs the registered plugins and assembles\n"
" their per-module sections, `codegen/module_header` computes each\n"
" module's import header, then this module writes the files.\n"
).
-file("src/atproto_codegen.gleam", 216).
-spec parent_dir(binary()) -> binary().
parent_dir(Path) ->
case begin
_pipe = gleam@string:split(Path, <<"/"/utf8>>),
lists:reverse(_pipe)
end of
[_ | Rest] ->
_pipe@1 = Rest,
_pipe@2 = lists:reverse(_pipe@1),
gleam@string:join(_pipe@2, <<"/"/utf8>>);
[] ->
<<"."/utf8>>
end.
-file("src/atproto_codegen.gleam", 127).
?DOC(" The xrpc-client plugin owns a whole file, so this writes its `WholeFile` content verbatim instead of the per-lexicon banner/header wrapping above.\n").
-spec write_client(
atproto_codegen@config:config(),
gleam@dict:dict(binary(), binary())
) -> nil.
write_client(Cfg, Bodies) ->
case erlang:element(6, Cfg) of
none ->
nil;
{some, Path} ->
Full_path = <<<<<<(erlang:element(3, Cfg))/binary, "/"/utf8>>/binary,
Path/binary>>/binary,
".gleam"/utf8>>,
Content@1 = case gleam_stdlib:map_get(
Bodies,
<<Path/binary, ".gleam"/utf8>>
) of
{ok, Content} -> Content;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"write_client"/utf8>>,
line => 132,
value => _assert_fail,
start => 4540,
'end' => 4599,
pattern_start => 4551,
pattern_end => 4562})
end,
case simplifile:create_directory_all(parent_dir(Full_path)) of
{ok, _} -> nil;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"write_client"/utf8>>,
line => 133,
value => _assert_fail@1,
start => 4606,
'end' => 4679,
pattern_start => 4617,
pattern_end => 4622})
end,
case simplifile:write(Full_path, Content@1) of
{ok, _} -> nil;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"write_client"/utf8>>,
line => 134,
value => _assert_fail@2,
start => 4686,
'end' => 4755,
pattern_start => 4697,
pattern_end => 4702})
end,
gleam_stdlib:println(<<" gen "/utf8, Full_path/binary>>)
end.
-file("src/atproto_codegen.gleam", 59).
?DOC(" `types` then `decode-json` interleave per section; the opt-in `xrpc-client` runs last and owns its own whole file, so it never interleaves.\n").
-spec plugin_order(atproto_codegen@config:config()) -> list(binary()).
plugin_order(Cfg) ->
case erlang:element(6, Cfg) of
{some, _} ->
[<<"types"/utf8>>, <<"decode-json"/utf8>>, <<"xrpc-client"/utf8>>];
none ->
[<<"types"/utf8>>, <<"decode-json"/utf8>>]
end.
-file("src/atproto_codegen.gleam", 66).
-spec plugins(atproto_codegen@config:config()) -> list(atproto_codegen@plugin:plugin()).
plugins(Cfg) ->
case erlang:element(6, Cfg) of
{some, _} ->
[atproto_codegen@plugins@types:plugin(),
atproto_codegen@plugins@decode_json:plugin(),
atproto_codegen@plugins@xrpc_client:plugin()];
none ->
[atproto_codegen@plugins@types:plugin(),
atproto_codegen@plugins@decode_json:plugin()]
end.
-file("src/atproto_codegen.gleam", 141).
-spec internal_template() -> binary().
internal_template() ->
Priv = begin
_pipe = gleam_erlang_ffi:priv_directory(<<"atproto_codegen"/utf8>>),
gleam@result:unwrap(_pipe, <<"priv"/utf8>>)
end,
<<Priv/binary, "/internal.gleam"/utf8>>.
-file("src/atproto_codegen.gleam", 211).
-spec ref_key(binary(), binary()) -> list(binary()).
ref_key(Current, Ref) ->
R = atproto_codegen@naming:parse_ref(Current, Ref),
[<<<<(erlang:element(2, R))/binary, "#"/utf8>>/binary,
(erlang:element(3, R))/binary>>].
-file("src/atproto_codegen.gleam", 202).
-spec field_ref_keys(binary(), atproto_codegen@lower:flat_type()) -> list(binary()).
field_ref_keys(Current, Ft) ->
case Ft of
{t_ref, Ref} ->
ref_key(Current, Ref);
{t_union, Refs, _} ->
gleam@list:flat_map(
Refs,
fun(_capture) -> ref_key(Current, _capture) end
);
{t_array, Inner} ->
field_ref_keys(Current, Inner);
_ ->
[]
end.
-file("src/atproto_codegen.gleam", 173).
?DOC(" Warn about any `ref` pointing at a def we never generate, which would otherwise emit Gleam that doesn't compile with no signal from codegen.\n").
-spec report_unresolved_refs(list(atproto_codegen@lower:flat_lexicon())) -> nil.
report_unresolved_refs(Lexicons) ->
Known = gleam@list:flat_map(
Lexicons,
fun(Lex) ->
gleam@list:filter_map(erlang:element(4, Lex), fun(D) -> case D of
{flat_record, Nsid, _} ->
{ok, <<Nsid/binary, "#main"/utf8>>};
{flat_object, Nsid@1, Name, _} ->
{ok,
<<<<Nsid@1/binary, "#"/utf8>>/binary,
Name/binary>>};
_ ->
{error, nil}
end end)
end
),
Refs = gleam@list:flat_map(
Lexicons,
fun(Lex@1) ->
gleam@list:flat_map(
erlang:element(4, Lex@1),
fun(D@1) ->
gleam@list:flat_map(
atproto_codegen@lower:properties_of(D@1),
fun(P) ->
field_ref_keys(
erlang:element(2, Lex@1),
erlang:element(3, P)
)
end
)
end
)
end
),
_pipe = Refs,
_pipe@1 = gleam@list:unique(_pipe),
_pipe@2 = gleam@list:filter(
_pipe@1,
fun(Key) -> not gleam@list:contains(Known, Key) end
),
gleam@list:each(
_pipe@2,
fun(Key@1) ->
gleam_stdlib:println(
<<<<" WARN unresolved ref "/utf8, Key@1/binary>>/binary,
" (referencing def won't compile)"/utf8>>
)
end
).
-file("src/atproto_codegen.gleam", 158).
?DOC(" Loud on purpose: a dropped def must never be silent.\n").
-spec report_unsupported(list(atproto_codegen@lower:flat_lexicon())) -> nil.
report_unsupported(Lexicons) ->
gleam@list:each(
Lexicons,
fun(Lex) -> gleam@list:each(erlang:element(4, Lex), fun(D) -> case D of
{flat_unsupported, Nsid, Name, Reason} ->
gleam_stdlib:println(
<<<<<<<<<<" WARN skipping "/utf8,
Nsid/binary>>/binary,
"#"/utf8>>/binary,
Name/binary>>/binary,
": "/utf8>>/binary,
Reason/binary>>
);
_ ->
nil
end end) end
).
-file("src/atproto_codegen.gleam", 148).
-spec load_doc(binary()) -> {ok, atproto_lexicon@ast:lexicon_doc()} |
{error, binary()}.
load_doc(Path) ->
gleam@result:'try'(
begin
_pipe = simplifile:read(Path),
gleam@result:map_error(
_pipe,
fun(E) ->
<<"read failed: "/utf8, (gleam@string:inspect(E))/binary>>
end
)
end,
fun(Contents) ->
_pipe@1 = atproto_lexicon@decoding:decode_json(Contents),
gleam@result:map_error(
_pipe@1,
fun(E@1) ->
<<"parse failed: "/utf8,
(gleam@string:inspect(E@1))/binary>>
end
)
end
).
-file("src/atproto_codegen.gleam", 73).
-spec run(atproto_codegen@config:config()) -> nil.
run(Cfg) ->
Files@1 = case simplifile:get_files(erlang:element(2, Cfg)) of
{ok, Files} -> Files;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 74,
value => _assert_fail,
start => 2509,
'end' => 2570,
pattern_start => 2520,
pattern_end => 2529})
end,
Docs = begin
_pipe = Files@1,
_pipe@1 = gleam@list:filter(
_pipe,
fun(_capture) ->
gleam_stdlib:string_ends_with(_capture, <<".json"/utf8>>)
end
),
gleam@list:filter_map(_pipe@1, fun(Path) -> case load_doc(Path) of
{ok, Doc} ->
{ok, Doc};
{error, Reason} ->
gleam_stdlib:println(
<<<<<<" WARN skipping "/utf8, Path/binary>>/binary,
": "/utf8>>/binary,
Reason/binary>>
),
{error, nil}
end end)
end,
Lexicons = atproto_codegen@lower:lower(Docs),
report_unsupported(Lexicons),
report_unresolved_refs(Lexicons),
case simplifile:create_directory_all(erlang:element(3, Cfg)) of
{ok, _} -> nil;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 92,
value => _assert_fail@1,
start => 2978,
'end' => 3041,
pattern_start => 2989,
pattern_end => 2994})
end,
Internal@1 = case simplifile:read(internal_template()) of
{ok, Internal} -> Internal;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 93,
value => _assert_fail@2,
start => 3044,
'end' => 3106,
pattern_start => 3055,
pattern_end => 3067})
end,
case simplifile:write(
<<(erlang:element(3, Cfg))/binary, "/internal.gleam"/utf8>>,
<<"//// Generated by codegen from priv/internal.gleam. Do not edit by hand.\n\n"/utf8,
Internal@1/binary>>
) of
{ok, _} -> nil;
_assert_fail@3 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 94,
value => _assert_fail@3,
start => 3109,
'end' => 3245,
pattern_start => 3120,
pattern_end => 3125})
end,
Outputs@1 = case atproto_codegen@plugin:run(
plugins(Cfg),
Cfg,
Docs,
Lexicons
) of
{ok, Outputs} -> Outputs;
_assert_fail@4 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 100,
value => _assert_fail@4,
start => 3249,
'end' => 3319,
pattern_start => 3260,
pattern_end => 3271})
end,
Assembled@1 = case atproto_codegen@plugin:assemble(
Outputs@1,
plugin_order(Cfg)
) of
{ok, Assembled} -> Assembled;
_assert_fail@5 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 101,
value => _assert_fail@5,
start => 3322,
'end' => 3392,
pattern_start => 3333,
pattern_end => 3346})
end,
Bodies = maps:from_list(Assembled@1),
gleam@list:each(
Lexicons,
fun(Lex) ->
case gleam@list:filter(
erlang:element(4, Lex),
fun atproto_codegen@lower:is_emittable/1
) of
[] ->
nil;
Defs ->
Subpath = atproto_codegen@naming:module_subpath(
erlang:element(5, Cfg),
erlang:element(2, Lex)
),
Path@1 = <<Subpath/binary, ".gleam"/utf8>>,
Body@1 = case gleam_stdlib:map_get(Bodies, Path@1) of
{ok, Body} -> Body;
_assert_fail@6 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 110,
value => _assert_fail@6,
start => 3672,
'end' => 3716,
pattern_start => 3683,
pattern_end => 3691})
end,
Header = atproto_codegen@module_header:header(
Cfg,
erlang:element(2, Lex),
Defs
),
Source = <<<<<<<<"//// Generated by codegen from lexicons/. Do not edit by hand.\n\n"/utf8,
Header/binary>>/binary,
"\n"/utf8>>/binary,
Body@1/binary>>/binary,
"\n"/utf8>>,
Full_path = <<<<(erlang:element(3, Cfg))/binary, "/"/utf8>>/binary,
Path@1/binary>>,
case simplifile:create_directory_all(parent_dir(Full_path)) of
{ok, _} -> nil;
_assert_fail@7 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 114,
value => _assert_fail@7,
start => 3908,
'end' => 3991,
pattern_start => 3919,
pattern_end => 3924})
end,
case simplifile:write(Full_path, Source) of
{ok, _} -> nil;
_assert_fail@8 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Pattern match failed, no pattern matched the value."/utf8>>,
file => <<?FILEPATH/utf8>>,
module => <<"atproto_codegen"/utf8>>,
function => <<"run"/utf8>>,
line => 116,
value => _assert_fail@8,
start => 4000,
'end' => 4068,
pattern_start => 4011,
pattern_end => 4016})
end,
gleam_stdlib:println(<<" gen "/utf8, Full_path/binary>>)
end
end
),
write_client(Cfg, Bodies),
gleam_stdlib:println(<<"codegen: done"/utf8>>).
-file("src/atproto_codegen.gleam", 36).
-spec main() -> nil.
main() ->
case erlang:element(4, argv:load()) of
[Lexicons_dir, Out_dir, Out_module, Prefixes] ->
run(
{config,
Lexicons_dir,
Out_dir,
Out_module,
gleam@string:split(Prefixes, <<","/utf8>>),
none}
);
[Lexicons_dir@1, Out_dir@1, Out_module@1, Prefixes@1, Client] ->
run(
{config,
Lexicons_dir@1,
Out_dir@1,
Out_module@1,
gleam@string:split(Prefixes@1, <<","/utf8>>),
{some, Client}}
);
_ ->
gleam_stdlib:println(
<<"usage: gleam run -- <lexicons_dir> <out_dir> <out_module> <nsid_prefix>[,<nsid_prefix>...] [<client_module_subpath>]"/utf8>>
)
end.