Current section

Files

Jump to
caffeine_lang src caffeine_lang@linker@ir_builder.erl
Raw

src/caffeine_lang@linker@ir_builder.erl

-module(caffeine_lang@linker@ir_builder).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/linker/ir_builder.gleam").
-export([reserved_labels/1, build_all/4]).
-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/linker/ir_builder.gleam", 28).
?DOC(false).
-spec reserved_labels(
gleam@dict:dict(binary(), caffeine_lang@linker@artifacts:param_info())
) -> gleam@set:set(binary()).
reserved_labels(Params) ->
_pipe = Params,
_pipe@1 = maps:keys(_pipe),
gleam@set:from_list(_pipe@1).
-file("src/caffeine_lang/linker/ir_builder.gleam", 330).
?DOC(
" Extract SLO-specific fields from an indexed Dict of ValueTuples.\n"
" Threshold defaults to the standard default when not present (e.g. unmeasured expectations).\n"
" `description` is sourced from the expectation's leading doc comments, not\n"
" from value tuples.\n"
).
-spec build_slo_fields(
gleam@dict:dict(binary(), caffeine_lang@helpers:value_tuple()),
gleam@option:option(binary())
) -> caffeine_lang@linker@ir:slo_fields().
build_slo_fields(Index, Description) ->
Threshold = begin
_pipe = caffeine_lang@helpers:extract_value(
Index,
<<"threshold"/utf8>>,
fun caffeine_lang@value:extract_percentage/1
),
gleam@result:unwrap(_pipe, 99.9)
end,
Indicators = caffeine_lang@helpers:extract_indicators(Index),
Window_in_days = caffeine_lang@helpers:extract_window_in_days(Index),
Evaluation = begin
_pipe@1 = caffeine_lang@helpers:extract_value(
Index,
<<"evaluation"/utf8>>,
fun caffeine_lang@value:extract_string/1
),
gleam@option:from_result(_pipe@1)
end,
Tags = caffeine_lang@helpers:extract_tags(Index),
Runbook = begin
_pipe@2 = caffeine_lang@helpers:extract_value(
Index,
<<"runbook"/utf8>>,
fun(V) -> case V of
nil_value ->
{ok, none};
{string_value, S} ->
{ok, {some, S}};
_ ->
{error, nil}
end end
),
gleam@result:unwrap(_pipe@2, none)
end,
Relations = caffeine_lang@helpers:extract_depends_on(Index),
Depends_on = case gleam@dict:is_empty(Relations) of
true ->
none;
false ->
{some, Relations}
end,
{slo_fields,
Threshold,
Indicators,
Window_in_days,
Evaluation,
Tags,
Runbook,
Depends_on,
Description}.
-file("src/caffeine_lang/linker/ir_builder.gleam", 280).
?DOC(
" Resolves a value tuple to a list of strings for use as tags.\n"
" Primitives and refinements produce a single-element list.\n"
" Lists are exploded into multiple string values.\n"
" Dicts and type alias refs are unsupported.\n"
" Optional(None) produces an empty list (filtered out).\n"
" Defaulted(None) produces the default value.\n"
).
-spec resolve_values_for_tag(
caffeine_lang@types:accepted_types(),
caffeine_lang@value:value()
) -> {ok, list(binary())} | {error, nil}.
resolve_values_for_tag(Typ, Val) ->
case Typ of
{primitive_type, _} ->
{ok, [caffeine_lang@value:to_string(Val)]};
{refinement_type, Refinement} ->
case Refinement of
{one_of, Inner, _} ->
resolve_values_for_tag(Inner, Val);
{inclusive_range, Inner@1, _, _} ->
resolve_values_for_tag(Inner@1, Val)
end;
{collection_type, {list, Inner@2}} ->
case Val of
{list_value, Items} ->
_pipe = Items,
_pipe@2 = gleam@list:try_map(
_pipe,
fun(Item) ->
_pipe@1 = resolve_values_for_tag(Inner@2, Item),
gleam@result:map(
_pipe@1,
fun(Strings) -> Strings end
)
end
),
gleam@result:map(_pipe@2, fun lists:append/1);
_ ->
{error, nil}
end;
{collection_type, {dict, _, _}} ->
{error, nil};
{record_type, _} ->
{error, nil};
{modifier_type, {optional, Inner@3}} ->
case Val of
nil_value ->
{ok, []};
_ ->
resolve_values_for_tag(Inner@3, Val)
end;
{modifier_type, {defaulted, Inner@4, Default}} ->
case Val of
nil_value ->
case Inner@4 of
{collection_type, {list, _}} ->
{ok,
caffeine_lang@types:parse_list_default_string(
Default
)};
_ ->
{ok, [Default]}
end;
_ ->
resolve_values_for_tag(Inner@4, Val)
end
end.
-file("src/caffeine_lang/linker/ir_builder.gleam", 255).
?DOC(
" Extract misc metadata from value tuples.\n"
" Filters out reserved labels (derived from artifact params) and unsupported types.\n"
" Each key maps to a list of string values (primitives become single-element\n"
" lists, collection lists are exploded, nulls are excluded).\n"
).
-spec extract_misc_metadata(
list(caffeine_lang@helpers:value_tuple()),
gleam@set:set(binary())
) -> gleam@dict:dict(binary(), list(binary())).
extract_misc_metadata(Value_tuples, Reserved_labels) ->
_pipe = Value_tuples,
_pipe@1 = gleam@list:filter_map(
_pipe,
fun(Value_tuple) ->
gleam@bool:guard(
gleam@set:contains(
Reserved_labels,
erlang:element(2, Value_tuple)
),
{error, nil},
fun() ->
case resolve_values_for_tag(
erlang:element(3, Value_tuple),
erlang:element(4, Value_tuple)
) of
{ok, []} ->
{error, nil};
{ok, Values} ->
{ok, {erlang:element(2, Value_tuple), Values}};
{error, _} ->
{error, nil}
end
end
)
end
),
maps:from_list(_pipe@1).
-file("src/caffeine_lang/linker/ir_builder.gleam", 232).
?DOC(
" Build value tuples for Optional/Defaulted params that weren't provided.\n"
" These need to be in value_tuples so the templatizer can resolve them.\n"
).
-spec build_unprovided_optional_value_tuples(
gleam@dict:dict(binary(), caffeine_lang@value:value()),
gleam@dict:dict(binary(), caffeine_lang@types:accepted_types())
) -> list(caffeine_lang@helpers:value_tuple()).
build_unprovided_optional_value_tuples(Merged_inputs, Params) ->
_pipe = Params,
_pipe@1 = maps:to_list(_pipe),
gleam@list:filter_map(
_pipe@1,
fun(Param) ->
{Label, Typ} = Param,
gleam@bool:guard(
gleam@dict:has_key(Merged_inputs, Label),
{error, nil},
fun() ->
case caffeine_lang@types:is_optional_or_defaulted(Typ) of
true ->
{ok, {value_tuple, Label, Typ, nil_value}};
false ->
{error, nil}
end
end
)
end
).
-file("src/caffeine_lang/linker/ir_builder.gleam", 215).
?DOC(" Build value tuples from provided inputs.\n").
-spec build_provided_value_tuples(
gleam@dict:dict(binary(), caffeine_lang@value:value()),
gleam@dict:dict(binary(), caffeine_lang@types:accepted_types())
) -> list(caffeine_lang@helpers:value_tuple()).
build_provided_value_tuples(Merged_inputs, Params) ->
_pipe = Merged_inputs,
_pipe@1 = maps:to_list(_pipe),
gleam@list:filter_map(
_pipe@1,
fun(Pair) ->
{Label, Val} = Pair,
case gleam_stdlib:map_get(Params, Label) of
{ok, Typ} ->
{ok, {value_tuple, Label, Typ, Val}};
{error, nil} ->
{error, nil}
end
end
).
-file("src/caffeine_lang/linker/ir_builder.gleam", 205).
?DOC(
" Build value tuples from merged inputs and params.\n"
" Includes both provided inputs and unprovided Optional/Defaulted params.\n"
).
-spec build_value_tuples(
gleam@dict:dict(binary(), caffeine_lang@value:value()),
gleam@dict:dict(binary(), caffeine_lang@types:accepted_types())
) -> list(caffeine_lang@helpers:value_tuple()).
build_value_tuples(Merged_inputs, Params) ->
Provided = build_provided_value_tuples(Merged_inputs, Params),
Unprovided = build_unprovided_optional_value_tuples(Merged_inputs, Params),
lists:append(Provided, Unprovided).
-file("src/caffeine_lang/linker/ir_builder.gleam", 162).
?DOC(
" Build an IR from an unmeasured expectation.\n"
" Uses restricted params (threshold, window_in_days, depends_on) and sets vendor to None.\n"
).
-spec build_unmeasured(
caffeine_lang@linker@expectations:expectation(),
binary(),
binary(),
binary(),
gleam@set:set(binary()),
gleam@dict:dict(binary(), caffeine_lang@types:accepted_types())
) -> caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:linked()).
build_unmeasured(
Expectation,
Org,
Team,
Service,
Reserved_labels,
Unmeasured_params
) ->
Value_tuples = build_value_tuples(
erlang:element(4, Expectation),
Unmeasured_params
),
Index = caffeine_lang@helpers:index_value_tuples(Value_tuples),
Misc_metadata = extract_misc_metadata(Value_tuples, Reserved_labels),
Unique_name = <<<<<<<<Org/binary, "_"/utf8>>/binary, Service/binary>>/binary,
"_"/utf8>>/binary,
(erlang:element(2, Expectation))/binary>>,
Slo = build_slo_fields(Index, erlang:element(5, Expectation)),
{intermediate_representation,
{intermediate_representation_meta_data,
{expectation_label, erlang:element(2, Expectation)},
{org_name, Org},
{service_name, Service},
{measurement_name, <<"unmeasured"/utf8>>},
{team_name, Team},
Misc_metadata},
Unique_name,
Value_tuples,
Slo,
none}.
-file("src/caffeine_lang/linker/ir_builder.gleam", 105).
?DOC(" Build an IR from a measured expectation paired with its measurement.\n").
-spec build_measured(
caffeine_lang@linker@expectations:expectation(),
caffeine_lang@linker@measurements:measurement(caffeine_lang@linker@measurements:measurement_validated()),
binary(),
binary(),
binary(),
gleam@set:set(binary()),
gleam@dict:dict(binary(), caffeine_lang@analysis@vendor:vendor())
) -> {ok,
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:linked())} |
{error, caffeine_lang@errors:compilation_error()}.
build_measured(
Expectation,
Measurement,
Org,
Team,
Service,
Reserved_labels,
Vendor_lookup
) ->
Merged_inputs = maps:merge(
erlang:element(4, Measurement),
erlang:element(4, Expectation)
),
Value_tuples = build_value_tuples(
Merged_inputs,
erlang:element(3, Measurement)
),
Index = caffeine_lang@helpers:index_value_tuples(Value_tuples),
Misc_metadata = extract_misc_metadata(Value_tuples, Reserved_labels),
Unique_name = <<<<<<<<Org/binary, "_"/utf8>>/binary, Service/binary>>/binary,
"_"/utf8>>/binary,
(erlang:element(2, Expectation))/binary>>,
Slo = build_slo_fields(Index, erlang:element(5, Expectation)),
Resolved_vendor = case gleam_stdlib:map_get(
Vendor_lookup,
erlang:element(2, Measurement)
) of
{ok, V} ->
{ok, {some, V}};
{error, nil} ->
{error,
caffeine_lang@errors:linker_vendor_resolution_error(
<<<<<<<<<<<<<<<<<<<<"expectation '"/utf8, Org/binary>>/binary,
"."/utf8>>/binary,
Team/binary>>/binary,
"."/utf8>>/binary,
Service/binary>>/binary,
"."/utf8>>/binary,
(erlang:element(2, Expectation))/binary>>/binary,
"' - measurement '"/utf8>>/binary,
(erlang:element(2, Measurement))/binary>>/binary,
"' has no associated vendor"/utf8>>
)}
end,
gleam@result:'try'(
Resolved_vendor,
fun(Resolved_vendor@1) ->
{ok,
{intermediate_representation,
{intermediate_representation_meta_data,
{expectation_label, erlang:element(2, Expectation)},
{org_name, Org},
{service_name, Service},
{measurement_name, erlang:element(2, Measurement)},
{team_name, Team},
Misc_metadata},
Unique_name,
Value_tuples,
Slo,
Resolved_vendor@1}}
end
).
-file("src/caffeine_lang/linker/ir_builder.gleam", 65).
?DOC(" Build intermediate representations from validated expectations for a single file.\n").
-spec build(
list({caffeine_lang@linker@expectations:expectation(),
gleam@option:option(caffeine_lang@linker@measurements:measurement(caffeine_lang@linker@measurements:measurement_validated()))}),
binary(),
gleam@set:set(binary()),
gleam@dict:dict(binary(), caffeine_lang@analysis@vendor:vendor()),
gleam@dict:dict(binary(), caffeine_lang@types:accepted_types())
) -> {ok,
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:linked()))} |
{error, caffeine_lang@errors:compilation_error()}.
build(
Expectations_measurement_collection,
File_path,
Reserved_labels,
Vendor_lookup,
Unmeasured_params
) ->
{Org, Team, Service} = caffeine_lang@helpers:extract_path_prefix(File_path),
_pipe = Expectations_measurement_collection,
gleam@list:try_map(
_pipe,
fun(Expectation_and_measurement_pair) ->
{Expectation, Maybe_measurement} = Expectation_and_measurement_pair,
case Maybe_measurement of
{some, Measurement} ->
build_measured(
Expectation,
Measurement,
Org,
Team,
Service,
Reserved_labels,
Vendor_lookup
);
none ->
{ok,
build_unmeasured(
Expectation,
Org,
Team,
Service,
Reserved_labels,
Unmeasured_params
)}
end
end
).
-file("src/caffeine_lang/linker/ir_builder.gleam", 194).
?DOC(
" Derives the restricted param types for unmeasured expectations from SLO params.\n"
" Only includes threshold, window_in_days, and depends_on.\n"
).
-spec build_unmeasured_param_types(
gleam@dict:dict(binary(), caffeine_lang@linker@artifacts:param_info())
) -> gleam@dict:dict(binary(), caffeine_lang@types:accepted_types()).
build_unmeasured_param_types(Slo_params) ->
Allowed = gleam@set:from_list(
[<<"threshold"/utf8>>, <<"window_in_days"/utf8>>, <<"depends_on"/utf8>>]
),
_pipe = Slo_params,
_pipe@1 = gleam@dict:filter(
_pipe,
fun(Key, _) -> gleam@set:contains(Allowed, Key) end
),
caffeine_lang@linker@artifacts:params_to_types(_pipe@1).
-file("src/caffeine_lang/linker/ir_builder.gleam", 36).
?DOC(false).
-spec build_all(
list({list({caffeine_lang@linker@expectations:expectation(),
gleam@option:option(caffeine_lang@linker@measurements:measurement(caffeine_lang@linker@measurements:measurement_validated()))}),
binary()}),
gleam@set:set(binary()),
gleam@dict:dict(binary(), caffeine_lang@analysis@vendor:vendor()),
gleam@dict:dict(binary(), caffeine_lang@linker@artifacts:param_info())
) -> {ok,
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:linked()))} |
{error, caffeine_lang@errors:compilation_error()}.
build_all(Expectations_with_paths, Reserved_labels, Vendor_lookup, Slo_params) ->
Unmeasured_params = build_unmeasured_param_types(Slo_params),
_pipe = Expectations_with_paths,
_pipe@1 = gleam@list:map(
_pipe,
fun(Pair) ->
{Expectations_measurement_collection, File_path} = Pair,
build(
Expectations_measurement_collection,
File_path,
Reserved_labels,
Vendor_lookup,
Unmeasured_params
)
end
),
_pipe@2 = caffeine_lang@errors:from_results(_pipe@1),
gleam@result:map(_pipe@2, fun lists:append/1).