Packages
caffeine_lang
1.0.1
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_query_language@generator.erl
-module(caffeine_query_language@generator).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_query_language/generator.gleam").
-export([operator_to_datadog_query/1, substitute_words/2, extract_words/1, exp_to_string/1, resolve_slo_query_typed/2, resolve_slo_query/2, resolve_slo_to_hcl/2]).
-export_type([named_query/0, resolved_slo_query/0, slo_type/0, resolved_slo_hcl/0]).
-if(?OTP_RELEASE >= 27).
-define(MODULEDOC(Str), -moduledoc(Str)).
-define(DOC(Str), -doc(Str)).
-else.
-define(MODULEDOC(Str), -compile([])).
-define(DOC(Str), -compile([])).
-endif.
-type named_query() :: {named_query, binary(), binary()}.
-type resolved_slo_query() :: {resolved_good_over_total, binary(), binary()} |
{resolved_time_slice,
binary(),
integer(),
float(),
binary(),
list(named_query())}.
-type slo_type() :: metric_slo | time_slice_slo.
-type resolved_slo_hcl() :: {resolved_slo_hcl,
slo_type(),
list(terra_madre@hcl:block())}.
-file("src/caffeine_query_language/generator.gleam", 48).
-spec comparator_to_string(caffeine_query_language@parser:comparator()) -> binary().
comparator_to_string(Comparator) ->
case Comparator of
less_than ->
<<"<"/utf8>>;
less_than_or_equal_to ->
<<"<="/utf8>>;
greater_than ->
<<">"/utf8>>;
greater_than_or_equal_to ->
<<">="/utf8>>
end.
-file("src/caffeine_query_language/generator.gleam", 57).
-spec float_to_string(float()) -> binary().
float_to_string(F) ->
Truncated = erlang:trunc(F),
Is_whole = erlang:float(Truncated) =:= F,
case Is_whole of
true ->
erlang:integer_to_binary(Truncated);
false ->
gleam_stdlib:float_to_string(F)
end.
-file("src/caffeine_query_language/generator.gleam", 83).
-spec get_leftmost_word(caffeine_query_language@parser:exp()) -> gleam@option:option(binary()).
get_leftmost_word(Exp) ->
case Exp of
{primary, {primary_word, {word, W}}} ->
{some, W};
{primary, {primary_exp, Inner_exp}} ->
get_leftmost_word(Inner_exp);
{time_slice_expr, _} ->
none;
{operator_expr, Left, _, _} ->
get_leftmost_word(Left)
end.
-file("src/caffeine_query_language/generator.gleam", 93).
-spec all_divisions(caffeine_query_language@parser:exp()) -> boolean().
all_divisions(Exp) ->
case Exp of
{primary, _} ->
true;
{operator_expr, Left, Right, 'div'} ->
all_divisions(Left) andalso all_divisions(Right);
_ ->
false
end.
-file("src/caffeine_query_language/generator.gleam", 69).
-spec is_path_expression(caffeine_query_language@parser:exp()) -> boolean().
is_path_expression(Exp) ->
case get_leftmost_word(Exp) of
{some, W} ->
case gleam_stdlib:string_ends_with(W, <<":"/utf8>>) of
true ->
all_divisions(Exp);
false ->
false
end;
none ->
false
end.
-file("src/caffeine_query_language/generator.gleam", 149).
?DOC(false).
-spec operator_to_datadog_query(caffeine_query_language@parser:operator()) -> binary().
operator_to_datadog_query(Operator) ->
case Operator of
add ->
<<"+"/utf8>>;
sub ->
<<"-"/utf8>>;
mul ->
<<"*"/utf8>>;
'div' ->
<<"/"/utf8>>
end.
-file("src/caffeine_query_language/generator.gleam", 162).
?DOC(false).
-spec substitute_words(
caffeine_query_language@parser:exp(),
gleam@dict:dict(binary(), binary())
) -> caffeine_query_language@parser:exp().
substitute_words(Exp, Substitutions) ->
case Exp of
{primary, {primary_word, {word, Name}}} ->
Value = begin
_pipe = gleam_stdlib:map_get(Substitutions, Name),
gleam@result:unwrap(_pipe, Name)
end,
{primary, {primary_word, {word, Value}}};
{primary, {primary_exp, Inner}} ->
{primary, {primary_exp, substitute_words(Inner, Substitutions)}};
{time_slice_expr, Spec} ->
Query = begin
_pipe@1 = gleam_stdlib:map_get(
Substitutions,
erlang:element(2, Spec)
),
gleam@result:unwrap(_pipe@1, erlang:element(2, Spec))
end,
{time_slice_expr,
{time_slice_exp,
Query,
erlang:element(3, Spec),
erlang:element(4, Spec),
erlang:element(5, Spec)}};
{operator_expr, Left, Right, Op} ->
{operator_expr,
substitute_words(Left, Substitutions),
substitute_words(Right, Substitutions),
Op}
end.
-file("src/caffeine_query_language/generator.gleam", 191).
?DOC(false).
-spec extract_words(caffeine_query_language@parser:exp()) -> list(binary()).
extract_words(Exp) ->
case Exp of
{primary, {primary_word, {word, Name}}} ->
[Name];
{primary, {primary_exp, Inner}} ->
extract_words(Inner);
{time_slice_expr, _} ->
[];
{operator_expr, Left, Right, _} ->
_pipe = lists:append(extract_words(Left), extract_words(Right)),
gleam@list:unique(_pipe)
end.
-file("src/caffeine_query_language/generator.gleam", 223).
?DOC(" Check if parentheses are balanced in a string.\n").
-spec is_balanced(binary(), integer()) -> boolean().
is_balanced(S, Depth) ->
case gleam_stdlib:string_pop_grapheme(S) of
{error, _} ->
Depth =:= 0;
{ok, {<<"("/utf8>>, Rest}} ->
is_balanced(Rest, Depth + 1);
{ok, {<<")"/utf8>>, Rest@1}} ->
case Depth of
0 ->
false;
_ ->
is_balanced(Rest@1, Depth - 1)
end;
{ok, {_, Rest@2}} ->
is_balanced(Rest@2, Depth)
end.
-file("src/caffeine_query_language/generator.gleam", 204).
?DOC(
" Strips outer parentheses from a string if they wrap the entire expression.\n"
" E.g., \"(a + b)\" -> \"a + b\", but \"(a + b) * c\" stays unchanged.\n"
).
-spec strip_outer_parens(binary()) -> binary().
strip_outer_parens(S) ->
Trimmed = gleam@string:trim(S),
case gleam_stdlib:string_starts_with(Trimmed, <<"("/utf8>>) andalso gleam_stdlib:string_ends_with(
Trimmed,
<<")"/utf8>>
) of
true ->
Inner = gleam@string:slice(Trimmed, 1, string:length(Trimmed) - 2),
case is_balanced(Inner, 0) of
true ->
Inner;
false ->
Trimmed
end;
false ->
Trimmed
end.
-file("src/caffeine_query_language/generator.gleam", 103).
-spec exp_to_string_no_spaces(caffeine_query_language@parser:exp()) -> binary().
exp_to_string_no_spaces(Exp) ->
case Exp of
{primary, {primary_word, {word, W}}} ->
W;
{operator_expr, Left, Right, 'div'} ->
<<<<(exp_to_string_no_spaces(Left))/binary, "/"/utf8>>/binary,
(exp_to_string_no_spaces(Right))/binary>>;
_ ->
exp_to_string(Exp)
end.
-file("src/caffeine_query_language/generator.gleam", 15).
?DOC(" Converts an expression AST node to its string representation.\n").
-spec exp_to_string(caffeine_query_language@parser:exp()) -> binary().
exp_to_string(Exp) ->
case Exp of
{primary, Primary} ->
primary_to_string(Primary, none);
{time_slice_expr, Spec} ->
<<<<<<<<<<<<<<<<"time_slice("/utf8,
(erlang:element(2, Spec))/binary>>/binary,
" "/utf8>>/binary,
(comparator_to_string(
erlang:element(3, Spec)
))/binary>>/binary,
" "/utf8>>/binary,
(float_to_string(erlang:element(4, Spec)))/binary>>/binary,
" per "/utf8>>/binary,
(float_to_string(erlang:element(5, Spec)))/binary>>/binary,
"s)"/utf8>>;
{operator_expr, Numerator, Denominator, Operator} ->
case {Operator, is_path_expression(Exp)} of
{'div', true} ->
exp_to_string_no_spaces(Exp);
{_, _} ->
Left = exp_to_string_with_context(
Numerator,
{some, Operator},
true
),
Right = exp_to_string_with_context(
Denominator,
{some, Operator},
false
),
Op = operator_to_datadog_query(Operator),
<<<<<<<<Left/binary, " "/utf8>>/binary, Op/binary>>/binary,
" "/utf8>>/binary,
Right/binary>>
end
end.
-file("src/caffeine_query_language/generator.gleam", 138).
-spec primary_to_string(
caffeine_query_language@parser:primary(),
gleam@option:option(caffeine_query_language@parser:operator())
) -> binary().
primary_to_string(Primary, _) ->
case Primary of
{primary_word, Word} ->
erlang:element(2, Word);
{primary_exp, Exp} ->
<<<<"("/utf8, (exp_to_string(Exp))/binary>>/binary, ")"/utf8>>
end.
-file("src/caffeine_query_language/generator.gleam", 112).
-spec exp_to_string_with_context(
caffeine_query_language@parser:exp(),
gleam@option:option(caffeine_query_language@parser:operator()),
boolean()
) -> binary().
exp_to_string_with_context(Exp, Parent_op, _) ->
case Exp of
{primary, Primary} ->
primary_to_string(Primary, Parent_op);
{time_slice_expr, _} ->
exp_to_string(Exp);
{operator_expr, Numerator, Denominator, Operator} ->
case {Operator, is_path_expression(Exp)} of
{'div', true} ->
exp_to_string_no_spaces(Exp);
{_, _} ->
Left = exp_to_string_with_context(
Numerator,
{some, Operator},
true
),
Right = exp_to_string_with_context(
Denominator,
{some, Operator},
false
),
Op = operator_to_datadog_query(Operator),
<<<<<<<<Left/binary, " "/utf8>>/binary, Op/binary>>/binary,
" "/utf8>>/binary,
Right/binary>>
end
end.
-file("src/caffeine_query_language/generator.gleam", 269).
?DOC(
" Parse a value expression, resolve to primitive, substitute words,\n"
" and return the resolved SLO query type.\n"
).
-spec resolve_slo_query_typed(binary(), gleam@dict:dict(binary(), binary())) -> {ok,
resolved_slo_query()} |
{error, binary()}.
resolve_slo_query_typed(Value_expr, Substitutions) ->
case caffeine_query_language@parser:parse_expr(Value_expr) of
{error, Err} ->
{error, <<"Parse error: "/utf8, Err/binary>>};
{ok, Exp_container} ->
case caffeine_query_language@resolver:resolve_primitives(
Exp_container
) of
{ok, {good_over_total, Numerator_exp, Denominator_exp}} ->
Numerator_str = begin
_pipe = substitute_words(Numerator_exp, Substitutions),
exp_to_string(_pipe)
end,
Denominator_str = begin
_pipe@1 = substitute_words(
Denominator_exp,
Substitutions
),
exp_to_string(_pipe@1)
end,
{ok,
{resolved_good_over_total,
Numerator_str,
Denominator_str}};
{ok,
{time_slice, Comparator, Interval_seconds, Threshold, Query}} ->
Comparator_str = case Comparator of
less_than ->
<<"<"/utf8>>;
less_than_or_equal_to ->
<<"<="/utf8>>;
greater_than ->
<<">"/utf8>>;
greater_than_or_equal_to ->
<<">="/utf8>>
end,
case caffeine_query_language@parser:parse_expr(Query) of
{ok, Query_exp_container} ->
Query_exp = erlang:element(2, Query_exp_container),
Words = extract_words(Query_exp),
Named_queries = begin
_pipe@2 = Words,
gleam@list:filter_map(
_pipe@2,
fun(Word) ->
case gleam_stdlib:map_get(
Substitutions,
Word
) of
{ok, Resolved} ->
{ok,
{named_query,
Word,
Resolved}};
{error, _} ->
{error, nil}
end
end
)
end,
case Named_queries of
[] ->
{ok,
{resolved_time_slice,
Comparator_str,
Interval_seconds,
Threshold,
<<"query1"/utf8>>,
[{named_query,
<<"query1"/utf8>>,
Query}]}};
_ ->
Formula_expr = strip_outer_parens(Query),
{ok,
{resolved_time_slice,
Comparator_str,
Interval_seconds,
Threshold,
Formula_expr,
Named_queries}}
end;
{error, _} ->
Resolved_query = begin
_pipe@3 = gleam_stdlib:map_get(
Substitutions,
Query
),
gleam@result:unwrap(_pipe@3, Query)
end,
{ok,
{resolved_time_slice,
Comparator_str,
Interval_seconds,
Threshold,
<<"query1"/utf8>>,
[{named_query,
<<"query1"/utf8>>,
Resolved_query}]}}
end;
{error, Err@1} ->
{error,
<<"Resolution error: "/utf8,
(erlang:element(2, Err@1))/binary>>}
end
end.
-file("src/caffeine_query_language/generator.gleam", 353).
?DOC(
" Parse a value expression, resolve to GoodOverTotal primitive, substitute words,\n"
" and return the numerator and denominator as strings.\n"
" Panics if parsing or resolution fails.\n"
).
-spec resolve_slo_query(binary(), gleam@dict:dict(binary(), binary())) -> {binary(),
binary()}.
resolve_slo_query(Value_expr, Substitutions) ->
case resolve_slo_query_typed(Value_expr, Substitutions) of
{ok, {resolved_good_over_total, Numerator, Denominator}} ->
{Numerator, Denominator};
_ ->
{<<""/utf8>>, <<""/utf8>>}
end.
-file("src/caffeine_query_language/generator.gleam", 365).
?DOC(
" Parse a value expression, resolve to primitive, substitute words,\n"
" and return HCL blocks ready for Datadog terraform generation.\n"
).
-spec resolve_slo_to_hcl(binary(), gleam@dict:dict(binary(), binary())) -> {ok,
resolved_slo_hcl()} |
{error, binary()}.
resolve_slo_to_hcl(Value_expr, Substitutions) ->
case resolve_slo_query_typed(Value_expr, Substitutions) of
{ok, {resolved_good_over_total, Numerator, Denominator}} ->
Query_block = terra_madre@hcl:simple_block(
<<"query"/utf8>>,
[{<<"numerator"/utf8>>, {string_literal, Numerator}},
{<<"denominator"/utf8>>, {string_literal, Denominator}}]
),
{ok, {resolved_slo_hcl, metric_slo, [Query_block]}};
{ok,
{resolved_time_slice,
Comparator,
Interval_seconds,
Threshold,
Formula_expression,
Named_queries}} ->
Inner_query_blocks = begin
_pipe = Named_queries,
gleam@list:map(
_pipe,
fun(Nq) ->
Metric_query_block = {block,
<<"metric_query"/utf8>>,
[],
maps:from_list(
[{<<"data_source"/utf8>>,
{string_literal, <<"metrics"/utf8>>}},
{<<"name"/utf8>>,
{string_literal, erlang:element(2, Nq)}},
{<<"query"/utf8>>,
{string_literal, erlang:element(3, Nq)}}]
),
[]},
{block,
<<"query"/utf8>>,
[],
maps:new(),
[Metric_query_block]}
end
)
end,
Formula_block = {block,
<<"formula"/utf8>>,
[],
maps:from_list(
[{<<"formula_expression"/utf8>>,
{string_literal, Formula_expression}}]
),
[]},
Outer_query_block = {block,
<<"query"/utf8>>,
[],
maps:new(),
[Formula_block | Inner_query_blocks]},
Time_slice_block = {block,
<<"time_slice"/utf8>>,
[],
maps:from_list(
[{<<"comparator"/utf8>>, {string_literal, Comparator}},
{<<"query_interval_seconds"/utf8>>,
{int_literal, Interval_seconds}},
{<<"threshold"/utf8>>, {float_literal, Threshold}}]
),
[Outer_query_block]},
Sli_specification_block = {block,
<<"sli_specification"/utf8>>,
[],
maps:new(),
[Time_slice_block]},
{ok, {resolved_slo_hcl, time_slice_slo, [Sli_specification_block]}};
{error, Err} ->
{error, Err}
end.