src/lightspeed@ops@template_harness.erl

-module(lightspeed@ops@template_harness).
-compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]).
-define(FILEPATH, "src/lightspeed/ops/template_harness.gleam").
-export([run_scenario/1, run_matrix/0, scenario_label/1, pass_fail_label/1, signature/1, scenario/1, outcomes/1, failed_scenarios/1, report_signature/1]).
-export_type([card_assigns/0, card_slots/0, scenario/0, scenario_outcome/0, report/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(" Deterministic M23 template compiler conformance harness.\n").

-type card_assigns() :: {card_assigns, binary(), boolean()}.

-type card_slots() :: {card_slots, binary(), binary()}.

-type scenario() :: compile_success |
    missing_assign_diagnostic |
    missing_slot_diagnostic |
    invalid_attr_type_diagnostic.

-type scenario_outcome() :: {scenario_outcome, scenario(), boolean(), binary()}.

-type report() :: {report, list(scenario_outcome()), integer()}.

-file("src/lightspeed/ops/template_harness.gleam", 283).
-spec count_failed(list(scenario_outcome())) -> integer().
count_failed(Outcomes) ->
    case Outcomes of
        [] ->
            0;

        [Outcome | Rest] ->
            case erlang:element(3, Outcome) of
                true ->
                    count_failed(Rest);

                false ->
                    1 + count_failed(Rest)
            end
    end.

-file("src/lightspeed/ops/template_harness.gleam", 294).
-spec bool_to_string(boolean()) -> binary().
bool_to_string(Value) ->
    case Value of
        true ->
            <<"true"/utf8>>;

        false ->
            <<"false"/utf8>>
    end.

-file("src/lightspeed/ops/template_harness.gleam", 232).
-spec card_component() -> fun((card_assigns(), card_slots()) -> lightspeed@component:rendered()).
card_component() ->
    lightspeed@component@template:function(
        fun(Assigns, Slots) ->
            lightspeed@component:html(
                <<<<<<<<<<<<<<<<"<article data-highlighted=\""/utf8,
                                                (bool_to_string(
                                                    erlang:element(3, Assigns)
                                                ))/binary>>/binary,
                                            "\"><h1>"/utf8>>/binary,
                                        (erlang:element(2, Assigns))/binary>>/binary,
                                    "</h1><section>"/utf8>>/binary,
                                (erlang:element(2, Slots))/binary>>/binary,
                            "</section><footer>"/utf8>>/binary,
                        (erlang:element(3, Slots))/binary>>/binary,
                    "</footer></article>"/utf8>>
            )
        end
    ).

-file("src/lightspeed/ops/template_harness.gleam", 263).
-spec compile_card_slots(list({binary(), lightspeed@component:rendered()})) -> {ok,
        card_slots()} |
    {error, binary()}.
compile_card_slots(Slots) ->
    case {lightspeed@component@template_compiler:require_slot(
            Slots,
            <<"body"/utf8>>
        ),
        lightspeed@component@template_compiler:slot_rendered(
            Slots,
            <<"footer"/utf8>>
        )} of
        {{ok, Body}, {some, Footer}} ->
            {ok,
                {card_slots,
                    lightspeed@component:to_html(Body),
                    lightspeed@component:to_html(Footer)}};

        {{ok, Body@1}, _} ->
            {ok,
                {card_slots, lightspeed@component:to_html(Body@1), <<""/utf8>>}};

        {{error, Reason}, _} ->
            {error, Reason}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 248).
-spec compile_card_assigns(
    list({binary(), lightspeed@component@template_compiler:attr_value()})
) -> {ok, card_assigns()} | {error, binary()}.
compile_card_assigns(Attrs) ->
    case {lightspeed@component@template_compiler:require_string(
            Attrs,
            <<"title"/utf8>>
        ),
        lightspeed@component@template_compiler:require_bool(
            Attrs,
            <<"highlighted"/utf8>>
        )} of
        {{ok, Title}, {ok, Highlighted}} ->
            {ok, {card_assigns, Title, Highlighted}};

        {{error, Reason}, _} ->
            {error, Reason};

        {_, {error, Reason@1}} ->
            {error, Reason@1}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 215).
-spec card_schema() -> lightspeed@component@template_compiler:schema(card_assigns(), card_slots()).
card_schema() ->
    lightspeed@component@template_compiler:schema(
        <<"card"/utf8>>,
        [lightspeed@component@template_compiler:required_string(
                <<"title"/utf8>>
            ),
            lightspeed@component@template_compiler:optional_bool(
                <<"highlighted"/utf8>>,
                {some, false}
            )],
        [lightspeed@component@template_compiler:required_slot(<<"body"/utf8>>),
            lightspeed@component@template_compiler:optional_slot(
                <<"footer"/utf8>>
            )],
        fun compile_card_assigns/1,
        fun compile_card_slots/1,
        card_component()
    ).

-file("src/lightspeed/ops/template_harness.gleam", 187).
-spec scenario_invalid_attr_type() -> scenario_outcome().
scenario_invalid_attr_type() ->
    Result = lightspeed@component@template_compiler:compile(
        card_schema(),
        [{<<"title"/utf8>>, {int_attr, 9}}],
        [{<<"body"/utf8>>, lightspeed@component:html(<<"<p>x</p>"/utf8>>)}]
    ),
    case Result of
        {ok, Compiled} ->
            {scenario_outcome,
                invalid_attr_type_diagnostic,
                false,
                lightspeed@component@template_compiler:signature(Compiled)};

        {error, Diagnostics} ->
            Signature = lightspeed@component@template_compiler:diagnostics_signature(
                Diagnostics
            ),
            {scenario_outcome,
                invalid_attr_type_diagnostic,
                Signature =:= <<"invalid_attribute_type:card:title:expected=string:actual=int"/utf8>>,
                Signature}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 160).
-spec scenario_missing_slot() -> scenario_outcome().
scenario_missing_slot() ->
    Result = lightspeed@component@template_compiler:compile(
        card_schema(),
        [{<<"title"/utf8>>, {string_attr, <<"Inbox"/utf8>>}}],
        []
    ),
    case Result of
        {ok, Compiled} ->
            {scenario_outcome,
                missing_slot_diagnostic,
                false,
                lightspeed@component@template_compiler:signature(Compiled)};

        {error, Diagnostics} ->
            Signature = lightspeed@component@template_compiler:diagnostics_signature(
                Diagnostics
            ),
            {scenario_outcome,
                missing_slot_diagnostic,
                Signature =:= <<"missing_slot:card:body"/utf8>>,
                Signature}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 137).
-spec scenario_missing_assign() -> scenario_outcome().
scenario_missing_assign() ->
    Result = lightspeed@component@template_compiler:compile(
        card_schema(),
        [],
        []
    ),
    case Result of
        {ok, Compiled} ->
            {scenario_outcome,
                missing_assign_diagnostic,
                false,
                lightspeed@component@template_compiler:signature(Compiled)};

        {error, Diagnostics} ->
            Signature = lightspeed@component@template_compiler:diagnostics_signature(
                Diagnostics
            ),
            {scenario_outcome,
                missing_assign_diagnostic,
                Signature =:= <<"missing_attribute:card:title;missing_slot:card:body"/utf8>>,
                Signature}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 111).
-spec scenario_compile_success() -> scenario_outcome().
scenario_compile_success() ->
    Result = lightspeed@component@template_compiler:compile(
        card_schema(),
        [{<<"title"/utf8>>, {string_attr, <<"Inbox"/utf8>>}}],
        [{<<"body"/utf8>>,
                lightspeed@component:html(<<"<p>3 notifications</p>"/utf8>>)}]
    ),
    case Result of
        {ok, Compiled} ->
            {scenario_outcome,
                compile_success,
                lightspeed@component@template_compiler:html(Compiled) =:= <<"<article data-highlighted=\"false\"><h1>Inbox</h1><section><p>3 notifications</p></section><footer></footer></article>"/utf8>>,
                lightspeed@component@template_compiler:signature(Compiled)};

        {error, Diagnostics} ->
            {scenario_outcome,
                compile_success,
                false,
                lightspeed@component@template_compiler:diagnostics_signature(
                    Diagnostics
                )}
    end.

-file("src/lightspeed/ops/template_harness.gleam", 50).
?DOC(" Run one M23 scenario.\n").
-spec run_scenario(scenario()) -> scenario_outcome().
run_scenario(Scenario) ->
    case Scenario of
        compile_success ->
            scenario_compile_success();

        missing_assign_diagnostic ->
            scenario_missing_assign();

        missing_slot_diagnostic ->
            scenario_missing_slot();

        invalid_attr_type_diagnostic ->
            scenario_invalid_attr_type()
    end.

-file("src/lightspeed/ops/template_harness.gleam", 36).
?DOC(" Run all M23 conformance scenarios.\n").
-spec run_matrix() -> report().
run_matrix() ->
    Outcomes = begin
        _pipe = [compile_success,
            missing_assign_diagnostic,
            missing_slot_diagnostic,
            invalid_attr_type_diagnostic],
        gleam@list:map(_pipe, fun run_scenario/1)
    end,
    {report, Outcomes, count_failed(Outcomes)}.

-file("src/lightspeed/ops/template_harness.gleam", 60).
?DOC(" Scenario label.\n").
-spec scenario_label(scenario()) -> binary().
scenario_label(Scenario) ->
    case Scenario of
        compile_success ->
            <<"compile_success"/utf8>>;

        missing_assign_diagnostic ->
            <<"missing_assign_diagnostic"/utf8>>;

        missing_slot_diagnostic ->
            <<"missing_slot_diagnostic"/utf8>>;

        invalid_attr_type_diagnostic ->
            <<"invalid_attr_type_diagnostic"/utf8>>
    end.

-file("src/lightspeed/ops/template_harness.gleam", 70).
?DOC(" Pass/fail label.\n").
-spec pass_fail_label(scenario_outcome()) -> binary().
pass_fail_label(Outcome) ->
    case erlang:element(3, Outcome) of
        true ->
            <<"pass"/utf8>>;

        false ->
            <<"fail"/utf8>>
    end.

-file("src/lightspeed/ops/template_harness.gleam", 78).
?DOC(" Scenario signature.\n").
-spec signature(scenario_outcome()) -> binary().
signature(Outcome) ->
    erlang:element(4, Outcome).

-file("src/lightspeed/ops/template_harness.gleam", 83).
?DOC(" Scenario accessor.\n").
-spec scenario(scenario_outcome()) -> scenario().
scenario(Outcome) ->
    erlang:element(2, Outcome).

-file("src/lightspeed/ops/template_harness.gleam", 88).
?DOC(" Outcomes accessor.\n").
-spec outcomes(report()) -> list(scenario_outcome()).
outcomes(Report) ->
    erlang:element(2, Report).

-file("src/lightspeed/ops/template_harness.gleam", 93).
?DOC(" Failed scenario count.\n").
-spec failed_scenarios(report()) -> integer().
failed_scenarios(Report) ->
    erlang:element(3, Report).

-file("src/lightspeed/ops/template_harness.gleam", 301).
-spec join_with(binary(), list(binary())) -> binary().
join_with(Separator, Values) ->
    case Values of
        [] ->
            <<""/utf8>>;

        [Value] ->
            Value;

        [Value@1 | Rest] ->
            <<<<Value@1/binary, Separator/binary>>/binary,
                (join_with(Separator, Rest))/binary>>
    end.

-file("src/lightspeed/ops/template_harness.gleam", 98).
?DOC(" Stable report signature.\n").
-spec report_signature(report()) -> binary().
report_signature(Report) ->
    Entries = gleam@list:map(
        erlang:element(2, Report),
        fun(Outcome) ->
            <<<<<<<<(scenario_label(erlang:element(2, Outcome)))/binary,
                            "="/utf8>>/binary,
                        (pass_fail_label(Outcome))/binary>>/binary,
                    ":"/utf8>>/binary,
                (erlang:element(4, Outcome))/binary>>
        end
    ),
    join_with(<<";"/utf8>>, Entries).