# `Malla.Status`
[🔗](https://github.com/netkubes/malla/blob/main/lib/malla/status.ex#L21)

Provides core utilities for standardizing status and error responses.

This module defines the `t:Malla.Status.t/0` struct and the foundational logic for
converting internal application responses into this standardized structure. It works
in tandem with the `Malla.Plugins.Status` plugin, which provides an extensible
callback-based system for defining status patterns.

For a complete guide on how to use the status system, including defining custom
statuses, see the **[Status and Error Handling guide](guides/09-observability/02-status-handling.md)**.

# `status_opt`

```elixir
@type status_opt() ::
  {:info, term()}
  | {:code, integer()}
  | {:status, term()}
  | {:data, list() | map()}
  | {:metadata, list() | map()}
```

Options for describing a status in callback implementations.

Available options:
- `:info` - Human-readable message (becomes the `info` field)
- `:code` - Numeric code, typically HTTP status code
- `:status` - Override the status identifier string
- `:data` - Additional structured data (map or keyword list)
- `:metadata` - Service metadata (map or keyword list)

If no `:status` is provided, we try to find one based on original _user_status_:
- if it was an _atom_ or _binary_, that is the `:status`
- if it is a tuple, we check if the first element is an _atom_ or _binary_, even if nested
- otherwise we set it to "unknown"

# `t`

```elixir
@type t() :: %Malla.Status{
  code: integer(),
  data: map(),
  info: String.t(),
  metadata: map(),
  status: String.t()
}
```

# `user_status`

```elixir
@type user_status() :: any() | t()
```

# `public`

```elixir
@spec public(user_status()) :: t()
```

Expands a service response into a `t:t/0` structure for public consumption.

This is a convenience wrapper around `public/2` that uses the service ID
from the current process dictionary using `Malla.get_service_id!/0`.

# `public`

```elixir
@spec public(Malla.id(), user_status()) :: t()
```

Expands a service response into a `t:t/0` structure for public/external use.

Similar to `status/2`, but provides additional safety to avoid exposing internal
errors to external systems. If we find no result from the `c:Malla.Plugins.Status.status/1`
callback, the `c:Malla.Plugins.Status.status_public/1` callback is invoked to handle
the unmatched status.

By default, `status_public/2` will:
- Log a warning with the full _user_status_ and a unique reference
- Return a sanitized status with `:status` set to "internal_error" and `:info` indicating the reference

Services can override `status_public/2` to customize this behavior, such as exposing
certain error patterns or implementing custom logging strategies.

## Examples

    iex> Malla.Status.public(MyService, :ok)
    %Malla.Status{status: "ok"}

    iex> Malla.Status.public(MyService, {:badarg, %{some: :internal_data}})
    %Malla.Status{status: "internal_error", info: "Internal reference 1234"}

# `status`

```elixir
@spec status(user_status()) :: t()
```

Expands a service response into a `t:t/0` structure for public consumption.

This is a convenience wrapper around `status/2` that uses the service ID
from the current process dictionary using `Malla.get_service_id!/0`.

# `status`

```elixir
@spec status(Malla.id(), user_status()) :: t()
```

Expands a service response into a `t:t/0` structure.

This function converts internal service responses (statuses and errors) into
a standardized Status struct for external consumption.

## Conversion Process

1. First we call callback `c:Malla.Plugins.Status.status/1` that must be defined if we included
   plugin `Malla.Plugins.Status` in the indicated service. If we (or our plugins) implemented
   this function, it will be called first, using, as last resort, the base implementation.

2. Callback function can return:
   - an string: we will consider this as `:info` field. We will try to extract `:status` field from
     the _user_status_ (if it is an _atom_ or _string_ or first element of a _tuple_) or it will set to "unknown".
   - a list of options (`t:status_opt/0`). Struct will be populated according to this.
     If no `:status` is provided, we follow same rules as above.

3. If no callback implementation matches this _user_status_:
   - we try to guess `:status` with same rules as above or it will be set to "unknown".
   - we include `:info` as a string representation of the whole _user_status_, unless it is
     a _two-elements tuple_, in that case we use only the second part of it.

4. Then `:metadata` may be added calling `c:Malla.Plugins.Status.status_metadata/1`. If it was provided by
   the `c:Malla.Plugins.Status.status/1` callback, it will be merged.

---

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