# `StatifierUI.EventInjection.Draft`
[🔗](https://github.com/riddler/statifier-ui/blob/v0.11.0/lib/statifier_ui/event_injection/draft.ex#L1)

Turns a form's two free-form fields - an event name and a payload text -
into a `Statifier.Event.t()`, the ordinary recordable input
(`Statifier.Event.external/2`) rather than a side door.

## The three-way payload distinction

`Statifier.Event`'s own moduledoc is explicit that `data` has three
states that must not collapse into each other: **no data** (`:undefined`),
**data, present, and null** (`nil`), and **data, present, and empty**
(`%{}`). One text field has to spell all three, and `build/2` resolves
them this way:

  * **blank** (`nil`, or a string that is empty after trimming) means
    `data: :undefined` - "no data" - the same default `Event.external/2`
    itself uses when the caller passes no `:data` option at all.
  * `"null"` decodes through `JSON.decode/1` to `nil`, which
    `StatifierUI.Value.decode/1` passes through unchanged - "data,
    present, and null".
  * `"{}"` decodes to `%{}` the same way - "data, present, and empty".

Any other payload text is decoded as ADR-0005 JSON via `JSON.decode/1`
and then `StatifierUI.Value.decode/1`, so a `$`-tagged shape
(`$undefined`, `$date`, `$datetime`, `$duration`) resolves to the
predicator value it encodes.

# `reason`

```elixir
@type reason() ::
  :blank_event_name
  | {:invalid_event_name, term()}
  | {:invalid_json, term()}
  | {:invalid_payload, term()}
```

Why `build/2` refused to produce an event.

  * `:blank_event_name` - the name was empty, or all whitespace.
  * `{:invalid_event_name, name}` - `name` was not a binary, or contained
    whitespace or a control character (SCXML event names are
    dot-delimited tokens; one with a space in it can never match a
    transition's `event` attribute, so it is a typo rather than a
    debugging choice).
  * `{:invalid_json, reason}` - the payload text was not valid JSON;
    `reason` is `JSON.decode/1`'s own error value.
  * `{:invalid_payload, reason}` - the payload decoded as JSON but not as
    an ADR-0005 value (an unknown `$`-prefixed tag, a malformed `$date`);
    `reason` is `StatifierUI.Value.decode/1`'s own error value.

# `build`

```elixir
@spec build(String.t(), String.t() | nil) ::
  {:ok, Statifier.Event.t()} | {:error, reason()}
```

Builds a `Statifier.Event.t()` from a form's name and payload text.

`name` is trimmed of leading/trailing whitespace before validation. An
unmatched but well-formed name is legal and is not rejected here - only
syntax is checked; whether the name matches any transition is the
chart's business, not this function's.

`payload_text` defaults to `nil`. `nil`, or a string that is blank after
trimming, means no data (`data: :undefined`); see the moduledoc for the
full three-way spelling.

No option other than `:data` is passed to `Statifier.Event.external/2`:
`invokeid`, `origin`, `origintype`, and `sendid` all belong to delivery
paths this function is not.

## Examples

    iex> {:ok, event} = StatifierUI.EventInjection.Draft.build("authorize.approved", ~s({"amount_cents":1999}))
    iex> event.data
    %{"amount_cents" => 1999}

    iex> {:ok, event} = StatifierUI.EventInjection.Draft.build("authorize.approved", "")
    iex> event.data
    :undefined

    iex> StatifierUI.EventInjection.Draft.build("")
    {:error, :blank_event_name}

---

*Consult [api-reference.md](api-reference.md) for complete listing*
