GeoGenius.Manifest (geo_genius v0.2.0)

Copy Markdown View Source

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.

Summary

Types

One entry of a manifest's area_types list.

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

t()

Functions

Decodes a manifest document with Jason.

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

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

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

Produces the JSON-shaped map a manifest document contains.

Types

area_type()

@type area_type() :: %{
  :key => String.t(),
  :rank => pos_integer(),
  optional(:requires_geometry) => boolean()
}

One entry of a manifest's area_types list.

authority()

@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()

@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()]
}

Functions

decode(content, path \\ nil)

@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(map, path \\ nil)

@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(collection, release, opts \\ [])

@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!(collection, release, opts \\ [])

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

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

to_map(manifest)

@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.