# `CEL.Value`
[🔗](https://github.com/hansihe/pb_ex/blob/v0.2.0/cel_ex/lib/cel/value.ex#L1)

CEL values, as evaluation returns them and as variables can be bound.

Values are tagged so that CEL's distinctions survive, most importantly `int`
versus `uint` and `string` versus `bytes`:

| CEL | Value |
|-----|-------|
| `null` | `:null` |
| `bool`, `int`, `uint`, `double` | `{:bool, true}`, `{:int, -1}`, `{:uint, 1}`, `{:double, 1.5}` (`:nan`, `:infinity` and `:negative_infinity` for the special doubles) |
| `string`, `bytes` | `{:string, "a"}`, `{:bytes, <<1>>}` |
| `google.protobuf.Timestamp`, `Duration` | `{:timestamp, unix_nanoseconds}`, `{:duration, nanoseconds}` |
| `list`, `map` | `{:list, [value]}`, `{:map, %{value => value}}` |
| a message | `{:message, name, payload}`, its `CEL.TypeProvider`'s name and payload |
| an enum value | `{:enum, name, number}`, equal to `{:int, number}` |
| `optional_type` | `{:optional, :none}`, `{:optional, {:some, value}}` |
| a type | `{:type, _}`; `type_name/1` gives its name, such as `"int"` |

`to_native/1` turns a value into a plain Elixir term. The constructors
(`int/1`, `string/1`, ...) build values, for example in a custom function's
result.

# `double_value`

```elixir
@type double_value() :: float() | special_double()
```

# `special_double`

```elixir
@type special_double() :: :nan | :infinity | :negative_infinity
```

# `t`

```elixir
@type t() ::
  :null
  | {:bool, boolean()}
  | {:int, integer()}
  | {:uint, non_neg_integer()}
  | {:double, double_value()}
  | {:string, String.t()}
  | {:bytes, binary()}
  | CEL.Runtime.Time.timestamp()
  | CEL.Runtime.Time.duration()
  | {:enum, CEL.TypeProvider.name(), integer()}
  | {:object, String.t(), binary()}
  | {:list, [t()]}
  | {:map, %{required(t()) =&gt; t()}}
  | {:message, CEL.TypeProvider.name(), CEL.TypeProvider.payload()}
  | {:optional, :none | {:some, t()}}
  | {:type, term()}
```

# `bool`

```elixir
@spec bool(boolean()) :: t()
```

# `bytes`

```elixir
@spec bytes(binary()) :: t()
```

# `double`

```elixir
@spec double(double_value()) :: t()
```

# `duration`

```elixir
@spec duration(integer()) :: t()
```

# `enum`

```elixir
@spec enum(CEL.TypeProvider.name(), integer()) :: t()
```

# `int`

```elixir
@spec int(integer()) :: t()
```

# `list`

```elixir
@spec list([t()]) :: t()
```

# `map`

```elixir
@spec map(%{required(t()) =&gt; t()}) :: t()
```

# `null`

```elixir
@spec null() :: t()
```

# `object`

```elixir
@spec object(String.t(), binary()) :: t()
```

# `optional_none`

```elixir
@spec optional_none() :: t()
```

# `optional_some`

```elixir
@spec optional_some(t()) :: t()
```

# `string`

```elixir
@spec string(String.t()) :: t()
```

# `timestamp`

```elixir
@spec timestamp(integer()) :: t()
```

# `to_native`

```elixir
@spec to_native(t()) :: term()
```

Converts a CEL value into a plain Elixir term, discarding the type tags.

CEL entry points (`CEL.Program.eval/2`, `eval/3`) return tagged values of
type `t:t/0` — `{:int, 5}`, `{:string, "x"}`, nested `{:list, …}`/`{:map, …}`
— so that distinctions plain Elixir would erase (`int` vs `uint`, `string` vs
`bytes`) survive evaluation. `to_native/1` strips those tags off when you just
want an ordinary value back; it is the rough inverse of `infer/1`.

Distinctions with no plain-Elixir counterpart are necessarily lost:

  * `int` and `uint` both become a plain `t:integer/0`.
  * `string` and `bytes` both become a plain `t:binary/0`.
  * an `enum` becomes its integer value.
  * a `type` becomes its name, as `type_name/1` gives it.
  * `null` and an empty `optional` both become `nil`.
  * a non-finite `double` stays the atom `:nan`/`:infinity`/`:negative_infinity`,
    since Elixir floats cannot represent those.

A message becomes its payload, which its `CEL.TypeProvider` owns: for
`PB.CEL.types/1`, the message's fields. Values that have no native Elixir
form pass through unchanged in their `t:t/0` shape: `timestamp`/`duration`
and an un-decoded `object` (an `Any` whose type the provider does not
know).

## Examples

    iex> CEL.Value.to_native({:int, 5})
    5

    iex> CEL.Value.to_native({:list, [{:int, 1}, {:string, "a"}]})
    [1, "a"]

    iex> CEL.Value.to_native({:map, %{{:string, "k"} => {:bool, true}}})
    %{"k" => true}

# `type_name`

```elixir
@spec type_name(t()) :: String.t()
```

The name of a type value, such as `type(x)` returns: `"int"`, `"string"`,
`"null_type"`, `"list"`, `"map"`, `"type"`, `"optional_type"`,
`"google.protobuf.Timestamp"`, or a message's or enum's full name.

These are CEL's runtime type names, as in cel-go and cel-cpp: they have no
parameters, so the type of `[1]` is `"list"`. Type values are equal when
their names are.

    iex> {:ok, type} = CEL.eval("type([1])")
    iex> CEL.Value.type_name(type)
    "list"

# `uint`

```elixir
@spec uint(non_neg_integer()) :: t()
```

---

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