Packages
caffeine_lang
6.2.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@codegen@datadog.erl
-module(caffeine_lang@codegen@datadog).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/codegen/datadog.gleam").
-export([resolve_indicators/1, window_to_timeframe/1, ir_to_terraform_resource/1, generate_resources/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/codegen/datadog.gleam", 166).
?DOC(
" Resolve a single indicator entry. Returns a triple `#(name, resolved_value,\n"
" indicator_source)`:\n"
" - `resolved_value` is the post-template-resolution Value (StringValue\n"
" for inline-query indicators, ExternalIndicatorValue for relay-fed)\n"
" for the `ir.values` writeback.\n"
" - `indicator_source` is the typed `IndicatorSource` for `slo.indicators`.\n"
" External-indicator `value_extraction` is preserved from the pre-existing\n"
" `ir.slo.indicators` entry (populated by the linker) — the Value layer\n"
" can't carry the resolved type, so we look it up here.\n"
).
-spec resolve_one_indicator(
binary(),
caffeine_lang@value:value(),
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:deps_validated()),
binary()
) -> {ok,
{binary(),
caffeine_lang@value:value(),
caffeine_lang@linker@ir:indicator_source()}} |
{error, caffeine_lang@errors:compilation_error()}.
resolve_one_indicator(Name, Val, Ir, Identifier) ->
case Val of
{string_value, Q} ->
gleam@result:map(
caffeine_lang@codegen@datadog_template:parse_and_resolve_query_template(
Q,
erlang:element(4, Ir),
Identifier
),
fun(Resolved) ->
{Name, {string_value, Resolved}, {literal_query, Resolved}}
end
);
{external_indicator_value, Source, Match, Value_path} ->
gleam@result:'try'(
begin
_pipe = Match,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:try_map(
_pipe@1,
fun(Pair) ->
{Field, Field_val} = Pair,
case Field_val of
{string_value, S} ->
gleam@result:map(
caffeine_lang@codegen@datadog_template:parse_and_resolve_query_template(
S,
erlang:element(4, Ir),
Identifier
),
fun(Resolved@1) ->
{Field, {string_value, Resolved@1}}
end
);
Other ->
{ok, {Field, Other}}
end
end
),
gleam@result:map(_pipe@2, fun maps:from_list/1)
end,
fun(Resolved_match) ->
Value_extraction = case gleam_stdlib:map_get(
erlang:element(3, erlang:element(5, Ir)),
Name
) of
{ok, {external_signal, _, _, Ve}} ->
Ve;
_ ->
none
end,
{ok,
{Name,
{external_indicator_value,
Source,
Resolved_match,
Value_path},
{external_signal,
Source,
Resolved_match,
Value_extraction}}}
end
);
Other@1 ->
{error,
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<<<<<<<"expectation '"/utf8, Identifier/binary>>/binary,
"' - indicator '"/utf8>>/binary,
Name/binary>>/binary,
"' has unsupported value shape: "/utf8>>/binary,
(caffeine_lang@value:classify(Other@1))/binary>>
)}
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 39).
?DOC(false).
-spec resolve_indicators(
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:deps_validated())
) -> {ok,
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved())} |
{error, caffeine_lang@errors:compilation_error()}.
resolve_indicators(Ir) ->
Values_index = caffeine_lang@helpers:index_value_tuples(
erlang:element(4, Ir)
),
gleam@result:'try'(
begin
_pipe = gleam_stdlib:map_get(Values_index, <<"indicators"/utf8>>),
gleam@result:replace_error(
_pipe,
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(Ir))/binary>>/binary,
"' - missing 'indicators' field in IR"/utf8>>
)
)
end,
fun(Indicators_value_tuple) ->
gleam@result:'try'(
begin
_pipe@1 = caffeine_lang@value:extract_dict(
erlang:element(4, Indicators_value_tuple)
),
gleam@result:map_error(
_pipe@1,
fun(_) ->
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to decode indicators"/utf8>>
)
end
)
end,
fun(Raw_indicators_dict) ->
Identifier = caffeine_lang@linker@ir:ir_to_identifier(Ir),
gleam@result:'try'(
begin
_pipe@2 = Raw_indicators_dict,
_pipe@3 = maps:to_list(_pipe@2),
gleam@list:try_map(
_pipe@3,
fun(Pair) ->
{Name, Raw_value} = Pair,
resolve_one_indicator(
Name,
Raw_value,
Ir,
Identifier
)
end
)
end,
fun(Resolved_pairs) ->
New_value_dict = begin
_pipe@4 = Resolved_pairs,
_pipe@5 = gleam@list:map(
_pipe@4,
fun(Triple) ->
{erlang:element(1, Triple),
erlang:element(2, Triple)}
end
),
_pipe@6 = maps:from_list(_pipe@5),
{dict_value, _pipe@6}
end,
New_indicators_value_tuple = {value_tuple,
<<"indicators"/utf8>>,
erlang:element(3, Indicators_value_tuple),
New_value_dict},
Resolved_indicators_dict = begin
_pipe@7 = Resolved_pairs,
_pipe@8 = gleam@list:map(
_pipe@7,
fun(Triple@1) ->
{erlang:element(1, Triple@1),
erlang:element(3, Triple@1)}
end
),
maps:from_list(_pipe@8)
end,
gleam@result:'try'(
case gleam_stdlib:map_get(
Values_index,
<<"evaluation"/utf8>>
) of
{error, _} ->
{ok, none};
{ok, Evaluation_tuple} ->
gleam@result:'try'(
begin
_pipe@9 = caffeine_lang@value:extract_string(
erlang:element(
4,
Evaluation_tuple
)
),
gleam@result:map_error(
_pipe@9,
fun(_) ->
caffeine_lang@errors:semantic_analysis_template_resolution_error(
<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to decode 'evaluation' field as string"/utf8>>
)
end
)
end,
fun(Evaluation_string) ->
_pipe@10 = caffeine_lang@codegen@datadog_template:parse_and_resolve_query_template(
Evaluation_string,
erlang:element(4, Ir),
Identifier
),
gleam@result:map(
_pipe@10,
fun(Resolved_evaluation) ->
{some,
{value_tuple,
<<"evaluation"/utf8>>,
erlang:element(
3,
Evaluation_tuple
),
{string_value,
Resolved_evaluation}}}
end
)
end
)
end,
fun(Resolved_evaluation_tuple) ->
New_values = begin
_pipe@11 = erlang:element(4, Ir),
gleam@list:map(
_pipe@11,
fun(Vt) ->
case erlang:element(2, Vt) of
<<"indicators"/utf8>> ->
New_indicators_value_tuple;
<<"evaluation"/utf8>> ->
case Resolved_evaluation_tuple of
{some,
New_evaluation} ->
New_evaluation;
none ->
Vt
end;
_ ->
Vt
end
end
)
end,
Resolved_eval = case Resolved_evaluation_tuple of
{some, Vt@1} ->
_pipe@12 = caffeine_lang@value:extract_string(
erlang:element(4, Vt@1)
),
gleam@option:from_result(_pipe@12);
none ->
erlang:element(
5,
erlang:element(5, Ir)
)
end,
New_ir = caffeine_lang@linker@ir:map_slo(
{intermediate_representation,
erlang:element(2, Ir),
erlang:element(3, Ir),
New_values,
erlang:element(5, Ir),
erlang:element(6, Ir)},
fun(Slo) ->
{slo_fields,
erlang:element(2, Slo),
Resolved_indicators_dict,
erlang:element(4, Slo),
Resolved_eval,
erlang:element(6, Slo),
erlang:element(7, Slo),
erlang:element(8, Slo),
erlang:element(9, Slo),
erlang:element(10, Slo),
erlang:element(11, Slo)}
end
),
{ok,
caffeine_lang@linker@ir:promote(New_ir)}
end
)
end
)
end
)
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 252).
?DOC(
" Synthesize a Datadog metric query string for an indicator. `LiteralQuery`\n"
" passes through unchanged (the user-authored query). `ExternalSignal`\n"
" produces a query against the synthesized metric the relay will emit to:\n"
" - no value extraction → count-style:\n"
" `sum:caffeine.<unique>{indicator:<name>}.as_count()`\n"
" - with value extraction → distribution-style:\n"
" `avg:caffeine.<unique>{indicator:<name>}`\n"
"\n"
" One metric per measurement, distinguished by the `indicator:<name>` tag.\n"
" This is the idiomatic Datadog metric-SLO shape — every working example\n"
" in the provider's acceptance tests uses the same metric on both sides\n"
" of the ratio, sliced by tags (e.g. `{type:good}` vs `{*}`). Emitting two\n"
" sibling metrics like `caffeine.<unique>.good` and `caffeine.<unique>.total`\n"
" works in theory but doubles the chicken-and-egg problem (both metrics\n"
" must exist before the SLO can be created).\n"
).
-spec synthesize_indicator_query(
binary(),
binary(),
caffeine_lang@linker@ir:indicator_source()
) -> binary().
synthesize_indicator_query(Unique_identifier, Indicator_name, Src) ->
case Src of
{literal_query, Q} ->
Q;
{external_signal, _, _, Value_extraction} ->
Metric = <<"caffeine."/utf8,
(caffeine_lang@codegen@generator_utils:dd_metric_safe(
Unique_identifier
))/binary>>,
Filter = <<<<"{indicator:"/utf8,
(caffeine_lang@codegen@generator_utils:dd_metric_safe(
Indicator_name
))/binary>>/binary,
"}"/utf8>>,
case Value_extraction of
none ->
<<<<<<"sum:"/utf8, Metric/binary>>/binary, Filter/binary>>/binary,
".as_count()"/utf8>>;
{some, _} ->
<<<<"avg:"/utf8, Metric/binary>>/binary, Filter/binary>>
end
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 483).
-spec description_text_to_expr(binary()) -> terra_madre@hcl:expr().
description_text_to_expr(Text) ->
case gleam_stdlib:contains_string(Text, <<"\n"/utf8>>) of
true ->
{heredoc,
<<"EOT"/utf8>>,
true,
[{literal_part, <<Text/binary, "\n"/utf8>>}]};
false ->
{string_literal, Text}
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 469).
?DOC(
" Combine the SLO description (from `###` doc comments) with the runbook\n"
" link, if either is present. Multi-line descriptions render as an HCL\n"
" heredoc; single-line descriptions render as a plain string literal.\n"
).
-spec build_description_expr(
gleam@option:option(binary()),
gleam@option:option(binary())
) -> gleam@option:option(terra_madre@hcl:expr()).
build_description_expr(Description, Runbook) ->
Runbook_link = gleam@option:map(
Runbook,
fun(Url) -> <<<<"[Runbook]("/utf8, Url/binary>>/binary, ")"/utf8>> end
),
Combined = case {Description, Runbook_link} of
{none, none} ->
none;
{{some, D}, none} ->
{some, D};
{none, {some, R}} ->
{some, R};
{{some, D@1}, {some, R@1}} ->
{some, <<<<D@1/binary, "\n\n"/utf8>>/binary, R@1/binary>>}
end,
gleam@option:map(Combined, fun description_text_to_expr/1).
-file("src/caffeine_lang/codegen/datadog.gleam", 516).
?DOC(false).
-spec window_to_timeframe(integer()) -> {ok, binary()} |
{error, caffeine_lang@errors:compilation_error()}.
window_to_timeframe(Days) ->
Days_string = erlang:integer_to_binary(Days),
case Days of
7 ->
{ok, <<Days_string/binary, "d"/utf8>>};
30 ->
{ok, <<Days_string/binary, "d"/utf8>>};
90 ->
{ok, <<Days_string/binary, "d"/utf8>>};
_ ->
{error,
caffeine_lang@codegen@generator_utils:resolution_error(
<<"datadog"/utf8>>,
<<<<"Illegal window_in_days value: "/utf8,
Days_string/binary>>/binary,
". Accepted values are 7, 30, or 90."/utf8>>
)}
end.
-file("src/caffeine_lang/codegen/datadog.gleam", 492).
?DOC(
" Build dependency relation tag pairs from the relations dict.\n"
" Generates pairs like #(\"soft_dependency\", \"target1,target2\").\n"
).
-spec build_dependency_tags(
gleam@dict:dict(caffeine_lang@linker@dependency:dependency_relation_type(), list(binary()))
) -> list({binary(), binary()}).
build_dependency_tags(Relations) ->
_pipe = Relations,
_pipe@1 = maps:to_list(_pipe),
_pipe@2 = gleam@list:sort(
_pipe@1,
fun(A, B) ->
gleam@string:compare(
caffeine_lang@linker@dependency:relation_type_to_string(
erlang:element(1, A)
),
caffeine_lang@linker@dependency:relation_type_to_string(
erlang:element(1, B)
)
)
end
),
gleam@list:map(
_pipe@2,
fun(Pair) ->
{Relation_type, Targets} = Pair,
Sorted_targets = begin
_pipe@3 = Targets,
gleam@list:sort(_pipe@3, fun gleam@string:compare/2)
end,
{<<(caffeine_lang@linker@dependency:relation_type_to_string(
Relation_type
))/binary,
"_dependency"/utf8>>,
gleam@string:join(Sorted_targets, <<","/utf8>>)}
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 297).
?DOC(false).
-spec ir_to_terraform_resource(
caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved())
) -> {ok, {terra_madre@terraform:resource(), list(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
ir_to_terraform_resource(Ir) ->
Resource_name = terra_madre@common:sanitize_terraform_identifier(
erlang:element(3, Ir)
),
Slo = erlang:element(5, Ir),
Threshold = erlang:element(2, Slo),
Window_in_days = erlang:element(4, Slo),
Indicators = erlang:element(3, Slo),
Evaluation_expr = begin
_pipe = erlang:element(5, Slo),
gleam@option:unwrap(_pipe, <<"numerator / denominator"/utf8>>)
end,
Runbook = erlang:element(7, Slo),
Description = erlang:element(9, Slo),
Indicator_strings = begin
_pipe@1 = Indicators,
_pipe@2 = maps:to_list(_pipe@1),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Pair) ->
{Name, Src} = Pair,
{Name,
synthesize_indicator_query(erlang:element(3, Ir), Name, Src)}
end
),
maps:from_list(_pipe@3)
end,
gleam@result:'try'(
begin
_pipe@4 = caffeine_lang@codegen@datadog_cql:resolve_slo_to_hcl(
Evaluation_expr,
Indicator_strings,
erlang:element(10, Slo)
),
gleam@result:map_error(
_pipe@4,
fun(Err) ->
caffeine_lang@errors:generator_slo_query_resolution_error(
<<<<<<"expectation '"/utf8,
(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary>>/binary,
"' - failed to resolve SLO query: "/utf8>>/binary,
Err/binary>>
)
end
)
end,
fun(_use0) ->
{resolved_slo_hcl, Slo_type, Slo_blocks} = _use0,
Dependency_tags = case erlang:element(8, Slo) of
{some, Relations} ->
build_dependency_tags(Relations);
none ->
[]
end,
User_tag_pairs = erlang:element(6, Slo),
System_tag_pairs = begin
_pipe@5 = caffeine_lang@helpers:build_system_tag_pairs(
erlang:element(3, erlang:element(2, Ir)),
erlang:element(6, erlang:element(2, Ir)),
erlang:element(4, erlang:element(2, Ir)),
erlang:element(5, erlang:element(2, Ir)),
erlang:element(2, erlang:element(2, Ir)),
erlang:element(7, erlang:element(2, Ir))
),
lists:append(_pipe@5, Dependency_tags)
end,
System_tag_keys = begin
_pipe@6 = System_tag_pairs,
_pipe@7 = gleam@list:map(
_pipe@6,
fun(Pair@1) -> erlang:element(1, Pair@1) end
),
gleam@set:from_list(_pipe@7)
end,
User_tag_keys = begin
_pipe@8 = User_tag_pairs,
_pipe@9 = gleam@list:map(
_pipe@8,
fun(Pair@2) -> erlang:element(1, Pair@2) end
),
gleam@set:from_list(_pipe@9)
end,
Overlapping_keys = gleam@set:intersection(
System_tag_keys,
User_tag_keys
),
{Final_system_tag_pairs, Overshadowing_warnings} = case gleam@set:size(
Overlapping_keys
)
> 0 of
true ->
Warn_msgs = begin
_pipe@10 = Overlapping_keys,
_pipe@11 = gleam@set:to_list(_pipe@10),
_pipe@12 = gleam@list:sort(
_pipe@11,
fun gleam@string:compare/2
),
gleam@list:map(
_pipe@12,
fun(Key) ->
<<<<<<(caffeine_lang@linker@ir:ir_to_identifier(
Ir
))/binary,
" - user tag '"/utf8>>/binary,
Key/binary>>/binary,
"' overshadows system tag"/utf8>>
end
)
end,
Filtered = begin
_pipe@13 = System_tag_pairs,
gleam@list:filter(
_pipe@13,
fun(Pair@3) ->
not gleam@set:contains(
Overlapping_keys,
erlang:element(1, Pair@3)
)
end
)
end,
{Filtered, Warn_msgs};
false ->
{System_tag_pairs, []}
end,
At_prefixed_attrs = begin
_pipe@14 = Indicator_strings,
_pipe@15 = maps:values(_pipe@14),
_pipe@16 = lists:append(_pipe@15, [Evaluation_expr]),
_pipe@17 = gleam@list:flat_map(
_pipe@16,
fun datadog_query@lint:at_prefixed_attrs/1
),
gleam@list:unique(_pipe@17)
end,
At_prefix_warnings = begin
_pipe@18 = At_prefixed_attrs,
_pipe@19 = gleam@list:sort(_pipe@18, fun gleam@string:compare/2),
gleam@list:map(
_pipe@19,
fun(Attr) ->
<<<<<<<<<<(caffeine_lang@linker@ir:ir_to_identifier(Ir))/binary,
" - query references '"/utf8>>/binary,
Attr/binary>>/binary,
"' which Datadog rejects in metric SLO filters; use bare '"/utf8>>/binary,
(gleam@string:drop_start(Attr, 1))/binary>>/binary,
"' instead (Datadog strips '@' from log-based metric attribute names)"/utf8>>
end
)
end,
Warnings = lists:append(Overshadowing_warnings, At_prefix_warnings),
Tags = begin
_pipe@20 = lists:append(Final_system_tag_pairs, User_tag_pairs),
_pipe@21 = gleam@list:map(
_pipe@20,
fun(Pair@4) ->
{string_literal,
datadog_query@filter:tag(
erlang:element(1, Pair@4),
erlang:element(2, Pair@4)
)}
end
),
{list_expr, _pipe@21}
end,
Identifier = caffeine_lang@linker@ir:ir_to_identifier(Ir),
gleam@result:'try'(
begin
_pipe@22 = window_to_timeframe(Window_in_days),
gleam@result:map_error(
_pipe@22,
fun(Err@1) ->
caffeine_lang@errors:prefix_error(Err@1, Identifier)
end
)
end,
fun(Window_in_days_string) ->
Thresholds_block = terra_madre@hcl:simple_block(
<<"thresholds"/utf8>>,
[{<<"timeframe"/utf8>>,
{string_literal, Window_in_days_string}},
{<<"target"/utf8>>, {float_literal, Threshold}}]
),
Type_str = case Slo_type of
time_slice_slo ->
<<"time_slice"/utf8>>;
metric_slo ->
<<"metric"/utf8>>
end,
Base_attributes = [{<<"name"/utf8>>,
{string_literal,
erlang:element(
2,
erlang:element(2, erlang:element(2, Ir))
)}},
{<<"type"/utf8>>, {string_literal, Type_str}},
{<<"tags"/utf8>>, Tags}],
Attributes = case build_description_expr(
Description,
Runbook
) of
{some, Desc_expr} ->
[{<<"description"/utf8>>, Desc_expr} |
Base_attributes];
none ->
Base_attributes
end,
{ok,
{{resource,
<<"datadog_service_level_objective"/utf8>>,
Resource_name,
maps:from_list(Attributes),
lists:append(Slo_blocks, [Thresholds_block]),
terra_madre@hcl:empty_meta(),
none},
Warnings}}
end
)
end
).
-file("src/caffeine_lang/codegen/datadog.gleam", 280).
?DOC(false).
-spec generate_resources(
list(caffeine_lang@linker@ir:intermediate_representation(caffeine_lang@linker@ir:resolved()))
) -> {ok, {list(terra_madre@terraform:resource()), list(binary())}} |
{error, caffeine_lang@errors:compilation_error()}.
generate_resources(Irs) ->
_pipe = Irs,
_pipe@1 = gleam@list:try_fold(
_pipe,
{[], []},
fun(Acc, Ir) ->
{Resources, Warning_lists} = Acc,
gleam@result:'try'(
ir_to_terraform_resource(Ir),
fun(_use0) ->
{Resource, Ir_warnings} = _use0,
{ok,
{[Resource | Resources], [Ir_warnings | Warning_lists]}}
end
)
end
),
gleam@result:map(
_pipe@1,
fun(Pair) ->
{lists:reverse(erlang:element(1, Pair)),
lists:append(lists:reverse(erlang:element(2, Pair)))}
end
).