Packages
caffeine_lang
3.0.2
6.3.1
6.3.0
6.2.2
6.2.1
6.2.0
6.1.2
6.1.1
6.1.0
6.0.0
5.6.0
5.5.0
5.4.4
5.4.3
5.4.2
5.4.1
5.4.0
5.3.0
5.2.0
5.1.1
5.1.0
5.0.12
5.0.11
5.0.10
5.0.8
5.0.7
5.0.6
5.0.5
5.0.4
5.0.1
5.0.0
4.10.0
4.9.0
4.8.3
4.8.2
4.8.1
4.8.0
4.7.9
4.7.8
4.7.7
4.7.6
4.7.5
4.6.7
4.6.6
4.6.5
4.6.4
4.6.3
4.6.2
4.6.0
4.5.1
4.5.0
4.4.4
4.4.3
4.4.1
4.4.0
4.3.7
4.3.6
3.0.6
3.0.5
3.0.4
3.0.3
3.0.2
3.0.1
3.0.0
2.0.5
2.0.4
2.0.3
2.0.2
2.0.1
2.0.0
1.0.2
1.0.1
0.1.0
0.0.24
0.0.23
0.0.22
0.0.21
0.0.20
0.0.19
0.0.18
0.0.17
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.2
0.0.1
A compiler for generating reliability artifacts from service expectation definitions.
Current section
Files
Jump to
Current section
Files
src/caffeine_lang@frontend@generator.erl
-module(caffeine_lang@frontend@generator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/frontend/generator.gleam").
-export([generate_expects_json/1, generate_blueprints_json/1]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-file("src/caffeine_lang/frontend/generator.gleam", 57).
?DOC(" Builds a map of extendable name to extendable for quick lookup.\n").
-spec build_extendable_map(list(caffeine_lang@frontend@ast:extendable())) -> gleam@dict:dict(binary(), caffeine_lang@frontend@ast:extendable()).
build_extendable_map(Extendables) ->
_pipe = Extendables,
_pipe@1 = gleam@list:map(_pipe, fun(E) -> {erlang:element(2, E), E} end),
maps:from_list(_pipe@1).
-file("src/caffeine_lang/frontend/generator.gleam", 66).
?DOC(" Builds a map of type alias name to its resolved type for quick lookup.\n").
-spec build_type_alias_map(list(caffeine_lang@frontend@ast:type_alias())) -> gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types()).
build_type_alias_map(Type_aliases) ->
_pipe = Type_aliases,
_pipe@1 = gleam@list:map(
_pipe,
fun(Ta) -> {erlang:element(2, Ta), erlang:element(3, Ta)} end
),
maps:from_list(_pipe@1).
-file("src/caffeine_lang/frontend/generator.gleam", 179).
?DOC(
" Removes duplicate field names, keeping the last occurrence (allows overrides).\n"
" Returns fields sorted by name for consistent JSON output.\n"
).
-spec dedupe_fields(list(caffeine_lang@frontend@ast:field())) -> list(caffeine_lang@frontend@ast:field()).
dedupe_fields(Fields) ->
_pipe = Fields,
_pipe@1 = gleam@list:fold(
_pipe,
maps:new(),
fun(Acc, Field) ->
gleam@dict:insert(Acc, erlang:element(2, Field), Field)
end
),
_pipe@2 = maps:to_list(_pipe@1),
_pipe@3 = gleam@list:sort(
_pipe@2,
fun(A, B) ->
gleam@string:compare(erlang:element(1, A), erlang:element(1, B))
end
),
gleam@list:map(_pipe@3, fun(Pair) -> erlang:element(2, Pair) end).
-file("src/caffeine_lang/frontend/generator.gleam", 120).
?DOC(
" Merges extended fields into a blueprint item's requires and provides.\n"
" Order: extended extendables left-to-right, then item's own fields (can override).\n"
).
-spec merge_blueprint_extends(
caffeine_lang@frontend@ast:blueprint_item(),
gleam@dict:dict(binary(), caffeine_lang@frontend@ast:extendable())
) -> {caffeine_lang@frontend@ast:struct(), caffeine_lang@frontend@ast:struct()}.
merge_blueprint_extends(Item, Extendables) ->
Requires_fields = begin
_pipe = erlang:element(3, Item),
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(Name) -> case gleam_stdlib:map_get(Extendables, Name) of
{ok, Ext} ->
case erlang:element(3, Ext) of
extendable_requires ->
erlang:element(2, erlang:element(4, Ext));
extendable_provides ->
[]
end;
{error, _} ->
[]
end end
),
_pipe@2 = lists:append(
_pipe@1,
erlang:element(2, erlang:element(4, Item))
),
dedupe_fields(_pipe@2)
end,
Provides_fields = begin
_pipe@3 = erlang:element(3, Item),
_pipe@4 = gleam@list:flat_map(
_pipe@3,
fun(Name@1) -> case gleam_stdlib:map_get(Extendables, Name@1) of
{ok, Ext@1} ->
case erlang:element(3, Ext@1) of
extendable_provides ->
erlang:element(2, erlang:element(4, Ext@1));
extendable_requires ->
[]
end;
{error, _} ->
[]
end end
),
_pipe@5 = lists:append(
_pipe@4,
erlang:element(2, erlang:element(5, Item))
),
dedupe_fields(_pipe@5)
end,
{{struct, Requires_fields}, {struct, Provides_fields}}.
-file("src/caffeine_lang/frontend/generator.gleam", 159).
?DOC(
" Merges extended fields into an expect item's provides.\n"
" Order: extended extendables left-to-right, then item's own fields (can override).\n"
).
-spec merge_expect_extends(
caffeine_lang@frontend@ast:expect_item(),
gleam@dict:dict(binary(), caffeine_lang@frontend@ast:extendable())
) -> caffeine_lang@frontend@ast:struct().
merge_expect_extends(Item, Extendables) ->
Provides_fields = begin
_pipe = erlang:element(3, Item),
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(Name) -> case gleam_stdlib:map_get(Extendables, Name) of
{ok, Ext} ->
erlang:element(2, erlang:element(4, Ext));
{error, _} ->
[]
end end
),
_pipe@2 = lists:append(
_pipe@1,
erlang:element(2, erlang:element(4, Item))
),
dedupe_fields(_pipe@2)
end,
{struct, Provides_fields}.
-file("src/caffeine_lang/frontend/generator.gleam", 332).
-spec transform_template_vars_loop(binary(), binary()) -> binary().
transform_template_vars_loop(Remaining, Acc) ->
case gleam@string:split_once(Remaining, <<"$"/utf8>>) of
{ok, {Before, After}} ->
case gleam_stdlib:string_starts_with(After, <<"$"/utf8>>) of
true ->
transform_template_vars_loop(
gleam@string:drop_start(After, 1),
<<<<Acc/binary, Before/binary>>/binary, "$$"/utf8>>
);
false ->
case gleam@string:split_once(After, <<"$"/utf8>>) of
{ok, {Var_content, Rest}} ->
Transformed = gleam@string:replace(
Var_content,
<<".not"/utf8>>,
<<":not"/utf8>>
),
transform_template_vars_loop(
Rest,
<<<<<<<<Acc/binary, Before/binary>>/binary,
"$$"/utf8>>/binary,
Transformed/binary>>/binary,
"$$"/utf8>>
);
{error, nil} ->
<<<<<<Acc/binary, Before/binary>>/binary, "$"/utf8>>/binary,
After/binary>>
end
end;
{error, nil} ->
<<Acc/binary, Remaining/binary>>
end.
-file("src/caffeine_lang/frontend/generator.gleam", 328).
?DOC(
" Transforms template variables from $var->attr$ to $$var->attr$$ format.\n"
" Also handles $var->attr.not$ to $$var->attr:not$$ format.\n"
).
-spec transform_template_vars(binary()) -> binary().
transform_template_vars(S) ->
transform_template_vars_loop(S, <<""/utf8>>).
-file("src/caffeine_lang/frontend/generator.gleam", 313).
?DOC(" Converts a literal struct's fields to a JSON object.\n").
-spec literal_struct_to_json(list(caffeine_lang@frontend@ast:field())) -> gleam@json:json().
literal_struct_to_json(Fields) ->
_pipe = Fields,
_pipe@1 = gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@string:compare(erlang:element(2, A), erlang:element(2, B))
end
),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Field) ->
Json_value = case erlang:element(3, Field) of
{literal_value, Lit} ->
literal_to_json(Lit);
{type_value, _} ->
gleam@json:null()
end,
{erlang:element(2, Field), Json_value}
end
),
gleam@json:object(_pipe@2).
-file("src/caffeine_lang/frontend/generator.gleam", 299).
?DOC(" Converts a literal to a JSON value.\n").
-spec literal_to_json(caffeine_lang@frontend@ast:literal()) -> gleam@json:json().
literal_to_json(Lit) ->
case Lit of
{literal_string, S} ->
gleam@json:string(transform_template_vars(S));
{literal_integer, I} ->
gleam@json:int(I);
{literal_float, F} ->
gleam@json:float(F);
literal_true ->
gleam@json:bool(true);
literal_false ->
gleam@json:bool(false);
{literal_list, Elements} ->
gleam@json:array(Elements, fun(E) -> literal_to_json(E) end);
{literal_struct, Fields} ->
literal_struct_to_json(Fields)
end.
-file("src/caffeine_lang/frontend/generator.gleam", 285).
?DOC(" Converts a struct with literal values to a JSON inputs object.\n").
-spec struct_to_inputs_json(caffeine_lang@frontend@ast:struct()) -> gleam@json:json().
struct_to_inputs_json(S) ->
_pipe = erlang:element(2, S),
_pipe@1 = gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@string:compare(erlang:element(2, A), erlang:element(2, B))
end
),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Field) ->
Json_value = case erlang:element(3, Field) of
{literal_value, Lit} ->
literal_to_json(Lit);
{type_value, _} ->
gleam@json:null()
end,
{erlang:element(2, Field), Json_value}
end
),
gleam@json:object(_pipe@2).
-file("src/caffeine_lang/frontend/generator.gleam", 100).
?DOC(" Generates JSON for a single expect item.\n").
-spec generate_expect_item_json(
caffeine_lang@frontend@ast:expect_item(),
binary(),
gleam@dict:dict(binary(), caffeine_lang@frontend@ast:extendable())
) -> gleam@json:json().
generate_expect_item_json(Item, Blueprint, Extendables) ->
Merged_provides = merge_expect_extends(Item, Extendables),
Inputs = struct_to_inputs_json(Merged_provides),
gleam@json:object(
[{<<"name"/utf8>>, gleam@json:string(erlang:element(2, Item))},
{<<"blueprint_ref"/utf8>>, gleam@json:string(Blueprint)},
{<<"inputs"/utf8>>, Inputs}]
).
-file("src/caffeine_lang/frontend/generator.gleam", 41).
?DOC(false).
-spec generate_expects_json(caffeine_lang@frontend@ast:expects_file()) -> gleam@json:json().
generate_expects_json(File) ->
Extendables = build_extendable_map(erlang:element(2, File)),
Expectations = begin
_pipe = erlang:element(3, File),
gleam@list:flat_map(
_pipe,
fun(Block) -> _pipe@1 = erlang:element(3, Block),
gleam@list:map(
_pipe@1,
fun(Item) ->
generate_expect_item_json(
Item,
erlang:element(2, Block),
Extendables
)
end
) end
)
end,
gleam@json:object(
[{<<"expectations"/utf8>>,
gleam@json:array(Expectations, fun(X) -> X end)}]
).
-file("src/caffeine_lang/frontend/generator.gleam", 239).
?DOC(" Resolves type aliases in collection types.\n").
-spec resolve_collection_aliases(
caffeine_lang@common@collection_types:collection_types(caffeine_lang@common@accepted_types:accepted_types()),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> caffeine_lang@common@collection_types:collection_types(caffeine_lang@common@accepted_types:accepted_types()).
resolve_collection_aliases(Collection, Aliases) ->
case Collection of
{list, Inner} ->
{list, resolve_type_aliases(Inner, Aliases)};
{dict, Key, Value} ->
{dict,
resolve_type_aliases(Key, Aliases),
resolve_type_aliases(Value, Aliases)}
end.
-file("src/caffeine_lang/frontend/generator.gleam", 212).
?DOC(
" Resolves all TypeAliasRef instances in a type by looking them up in the alias map.\n"
" Recursively resolves nested types (in collections, modifiers, refinements).\n"
).
-spec resolve_type_aliases(
caffeine_lang@common@accepted_types:accepted_types(),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> caffeine_lang@common@accepted_types:accepted_types().
resolve_type_aliases(T, Aliases) ->
case T of
{primitive_type, _} ->
T;
{type_alias_ref, Name} ->
case gleam_stdlib:map_get(Aliases, Name) of
{ok, Resolved} ->
resolve_type_aliases(Resolved, Aliases);
{error, _} ->
T
end;
{collection_type, Collection} ->
{collection_type, resolve_collection_aliases(Collection, Aliases)};
{modifier_type, Modifier} ->
{modifier_type, resolve_modifier_aliases(Modifier, Aliases)};
{refinement_type, Refinement} ->
{refinement_type, resolve_refinement_aliases(Refinement, Aliases)}
end.
-file("src/caffeine_lang/frontend/generator.gleam", 191).
?DOC(
" Converts a struct with type values to a JSON params object.\n"
" Resolves type alias references before converting to strings.\n"
).
-spec struct_to_params_json(
caffeine_lang@frontend@ast:struct(),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> gleam@json:json().
struct_to_params_json(S, Type_aliases) ->
_pipe = erlang:element(2, S),
_pipe@1 = gleam@list:sort(
_pipe,
fun(A, B) ->
gleam@string:compare(erlang:element(2, A), erlang:element(2, B))
end
),
_pipe@2 = gleam@list:map(
_pipe@1,
fun(Field) ->
Type_string = case erlang:element(3, Field) of
{type_value, T} ->
Resolved = resolve_type_aliases(T, Type_aliases),
caffeine_lang@common@accepted_types:accepted_type_to_string(
Resolved
);
{literal_value, _} ->
<<""/utf8>>
end,
{erlang:element(2, Field), gleam@json:string(Type_string)}
end
),
gleam@json:object(_pipe@2).
-file("src/caffeine_lang/frontend/generator.gleam", 75).
?DOC(" Generates JSON for a single blueprint item.\n").
-spec generate_blueprint_item_json(
caffeine_lang@frontend@ast:blueprint_item(),
list(binary()),
gleam@dict:dict(binary(), caffeine_lang@frontend@ast:extendable()),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> gleam@json:json().
generate_blueprint_item_json(Item, Artifacts, Extendables, Type_aliases) ->
{Merged_requires, Merged_provides} = merge_blueprint_extends(
Item,
Extendables
),
Params = struct_to_params_json(Merged_requires, Type_aliases),
Inputs = struct_to_inputs_json(Merged_provides),
gleam@json:object(
[{<<"name"/utf8>>, gleam@json:string(erlang:element(2, Item))},
{<<"artifact_refs"/utf8>>,
gleam@json:array(Artifacts, fun gleam@json:string/1)},
{<<"params"/utf8>>, Params},
{<<"inputs"/utf8>>, Inputs}]
).
-file("src/caffeine_lang/frontend/generator.gleam", 18).
?DOC(false).
-spec generate_blueprints_json(caffeine_lang@frontend@ast:blueprints_file()) -> gleam@json:json().
generate_blueprints_json(File) ->
Type_aliases = build_type_alias_map(erlang:element(2, File)),
Extendables = build_extendable_map(erlang:element(3, File)),
Blueprints = begin
_pipe = erlang:element(4, File),
gleam@list:flat_map(
_pipe,
fun(Block) -> _pipe@1 = erlang:element(3, Block),
gleam@list:map(
_pipe@1,
fun(Item) ->
generate_blueprint_item_json(
Item,
erlang:element(2, Block),
Extendables,
Type_aliases
)
end
) end
)
end,
gleam@json:object(
[{<<"blueprints"/utf8>>, gleam@json:array(Blueprints, fun(X) -> X end)}]
).
-file("src/caffeine_lang/frontend/generator.gleam", 255).
?DOC(" Resolves type aliases in modifier types.\n").
-spec resolve_modifier_aliases(
caffeine_lang@common@modifier_types:modifier_types(caffeine_lang@common@accepted_types:accepted_types()),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> caffeine_lang@common@modifier_types:modifier_types(caffeine_lang@common@accepted_types:accepted_types()).
resolve_modifier_aliases(Modifier, Aliases) ->
case Modifier of
{optional, Inner} ->
{optional, resolve_type_aliases(Inner, Aliases)};
{defaulted, Inner@1, Default} ->
{defaulted, resolve_type_aliases(Inner@1, Aliases), Default}
end.
-file("src/caffeine_lang/frontend/generator.gleam", 268).
?DOC(" Resolves type aliases in refinement types.\n").
-spec resolve_refinement_aliases(
caffeine_lang@common@refinement_types:refinement_types(caffeine_lang@common@accepted_types:accepted_types()),
gleam@dict:dict(binary(), caffeine_lang@common@accepted_types:accepted_types())
) -> caffeine_lang@common@refinement_types:refinement_types(caffeine_lang@common@accepted_types:accepted_types()).
resolve_refinement_aliases(Refinement, Aliases) ->
case Refinement of
{one_of, Inner, Values} ->
{one_of, resolve_type_aliases(Inner, Aliases), Values};
{inclusive_range, Inner@1, Min, Max} ->
{inclusive_range, resolve_type_aliases(Inner@1, Aliases), Min, Max}
end.