-module(metamon@command). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/metamon/command.gleam"). -export([new/4, no_precondition/3, always/3, name_of/1]). -export_type([command/2]). -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( " Stateful / model-based testing primitives.\n" "\n" " A `Command(model, real)` represents one transition that can be\n" " applied to two parallel worlds:\n" "\n" " * **model**: a pure description of the expected state (typically\n" " a record or a `Dict`). The `next_model` step says how the\n" " command updates this expected state.\n" " * **real**: the system under test (a database connection, a\n" " stateful module, an in-memory cache). The `run` step performs\n" " the command on the real world and returns `Ok(Nil)` on\n" " success or `Error(reason)` when an invariant is violated.\n" "\n" " The `precondition` decides whether the command can fire in the\n" " current model state; the runner skips commands whose preconditions\n" " are false.\n" "\n" " See `metamon/stateful` for the runner that drives a list of\n" " commands against an initial `(model, real)` pair.\n" ). -type command(HWL, HWM) :: {command, binary(), fun((HWL) -> boolean()), fun((HWL) -> HWL), fun((HWM) -> {ok, nil} | {error, binary()})}. -file("src/metamon/command.gleam", 32). ?DOC(" Construct a command. `name` is shown in failure reports.\n"). -spec new( binary(), fun((HWN) -> boolean()), fun((HWN) -> HWN), fun((HWO) -> {ok, nil} | {error, binary()}) ) -> command(HWN, HWO). new(Name, Pre, Next, Run) -> {command, Name, Pre, Next, Run}. -file("src/metamon/command.gleam", 54). ?DOC( " Construct a command with no precondition — the precondition arm is\n" " fixed to `fn(_m) { True }`, so the command's gating step is a\n" " no-op.\n" "\n" " \"No precondition\" is **not** the same as \"always runs\": if the\n" " command's `run` returns `Error(reason)`, the runner halts the\n" " sequence with `Failed`, and any later commands in the list are\n" " skipped. Use `command.new` when the command should be gated on the\n" " current model state.\n" "\n" " `command.always` is a synonym for `no_precondition` kept for\n" " backward compatibility; new code should prefer this name because\n" " it reads correctly at the call site.\n" ). -spec no_precondition( binary(), fun((HWT) -> HWT), fun((HWU) -> {ok, nil} | {error, binary()}) ) -> command(HWT, HWU). no_precondition(Name, Next, Run) -> {command, Name, fun(_) -> true end, Next, Run}. -file("src/metamon/command.gleam", 67). ?DOC( " Synonym for `no_precondition`. The name reads as \"always runs\",\n" " which overstates the contract — the command's `run` can still\n" " return `Error`, halting the sequence. Prefer `no_precondition` in\n" " new code; this constructor is retained as an alias so existing\n" " call sites continue to compile.\n" ). -spec always( binary(), fun((HWZ) -> HWZ), fun((HXA) -> {ok, nil} | {error, binary()}) ) -> command(HWZ, HXA). always(Name, Next, Run) -> no_precondition(Name, Next, Run). -file("src/metamon/command.gleam", 76). ?DOC(" Get the command's user-facing name.\n"). -spec name_of(command(any(), any())) -> binary(). name_of(Cmd) -> erlang:element(2, Cmd).