# `Macaw.Data.Source`
[🔗](https://github.com/dcoai/macaw_data/blob/v0.5.3/lib/macaw/data/source.ex#L1)

The source contract (spec/01 §2): anything a component's slot reads. One contract
serves feeds, queries, derived sources, state sources and exports. It is a strict
superset of visualize's `Visualize.Feed`: an existing feed is a source with no
parameters.

A source is a struct whose module implements this behavior:

- `describe(source, context)`: what the source is, a `Macaw.Data.Source.Description`.
  It takes the host's configuration for the source as `context`, **never a viewer**, so
  it is answered at boot and cached by schema version;
- `fetch(source, request)`: one answer, a `Macaw.Data.Delivery`, for a
  `Macaw.Data.Source.Request`;
- `subscribe(source, pid)` and `unsubscribe(source, ref)`, optional, for a source that
  pushes (spec/01 §4).

**Errors are values:** every callback answers `{:error, reason}` rather than raising.

The functions here dispatch to a source's module. `fetch/3` checks the request's
parameters against the description's before the call (`Macaw.Data.Parameter.validate/2`):
a request with a bad parameter never reaches the source, and the source receives every
parameter, defaults filled in.

# `t`

```elixir
@type t() :: struct()
```

A source: a struct whose module implements `Macaw.Data.Source`.

# `describe`

```elixir
@callback describe(source :: t(), context :: term()) ::
  {:ok, Macaw.Data.Source.Description.t()} | {:error, term()}
```

What the source is, from the host's configuration for it; never a viewer.

# `fetch`

```elixir
@callback fetch(source :: t(), Macaw.Data.Source.Request.t()) ::
  {:ok, Macaw.Data.Delivery.t()} | {:error, term()}
```

One answer for a request.

# `subscribe`
*optional* 

```elixir
@callback subscribe(source :: t(), pid()) :: {:ok, reference()} | {:error, term()}
```

The source's pushes sent to `pid`, tagged with the answered reference (spec/01 §4).

# `unsubscribe`
*optional* 

```elixir
@callback unsubscribe(source :: t(), reference()) :: :ok
```

A subscription stopped.

# `describe`

```elixir
@spec describe(t(), term()) ::
  {:ok, Macaw.Data.Source.Description.t()} | {:error, term()}
```

The source's description, `{:ok, description}` or `{:error, reason}`. An answer that is
neither is `{:error, {:bad_describe, answer}}`: a source's fault the caller can show.

# `describer`

```elixir
@spec describer(%{required(term()) =&gt; {t(), term()}}) :: (term() -&gt;
                                                      {:ok,
                                                       Macaw.Data.Schema.t()}
                                                      | {:error, term()})
```

A map of sources by name, with the host's context for each, as the describe function
`Macaw.Data.Schema.Cache.resolve/4` takes: a name's present schema, or
`{:error, :no_source}` for a name not in the map.

# `fetch`

```elixir
@spec fetch(t(), Macaw.Data.Source.Description.t(), Macaw.Data.Source.Request.t()) ::
  {:ok, Macaw.Data.Delivery.t()} | {:error, term()}
```

One answer for a request, checked first against the description:
`{:error, {:params, faults}}` without calling the source when a parameter is unknown,
missing or not legal (`Macaw.Data.Parameter.validate/2`). Otherwise the source is called
with every parameter, defaults filled in. An answer that is neither `{:ok, delivery}` nor
`{:error, reason}` is `{:error, {:bad_fetch, answer}}`.

# `subscribe`

```elixir
@spec subscribe(t(), pid()) :: {:ok, reference()} | {:error, term()}
```

The source's pushes sent to `pid` (spec/01 §4): `{:ok, ref}`, or `{:error, :no_push}` for
a source whose module does not implement `subscribe/2`.

# `unsubscribe`

```elixir
@spec unsubscribe(t(), reference()) :: :ok
```

A subscription stopped; `:ok` for a source that does not push.

---

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