# `GeoGenius.Manifest`
[🔗](https://github.com/agoodway/geo_genius/blob/main/lib/geo_genius/manifest.ex#L69)

The reviewed document that describes one release: `<manifest_dir>/<collection>/<release>.json`.

A manifest is how a new release becomes available -- nothing discovers or
follows a mutable "latest" pointer. `load/3` reads and validates the file at
that path; `from_map/2` runs the same validation over an already-decoded
document, which is how a manifest stored on `release.manifest` is rebuilt
without touching the filesystem again. `to_map/1` is the inverse of both:
it produces the exact JSON-shaped map a manifest document contains, so
`from_map(to_map(m))` round-trips.

Every failure -- a malformed field, an unknown provider, a name that could
escape the manifest directory -- raises or returns `GeoGenius.ManifestError`
naming the field that was wrong.

# `area_type`

```elixir
@type area_type() :: %{
  :key =&gt; String.t(),
  :rank =&gt; pos_integer(),
  optional(:requires_geometry) =&gt; boolean()
}
```

One entry of a manifest's `area_types` list.

# `authority`

```elixir
@type authority() :: %{key: String.t(), name: String.t()}
```

One entry of a manifest's `authorities` list: who is responsible for the
identifiers a set of areas is keyed under.

A collection may draw on more than one -- a US release keyed partly by the
Census, partly by the USPS, and partly by its own vendor identifiers names
all three -- so this is a list rather than a single value.

`authorities` is required and must name at least one, unlike `area_types`,
which a manifest may declare as an empty list: a manifest declaring no area
types registers none, and a release that normalizes an area whose type it
never declared fails. An empty `authorities` list is a manifest that can
register no area at all: `upsert_area` resolves an authority with
`SELECT ... INTO STRICT` and raises `:no_data_found` deep in normalization.
Rejecting it here is what turns that into an error naming the field, at load.

`:authorities` is in `@enforce_keys` for the same reason. Validation covers
the documents `load/3` and `from_map/2` read; the enforced key covers a
`%GeoGenius.Manifest{}` built in Elixir, which would otherwise default the
field to `nil` and reach registration's `Enum.each/2` as a
`Protocol.UndefinedError` rather than an error naming the field.

# `t`

```elixir
@type t() :: %GeoGenius.Manifest{
  area_types: [area_type()],
  authorities: [authority()],
  collection: String.t(),
  collection_name: String.t() | nil,
  description: String.t() | nil,
  options: map(),
  provider: String.t(),
  release: String.t(),
  requires_geometry: boolean(),
  source_date: Date.t() | nil,
  sources: [GeoGenius.Manifest.Source.t()]
}
```

# `decode`

```elixir
@spec decode(String.t(), Path.t() | nil) ::
  {:ok, map()} | {:error, GeoGenius.ManifestError.t()}
```

Decodes a manifest document with Jason.

`path` is carried onto any `GeoGenius.ManifestError` this returns, so a
decode failure names the file it came from.

# `from_map`

```elixir
@spec from_map(map(), Path.t() | nil) ::
  {:ok, t()} | {:error, GeoGenius.ManifestError.t()}
```

Validates an already-decoded manifest document, skipping the file and
decoding steps `load/3` performs.

This is how a manifest stored on `release.manifest` is rebuilt without
reading it from disk again.

# `load`

```elixir
@spec load(String.t(), String.t(), keyword()) ::
  {:ok, t()} | {:error, GeoGenius.ManifestError.t()}
```

Loads and validates the manifest for `collection`/`release` from the search
path.

Returns `{:error, %GeoGenius.ManifestError{}}` for a name that cannot be
looked up, a file that is not on the search path, JSON that does not parse,
or a document that fails validation.

# `load!`

```elixir
@spec load!(String.t(), String.t(), keyword()) :: t()
```

Like `load/3`, but raises `GeoGenius.ManifestError` instead of returning an error tuple.

# `to_map`

```elixir
@spec to_map(t()) :: map()
```

Produces the JSON-shaped map a manifest document contains.

`from_map(to_map(manifest))` is the identity: this is what makes storing a
manifest on the release row lossless.

---

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