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

Finishing (spec/01 §2.4): Macaw does what a source did not.

Every capability is optional. Where a source's description does not list one the
request asks, `finish/3` does it to the delivery and reports it in `served`:

1. `:window`: rows trimmed to the window field's range, the served window intersected;
2. `:box`: rows trimmed to the box over the `:point` window field;
3. `:region`: rows trimmed to each ranged field's range;
4. `:latest`: each identity's latest row by the window field (spec/01 §1.4);
5. `:budget`: the rows reduced to the budget by a registered reducer (`reduce/5`, spec/04
   §7), `reduced_by: :runtime`.

The budget never widens the window: finishing only removes rows. A delivery that needs
none of it comes back as it is.

The pure rules of running sources (spec/01 §6) are here too: `ceiling/2` clamps a
budget to the host's ceiling, and `pooled/1` is the one budget a pooled fetch asks for,
the largest among its readers, each reader then reduced to its own by `reduce/5`.

Ported from visualize's `Visualize.Feed.finish/3` and `reduce/4`.

# `ceiling`

```elixir
@spec ceiling(pos_integer() | nil, keyword()) :: {pos_integer(), boolean()}
```

A budget clamped to the host's ceiling (spec/01 §6, macaw_compose 05 §1.2): `{budget,
clamped?}`. A `nil` budget reads at the ceiling; a page's ceiling, given as `page:`,
may lower the host's, never raise it.

    iex> Macaw.Data.Source.Finish.ceiling(50_000, host: 10_000)
    {10_000, true}
    iex> Macaw.Data.Source.Finish.ceiling(nil, host: 10_000, page: 2_000)
    {2_000, false}

# `finish`

```elixir
@spec finish(
  Macaw.Data.Delivery.t(),
  Macaw.Data.Source.Description.t(),
  Macaw.Data.Source.Request.t(),
  keyword()
) :: Macaw.Data.Delivery.t()
```

The delivery finished for the request, each step where the description's `supports`
lacks it. `reduce:` names the budget's reducer by spelling, a registered transform that
declares `reduces/0` (`"lttb"`, the default, or `"m4"` in `macaw_data`'s own package),
and `operations:` is the registry it is found in (`macaw_data`'s own by default). A name
that is no registered reducer raises `ArgumentError` naming it (spec/04 §7).

Raises `ArgumentError` for a request the schema cannot answer (`Window.check/2`): the
request is the caller's.

# `pooled`

```elixir
@spec pooled([pos_integer() | nil, ...]) :: pos_integer() | nil
```

The budget a pooled fetch asks for: the largest among its readers' (spec/01 §6), `nil`
when any reader wants every row.

    iex> Macaw.Data.Source.Finish.pooled([100, 4_000, 250])
    4000

# `reduce`

```elixir
@spec reduce(
  Macaw.Data.Delivery.t(),
  Macaw.Data.Schema.t(),
  pos_integer() | nil,
  String.t() | atom(),
  Macaw.Data.Operations.Registry.t()
) :: Macaw.Data.Delivery.t()
```

The delivery reduced to `budget` rows by the reducer `method` names in `registry`
(spec/04 §7): `"lttb"` by default, `"m4"`, or any registered transform declaring
`reduces/0`, by spelling; `macaw_data`'s own registry by default. It records
`reduced_by: :runtime`. Each series (the rows sharing every `:category` field's value)
is reduced field by field against the window field, or the row's place without one, by
the reducer's node `%{fields: [x, y], count => c}`, `c` the budget's share in the units
`reduces/0` counts, never below the least its option takes, and
the union of the kept rows returned whole, in their order, never more than the budget.
A `nil` budget, a delivery within it, and a schema with no `:number` field to reduce by
come back as they are.

    iex> alias Macaw.Data.{Delivery, Schema, Source.Finish}
    iex> schema = Schema.new!(%{t: %{type: :number, window: true}, v: %{type: :number}})
    iex> reduced = Finish.reduce(Delivery.new(for t <- 1..100, do: %{t: t, v: rem(t * 7, 5)}), schema, 10)
    iex> {Table.size(reduced.table), reduced.served.reduced_by}
    {10, :runtime}

---

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