# `Expression.Context`

A helper module for creating a context that can be
used with Expression.Eval.

## Plain map context

`new/2` returns a plain map with lowercased string keys and
auto-coerced values. This is the legacy format and remains the
default for backwards compatibility.

  iex> Expression.Context.new(%{foo: "bar"})
  %{"foo" => "bar"}
  iex> Expression.Context.new(%{FOO: "bar"})
  %{"foo" => "bar"}
  iex> Expression.Context.new(%{foo: %{bar: "baz"}})
  %{"foo" => %{"bar" => "baz"}}
  iex> Expression.Context.new(%{Foo: %{Bar: "baz"}})
  %{"foo" => %{"bar" => "baz"}}
  iex> Expression.Context.new(%{foo: %{bar: 1}})
  %{"foo" => %{"bar" => 1}}
  iex> Expression.Context.new(%{date: "2020-12-13T23:34:45"})
  %{"date" => ~U[2020-12-13 23:34:45.0Z]}
  iex> Expression.Context.new(%{boolean: "true"})
  %{"boolean" => true}
  iex> Expression.Context.new(%{float: 1.234})
  %{"float" => 1.234}
  iex> now = DateTime.utc_now()
  iex> ctx = Expression.Context.new(%{float: "1.234", nested: %{date: now}})
  iex> ctx["float"]
  1.234
  iex> now == ctx["nested"]["date"]
  true
  iex> Expression.Context.new(%{mixed: ["2020-12-13T23:34:45", 1, "true", "binary"]})
  %{"mixed" => [~U[2020-12-13 23:34:45.0Z], 1, true, "binary"]}

## Options

`new/2` accepts the following options:

  * `:lowercase_keys` - when `true` (default), all keys are lowercased.
    Set to `false` to preserve original casing — the evaluator uses
    case-insensitive lookup so expressions still resolve correctly.
  * `:coerce_strings` - when `true` (default), string values are
    auto-parsed to their typed equivalents (dates, booleans, numbers).
    Set to `false` to preserve all string values as-is.
  * `:skip_context_evaluation?` - legacy alias for `coerce_strings: false`.

## Structured context with private state

`build/2` returns an `%Expression.Context{}` struct that separates
user-visible variables from callback-private state.

Variable resolution (`@foo`) only reads from `vars`.
Callbacks receive the full struct and can access `private` for
trusted data (database records, tokens, internal IDs) that
expression authors must not be able to read.

  iex> ctx = Expression.Context.build(%{name: "Jane"}, private: %{number: %{uuid: "abc"}})
  iex> ctx.vars["name"]
  "Jane"
  iex> ctx.private
  %{number: %{uuid: "abc"}}

# `t`

```elixir
@type t() :: %Expression.Context{private: map(), vars: map()}
```

# `build`

```elixir
@spec build(map(), Keyword.t()) :: t()
```

Build a structured context with separate variable and private state compartments.

Variable resolution (`@foo`) only reads from `vars`. Callbacks receive the
full struct and can access `private` for trusted data that expression authors
must not be able to read.

## Options

  * `:private` - a map of callback-only data (default: `%{}`)

Any other options are passed through to `new/2` for variable normalization.

## Examples

    iex> ctx = Expression.Context.build(%{name: "Jane"}, private: %{token: "secret"})
    iex> ctx.vars["name"]
    "Jane"
    iex> ctx.private.token
    "secret"

# `new`

```elixir
@spec new(map(), Keyword.t() | nil) :: map()
```

---

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