-module(dream_test@gherkin@steps). -compile([no_auto_import, nowarn_unused_vars, nowarn_unused_function, nowarn_nomatch, inline]). -define(FILEPATH, "src/dream_test/gherkin/steps.gleam"). -export([new_registry/0, given/3, when_/3, then_/3, step/3, find_step/3, capture_count/1, get_int/2, get_float/2, get_string/2, get_word/2]). -export_type([step_context/0, step_registry/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( " Step definition registry for Gherkin scenarios.\n" "\n" " This module provides the user-facing API for defining step definitions\n" " that match Gherkin steps (Given/When/Then) to Gleam handler functions.\n" "\n" " ## Quick Start\n" "\n" " ```gleam\n" " import dream_test/gherkin/steps.{\n" " type StepContext, given, new_registry, then_, when_, get_int, get_float,\n" " }\n" "\n" " pub fn cart_steps() -> StepRegistry {\n" " new_registry()\n" " |> given(\"I have {int} items in my cart\", have_items)\n" " |> when_(\"I add {int} items of {word}\", add_items)\n" " |> then_(\"the total should be ${float}\", check_total)\n" " }\n" "\n" " fn have_items(context: StepContext) -> AssertionResult {\n" " case get_int(context.captures, 0) {\n" " Ok(count) -> {\n" " world.put(context.world, \"count\", count)\n" " AssertionOk\n" " }\n" " Error(msg) -> fail_with(msg)\n" " }\n" " }\n" " ```\n" "\n" " ## Pattern Syntax\n" "\n" " Step patterns use Cucumber Expression syntax with typed placeholders:\n" "\n" " | Placeholder | Matches | Example |\n" " |-------------|-----------------------|----------------|\n" " | `{int}` | Integers | `42`, `-5` |\n" " | `{float}` | Decimals | `3.14`, `-0.5` |\n" " | `{string}` | Quoted strings | `\"hello\"` |\n" " | `{word}` | Single unquoted word | `apple` |\n" " | `{}` | Any single token | (anonymous) |\n" "\n" " ## Prefix and Suffix Support\n" "\n" " Placeholders can have literal prefixes and suffixes attached:\n" "\n" " | Pattern | Matches | Captures |\n" " |---------|---------|----------|\n" " | `${float}` | `$19.99` | `19.99` as Float |\n" " | `{int}%` | `50%` | `50` as Int |\n" " | `${float}USD` | `$99.99USD` | `99.99` as Float |\n" "\n" " This is useful for currency, percentages, and other formatted values.\n" "\n" " ## Capture Extraction\n" "\n" " Use the typed extraction helpers to get captured values:\n" "\n" " ```gleam\n" " fn check_total(context: StepContext) -> AssertionResult {\n" " // Pattern was \"the total should be ${float}\"\n" " // Step text was \"the total should be $19.99\"\n" " case get_float(context.captures, 0) {\n" " Ok(amount) -> {\n" " // amount is 19.99 (the $ prefix was matched but not captured)\n" " AssertionOk\n" " }\n" " Error(msg) -> fail_with(msg)\n" " }\n" " }\n" " ```\n" ). -type step_context() :: {step_context, list(dream_test@gherkin@step_trie:captured_value()), gleam@option:option(list(list(binary()))), gleam@option:option(binary()), dream_test@gherkin@world:world()}. -opaque step_registry() :: {step_registry, dream_test@gherkin@step_trie:step_trie(fun((step_context()) -> dream_test@types:assertion_result()))}. -file("src/dream_test/gherkin/steps.gleam", 141). ?DOC( " Create an empty step registry.\n" "\n" " Start with this and chain `given`, `when_`, `then_` calls to add steps.\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let steps = new_registry()\n" " |> given(\"I have {int} items\", have_items)\n" " |> when_(\"I add {int} more\", add_items)\n" " |> then_(\"I should have {int} total\", check_total)\n" " ```\n" ). -spec new_registry() -> step_registry(). new_registry() -> {step_registry, dream_test@gherkin@step_trie:new()}. -file("src/dream_test/gherkin/steps.gleam", 163). ?DOC( " Register a Given step definition.\n" "\n" " Given steps describe initial context or preconditions.\n" "\n" " ## Parameters\n" "\n" " - `registry`: The registry to add to\n" " - `pattern`: Step pattern with placeholders\n" " - `handler`: Handler function to execute\n" "\n" " ## Example\n" "\n" " ```gleam\n" " new_registry()\n" " |> given(\"I have {int} items in my cart\", have_items)\n" " |> given(\"I am logged in as {string}\", logged_in_as)\n" " ```\n" ). -spec given( step_registry(), binary(), fun((step_context()) -> dream_test@types:assertion_result()) ) -> step_registry(). given(Registry, Pattern, Handler) -> Updated = dream_test@gherkin@step_trie:insert( erlang:element(2, Registry), <<"Given"/utf8>>, Pattern, Handler ), {step_registry, Updated}. -file("src/dream_test/gherkin/steps.gleam", 192). ?DOC( " Register a When step definition.\n" "\n" " When steps describe an action or event.\n" "\n" " Note: Named `when_` with trailing underscore because `when` is reserved.\n" "\n" " ## Parameters\n" "\n" " - `registry`: The registry to add to\n" " - `pattern`: Step pattern with placeholders\n" " - `handler`: Handler function to execute\n" "\n" " ## Example\n" "\n" " ```gleam\n" " new_registry()\n" " |> when_(\"I add {int} items of {string}\", add_items)\n" " |> when_(\"I click the {string} button\", click_button)\n" " ```\n" ). -spec when_( step_registry(), binary(), fun((step_context()) -> dream_test@types:assertion_result()) ) -> step_registry(). when_(Registry, Pattern, Handler) -> Updated = dream_test@gherkin@step_trie:insert( erlang:element(2, Registry), <<"When"/utf8>>, Pattern, Handler ), {step_registry, Updated}. -file("src/dream_test/gherkin/steps.gleam", 221). ?DOC( " Register a Then step definition.\n" "\n" " Then steps describe expected outcomes or assertions.\n" "\n" " Note: Named `then_` with trailing underscore because `then` is reserved.\n" "\n" " ## Parameters\n" "\n" " - `registry`: The registry to add to\n" " - `pattern`: Step pattern with placeholders\n" " - `handler`: Handler function to execute\n" "\n" " ## Example\n" "\n" " ```gleam\n" " new_registry()\n" " |> then_(\"my cart should have {int} items\", check_count)\n" " |> then_(\"I should see {string}\", check_text)\n" " ```\n" ). -spec then_( step_registry(), binary(), fun((step_context()) -> dream_test@types:assertion_result()) ) -> step_registry(). then_(Registry, Pattern, Handler) -> Updated = dream_test@gherkin@step_trie:insert( erlang:element(2, Registry), <<"Then"/utf8>>, Pattern, Handler ), {step_registry, Updated}. -file("src/dream_test/gherkin/steps.gleam", 247). ?DOC( " Register a step that matches any keyword.\n" "\n" " Use this for steps that work regardless of Given/When/Then context.\n" "\n" " ## Parameters\n" "\n" " - `registry`: The registry to add to\n" " - `pattern`: Step pattern with placeholders\n" " - `handler`: Handler function to execute\n" "\n" " ## Example\n" "\n" " ```gleam\n" " new_registry()\n" " |> step(\"I wait {int} seconds\", wait_seconds)\n" " ```\n" ). -spec step( step_registry(), binary(), fun((step_context()) -> dream_test@types:assertion_result()) ) -> step_registry(). step(Registry, Pattern, Handler) -> Updated = dream_test@gherkin@step_trie:insert( erlang:element(2, Registry), <<"*"/utf8>>, Pattern, Handler ), {step_registry, Updated}. -file("src/dream_test/gherkin/steps.gleam", 290). -spec keyword_to_string(dream_test@gherkin@types:step_keyword()) -> binary(). keyword_to_string(Keyword) -> case Keyword of given -> <<"Given"/utf8>>; 'when' -> <<"When"/utf8>>; then -> <<"Then"/utf8>>; 'and' -> <<"And"/utf8>>; but -> <<"But"/utf8>> end. -file("src/dream_test/gherkin/steps.gleam", 278). ?DOC( " Find a matching step definition.\n" "\n" " Searches the registry for a handler matching the given keyword and text.\n" " Returns the handler and captured values on success, or an error message.\n" "\n" " This is O(words in step text), not O(number of step definitions).\n" "\n" " ## Parameters\n" "\n" " - `registry`: The registry to search\n" " - `keyword`: Step keyword (Given, When, Then, And, But)\n" " - `text`: Step text to match\n" "\n" " ## Returns\n" "\n" " - `Ok(StepMatch)`: Contains matched handler and captured values\n" " - `Error(String)`: Error message if no match found\n" ). -spec find_step( step_registry(), dream_test@gherkin@types:step_keyword(), binary() ) -> {ok, dream_test@gherkin@step_trie:step_match(fun((step_context()) -> dream_test@types:assertion_result()))} | {error, binary()}. find_step(Registry, Keyword, Text) -> Keyword_str = keyword_to_string(Keyword), case dream_test@gherkin@step_trie:lookup( erlang:element(2, Registry), Keyword_str, Text ) of {some, Match} -> {ok, Match}; none -> {error, <<<<<<"Undefined step: "/utf8, Keyword_str/binary>>/binary, " "/utf8>>/binary, Text/binary>>} end. -file("src/dream_test/gherkin/steps.gleam", 446). ?DOC( " Get the number of captures.\n" "\n" " Returns the total number of values captured from placeholders.\n" "\n" " ## Parameters\n" "\n" " - `captures`: List of captured values from StepContext\n" "\n" " ## Example\n" "\n" " ```gleam\n" " let count = capture_count(context.captures)\n" " // For \"I add {int} items of {string}\", count would be 2\n" " ```\n" ). -spec capture_count(list(dream_test@gherkin@step_trie:captured_value())) -> integer(). capture_count(Captures) -> erlang:length(Captures). -file("src/dream_test/gherkin/steps.gleam", 458). -spec list_at_loop(list(HBM), integer(), integer()) -> gleam@option:option(HBM). list_at_loop(Items, Target, Current) -> case Items of [] -> none; [Head | Tail] -> case Current =:= Target of true -> {some, Head}; false -> list_at_loop(Tail, Target, Current + 1) end end. -file("src/dream_test/gherkin/steps.gleam", 454). -spec list_at(list(HBJ), integer()) -> gleam@option:option(HBJ). list_at(Items, Index) -> list_at_loop(Items, Index, 0). -file("src/dream_test/gherkin/steps.gleam", 325). ?DOC( " Get an Int capture by index (0-based).\n" "\n" " Returns the captured integer value at the given index, or an error\n" " if the index is out of bounds or the value isn't an integer.\n" "\n" " ## Parameters\n" "\n" " - `captures`: List of captured values from StepContext\n" " - `index`: 0-based index of the capture to retrieve\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Pattern: \"I have {int} items\"\n" " // Step: \"I have 42 items\"\n" " case get_int(context.captures, 0) {\n" " Ok(count) -> // count = 42\n" " Error(msg) -> fail_with(msg)\n" " }\n" " ```\n" ). -spec get_int(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok, integer()} | {error, binary()}. get_int(Captures, Index) -> case list_at(Captures, Index) of {some, {captured_int, Value}} -> {ok, Value}; {some, _} -> {error, <<"Capture at index is not an integer"/utf8>>}; none -> {error, <<"No capture at index"/utf8>>} end. -file("src/dream_test/gherkin/steps.gleam", 354). ?DOC( " Get a Float capture by index (0-based).\n" "\n" " Returns the captured float value at the given index, or an error\n" " if the index is out of bounds or the value isn't a float.\n" "\n" " ## Parameters\n" "\n" " - `captures`: List of captured values from StepContext\n" " - `index`: 0-based index of the capture to retrieve\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Pattern: \"the price is {float} dollars\"\n" " // Step: \"the price is 19.99 dollars\"\n" " case get_float(context.captures, 0) {\n" " Ok(price) -> // price = 19.99\n" " Error(msg) -> fail_with(msg)\n" " }\n" " ```\n" ). -spec get_float(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok, float()} | {error, binary()}. get_float(Captures, Index) -> case list_at(Captures, Index) of {some, {captured_float, Value}} -> {ok, Value}; {some, _} -> {error, <<"Capture at index is not a float"/utf8>>}; none -> {error, <<"No capture at index"/utf8>>} end. -file("src/dream_test/gherkin/steps.gleam", 386). ?DOC( " Get a String capture by index (0-based).\n" "\n" " Returns the captured string value at the given index. Works for\n" " both {string} (quoted) and {word} captures.\n" "\n" " ## Parameters\n" "\n" " - `captures`: List of captured values from StepContext\n" " - `index`: 0-based index of the capture to retrieve\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Pattern: \"I add items of {string}\"\n" " // Step: \"I add items of \\\"Red Widget\\\"\"\n" " case get_string(context.captures, 0) {\n" " Ok(product) -> // product = \"Red Widget\"\n" " Error(msg) -> fail_with(msg)\n" " }\n" " ```\n" ). -spec get_string(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok, binary()} | {error, binary()}. get_string(Captures, Index) -> case list_at(Captures, Index) of {some, {captured_string, Value}} -> {ok, Value}; {some, {captured_word, Value@1}} -> {ok, Value@1}; {some, _} -> {error, <<"Capture at index is not a string"/utf8>>}; none -> {error, <<"No capture at index"/utf8>>} end. -file("src/dream_test/gherkin/steps.gleam", 419). ?DOC( " Get a word capture by index (0-based).\n" "\n" " Returns the captured word value at the given index. Works for\n" " both {word} and {} (anonymous) captures.\n" "\n" " ## Parameters\n" "\n" " - `captures`: List of captured values from StepContext\n" " - `index`: 0-based index of the capture to retrieve\n" "\n" " ## Example\n" "\n" " ```gleam\n" " // Pattern: \"the user {word} exists\"\n" " // Step: \"the user alice exists\"\n" " case get_word(context.captures, 0) {\n" " Ok(username) -> // username = \"alice\"\n" " Error(msg) -> fail_with(msg)\n" " }\n" " ```\n" ). -spec get_word(list(dream_test@gherkin@step_trie:captured_value()), integer()) -> {ok, binary()} | {error, binary()}. get_word(Captures, Index) -> case list_at(Captures, Index) of {some, {captured_word, Value}} -> {ok, Value}; {some, {captured_string, Value@1}} -> {ok, Value@1}; {some, _} -> {error, <<"Capture at index is not a word"/utf8>>}; none -> {error, <<"No capture at index"/utf8>>} end.