Current section
Files
Jump to
Current section
Files
src/datadog_query@lint.erl
-module(datadog_query@lint).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/datadog_query/lint.gleam").
-export([at_prefixed_attrs/1, unbalanced_braces/1, unbalanced_parens/1, dangling_joiner/1, reserved_word_as_tag_name/1, wildcard_in_quoted_value/1, empty_scope_with_grouping/1]).
-export_type([balance_error/0, balance_state/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 balance_error() :: {unmatched, binary()} | {unclosed, binary()}.
-type balance_state() :: balance_ok | balance_unmatched | balance_unclosed.
-file("src/datadog_query/lint.gleam", 35).
-spec token_to_at_attr(binary()) -> {ok, binary()} | {error, nil}.
token_to_at_attr(Token) ->
Stripped = case gleam_stdlib:string_starts_with(Token, <<"!"/utf8>>) of
true ->
gleam@string:drop_start(Token, 1);
false ->
Token
end,
case gleam_stdlib:string_starts_with(Stripped, <<"@"/utf8>>) of
false ->
{error, nil};
true ->
Attr = case gleam@string:split_once(Stripped, <<":"/utf8>>) of
{ok, {Before_colon, _}} ->
Before_colon;
{error, _} ->
Stripped
end,
case Attr of
<<"@"/utf8>> ->
{error, nil};
_ ->
{ok, Attr}
end
end.
-file("src/datadog_query/lint.gleam", 23).
?DOC(
" Scans a Datadog query string for `@`-prefixed attribute names used in\n"
" filter position (e.g. `@type:foo` or `@type IN (...)`).\n"
"\n"
" Datadog metric filters strip the leading `@` from log facets when log-based\n"
" metrics are generated — the metric tag for log facet `@type` is just `type`.\n"
" A query that filters on `@type` against a log-based metric is rejected by\n"
" the SLO API with `400: numerator query is invalid`.\n"
"\n"
" Returns a deduplicated list of `@`-prefixed attribute names found.\n"
).
-spec at_prefixed_attrs(binary()) -> list(binary()).
at_prefixed_attrs(Query) ->
_pipe = Query,
_pipe@1 = gleam@string:replace(_pipe, <<"{"/utf8>>, <<" "/utf8>>),
_pipe@2 = gleam@string:replace(_pipe@1, <<"}"/utf8>>, <<" "/utf8>>),
_pipe@3 = gleam@string:replace(_pipe@2, <<"("/utf8>>, <<" "/utf8>>),
_pipe@4 = gleam@string:replace(_pipe@3, <<")"/utf8>>, <<" "/utf8>>),
_pipe@5 = gleam@string:replace(_pipe@4, <<","/utf8>>, <<" "/utf8>>),
_pipe@6 = gleam@string:split(_pipe@5, <<" "/utf8>>),
_pipe@7 = gleam@list:filter_map(_pipe@6, fun token_to_at_attr/1),
gleam@list:unique(_pipe@7).
-file("src/datadog_query/lint.gleam", 85).
-spec scan_balance(list(binary()), binary(), binary(), integer(), boolean()) -> balance_state().
scan_balance(Graphemes, Open, Close, Depth, In_string) ->
case Graphemes of
[] ->
case Depth of
0 ->
balance_ok;
_ ->
balance_unclosed
end;
[G | Rest] ->
case {In_string, G} of
{true, <<"\""/utf8>>} ->
scan_balance(Rest, Open, Close, Depth, false);
{true, _} ->
scan_balance(Rest, Open, Close, Depth, true);
{false, <<"\""/utf8>>} ->
scan_balance(Rest, Open, Close, Depth, true);
{false, _} ->
case {G =:= Open, G =:= Close} of
{true, _} ->
scan_balance(Rest, Open, Close, Depth + 1, false);
{_, true} ->
case Depth of
0 ->
balance_unmatched;
_ ->
scan_balance(
Rest,
Open,
Close,
Depth - 1,
false
)
end;
{_, _} ->
scan_balance(Rest, Open, Close, Depth, false)
end
end
end.
-file("src/datadog_query/lint.gleam", 66).
-spec check_balance(binary(), binary(), binary()) -> {ok, nil} |
{error, balance_error()}.
check_balance(Query, Open, Close) ->
Result = scan_balance(
gleam@string:to_graphemes(Query),
Open,
Close,
0,
false
),
case Result of
balance_ok ->
{ok, nil};
balance_unmatched ->
{error, {unmatched, Close}};
balance_unclosed ->
{error, {unclosed, Open}}
end.
-file("src/datadog_query/lint.gleam", 57).
?DOC(
" Verifies `{` / `}` are balanced. Ignores braces inside double-quoted\n"
" strings so `tag:\"a{b}c\"` doesn't trigger a false positive.\n"
).
-spec unbalanced_braces(binary()) -> {ok, nil} | {error, balance_error()}.
unbalanced_braces(Query) ->
check_balance(Query, <<"{"/utf8>>, <<"}"/utf8>>).
-file("src/datadog_query/lint.gleam", 62).
?DOC(" Verifies `(` / `)` are balanced (string-aware).\n").
-spec unbalanced_parens(binary()) -> {ok, nil} | {error, balance_error()}.
unbalanced_parens(Query) ->
check_balance(Query, <<"("/utf8>>, <<")"/utf8>>).
-file("src/datadog_query/lint.gleam", 149).
-spec check_scope_joiners(binary()) -> list(binary()).
check_scope_joiners(Scope) ->
Trimmed = gleam@string:trim(Scope),
case Trimmed of
<<""/utf8>> ->
[];
_ ->
Leading = case gleam_stdlib:string_starts_with(
Trimmed,
<<"AND "/utf8>>
)
orelse gleam_stdlib:string_starts_with(Trimmed, <<"OR "/utf8>>) of
true ->
[<<"leading joiner: "/utf8, Trimmed/binary>>];
false ->
[]
end,
Trailing = case gleam_stdlib:string_ends_with(
Trimmed,
<<" AND"/utf8>>
)
orelse gleam_stdlib:string_ends_with(Trimmed, <<" OR"/utf8>>) of
true ->
[<<"trailing joiner: "/utf8, Trimmed/binary>>];
false ->
[]
end,
Doubled = case ((gleam_stdlib:contains_string(
Trimmed,
<<" AND AND "/utf8>>
)
orelse gleam_stdlib:contains_string(Trimmed, <<" OR OR "/utf8>>))
orelse gleam_stdlib:contains_string(Trimmed, <<" AND OR "/utf8>>))
orelse gleam_stdlib:contains_string(Trimmed, <<" OR AND "/utf8>>) of
true ->
[<<"doubled joiner: "/utf8, Trimmed/binary>>];
false ->
[]
end,
lists:append([Leading, Trailing, Doubled])
end.
-file("src/datadog_query/lint.gleam", 133).
-spec extract_scopes(binary()) -> list(binary()).
extract_scopes(Query) ->
case gleam@string:split(Query, <<"{"/utf8>>) of
[_ | Rest] ->
gleam@list:filter_map(
Rest,
fun(Chunk) ->
case gleam@string:split_once(Chunk, <<"}"/utf8>>) of
{ok, {Inside, _}} ->
{ok, Inside};
{error, _} ->
{error, nil}
end
end
);
_ ->
[]
end.
-file("src/datadog_query/lint.gleam", 127).
?DOC(
" Detects scope-internal artifacts produced by composing `tag_in([])` /\n"
" `tag_not_in([])` returning `\"\"`. Returns the offending fragment(s) when\n"
" the scope contains a leading/trailing/doubled boolean joiner.\n"
"\n"
" Examples flagged:\n"
" `metric{ AND env:prod}` → [\"leading AND\"]\n"
" `metric{env:prod AND }` → [\"trailing AND\"]\n"
" `metric{env:prod AND AND service:api}` → [\"doubled joiner\"]\n"
).
-spec dangling_joiner(binary()) -> list(binary()).
dangling_joiner(Query) ->
_pipe = Query,
_pipe@1 = extract_scopes(_pipe),
gleam@list:flat_map(_pipe@1, fun check_scope_joiners/1).
-file("src/datadog_query/lint.gleam", 185).
?DOC(
" Detects tag *names* that collide with grammar keywords. Datadog accepts\n"
" these tokens as separators, so a tag literally named `IN` produces\n"
" confusing parse errors.\n"
).
-spec reserved_word_as_tag_name(binary()) -> list(binary()).
reserved_word_as_tag_name(Query) ->
Reserved = [<<"IN"/utf8>>,
<<"NOT"/utf8>>,
<<"AND"/utf8>>,
<<"OR"/utf8>>,
<<"true"/utf8>>,
<<"false"/utf8>>],
_pipe = Query,
_pipe@1 = extract_scopes(_pipe),
_pipe@7 = gleam@list:flat_map(_pipe@1, fun(Scope) -> _pipe@2 = Scope,
_pipe@3 = gleam@string:replace(_pipe@2, <<"("/utf8>>, <<" "/utf8>>),
_pipe@4 = gleam@string:replace(_pipe@3, <<")"/utf8>>, <<" "/utf8>>),
_pipe@5 = gleam@string:replace(_pipe@4, <<","/utf8>>, <<" "/utf8>>),
_pipe@6 = gleam@string:split(_pipe@5, <<" "/utf8>>),
gleam@list:filter_map(
_pipe@6,
fun(Token) ->
case gleam@string:split_once(Token, <<":"/utf8>>) of
{ok, {Name, _}} ->
case gleam@list:contains(Reserved, Name) of
true ->
{ok, Name};
false ->
{error, nil}
end;
{error, _} ->
{error, nil}
end
end
) end),
gleam@list:unique(_pipe@7).
-file("src/datadog_query/lint.gleam", 218).
-spec scan_wildcard_quoted(list(binary()), boolean()) -> boolean().
scan_wildcard_quoted(Graphemes, In_string) ->
case Graphemes of
[] ->
false;
[G | Rest] ->
case {In_string, G} of
{true, <<"*"/utf8>>} ->
true;
{true, <<"\\"/utf8>>} ->
case Rest of
[_ | Tail] ->
scan_wildcard_quoted(Tail, true);
[] ->
false
end;
{true, <<"\""/utf8>>} ->
scan_wildcard_quoted(Rest, false);
{true, _} ->
scan_wildcard_quoted(Rest, true);
{false, <<"\""/utf8>>} ->
scan_wildcard_quoted(Rest, true);
{false, _} ->
scan_wildcard_quoted(Rest, false)
end
end.
-file("src/datadog_query/lint.gleam", 214).
?DOC(
" Detects values where wildcards have been collapsed to literals by\n"
" quoting. Datadog matches `*` as a wildcard in bare values but as a\n"
" literal asterisk inside `\"...\"`.\n"
"\n"
" Example: `metric{service:\"foo*\"}` — the `*` is now literal.\n"
).
-spec wildcard_in_quoted_value(binary()) -> boolean().
wildcard_in_quoted_value(Query) ->
scan_wildcard_quoted(gleam@string:to_graphemes(Query), false).
-file("src/datadog_query/lint.gleam", 240).
?DOC(
" Detects `metric{} by {tag}` — a legal but suspicious query (a copy-paste\n"
" bug that would group a \"match-everything\" set; usually `metric{*}` was\n"
" intended).\n"
).
-spec empty_scope_with_grouping(binary()) -> boolean().
empty_scope_with_grouping(Query) ->
case gleam@string:split_once(Query, <<"{}"/utf8>>) of
{ok, {_, After}} ->
gleam_stdlib:contains_string(After, <<"by {"/utf8>>);
{error, _} ->
false
end.