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@lexicon.erl
Raw

src/atproto_codegen@lexicon.erl

-module(atproto_codegen@lexicon).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/atproto_codegen/lexicon.gleam").
-export([lexicon_decoder/0]).
-export_type([field_type/0, property/0, def/0, lexicon/0, schema/0, raw_def/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 model + JSON decoding: parse lexicon files into typed defs.\n"
"\n"
" Supports record/object defs with string/integer/number/boolean/array/ref/\n"
" cid-link/bytes/blob/unknown fields. A def using anything else (e.g.\n"
" `union`, `token`) becomes `Unsupported` and is reported, not emitted wrong.\n"
).
-type field_type() :: t_string |
t_int |
t_bool |
t_number |
{t_ref, binary()} |
{t_array, field_type()} |
t_cid_link |
t_bytes |
t_blob |
t_unknown |
{t_unsupported, binary()}.
-type property() :: {property, binary(), field_type(), boolean()}.
-type def() :: {record, binary(), list(property())} |
{object, binary(), binary(), list(property())} |
{unsupported, binary(), binary(), binary()} |
ignored.
-type lexicon() :: {lexicon, binary(), list(def())}.
-type schema() :: {schema,
gleam@dict:dict(binary(), field_type()),
list(binary())}.
-type raw_def() :: {raw_def, binary(), schema()}.
-file("src/atproto_codegen/lexicon.gleam", 42).
-spec field_type_decoder() -> gleam@dynamic@decode:decoder(field_type()).
field_type_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Kind) -> case Kind of
<<"string"/utf8>> ->
gleam@dynamic@decode:success(t_string);
<<"integer"/utf8>> ->
gleam@dynamic@decode:success(t_int);
<<"boolean"/utf8>> ->
gleam@dynamic@decode:success(t_bool);
<<"number"/utf8>> ->
gleam@dynamic@decode:success(t_number);
<<"ref"/utf8>> ->
gleam@dynamic@decode:field(
<<"ref"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Ref) ->
gleam@dynamic@decode:success({t_ref, Ref})
end
);
<<"array"/utf8>> ->
gleam@dynamic@decode:field(
<<"items"/utf8>>,
field_type_decoder(),
fun(Items) ->
gleam@dynamic@decode:success({t_array, Items})
end
);
<<"cid-link"/utf8>> ->
gleam@dynamic@decode:success(t_cid_link);
<<"bytes"/utf8>> ->
gleam@dynamic@decode:success(t_bytes);
<<"blob"/utf8>> ->
gleam@dynamic@decode:success(t_blob);
<<"unknown"/utf8>> ->
gleam@dynamic@decode:success(t_unknown);
Other ->
gleam@dynamic@decode:success({t_unsupported, Other})
end end
).
-file("src/atproto_codegen/lexicon.gleam", 69).
-spec schema_decoder() -> gleam@dynamic@decode:decoder(schema()).
schema_decoder() ->
gleam@dynamic@decode:optional_field(
<<"properties"/utf8>>,
maps:new(),
gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_string/1},
field_type_decoder()
),
fun(Properties) ->
gleam@dynamic@decode:optional_field(
<<"required"/utf8>>,
[],
gleam@dynamic@decode:list(
{decoder, fun gleam@dynamic@decode:decode_string/1}
),
fun(Required) ->
gleam@dynamic@decode:success({schema, Properties, Required})
end
)
end
).
-file("src/atproto_codegen/lexicon.gleam", 87).
-spec raw_def_decoder() -> gleam@dynamic@decode:decoder(raw_def()).
raw_def_decoder() ->
gleam@dynamic@decode:field(
<<"type"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Kind) -> case Kind of
<<"object"/utf8>> ->
gleam@dynamic@decode:then(
schema_decoder(),
fun(Schema) ->
gleam@dynamic@decode:success(
{raw_def, <<"object"/utf8>>, Schema}
)
end
);
<<"record"/utf8>> ->
gleam@dynamic@decode:field(
<<"record"/utf8>>,
schema_decoder(),
fun(Schema@1) ->
gleam@dynamic@decode:success(
{raw_def, <<"record"/utf8>>, Schema@1}
)
end
);
Other ->
gleam@dynamic@decode:success(
{raw_def, Other, {schema, maps:new(), []}}
)
end end
).
-file("src/atproto_codegen/lexicon.gleam", 153).
-spec unsupported_type(field_type()) -> gleam@option:option(binary()).
unsupported_type(Ft) ->
case Ft of
{t_unsupported, Kind} ->
{some, Kind};
{t_array, Inner} ->
unsupported_type(Inner);
_ ->
none
end.
-file("src/atproto_codegen/lexicon.gleam", 140).
-spec unsupported_reason(list(property())) -> gleam@option:option(binary()).
unsupported_reason(Props) ->
_pipe = Props,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(P) -> case unsupported_type(erlang:element(3, P)) of
{some, Kind} ->
{ok,
<<<<<<<<"field `"/utf8, (erlang:element(2, P))/binary>>/binary,
"` has type `"/utf8>>/binary,
Kind/binary>>/binary,
"`"/utf8>>};
none ->
{error, nil}
end end
),
_pipe@2 = gleam@list:first(_pipe@1),
gleam@option:from_result(_pipe@2).
-file("src/atproto_codegen/lexicon.gleam", 113).
-spec build_def(binary(), binary(), raw_def()) -> def().
build_def(Nsid, Name, Raw) ->
Props = begin
_pipe = erlang:element(2, erlang:element(3, Raw)),
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
gleam@list:map(
_pipe@2,
fun(Pair) ->
{property,
erlang:element(1, Pair),
erlang:element(2, Pair),
gleam@list:contains(
erlang:element(3, erlang:element(3, Raw)),
erlang:element(1, Pair)
)}
end
)
end,
case unsupported_reason(Props) of
{some, Reason} ->
case erlang:element(2, Raw) of
<<"object"/utf8>> ->
{unsupported, Nsid, Name, Reason};
<<"record"/utf8>> ->
{unsupported, Nsid, Name, Reason};
_ ->
ignored
end;
none ->
case erlang:element(2, Raw) of
<<"object"/utf8>> ->
{object, Nsid, Name, Props};
<<"record"/utf8>> ->
{record, Nsid, Props};
_ ->
ignored
end
end.
-file("src/atproto_codegen/lexicon.gleam", 102).
-spec lexicon_decoder() -> gleam@dynamic@decode:decoder(lexicon()).
lexicon_decoder() ->
gleam@dynamic@decode:field(
<<"id"/utf8>>,
{decoder, fun gleam@dynamic@decode:decode_string/1},
fun(Nsid) ->
gleam@dynamic@decode:field(
<<"defs"/utf8>>,
gleam@dynamic@decode:dict(
{decoder, fun gleam@dynamic@decode:decode_string/1},
raw_def_decoder()
),
fun(Raw) ->
Defs = begin
_pipe = Raw,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(
erlang:element(1, A),
erlang:element(1, B)
)
end
),
gleam@list:map(
_pipe@2,
fun(Pair) ->
build_def(
Nsid,
erlang:element(1, Pair),
erlang:element(2, Pair)
)
end
)
end,
gleam@dynamic@decode:success({lexicon, Nsid, Defs})
end
)
end
).