Current section
Files
Jump to
Current section
Files
src/chrobot@internal@generate_bindings.erl
-module(chrobot@internal@generate_bindings).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch]).
-export([merge_protocols/2, gen_enum_encoder/2, gen_enum_decoder/2, gen_domain_module/2, gen_root_module/1, get_stable_protocol/3, apply_protocol_patches/1, parse_protocol/1, main/0]).
-export_type([protocol/0, version/0, domain/0, type/0, array_type_item/0, type_definition/0, property_definition/0, command/0, event/0]).
-type protocol() :: {protocol, version(), list(domain())}.
-type version() :: {version, binary(), binary()}.
-type domain() :: {domain,
binary(),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
gleam@option:option(list(binary())),
gleam@option:option(list(type_definition())),
list(command()),
gleam@option:option(list(event())),
gleam@option:option(binary())}.
-type type() :: {primitive_type, binary()} |
{enum_type, list(binary())} |
{object_type, gleam@option:option(list(property_definition()))} |
{array_type, array_type_item()} |
{ref_type, binary()}.
-type array_type_item() :: {reference_item, binary()} |
{primitive_item, binary()}.
-type type_definition() :: {type_definition,
binary(),
gleam@option:option(binary()),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
type()}.
-type property_definition() :: {property_definition,
binary(),
gleam@option:option(binary()),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
type()}.
-type command() :: {command,
binary(),
gleam@option:option(binary()),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
gleam@option:option(list(property_definition())),
gleam@option:option(list(property_definition()))}.
-type event() :: {event,
binary(),
gleam@option:option(binary()),
gleam@option:option(boolean()),
gleam@option:option(boolean()),
gleam@option:option(list(property_definition()))}.
-spec get_protocol_stats(protocol()) -> {integer(),
integer(),
integer(),
integer()}.
get_protocol_stats(Protocol) ->
{erlang:length(erlang:element(3, Protocol)),
begin
_pipe = gleam@list:flat_map(
erlang:element(3, Protocol),
fun(D) -> case erlang:element(6, D) of
{some, Types} ->
Types;
none ->
[]
end end
),
erlang:length(_pipe)
end,
begin
_pipe@1 = gleam@list:flat_map(
erlang:element(3, Protocol),
fun(D@1) -> erlang:element(7, D@1) end
),
erlang:length(_pipe@1)
end,
begin
_pipe@2 = gleam@list:flat_map(
erlang:element(3, Protocol),
fun(D@2) -> case erlang:element(8, D@2) of
{some, Events} ->
Events;
none ->
[]
end end
),
erlang:length(_pipe@2)
end}.
-spec is_allowed(gleam@option:option(boolean()), boolean()) -> boolean().
is_allowed(Value, Rule) ->
case Value of
none ->
true;
{some, false} ->
true;
{some, true} when Rule ->
true;
{some, true} ->
false
end.
-spec merge_protocols(protocol(), protocol()) -> protocol().
merge_protocols(Left, Right) ->
_assert_subject = erlang:element(2, Left) =:= erlang:element(2, Right),
true = case _assert_subject of
true -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"merge_protocols"/utf8>>,
line => 678})
end,
{protocol,
erlang:element(2, Left),
lists:append(erlang:element(3, Left), erlang:element(3, Right))}.
-spec print_protocol_stats(protocol()) -> nil.
print_protocol_stats(Protocol) ->
{Num_domains, Num_types, Num_commands, Num_events} = get_protocol_stats(
Protocol
),
gleam@io:println(
<<<<<<<<<<<<<<<<"Protocol Stats: "/utf8, "Domains: "/utf8>>/binary,
(gleam@int:to_string(Num_domains))/binary>>/binary,
", Types: "/utf8>>/binary,
(gleam@int:to_string(Num_types))/binary>>/binary,
", Commands: "/utf8>>/binary,
(gleam@int:to_string(Num_commands))/binary>>/binary,
", Events: "/utf8>>/binary,
(gleam@int:to_string(Num_events))/binary>>
),
nil.
-spec parse_array_type_item(gleam@dynamic:dynamic_()) -> {ok, array_type_item()} |
{error, list(gleam@dynamic:decode_error())}.
parse_array_type_item(Input) ->
Ref = (gleam@dynamic:field(<<"$ref"/utf8>>, fun gleam@dynamic:string/1))(
Input
),
Type_name = (gleam@dynamic:field(
<<"type"/utf8>>,
fun gleam@dynamic:string/1
))(Input),
case {Ref, Type_name} of
{{ok, Ref_target}, _} ->
{ok, {reference_item, Ref_target}};
{_, {ok, Type_name_val}} ->
{ok, {primitive_item, Type_name_val}};
{{error, Any}, _} ->
{error, Any}
end.
-spec append_optional(
gleam@string_builder:string_builder(),
gleam@option:option(YBW),
fun((YBW) -> binary())
) -> gleam@string_builder:string_builder().
append_optional(Builder, Val, Callback) ->
case Val of
{some, A} ->
gleam@string_builder:append(Builder, Callback(A));
none ->
Builder
end.
-spec resolve_ref(binary()) -> binary().
resolve_ref(Ref_value) ->
Parts = gleam@string:split(Ref_value, <<"."/utf8>>),
case Parts of
[Domain, Type_name] ->
<<<<(justin:snake_case(Domain))/binary, "."/utf8>>/binary,
Type_name/binary>>;
_ ->
Ref_value
end.
-spec safe_snake_case(binary()) -> binary().
safe_snake_case(Input) ->
Res = justin:snake_case(Input),
case Res of
<<"type"/utf8>> ->
<<"type_"/utf8>>;
_ ->
Res
end.
-spec to_gleam_primitive(binary()) -> binary().
to_gleam_primitive(Protocol_primitive) ->
case Protocol_primitive of
<<"number"/utf8>> ->
<<"Float"/utf8>>;
<<"integer"/utf8>> ->
<<"Int"/utf8>>;
<<"string"/utf8>> ->
<<"String"/utf8>>;
<<"boolean"/utf8>> ->
<<"Bool"/utf8>>;
<<"any"/utf8>> ->
<<"dynamic.Dynamic"/utf8>>;
_ ->
gleam@io:debug(Protocol_primitive),
erlang:error(#{gleam_error => panic,
message => <<"can't translate to gleam primitive"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"to_gleam_primitive"/utf8>>,
line => 883})
end.
-spec to_gleam_primitive_function(binary()) -> binary().
to_gleam_primitive_function(Protocol_primitive) ->
case Protocol_primitive of
<<"number"/utf8>> ->
<<"float"/utf8>>;
<<"integer"/utf8>> ->
<<"int"/utf8>>;
<<"string"/utf8>> ->
<<"string"/utf8>>;
<<"boolean"/utf8>> ->
<<"bool"/utf8>>;
<<"any"/utf8>> ->
<<"dynamic"/utf8>>;
_ ->
gleam@io:debug(Protocol_primitive),
erlang:error(#{gleam_error => panic,
message => <<"can't translate to gleam primitive function"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"to_gleam_primitive_function"/utf8>>,
line => 897})
end.
-spec is(gleam@option:option(boolean())) -> boolean().
is(Value) ->
case Value of
{some, true} ->
true;
_ ->
false
end.
-spec gen_preamble(protocol()) -> binary().
gen_preamble(Protocol) ->
<<<<<<<<"
// ---------------------------------------------------------------------------
// | !!!!!! This is an autogenerated file - Do not edit manually !!!!!! |
// | Run ` gleam run -m scripts/generate_protocol_bindings.sh` to regenerate.|
// ---------------------------------------------------------------------------
//// > ⚙️ This module was generated from the Chrome DevTools Protocol version **"/utf8,
(erlang:element(2, erlang:element(2, Protocol)))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, erlang:element(2, Protocol)))/binary>>/binary,
"**\n"/utf8>>.
-spec gen_attached_comment(binary()) -> binary().
gen_attached_comment(Content) ->
<<<<"/// "/utf8,
(gleam@string:replace(Content, <<"\n"/utf8>>, <<"\n/// "/utf8>>))/binary>>/binary,
"\n"/utf8>>.
-spec gen_module_comment(binary()) -> binary().
gen_module_comment(Content) ->
<<<<"//// "/utf8,
(gleam@string:replace(Content, <<"\n"/utf8>>, <<"\n//// "/utf8>>))/binary>>/binary,
"\n"/utf8>>.
-spec multiline_module_comment(binary()) -> binary().
multiline_module_comment(Content) ->
gleam@string:replace(Content, <<"\n"/utf8>>, <<"\n//// "/utf8>>).
-spec gen_imports(domain()) -> binary().
gen_imports(Domain) ->
Domain_imports = begin
_pipe = gleam@option:unwrap(erlang:element(5, Domain), []),
gleam@list:map(
_pipe,
fun(Dependency) ->
<<<<"import protocol/"/utf8,
(justin:snake_case(Dependency))/binary>>/binary,
"\n"/utf8>>
end
)
end,
_pipe@1 = [<<"import chrome\n"/utf8>>,
<<"import gleam/dict\n"/utf8>>,
<<"import gleam/list\n"/utf8>>,
<<"import gleam/dynamic\n"/utf8>>,
<<"import gleam/result\n"/utf8>>,
<<"import gleam/option\n"/utf8>>,
<<"import gleam/json\n"/utf8>>,
<<"import chrobot/internal/utils\n"/utf8>> |
Domain_imports],
gleam@string:join(_pipe@1, <<""/utf8>>).
-spec gen_domain_module_header(protocol(), domain()) -> gleam@string_builder:string_builder().
gen_domain_module_header(Protocol, Domain) ->
_pipe = gleam@string_builder:new(),
_pipe@1 = gleam@string_builder:append(
_pipe,
<<<<"//// ## "/utf8, (erlang:element(2, Domain))/binary>>/binary,
" Domain"/utf8>>
),
_pipe@2 = gleam@string_builder:append(_pipe@1, <<" \n////\n"/utf8>>),
_pipe@3 = gleam@string_builder:append(_pipe@2, <<"//// "/utf8>>),
_pipe@5 = gleam@string_builder:append(
_pipe@3,
begin
_pipe@4 = gleam@option:unwrap(
erlang:element(9, Domain),
<<"This protocol domain has no description."/utf8>>
),
multiline_module_comment(_pipe@4)
end
),
_pipe@6 = gleam@string_builder:append(_pipe@5, <<" \n////\n"/utf8>>),
_pipe@7 = gleam@string_builder:append(
_pipe@6,
<<"//// [📖 View this domain on the DevTools Protocol API Docs](https://chromedevtools.github.io/devtools-protocol/"/utf8>>
),
_pipe@8 = gleam@string_builder:append(
_pipe@7,
erlang:element(2, erlang:element(2, Protocol))
),
_pipe@9 = gleam@string_builder:append(_pipe@8, <<"-"/utf8>>),
_pipe@10 = gleam@string_builder:append(
_pipe@9,
erlang:element(3, erlang:element(2, Protocol))
),
_pipe@11 = gleam@string_builder:append(_pipe@10, <<"/"/utf8>>),
_pipe@12 = gleam@string_builder:append(_pipe@11, erlang:element(2, Domain)),
_pipe@13 = gleam@string_builder:append(_pipe@12, <<"/)\n\n"/utf8>>),
_pipe@14 = gleam@string_builder:append(_pipe@13, gen_imports(Domain)),
gleam@string_builder:append(_pipe@14, <<"\n\n"/utf8>>).
-spec internal_fn(binary(), binary(), binary()) -> binary().
internal_fn(Name, Params, Body) ->
<<<<<<<<<<<<"@internal\npub fn "/utf8, Name/binary>>/binary, "(\n"/utf8>>/binary,
Params/binary>>/binary,
") {\n"/utf8>>/binary,
Body/binary>>/binary,
"}\n"/utf8>>.
-spec get_internal_function_name(binary(), binary()) -> binary().
get_internal_function_name(Internal_descriptor, Type_name) ->
case gleam@string:split(Type_name, <<"."/utf8>>) of
[Val] ->
<<<<Internal_descriptor/binary, "__"/utf8>>/binary,
(justin:snake_case(Val))/binary>>;
[Domain, Val@1] ->
<<<<<<<<(justin:snake_case(Domain))/binary, "."/utf8>>/binary,
Internal_descriptor/binary>>/binary,
"__"/utf8>>/binary,
(justin:snake_case(Val@1))/binary>>;
_ ->
gleam@io:debug({Internal_descriptor, Type_name}),
erlang:error(#{gleam_error => panic,
message => <<"Can't get internal name from passed type_name"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"get_internal_function_name"/utf8>>,
line => 1167})
end.
-spec get_encoder_name(binary()) -> binary().
get_encoder_name(Type_name) ->
get_internal_function_name(<<"encode"/utf8>>, Type_name).
-spec get_decoder_name(binary()) -> binary().
get_decoder_name(Type_name) ->
get_internal_function_name(<<"decode"/utf8>>, Type_name).
-spec gen_enum_encoder(binary(), list(binary())) -> binary().
gen_enum_encoder(Enum_type_name, Enum) ->
_pipe = get_encoder_name(Enum_type_name),
internal_fn(
_pipe,
<<<<"value__: "/utf8, Enum_type_name/binary>>/binary, "\n"/utf8>>,
<<<<"case value__{\n"/utf8,
(gleam@list:fold(
Enum,
<<""/utf8>>,
fun(Acc, Current) ->
<<<<<<<<<<Acc/binary, Enum_type_name/binary>>/binary,
(justin:pascal_case(Current))/binary>>/binary,
" -> \""/utf8>>/binary,
Current/binary>>/binary,
"\"\n"/utf8>>
end
))/binary>>/binary,
"}\n|> json.string()\n"/utf8>>
).
-spec gen_enum_decoder(binary(), list(binary())) -> binary().
gen_enum_decoder(Enum_type_name, Enum) ->
_pipe = get_decoder_name(Enum_type_name),
internal_fn(
_pipe,
<<"value__: dynamic.Dynamic\n"/utf8>>,
<<<<<<"case dynamic.string(value__){\n"/utf8,
(gleam@list:fold(
Enum,
<<""/utf8>>,
fun(Acc, Current) ->
<<<<<<<<<<<<Acc/binary, "Ok(\""/utf8>>/binary,
Current/binary>>/binary,
"\") -> Ok("/utf8>>/binary,
Enum_type_name/binary>>/binary,
(justin:pascal_case(Current))/binary>>/binary,
")\n"/utf8>>
end
))/binary>>/binary,
"Error(error) -> Error(error)\nOk(other) -> Error([dynamic.DecodeError(expected: \"valid enum property\", found:other, path: [\"enum decoder\"])])"/utf8>>/binary,
"}\n"/utf8>>
).
-spec gen_attribute(binary(), binary(), type(), boolean(), binary()) -> {binary(),
binary()}.
gen_attribute(Root_name, Name, T, Optional, Comment) ->
{Attr_value, Enum_def} = case T of
{primitive_type, Type_name} ->
{to_gleam_primitive(Type_name), <<""/utf8>>};
{array_type, {primitive_item, Type_name@1}} ->
{<<<<"List("/utf8, (to_gleam_primitive(Type_name@1))/binary>>/binary,
")"/utf8>>,
<<""/utf8>>};
{array_type, {reference_item, Ref_target}} ->
{<<<<"List("/utf8, (resolve_ref(Ref_target))/binary>>/binary,
")"/utf8>>,
<<""/utf8>>};
{ref_type, Ref_target@1} ->
{resolve_ref(Ref_target@1), <<""/utf8>>};
{enum_type, Enum} ->
Enum_type_name = <<(justin:pascal_case(Root_name))/binary,
(justin:pascal_case(Name))/binary>>,
Enum_type_def = <<<<<<<<<<<<<<<<<<(gen_attached_comment(
<<<<<<<<"This type is not part of the protocol spec, it has been generated dynamically
to represent the possible values of the enum property `"/utf8,
Name/binary>>/binary,
"` of `"/utf8>>/binary,
Root_name/binary>>/binary,
"`"/utf8>>
))/binary,
"\npub type "/utf8>>/binary,
Enum_type_name/binary>>/binary,
"{"/utf8>>/binary,
(begin
_pipe = Enum,
_pipe@1 = gleam@list:map(
_pipe,
fun(Item) ->
<<<<Enum_type_name/binary,
(justin:pascal_case(
Item
))/binary>>/binary,
"\n"/utf8>>
end
),
gleam@string:join(_pipe@1, <<""/utf8>>)
end)/binary>>/binary,
"}\n"/utf8>>/binary,
(gen_enum_encoder(Enum_type_name, Enum))/binary>>/binary,
"\n"/utf8>>/binary,
(gen_enum_decoder(Enum_type_name, Enum))/binary>>/binary,
"\n"/utf8>>,
{Enum_type_name, Enum_type_def};
{object_type, none} ->
{<<"dict.Dict(String,String)"/utf8>>, <<""/utf8>>};
{object_type, {some, _}} ->
gleam@io:debug({Root_name, Name, T, Optional}),
erlang:error(#{gleam_error => panic,
message => <<"Tried to generate an attribute from unsupported type (Object with properties)"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_attribute"/utf8>>,
line => 1057})
end,
Attached_comment = case Comment of
<<""/utf8>> ->
<<""/utf8>>;
Value ->
<<"\n"/utf8,
(gen_attached_comment(<<Value/binary, " "/utf8>>))/binary>>
end,
Attr_value@1 = case Optional of
true ->
<<<<"option.Option("/utf8, Attr_value/binary>>/binary, ")"/utf8>>;
false ->
Attr_value
end,
{<<<<<<<<Attached_comment/binary, (safe_snake_case(Name))/binary>>/binary,
": "/utf8>>/binary,
Attr_value@1/binary>>/binary,
",\n"/utf8>>,
Enum_def}.
-spec gen_type_body(binary(), type()) -> {binary(), binary()}.
gen_type_body(Name, T) ->
case T of
{primitive_type, Type_name} ->
{<<<<<<Name/binary, "("/utf8>>/binary,
(to_gleam_primitive(Type_name))/binary>>/binary,
")\n"/utf8>>,
<<""/utf8>>};
{enum_type, Enum} ->
{begin
_pipe = Enum,
_pipe@1 = gleam@list:map(
_pipe,
fun(Item) ->
<<<<Name/binary, (justin:pascal_case(Item))/binary>>/binary,
"\n"/utf8>>
end
),
gleam@string:join(_pipe@1, <<""/utf8>>)
end,
<<""/utf8>>};
{array_type, {primitive_item, Type_name@1}} ->
{<<<<<<Name/binary, "(List("/utf8>>/binary,
(to_gleam_primitive(Type_name@1))/binary>>/binary,
"))\n"/utf8>>,
<<""/utf8>>};
{array_type, {reference_item, Ref_target}} ->
{<<<<<<Name/binary, "(List("/utf8>>/binary,
(resolve_ref(Ref_target))/binary>>/binary,
"))\n"/utf8>>,
<<""/utf8>>};
{ref_type, Ref_target@1} ->
{<<<<<<Name/binary, "("/utf8>>/binary,
(resolve_ref(Ref_target@1))/binary>>/binary,
")\n"/utf8>>,
<<""/utf8>>};
{object_type, {some, Properties}} ->
Attribute_gen_results = begin
_pipe@2 = Properties,
gleam@list:map(
_pipe@2,
fun(Prop) ->
gen_attribute(
Name,
erlang:element(2, Prop),
erlang:element(7, Prop),
is(erlang:element(6, Prop)),
gleam@option:unwrap(
erlang:element(3, Prop),
<<""/utf8>>
)
)
end
)
end,
{Attributes, Enum_defs} = gleam@list:unzip(Attribute_gen_results),
{<<<<<<Name/binary, " (\n"/utf8>>/binary,
(gleam@string:join(Attributes, <<""/utf8>>))/binary>>/binary,
")\n"/utf8>>,
gleam@string:join(Enum_defs, <<""/utf8>>)};
{object_type, none} ->
{<<Name/binary, "(dict.Dict(String,String))\n"/utf8>>, <<""/utf8>>}
end.
-spec gen_type_def_body(
gleam@string_builder:string_builder(),
type_definition()
) -> gleam@string_builder:string_builder().
gen_type_def_body(Builder, T) ->
{Body, Appendage} = gen_type_body(
erlang:element(2, T),
erlang:element(6, T)
),
_pipe = Builder,
_pipe@1 = append_optional(
_pipe,
erlang:element(3, T),
fun gen_attached_comment/1
),
_pipe@2 = gleam@string_builder:append(_pipe@1, <<"pub type "/utf8>>),
_pipe@3 = gleam@string_builder:append(_pipe@2, erlang:element(2, T)),
_pipe@4 = gleam@string_builder:append(_pipe@3, <<"{\n"/utf8>>),
_pipe@5 = gleam@string_builder:append(_pipe@4, Body),
_pipe@6 = gleam@string_builder:append(_pipe@5, <<"}\n\n"/utf8>>),
_pipe@7 = gleam@string_builder:append(_pipe@6, Appendage),
gleam@string_builder:append(_pipe@7, <<"\n"/utf8>>).
-spec gen_property_encoder(binary(), binary(), binary(), type()) -> binary().
gen_property_encoder(Root_name, Attribute_name, Value_name, Value_type) ->
case Value_type of
{primitive_type, <<"any"/utf8>>} ->
<<"// dynamic values cannot be encoded!\n json.null()\n"/utf8>>;
{primitive_type, Type_name} ->
<<<<<<<<"json."/utf8,
(to_gleam_primitive_function(Type_name))/binary>>/binary,
"("/utf8>>/binary,
Value_name/binary>>/binary,
")"/utf8>>;
{array_type, {primitive_item, <<"any"/utf8>>}} ->
<<"// dynamic values cannot be encoded!\n json.null()\n"/utf8>>;
{array_type, {primitive_item, Type_name@1}} ->
<<<<<<<<"json.array("/utf8, Value_name/binary>>/binary,
", of: json."/utf8>>/binary,
(to_gleam_primitive_function(Type_name@1))/binary>>/binary,
")"/utf8>>;
{array_type, {reference_item, Ref_target}} ->
<<<<<<<<"json.array("/utf8, Value_name/binary>>/binary,
", of: "/utf8>>/binary,
(get_encoder_name(Ref_target))/binary>>/binary,
")"/utf8>>;
{enum_type, _} ->
<<<<<<(get_encoder_name(
<<(justin:pascal_case(Root_name))/binary,
(justin:pascal_case(Attribute_name))/binary>>
))/binary,
"("/utf8>>/binary,
Value_name/binary>>/binary,
")"/utf8>>;
{ref_type, Ref_target@1} ->
<<<<<<(get_encoder_name(Ref_target@1))/binary, "("/utf8>>/binary,
Value_name/binary>>/binary,
")"/utf8>>;
{object_type, {some, _}} ->
gleam@io:debug({Root_name, Attribute_name, Value_type}),
erlang:error(#{gleam_error => panic,
message => <<"Attempting nested object encoder generation"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_property_encoder"/utf8>>,
line => 1266});
{object_type, none} ->
<<<<"dict.to_list("/utf8, Value_name/binary>>/binary,
")
|> list.map(fn(i) { #(i.0, json.string(i.1)) })
|> json.object"/utf8>>
end.
-spec gen_object_property_encoder(binary(), property_definition(), binary()) -> {binary(),
binary()}.
gen_object_property_encoder(Root_name, Prop_def, Value_accessor) ->
Attribute_name = safe_snake_case(erlang:element(2, Prop_def)),
case erlang:element(6, Prop_def) of
{some, true} ->
Encoder = gen_property_encoder(
Root_name,
Attribute_name,
<<"inner_value__"/utf8>>,
erlang:element(7, Prop_def)
),
Wrapped_encoder = <<<<<<<<"#(\""/utf8,
(erlang:element(2, Prop_def))/binary>>/binary,
"\", "/utf8>>/binary,
Encoder/binary>>/binary,
")"/utf8>>,
{<<""/utf8>>,
<<<<<<<<<<"|> utils.add_optional("/utf8, Value_accessor/binary>>/binary,
Attribute_name/binary>>/binary,
", fn(inner_value__){"/utf8>>/binary,
Wrapped_encoder/binary>>/binary,
"})\n"/utf8>>};
_ ->
Encoder@1 = gen_property_encoder(
Root_name,
Attribute_name,
<<Value_accessor/binary, Attribute_name/binary>>,
erlang:element(7, Prop_def)
),
{<<<<<<<<"#(\""/utf8, (erlang:element(2, Prop_def))/binary>>/binary,
"\", "/utf8>>/binary,
Encoder@1/binary>>/binary,
"),\n"/utf8>>,
<<""/utf8>>}
end.
-spec gen_type_def_encoder(type_definition()) -> binary().
gen_type_def_encoder(Type_def) ->
case erlang:element(6, Type_def) of
{primitive_type, Primitive_type} ->
_pipe = get_encoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe,
<<<<"value__: "/utf8, (erlang:element(2, Type_def))/binary>>/binary,
"\n"/utf8>>,
<<<<<<<<"case value__{\n"/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
"(inner_value__) -> json."/utf8>>/binary,
(to_gleam_primitive_function(Primitive_type))/binary>>/binary,
"(inner_value__)\n}"/utf8>>
);
{enum_type, Enum} ->
gen_enum_encoder(erlang:element(2, Type_def), Enum);
{array_type, {primitive_item, Primitive_type@1}} ->
_pipe@1 = get_encoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@1,
<<<<"value__: "/utf8, (erlang:element(2, Type_def))/binary>>/binary,
"\n"/utf8>>,
<<<<<<<<"case value__{\n"/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
"(inner_value__) -> json.array(inner_value__, of: json."/utf8>>/binary,
(to_gleam_primitive_function(Primitive_type@1))/binary>>/binary,
")\n}"/utf8>>
);
{object_type, {some, Properties}} ->
{Property_encoders, Appendices} = begin
_pipe@2 = gleam@list:map(
Properties,
fun(P) ->
gen_object_property_encoder(
erlang:element(2, Type_def),
P,
<<"value__."/utf8>>
)
end
),
gleam@list:unzip(_pipe@2)
end,
_pipe@3 = get_encoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@3,
<<<<"value__: "/utf8, (erlang:element(2, Type_def))/binary>>/binary,
"\n"/utf8>>,
<<<<<<<<"json.object([\n"/utf8,
(gleam@string:join(
Property_encoders,
<<""/utf8>>
))/binary>>/binary,
"]"/utf8>>/binary,
(gleam@string:join(Appendices, <<""/utf8>>))/binary>>/binary,
")"/utf8>>
);
{object_type, none} ->
_pipe@4 = get_encoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@4,
<<<<"value__: "/utf8, (erlang:element(2, Type_def))/binary>>/binary,
"\n"/utf8>>,
<<<<"case value__{\n"/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
"(inner_value__) ->
dict.to_list(inner_value__)
|> list.map(fn(i) { #(i.0, json.string(i.1)) })
|> json.object
} "/utf8>>
);
{array_type, {reference_item, _}} ->
gleam@io:debug(Type_def),
erlang:error(#{gleam_error => panic,
message => <<"tried to generate type def encoder for an array of refs"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_type_def_encoder"/utf8>>,
line => 1388});
{ref_type, _} ->
gleam@io:debug(Type_def),
erlang:error(#{gleam_error => panic,
message => <<"tried to generate type def encoder for a type which is a ref!"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_type_def_encoder"/utf8>>,
line => 1392})
end.
-spec gen_property_decoder(binary(), binary(), type()) -> binary().
gen_property_decoder(Root_name, Attribute_name, Value_type) ->
case Value_type of
{primitive_type, Type_name} ->
<<"dynamic."/utf8, (to_gleam_primitive_function(Type_name))/binary>>;
{array_type, {primitive_item, Type_name@1}} ->
<<<<"dynamic.list(dynamic."/utf8,
(to_gleam_primitive_function(Type_name@1))/binary>>/binary,
")"/utf8>>;
{array_type, {reference_item, Ref_target}} ->
<<<<"dynamic.list("/utf8, (get_decoder_name(Ref_target))/binary>>/binary,
")"/utf8>>;
{enum_type, _} ->
get_decoder_name(
<<(justin:pascal_case(Root_name))/binary,
(justin:pascal_case(Attribute_name))/binary>>
);
{ref_type, Ref_target@1} ->
get_decoder_name(Ref_target@1);
{object_type, {some, _}} ->
gleam@io:debug({Root_name, Attribute_name, Value_type}),
erlang:error(#{gleam_error => panic,
message => <<"Attempting nested object decoder generation"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_property_decoder"/utf8>>,
line => 1420});
{object_type, none} ->
<<"dynamic.dict(dynamic.string, dynamic.string)"/utf8>>
end.
-spec gen_object_property_decoder(binary(), property_definition()) -> binary().
gen_object_property_decoder(Root_name, Prop_def) ->
Base_decoder = case erlang:element(6, Prop_def) of
{some, true} ->
<<"optional_field"/utf8>>;
_ ->
<<"field"/utf8>>
end,
<<<<<<<<<<<<<<<<"use "/utf8,
(safe_snake_case(
erlang:element(2, Prop_def)
))/binary>>/binary,
" <- result.try(dynamic."/utf8>>/binary,
Base_decoder/binary>>/binary,
"(\""/utf8>>/binary,
(erlang:element(2, Prop_def))/binary>>/binary,
"\","/utf8>>/binary,
(gen_property_decoder(
Root_name,
erlang:element(2, Prop_def),
erlang:element(7, Prop_def)
))/binary>>/binary,
")(value__))\n"/utf8>>.
-spec gen_type_def_decoder(type_definition()) -> binary().
gen_type_def_decoder(Type_def) ->
case erlang:element(6, Type_def) of
{primitive_type, Primitive_type} ->
_pipe = get_decoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe,
<<"value__: dynamic.Dynamic"/utf8>>,
<<<<<<<<"value__ |> dynamic.decode1("/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
", dynamic."/utf8>>/binary,
(to_gleam_primitive_function(Primitive_type))/binary>>/binary,
")"/utf8>>
);
{enum_type, Enum} ->
gen_enum_decoder(erlang:element(2, Type_def), Enum);
{array_type, {primitive_item, Primitive_type@1}} ->
_pipe@1 = get_decoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@1,
<<"value__: dynamic.Dynamic"/utf8>>,
<<<<<<<<"value__ |> dynamic.decode1("/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
", dynamic.list(dynamic."/utf8>>/binary,
(to_gleam_primitive_function(Primitive_type@1))/binary>>/binary,
"))"/utf8>>
);
{object_type, {some, Properties}} ->
Prop_encoder_lines = begin
_pipe@2 = gleam@list:map(
Properties,
fun(P) ->
gen_object_property_decoder(
erlang:element(2, Type_def),
P
)
end
),
gleam@string:join(_pipe@2, <<""/utf8>>)
end,
Return_statement = <<<<<<<<"Ok("/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
"(\n"/utf8>>/binary,
(begin
_pipe@3 = gleam@list:map(
Properties,
fun(P@1) ->
<<<<<<(safe_snake_case(erlang:element(2, P@1)))/binary,
":"/utf8>>/binary,
(safe_snake_case(erlang:element(2, P@1)))/binary>>/binary,
",\n"/utf8>>
end
),
gleam@string:join(_pipe@3, <<""/utf8>>)
end)/binary>>/binary,
"))"/utf8>>,
_pipe@4 = get_decoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@4,
<<"value__: dynamic.Dynamic"/utf8>>,
<<<<Prop_encoder_lines/binary, "\n"/utf8>>/binary,
Return_statement/binary>>
);
{object_type, none} ->
_pipe@5 = get_decoder_name(erlang:element(2, Type_def)),
internal_fn(
_pipe@5,
<<"value__: dynamic.Dynamic"/utf8>>,
<<<<"value__ |> dynamic.decode1("/utf8,
(erlang:element(2, Type_def))/binary>>/binary,
", dynamic.dict(dynamic.string, dynamic.string))"/utf8>>
);
{array_type, {reference_item, _}} ->
gleam@io:debug(Type_def),
erlang:error(#{gleam_error => panic,
message => <<"tried to generate type def encoder for an array of refs"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_type_def_decoder"/utf8>>,
line => 1515});
{ref_type, _} ->
gleam@io:debug(Type_def),
erlang:error(#{gleam_error => panic,
message => <<"tried to generate type def encoder for a type which is a ref!"/utf8>>,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_type_def_decoder"/utf8>>,
line => 1519})
end.
-spec gen_type_def(gleam@string_builder:string_builder(), type_definition()) -> gleam@string_builder:string_builder().
gen_type_def(Builder, T) ->
_pipe = gen_type_def_body(Builder, T),
_pipe@1 = gleam@string_builder:append(_pipe, gen_type_def_encoder(T)),
gleam@string_builder:append(_pipe@1, gen_type_def_decoder(T)).
-spec gen_type_definitions(domain()) -> gleam@string_builder:string_builder().
gen_type_definitions(Domain) ->
_pipe = gleam@option:unwrap(erlang:element(6, Domain), []),
gleam@list:fold(_pipe, gleam@string_builder:new(), fun gen_type_def/2).
-spec gen_command_return_type(command()) -> gleam@string_builder:string_builder().
gen_command_return_type(Command) ->
Builder = gleam@string_builder:new(),
case erlang:element(7, Command) of
{some, []} ->
Builder;
{some, Return_properties} ->
Return_type_def = {type_definition,
<<(justin:pascal_case(erlang:element(2, Command)))/binary,
"Response"/utf8>>,
{some,
<<<<"This type is not part of the protocol spec, it has been generated dynamically
to represent the response to the command `"/utf8,
(justin:snake_case(erlang:element(2, Command)))/binary>>/binary,
"`"/utf8>>},
none,
none,
{object_type, {some, Return_properties}}},
_pipe = gen_type_def_body(Builder, Return_type_def),
gleam@string_builder:append(
_pipe,
gen_type_def_decoder(Return_type_def)
);
none ->
Builder
end.
-spec gen_event_param_type(event()) -> gleam@string_builder:string_builder().
gen_event_param_type(Event) ->
Builder = gleam@string_builder:new(),
case erlang:element(6, Event) of
{some, []} ->
Builder;
{some, Params} ->
Return_type_def = {type_definition,
<<(justin:pascal_case(erlang:element(2, Event)))/binary,
"Event"/utf8>>,
{some,
<<<<<<"This type was generated to represent the event `"/utf8,
(erlang:element(2, Event))/binary>>/binary,
"`\n"/utf8>>/binary,
(gleam@option:unwrap(
erlang:element(3, Event),
<<""/utf8>>
))/binary>>},
none,
none,
{object_type, {some, Params}}},
_pipe = gen_type_def_body(Builder, Return_type_def),
gleam@string_builder:append(
_pipe,
gen_type_def_decoder(Return_type_def)
);
none ->
Builder
end.
-spec gen_command_parameters(command()) -> {binary(), binary()}.
gen_command_parameters(Command) ->
case erlang:element(6, Command) of
{some, Params} ->
Param_gen_results = gleam@list:map(
Params,
fun(Param) ->
gen_attribute(
erlang:element(2, Command),
erlang:element(2, Param),
erlang:element(7, Param),
is(erlang:element(6, Param)),
<<""/utf8>>
)
end
),
{Param_definitions, Extra_definitions} = gleam@list:unzip(
Param_gen_results
),
Param_definitions@1 = gleam@list:map(
Param_definitions,
fun(D) ->
_assert_subject = gleam@list:pop(
gleam@string:split(D, <<":"/utf8>>),
fun(_) -> true end
),
{ok, {Parameter_name, _}} = case _assert_subject of
{ok, {_, _}} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"gen_command_parameters"/utf8>>,
line => 1596})
end,
<<<<Parameter_name/binary, " "/utf8>>/binary, D/binary>>
end
),
{gleam@string:join(Param_definitions@1, <<""/utf8>>),
gleam@string:join(Extra_definitions, <<""/utf8>>)};
none ->
{<<""/utf8>>, <<""/utf8>>}
end.
-spec gen_command_body(command(), domain()) -> binary().
gen_command_body(Command, Domain) ->
Base_encoder_part = (case erlang:element(6, Command) of
none ->
<<<<<<<<<<"callback__(\""/utf8, (erlang:element(2, Domain))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(2, Command))/binary>>/binary,
"\", option.None,"/utf8>>/binary,
")\n"/utf8>>;
{some, []} ->
<<<<<<<<<<"callback__(\""/utf8, (erlang:element(2, Domain))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(2, Command))/binary>>/binary,
"\", option.None,"/utf8>>/binary,
")\n"/utf8>>;
{some, Properties} ->
{Property_encoders, Appendices} = begin
_pipe = gleam@list:map(
Properties,
fun(P) ->
gen_object_property_encoder(
erlang:element(2, Command),
P,
<<""/utf8>>
)
end
),
gleam@list:unzip(_pipe)
end,
<<<<<<<<<<<<<<<<"callback__(\""/utf8,
(erlang:element(2, Domain))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(2, Command))/binary>>/binary,
"\", option.Some(json.object(["/utf8>>/binary,
(gleam@string:join(Property_encoders, <<""/utf8>>))/binary>>/binary,
"]"/utf8>>/binary,
(gleam@string:join(Appendices, <<""/utf8>>))/binary>>/binary,
")))\n"/utf8>>
end),
{Decoder_part, Final_encoder_part} = (case erlang:element(7, Command) of
none ->
{<<""/utf8>>, <<Base_encoder_part/binary, "\n"/utf8>>};
{some, []} ->
{<<""/utf8>>, <<Base_encoder_part/binary, "\n"/utf8>>};
{some, _} ->
{<<<<<<"\n"/utf8,
(get_decoder_name(
<<(justin:pascal_case(
erlang:element(2, Command)
))/binary,
"Response"/utf8>>
))/binary>>/binary,
"(result__)"/utf8>>/binary,
"\n|> result.replace_error(chrome.ProtocolError)\n"/utf8>>,
<<<<"use result__ <- result.try("/utf8,
Base_encoder_part/binary>>/binary,
")\n"/utf8>>}
end),
<<Final_encoder_part/binary, Decoder_part/binary>>.
-spec gen_property_list_doc(list(property_definition())) -> binary().
gen_property_list_doc(Properties) ->
_pipe = gleam@list:map(
Properties,
fun(Prop) ->
<<<<<<" - `"/utf8,
(safe_snake_case(erlang:element(2, Prop)))/binary>>/binary,
"`"/utf8>>/binary,
((case erlang:element(3, Prop) of
none ->
<<"\n"/utf8>>;
{some, Description} ->
<<<<" : "/utf8, Description/binary>>/binary, "\n"/utf8>>
end))/binary>>
end
),
gleam@string:join(_pipe, <<""/utf8>>).
-spec gen_command_parameter_docs(command()) -> binary().
gen_command_parameter_docs(Command) ->
_pipe = gleam@string_builder:new(),
_pipe@1 = append_optional(
_pipe,
erlang:element(6, Command),
fun(_) -> <<"\nParameters: \n"/utf8>> end
),
_pipe@2 = append_optional(
_pipe@1,
erlang:element(6, Command),
fun(I) -> gen_property_list_doc(I) end
),
_pipe@3 = append_optional(
_pipe@2,
erlang:element(6, Command),
fun(_) -> <<"\nReturns: \n"/utf8>> end
),
_pipe@4 = append_optional(
_pipe@3,
erlang:element(7, Command),
fun(I@1) -> gen_property_list_doc(I@1) end
),
_pipe@5 = gleam@string_builder:to_string(_pipe@4),
gen_attached_comment(_pipe@5).
-spec gen_command_function(command(), domain()) -> gleam@string_builder:string_builder().
gen_command_function(Command, Domain) ->
{Param_definition, Appendage} = gen_command_parameters(Command),
_pipe = gleam@string_builder:new(),
_pipe@1 = gleam@string_builder:append(
_pipe,
gen_attached_comment(
gleam@option:unwrap(
erlang:element(3, Command),
<<"This generated protocol command has no description"/utf8>>
)
)
),
_pipe@2 = gleam@string_builder:append(
_pipe@1,
gen_command_parameter_docs(Command)
),
_pipe@3 = gleam@string_builder:append(_pipe@2, <<"pub fn "/utf8>>),
_pipe@4 = gleam@string_builder:append(
_pipe@3,
safe_snake_case(erlang:element(2, Command))
),
_pipe@5 = gleam@string_builder:append(_pipe@4, <<"(\n"/utf8>>),
_pipe@6 = gleam@string_builder:append(_pipe@5, <<"callback__, \n"/utf8>>),
_pipe@7 = gleam@string_builder:append(_pipe@6, Param_definition),
_pipe@8 = gleam@string_builder:append(_pipe@7, <<"){\n"/utf8>>),
_pipe@9 = gleam@string_builder:append(
_pipe@8,
gen_command_body(Command, Domain)
),
_pipe@10 = gleam@string_builder:append(_pipe@9, <<"\n}\n"/utf8>>),
_pipe@11 = gleam@string_builder:append(_pipe@10, Appendage),
gleam@string_builder:append(_pipe@11, <<"\n"/utf8>>).
-spec gen_commands(domain()) -> gleam@string_builder:string_builder().
gen_commands(Domain) ->
_pipe = gleam@string_builder:new(),
_pipe@1 = gleam@string_builder:append_builder(
_pipe,
gleam@string_builder:concat(
gleam@list:map(
erlang:element(7, Domain),
fun gen_command_return_type/1
)
)
),
gleam@string_builder:append_builder(
_pipe@1,
gleam@string_builder:concat(
gleam@list:map(
erlang:element(7, Domain),
fun(C) -> gen_command_function(C, Domain) end
)
)
).
-spec remove_import_if_unused(
gleam@string_builder:string_builder(),
binary(),
binary()
) -> gleam@string_builder:string_builder().
remove_import_if_unused(Builder, Full_string, Import_name) ->
_assert_subject = begin
_pipe = gleam@string:split(Import_name, <<"/"/utf8>>),
gleam@list:last(_pipe)
end,
{ok, Import_short_name} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"remove_import_if_unused"/utf8>>,
line => 1725})
end,
_assert_subject@1 = gleam@regex:from_string(
<<Import_short_name/binary, "\\.\\S+"/utf8>>
),
{ok, Matcher} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"remove_import_if_unused"/utf8>>,
line => 1728})
end,
case gleam@regex:check(Matcher, Full_string) of
false ->
gleam_stdlib:string_replace(
Builder,
<<<<"import "/utf8, Import_name/binary>>/binary, "\n"/utf8>>,
<<""/utf8>>
);
true ->
Builder
end.
-spec remove_unused_imports(gleam@string_builder:string_builder()) -> gleam@string_builder:string_builder().
remove_unused_imports(Builder) ->
Full_string = gleam@string_builder:to_string(Builder),
_pipe = gleam@string:split(Full_string, <<"\n"/utf8>>),
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Line) -> case gleam@string:starts_with(Line, <<"import "/utf8>>) of
true ->
{ok, gleam@string:drop_left(Line, 7)};
false ->
{error, nil}
end end
),
gleam@list:fold(
_pipe@1,
Builder,
fun(Acc, Current) ->
remove_import_if_unused(Acc, Full_string, Current)
end
).
-spec gen_domain_module(protocol(), domain()) -> binary().
gen_domain_module(Protocol, Domain) ->
_pipe = gleam@string_builder:new(),
_pipe@1 = gleam@string_builder:append(_pipe, gen_preamble(Protocol)),
_pipe@2 = gleam@string_builder:append_builder(
_pipe@1,
gen_domain_module_header(Protocol, Domain)
),
_pipe@3 = gleam@string_builder:append_builder(
_pipe@2,
gen_type_definitions(Domain)
),
_pipe@4 = gleam@string_builder:append_builder(_pipe@3, gen_commands(Domain)),
_pipe@5 = remove_unused_imports(_pipe@4),
gleam@string_builder:to_string(_pipe@5).
-spec gen_root_module(protocol()) -> binary().
gen_root_module(Protocol) ->
_pipe = gleam@string_builder:new(),
_pipe@1 = gleam@string_builder:append(_pipe, gen_preamble(Protocol)),
_pipe@2 = gleam@string_builder:append(
_pipe@1,
<<"//// For reference: [See the DevTools Protocol API Docs](https://chromedevtools.github.io/devtools-protocol/"/utf8>>
),
_pipe@3 = gleam@string_builder:append(
_pipe@2,
erlang:element(2, erlang:element(2, Protocol))
),
_pipe@4 = gleam@string_builder:append(_pipe@3, <<"-"/utf8>>),
_pipe@5 = gleam@string_builder:append(
_pipe@4,
erlang:element(3, erlang:element(2, Protocol))
),
_pipe@6 = gleam@string_builder:append(_pipe@5, <<"/"/utf8>>),
_pipe@7 = gleam@string_builder:append(_pipe@6, <<")\n\n"/utf8>>),
_pipe@8 = gleam@string_builder:append(
_pipe@7,
gen_module_comment(
<<"
This is the protocol definition entrypoint, it contains an overview of the protocol structure,
and a function to retrieve the version of the protocol used to generate the current bindings.
The protocol version is also displayed in the box above, which appears on every generated module.
## ⚠️ Really Important Notes
1) It's best never to work with the DOM domain for automation,
[an explanation of why can be found here](https://github.com/puppeteer/puppeteer/pull/71#issuecomment-314599749).
Instead, to automate DOM interaction, JavaScript can be injected using the Runtime domain.
2) Unfortunately, I haven't found a good way to map dynamic properties to gleam attributes bidirectionally.
**This means all dynamic values you supply to commands will be silently dropped**!
It's important to realize this to avoid confusion, for example in `runtime.call_function_on`
you may want to supply arguments which can be any value, but it won't work.
The only path to do that as far as I can tell, is write the protocol call logic yourself,
perhaps taking the codegen code as a basis.
Check the `call_custom_function_on` function from `chrobot` which does this for the mentioned function
## Structure
Each domain in the protocol is represented as a module under `protocol/`.
In general, the bindings are generated through codegen, directly from the JSON protocol schema [published here](https://github.com/ChromeDevTools/devtools-protocol),
however there are some little adjustments that needed to be made, to make the protocol schema usable, mainly due to
what I believe are minor bugs in the protocol.
To see these changes, check the `apply_protocol_patches` function in `chrobot/internal/generate_bindings`.
Domains may depend on the types of other domains, these dependencies are mirrored in the generated bindings where possible.
In some case, type references to other modules have been replaced by the respective inner type, because the references would
create a circular dependency.
## Types
The generated bindings include a mirror of the type defitions of each type in the protocol spec,
alongside with an `encode__` function to encode the type into JSON in order to send it to the browser
and a `decode__` function in order to decode the type out of a payload sent from the browser. Encoders and
decoders are marked internal and should be used through command functions which are described below.
Notes:
- Some object properties in the protocol have the type `any`, in this case the value is considered as dynamic
by decoders, and encoders will not encode it, setting it to `null` instead in the payload
- Object types that don't specify any properties are treated as a `Dict(String,String)`
Additional type definitions and encoders / decoders are generated,
for any enumerable property in the protocol, as well as the return values of commands.
These special type definitions are marked with a comment to indicate
the fact that they are not part of the protocol spec, but rather generated dynamically to support the bindings.
## Commands
A function is generated for each command, named after the command (in snake case).
The function handles both encoding the parameters to sent to the browser via the protocol, and decoding the response.
A `ProtocolError` error is returned if the decoding fails, this would mean there is a bug in the protocol
or the generated bindings.
The first parameter to the command function is always a `callback` of the form
```gleam
fn(method: String, parameters: Option(Json)) -> Result(Dynamic, RequestError)
```
By using this callback you can take advantage of the generated protocol encoders/decoders
while also passing in your browser subject to direct the command to, and passing along additional
arguments, like the `sessionId` which is required for some operations.
## Events
Events are not implemented yet!
"/utf8>>
)
),
_pipe@9 = gleam@string_builder:append(
_pipe@8,
<<<<"const version_major = \""/utf8,
(erlang:element(2, erlang:element(2, Protocol)))/binary>>/binary,
"\"\n"/utf8>>
),
_pipe@10 = gleam@string_builder:append(
_pipe@9,
<<<<"const version_minor = \""/utf8,
(erlang:element(3, erlang:element(2, Protocol)))/binary>>/binary,
"\"\n\n"/utf8>>
),
_pipe@11 = gleam@string_builder:append(
_pipe@10,
gen_attached_comment(
<<"Get the protocol version as a tuple of major and minor version"/utf8>>
)
),
_pipe@12 = gleam@string_builder:append(
_pipe@11,
<<"pub fn version() { #(version_major, version_minor)}\n"/utf8>>
),
gleam@string_builder:to_string(_pipe@12).
-spec get_stable_propdef(property_definition(), boolean(), boolean()) -> {ok,
property_definition()} |
{error, nil}.
get_stable_propdef(Propdef, Allow_experimental, Allow_deprecated) ->
case is_allowed(erlang:element(4, Propdef), Allow_experimental) andalso is_allowed(
erlang:element(5, Propdef),
Allow_deprecated
) of
true ->
{ok,
{property_definition,
erlang:element(2, Propdef),
erlang:element(3, Propdef),
erlang:element(4, Propdef),
erlang:element(5, Propdef),
erlang:element(6, Propdef),
get_stable_inner_type(
erlang:element(7, Propdef),
Allow_experimental,
Allow_deprecated
)}};
false ->
{error, nil}
end.
-spec get_stable_inner_type(type(), boolean(), boolean()) -> type().
get_stable_inner_type(Inner_type, Allow_experimental, Allow_deprecated) ->
case Inner_type of
{object_type, {some, Property_definitions}} ->
{object_type,
({some,
gleam@list:filter_map(
Property_definitions,
fun(Inner_propdef) ->
get_stable_propdef(
Inner_propdef,
Allow_experimental,
Allow_deprecated
)
end
)})};
_ ->
Inner_type
end.
-spec get_stable_event(event(), boolean(), boolean()) -> {ok, event()} |
{error, nil}.
get_stable_event(Event, Allow_experimental, Allow_deprecated) ->
case is_allowed(erlang:element(4, Event), Allow_experimental) andalso is_allowed(
erlang:element(5, Event),
Allow_deprecated
) of
true ->
{ok,
{event,
erlang:element(2, Event),
erlang:element(3, Event),
erlang:element(4, Event),
erlang:element(5, Event),
(case erlang:element(6, Event) of
{some, Params} ->
{some,
gleam@list:filter_map(
Params,
fun(Param) ->
get_stable_propdef(
Param,
Allow_experimental,
Allow_deprecated
)
end
)};
none ->
none
end)}};
false ->
{error, nil}
end.
-spec get_stable_command(command(), boolean(), boolean()) -> {ok, command()} |
{error, nil}.
get_stable_command(Command, Allow_experimental, Allow_deprecated) ->
case is_allowed(erlang:element(4, Command), Allow_experimental) andalso is_allowed(
erlang:element(5, Command),
Allow_deprecated
) of
true ->
{ok,
{command,
erlang:element(2, Command),
erlang:element(3, Command),
erlang:element(4, Command),
erlang:element(5, Command),
(case erlang:element(6, Command) of
{some, Params} ->
{some,
gleam@list:filter_map(
Params,
fun(Param) ->
get_stable_propdef(
Param,
Allow_experimental,
Allow_deprecated
)
end
)};
none ->
none
end),
(case erlang:element(7, Command) of
{some, Returns} ->
{some,
gleam@list:filter_map(
Returns,
fun(Param@1) ->
get_stable_propdef(
Param@1,
Allow_experimental,
Allow_deprecated
)
end
)};
none ->
none
end)}};
false ->
{error, nil}
end.
-spec get_stable_type(type_definition(), boolean(), boolean()) -> {ok,
type_definition()} |
{error, nil}.
get_stable_type(Param_type, Allow_experimental, Allow_deprecated) ->
case is_allowed(erlang:element(4, Param_type), Allow_experimental) andalso is_allowed(
erlang:element(5, Param_type),
Allow_deprecated
) of
true ->
{ok,
{type_definition,
erlang:element(2, Param_type),
erlang:element(3, Param_type),
erlang:element(4, Param_type),
erlang:element(5, Param_type),
get_stable_inner_type(
erlang:element(6, Param_type),
Allow_experimental,
Allow_deprecated
)}};
false ->
{error, nil}
end.
-spec get_stable_protocol(protocol(), boolean(), boolean()) -> protocol().
get_stable_protocol(Protocol, Allow_experimental, Allow_deprecated) ->
{protocol,
erlang:element(2, Protocol),
gleam@list:filter_map(
erlang:element(3, Protocol),
fun(Domain) ->
case is_allowed(erlang:element(3, Domain), Allow_experimental)
andalso is_allowed(erlang:element(4, Domain), Allow_deprecated) of
true ->
{ok,
{domain,
erlang:element(2, Domain),
erlang:element(3, Domain),
erlang:element(4, Domain),
erlang:element(5, Domain),
(case erlang:element(6, Domain) of
{some, Types} ->
{some,
gleam@list:filter_map(
Types,
fun(Type_item) ->
get_stable_type(
Type_item,
Allow_experimental,
Allow_deprecated
)
end
)};
none ->
none
end),
gleam@list:filter_map(
erlang:element(7, Domain),
fun(Cmd) ->
get_stable_command(
Cmd,
Allow_experimental,
Allow_deprecated
)
end
),
(case erlang:element(8, Domain) of
{some, Events} ->
{some,
gleam@list:filter_map(
Events,
fun(E) ->
get_stable_event(
E,
Allow_experimental,
Allow_deprecated
)
end
)};
none ->
none
end),
erlang:element(9, Domain)}};
false ->
{error, nil}
end
end
)}.
-spec apply_propdef_patches(property_definition(), domain()) -> property_definition().
apply_propdef_patches(Propdef, Domain) ->
{property_definition,
erlang:element(2, Propdef),
erlang:element(3, Propdef),
erlang:element(4, Propdef),
erlang:element(5, Propdef),
erlang:element(6, Propdef),
apply_type_patches(erlang:element(7, Propdef), Domain)}.
-spec apply_type_patches(type(), domain()) -> type().
apply_type_patches(Inner_type, Domain) ->
case Inner_type of
{ref_type, <<"Page.FrameId"/utf8>>} when (erlang:element(2, Domain) =:= <<"DOM"/utf8>>) orelse (erlang:element(
2,
Domain
) =:= <<"Accessibility"/utf8>>) ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Replacing instance of 'Page.FrameId' with its primitive type, because the domain is not a depencency of "/utf8,
(erlang:element(2, Domain))/binary>>
),
{primitive_type, <<"string"/utf8>>};
{ref_type, <<"Network.TimeSinceEpoch"/utf8>>} when (erlang:element(
2,
Domain
) =:= <<"Security"/utf8>>) orelse (erlang:element(2, Domain) =:= <<"Accessibility"/utf8>>) ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Replacing instance of 'Network.TimeSinceEpoch' with its primitive type, because the domain is not a depencency of "/utf8,
(erlang:element(2, Domain))/binary>>
),
{primitive_type, <<"number"/utf8>>};
{ref_type, <<"Browser.BrowserContextID"/utf8>>} ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Replacing instance of 'BrowserContextID' with its primitive type, because it is an experimental property referenced by a stable one"/utf8>>
),
{primitive_type, <<"string"/utf8>>};
{ref_type, <<"BrowserContextID"/utf8>>} ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Replacing instance of 'BrowserContextID' with its primitive type, because it is an experimental property referenced by a stable one"/utf8>>
),
{primitive_type, <<"string"/utf8>>};
{array_type, {reference_item, <<"Browser.BrowserContextID"/utf8>>}} when erlang:element(
2,
Domain
) =:= <<"Target"/utf8>> ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Replacing instance of 'BrowserContextID' (array) with its primitive type, because it is an experimental property referenced by a stable one"/utf8>>
),
{array_type, {primitive_item, <<"string"/utf8>>}};
{object_type, {some, Property_definitions}} ->
{object_type,
({some,
gleam@list:map(
Property_definitions,
fun(Inner_propdef) ->
apply_propdef_patches(Inner_propdef, Domain)
end
)})};
{ref_type, Ref_target} ->
Parts = gleam@string:split(Ref_target, <<"."/utf8>>),
case Parts of
[Ref_domain, Ref_name] when Ref_domain =:= erlang:element(
2,
Domain
) ->
gleam@io:println(
<<<<<<<<"[PATCHING PROTOCOL] Modifying ref target '"/utf8,
Ref_target/binary>>/binary,
"'' because it is in the '"/utf8>>/binary,
(erlang:element(2, Domain))/binary>>/binary,
"'' domain and does not need the domain qualifier."/utf8>>
),
{ref_type, Ref_name};
_ ->
Inner_type
end;
_ ->
Inner_type
end.
-spec apply_protocol_patches(protocol()) -> protocol().
apply_protocol_patches(Protocol) ->
{protocol,
erlang:element(2, Protocol),
gleam@list:map(
erlang:element(3, Protocol),
fun(Domain) ->
{domain,
erlang:element(2, Domain),
(case erlang:element(2, Domain) of
<<"Tracing"/utf8>> ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Marking 'Tracing' domain as experimental because it is not included in stable 1.3"/utf8>>
),
{some, true};
_ ->
erlang:element(3, Domain)
end),
erlang:element(4, Domain),
(case {erlang:element(2, Domain), erlang:element(5, Domain)} of
{<<"IO"/utf8>>, none} ->
gleam@io:println(
<<"[PATCHING PROTOCOL] Adding 'Runtime' dependency to IO domain"/utf8>>
),
{some, [<<"Runtime"/utf8>>]};
{_, _} ->
erlang:element(5, Domain)
end),
(case erlang:element(6, Domain) of
{some, Types} ->
{some,
gleam@list:map(
Types,
fun(Type_item) ->
{type_definition,
erlang:element(2, Type_item),
erlang:element(3, Type_item),
erlang:element(4, Type_item),
erlang:element(5, Type_item),
apply_type_patches(
erlang:element(6, Type_item),
Domain
)}
end
)};
none ->
none
end),
gleam@list:map(
erlang:element(7, Domain),
fun(Command) ->
{command,
erlang:element(2, Command),
erlang:element(3, Command),
erlang:element(4, Command),
erlang:element(5, Command),
(case erlang:element(6, Command) of
{some, Properties} ->
{some,
gleam@list:map(
Properties,
fun(P) ->
apply_propdef_patches(
P,
Domain
)
end
)};
none ->
none
end),
(case erlang:element(7, Command) of
{some, Properties@1} ->
{some,
gleam@list:map(
Properties@1,
fun(P@1) ->
apply_propdef_patches(
P@1,
Domain
)
end
)};
none ->
none
end)}
end
),
erlang:element(8, Domain),
erlang:element(9, Domain)}
end
)}.
-spec parse_type(gleam@dynamic:dynamic_()) -> {ok, type()} |
{error, list(gleam@dynamic:decode_error())}.
parse_type(Input) ->
Primitive_type_decoder = gleam@dynamic:decode1(
fun(Field@0) -> {primitive_type, Field@0} end,
gleam@dynamic:field(<<"type"/utf8>>, fun gleam@dynamic:string/1)
),
Enum_type_decoder = gleam@dynamic:decode1(
fun(Field@0) -> {enum_type, Field@0} end,
gleam@dynamic:field(
<<"enum"/utf8>>,
gleam@dynamic:list(fun gleam@dynamic:string/1)
)
),
Object_type_decoder = gleam@dynamic:decode1(
fun(Field@0) -> {object_type, Field@0} end,
gleam@dynamic:optional_field(
<<"properties"/utf8>>,
gleam@dynamic:list(fun parse_property_def/1)
)
),
Array_type_decoder = gleam@dynamic:decode1(
fun(Field@0) -> {array_type, Field@0} end,
gleam@dynamic:field(<<"items"/utf8>>, fun parse_array_type_item/1)
),
Ref_type_decoder = gleam@dynamic:decode1(
fun(Field@0) -> {ref_type, Field@0} end,
gleam@dynamic:field(<<"$ref"/utf8>>, fun gleam@dynamic:string/1)
),
Type_name = (gleam@dynamic:field(
<<"type"/utf8>>,
fun gleam@dynamic:string/1
))(Input),
gleam@result:'try'(
(gleam@dynamic:optional_field(
<<"enum"/utf8>>,
gleam@dynamic:list(fun gleam@dynamic:string/1)
))(Input),
fun(Enum) ->
gleam@result:'try'(
(gleam@dynamic:optional_field(
<<"$ref"/utf8>>,
fun gleam@dynamic:string/1
))(Input),
fun(Ref) -> case {Type_name, Enum, Ref} of
{{ok, <<"string"/utf8>>}, {some, _}, _} ->
Enum_type_decoder(Input);
{{ok, <<"string"/utf8>>}, none, _} ->
Primitive_type_decoder(Input);
{{ok, <<"boolean"/utf8>>}, _, _} ->
Primitive_type_decoder(Input);
{{ok, <<"number"/utf8>>}, _, _} ->
Primitive_type_decoder(Input);
{{ok, <<"any"/utf8>>}, _, _} ->
Primitive_type_decoder(Input);
{{ok, <<"integer"/utf8>>}, _, _} ->
Primitive_type_decoder(Input);
{{ok, <<"object"/utf8>>}, _, _} ->
Object_type_decoder(Input);
{{ok, <<"array"/utf8>>}, _, _} ->
Array_type_decoder(Input);
{_, _, {some, _}} ->
Ref_type_decoder(Input);
{{ok, Unknown}, _, _} ->
{error,
[{decode_error,
<<"A type with a valid 'type' field"/utf8>>,
Unknown,
[<<"parse_type"/utf8>>]}]};
{{error, Any}, _, _} ->
{error, Any}
end end
)
end
).
-spec parse_property_def(gleam@dynamic:dynamic_()) -> {ok,
property_definition()} |
{error, list(gleam@dynamic:decode_error())}.
parse_property_def(Input) ->
(gleam@dynamic:decode6(
fun(Field@0, Field@1, Field@2, Field@3, Field@4, Field@5) -> {property_definition, Field@0, Field@1, Field@2, Field@3, Field@4, Field@5} end,
gleam@dynamic:field(<<"name"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(
<<"description"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:optional_field(
<<"experimental"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"deprecated"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"optional"/utf8>>,
fun gleam@dynamic:bool/1
),
fun parse_type/1
))(Input).
-spec parse_type_def(gleam@dynamic:dynamic_()) -> {ok, type_definition()} |
{error, list(gleam@dynamic:decode_error())}.
parse_type_def(Input) ->
(gleam@dynamic:decode5(
fun(Field@0, Field@1, Field@2, Field@3, Field@4) -> {type_definition, Field@0, Field@1, Field@2, Field@3, Field@4} end,
gleam@dynamic:field(<<"id"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(
<<"description"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:optional_field(
<<"experimental"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"deprecated"/utf8>>,
fun gleam@dynamic:bool/1
),
fun parse_type/1
))(Input).
-spec parse_protocol(binary()) -> {ok, protocol()} |
{error, gleam@json:decode_error()}.
parse_protocol(From) ->
_assert_subject = simplifile:read(From),
{ok, Json_string} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"parse_protocol"/utf8>>,
line => 791})
end,
Command_decoder = gleam@dynamic:decode6(
fun(Field@0, Field@1, Field@2, Field@3, Field@4, Field@5) -> {command, Field@0, Field@1, Field@2, Field@3, Field@4, Field@5} end,
gleam@dynamic:field(<<"name"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(
<<"description"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:optional_field(
<<"experimental"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"deprecated"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"parameters"/utf8>>,
gleam@dynamic:list(fun parse_property_def/1)
),
gleam@dynamic:optional_field(
<<"returns"/utf8>>,
gleam@dynamic:list(fun parse_property_def/1)
)
),
Event_decoder = gleam@dynamic:decode5(
fun(Field@0, Field@1, Field@2, Field@3, Field@4) -> {event, Field@0, Field@1, Field@2, Field@3, Field@4} end,
gleam@dynamic:field(<<"name"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(
<<"description"/utf8>>,
fun gleam@dynamic:string/1
),
gleam@dynamic:optional_field(
<<"experimental"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"deprecated"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"parameters"/utf8>>,
gleam@dynamic:list(fun parse_property_def/1)
)
),
Domain_decoder = gleam@dynamic:decode8(
fun(Field@0, Field@1, Field@2, Field@3, Field@4, Field@5, Field@6, Field@7) -> {domain, Field@0, Field@1, Field@2, Field@3, Field@4, Field@5, Field@6, Field@7} end,
gleam@dynamic:field(<<"domain"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:optional_field(
<<"experimental"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"deprecated"/utf8>>,
fun gleam@dynamic:bool/1
),
gleam@dynamic:optional_field(
<<"dependencies"/utf8>>,
gleam@dynamic:list(fun gleam@dynamic:string/1)
),
gleam@dynamic:optional_field(
<<"types"/utf8>>,
gleam@dynamic:list(fun parse_type_def/1)
),
gleam@dynamic:field(
<<"commands"/utf8>>,
gleam@dynamic:list(Command_decoder)
),
gleam@dynamic:optional_field(
<<"events"/utf8>>,
gleam@dynamic:list(Event_decoder)
),
gleam@dynamic:optional_field(
<<"description"/utf8>>,
fun gleam@dynamic:string/1
)
),
Version_decoder = gleam@dynamic:decode2(
fun(Field@0, Field@1) -> {version, Field@0, Field@1} end,
gleam@dynamic:field(<<"major"/utf8>>, fun gleam@dynamic:string/1),
gleam@dynamic:field(<<"minor"/utf8>>, fun gleam@dynamic:string/1)
),
Protocol_decoder = gleam@dynamic:decode2(
fun(Field@0, Field@1) -> {protocol, Field@0, Field@1} end,
gleam@dynamic:field(<<"version"/utf8>>, Version_decoder),
gleam@dynamic:field(
<<"domains"/utf8>>,
gleam@dynamic:list(Domain_decoder)
)
),
gleam@json:decode(Json_string, Protocol_decoder).
-spec main() -> nil.
main() ->
_assert_subject = parse_protocol(<<"./assets/browser_protocol.json"/utf8>>),
{ok, Browser_protocol} = case _assert_subject of
{ok, _} -> _assert_subject;
_assert_fail ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"main"/utf8>>,
line => 214})
end,
_assert_subject@1 = parse_protocol(<<"./assets/js_protocol.json"/utf8>>),
{ok, Js_protocol} = case _assert_subject@1 of
{ok, _} -> _assert_subject@1;
_assert_fail@1 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@1,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"main"/utf8>>,
line => 216})
end,
Protocol = begin
_pipe = merge_protocols(Browser_protocol, Js_protocol),
apply_protocol_patches(_pipe)
end,
gleam@io:println(
<<<<<<"Browser protocol version: "/utf8,
(erlang:element(2, erlang:element(2, Protocol)))/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(3, erlang:element(2, Protocol)))/binary>>
),
print_protocol_stats(Protocol),
Stable_protocol = get_stable_protocol(Protocol, false, false),
gleam@io:println(<<"Stable protocol (experimental items removed):"/utf8>>),
print_protocol_stats(Stable_protocol),
Target = <<"src/protocol.gleam"/utf8>>,
gleam@io:println(
<<"Writing root protocol module to: "/utf8, Target/binary>>
),
_assert_subject@2 = simplifile:write(
Target,
gen_root_module(Stable_protocol)
),
{ok, _} = case _assert_subject@2 of
{ok, _} -> _assert_subject@2;
_assert_fail@2 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@2,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"main"/utf8>>,
line => 232})
end,
_assert_subject@3 = simplifile:create_directory_all(<<"src/protocol"/utf8>>),
{ok, _} = case _assert_subject@3 of
{ok, _} -> _assert_subject@3;
_assert_fail@3 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@3,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"main"/utf8>>,
line => 233})
end,
gleam@list:each(
erlang:element(3, Stable_protocol),
fun(Domain) ->
Target@1 = <<<<"src/protocol/"/utf8,
(justin:snake_case(erlang:element(2, Domain)))/binary>>/binary,
".gleam"/utf8>>,
gleam@io:println(
<<"Writing domain module to: "/utf8, Target@1/binary>>
),
_assert_subject@4 = simplifile:write(
Target@1,
gen_domain_module(Stable_protocol, Domain)
),
{ok, _} = case _assert_subject@4 of
{ok, _} -> _assert_subject@4;
_assert_fail@4 ->
erlang:error(#{gleam_error => let_assert,
message => <<"Assertion pattern match failed"/utf8>>,
value => _assert_fail@4,
module => <<"chrobot/internal/generate_bindings"/utf8>>,
function => <<"main"/utf8>>,
line => 237})
end
end
).