Current section

Files

Jump to
caffeine_lang src caffeine_lang@phase_3@semantic.erl
Raw

src/caffeine_lang@phase_3@semantic.erl

-module(caffeine_lang@phase_3@semantic).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/caffeine_lang/phase_3/semantic.gleam").
-export([validate_services_from_instantiation/1, validate_sli_types_exist_from_instantiation/1, validate_slos_thresholds_reasonable_from_instantiation/1, perform_semantic_analysis/1]).
-export_type([semantic_analysis_error/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.
?MODULEDOC(
" We perform the following semantic analysis against the intermediate representation of the organization.\n"
" (3) Ensure that all SLOs are referencing valid SLI types for their service.\n"
" (5) Ensure that all filters are valid for the SLI type.\n"
" (7) Ensure no duplicate service names or duplicate SLOs for a single service and team combination.\n"
" (8) Ensure that all services, sli types, and sli filters in the specification are unique.\n"
" (9) Ensure that all filters within the SLOs are the correct types for the SLI type's filters.\n"
" (10) For every required true sli filter, ensure that it is present in the SLO.\n"
" (11) For every filter within SLO instantiation, if unknown, reject it.\n"
" (12) Perform query template hygiene:\n"
" (12.1) Ensure that the query template is valid and not empty.\n"
" (12.2) Every template variable in the query template must be present in the filters of the SLI type.\n"
" (13) Warn on unused sli types, sli filters, and services.\n"
" (14) Normalize team names, service names, sli type names, sli filter names, and sli filter attribute names to lowercase.\n"
).
-type semantic_analysis_error() :: {undefined_service_error, list(binary())} |
{undefined_sli_type_error, list(binary())} |
{invalid_slo_threshold_error, list(float())} |
{duplicate_service_error, list(binary())}.
-file("src/caffeine_lang/phase_3/semantic.gleam", 25).
-spec slos_filtered_attribute(
caffeine_lang@types@ast:organization(),
fun((caffeine_lang@types@ast:slo()) -> PMC),
fun((PMC) -> boolean())
) -> list(PMC).
slos_filtered_attribute(Organization, Extract_fn, Predicate_fn) ->
_pipe = erlang:element(2, Organization),
_pipe@1 = gleam@list:flat_map(
_pipe,
fun(Team) -> erlang:element(3, Team) end
),
_pipe@2 = gleam@list:map(_pipe@1, Extract_fn),
_pipe@3 = gleam@list:filter(_pipe@2, Predicate_fn),
gleam@list:unique(_pipe@3).
-file("src/caffeine_lang/phase_3/semantic.gleam", 37).
-spec validate_services_from_instantiation(
caffeine_lang@types@ast:organization()
) -> {ok, boolean()} | {error, semantic_analysis_error()}.
validate_services_from_instantiation(Organization) ->
Defined_services = begin
_pipe = erlang:element(3, Organization),
gleam@list:map(
_pipe,
fun(Service_definition) -> erlang:element(2, Service_definition) end
)
end,
Undefined_services = slos_filtered_attribute(
Organization,
fun(Slo) -> erlang:element(5, Slo) end,
fun(Service_name) ->
not gleam@list:contains(Defined_services, Service_name)
end
),
case Undefined_services of
[] ->
{ok, true};
Services ->
{error, {undefined_service_error, Services}}
end.
-file("src/caffeine_lang/phase_3/semantic.gleam", 58).
-spec validate_sli_types_exist_from_instantiation(
caffeine_lang@types@ast:organization()
) -> {ok, boolean()} | {error, semantic_analysis_error()}.
validate_sli_types_exist_from_instantiation(Organization) ->
Defined_sli_types = begin
_pipe = erlang:element(3, Organization),
_pipe@1 = gleam@list:map(
_pipe,
fun(Service_definition) -> erlang:element(3, Service_definition) end
),
_pipe@2 = gleam@list:flat_map(_pipe@1, fun(Sli_types) -> Sli_types end),
_pipe@3 = gleam@list:map(
_pipe@2,
fun(Sli_type) -> erlang:element(2, Sli_type) end
),
gleam@list:unique(_pipe@3)
end,
Undefined_sli_types = slos_filtered_attribute(
Organization,
fun(Slo) -> erlang:element(4, Slo) end,
fun(Sli_type_name) ->
not gleam@list:contains(Defined_sli_types, Sli_type_name)
end
),
case Undefined_sli_types of
[] ->
{ok, true};
Sli_types@1 ->
{error, {undefined_sli_type_error, Sli_types@1}}
end.
-file("src/caffeine_lang/phase_3/semantic.gleam", 84).
?DOC(" Ensure that all SLO thresholds are between 0 and 100.\n").
-spec validate_slos_thresholds_reasonable_from_instantiation(
caffeine_lang@types@ast:organization()
) -> {ok, boolean()} | {error, semantic_analysis_error()}.
validate_slos_thresholds_reasonable_from_instantiation(Organization) ->
Invalid_thresholds = slos_filtered_attribute(
Organization,
fun(Slo) -> erlang:element(3, Slo) end,
fun(Threshold) -> (Threshold < +0.0) orelse (Threshold > 100.0) end
),
case Invalid_thresholds of
[] ->
{ok, true};
Thresholds ->
{error, {invalid_slo_threshold_error, Thresholds}}
end.
-file("src/caffeine_lang/phase_3/semantic.gleam", 100).
-spec perform_semantic_analysis(caffeine_lang@types@ast:organization()) -> {ok,
boolean()} |
{error, semantic_analysis_error()}.
perform_semantic_analysis(Organization) ->
case validate_services_from_instantiation(Organization) of
{ok, _} ->
case validate_sli_types_exist_from_instantiation(Organization) of
{ok, _} ->
case validate_slos_thresholds_reasonable_from_instantiation(
Organization
) of
{ok, _} ->
{ok, true};
{error, Error} ->
{error, Error}
end;
{error, Error@1} ->
{error, Error@1}
end;
{error, Error@2} ->
{error, Error@2}
end.