Storehouse.Metadata (Storehouse v0.2.0)

Copy Markdown View Source

A file's own metadata (Storehouse.File's :metadata): a map the app keeps with the file, in its record's column and in the JSON it sends, of what a person entered (a caption, a credit, a description for readers who cannot see it) and what the app decided.

It holds what JSON holds, so that it goes into a jsonb column and a response as it is: string keys; strings (UTF-8, with no NUL byte, which PostgreSQL's jsonb cannot keep), numbers, booleans and nil; lists and maps of them. normalize/1 turns atoms (keys and values) into strings, and dates and times into ISO 8601 strings; anything else is refused, with where it is.

What analysis reads from the file itself is not metadata: it is the file's :analysis (Storehouse.Analysis), so the two never overwrite each other.

Summary

Types

Where a value that is refused is: the keys and indexes down to it.

Functions

into with from merged in: maps merged key by key, all the way down; any other value, nil too, replaced.

Metadata as Storehouse.File keeps it, or {:error, {:invalid_metadata, path}} with where the first value it refuses is. At most 8 levels deep, and 65536 bytes of JSON.

Types

path()

@type path() :: [String.t() | non_neg_integer()]

Where a value that is refused is: the keys and indexes down to it.

Functions

merge(into, from)

@spec merge(map(), map()) :: map()

into with from merged in: maps merged key by key, all the way down; any other value, nil too, replaced.

Examples

iex> Storehouse.Metadata.merge(%{"credit" => %{"name" => "Ana", "year" => 2025}}, %{"credit" => %{"year" => 2026}})
%{"credit" => %{"name" => "Ana", "year" => 2026}}

normalize(metadata)

@spec normalize(map()) :: {:ok, map()} | {:error, {:invalid_metadata, path()}}

Metadata as Storehouse.File keeps it, or {:error, {:invalid_metadata, path}} with where the first value it refuses is. At most 8 levels deep, and 65536 bytes of JSON.

Examples

iex> Storehouse.Metadata.normalize(%{caption: "The quay", rating: 4, taken: ~D[2026-10-01]})
{:ok, %{"caption" => "The quay", "rating" => 4, "taken" => "2026-10-01"}}

iex> Storehouse.Metadata.normalize(%{"gear" => %{"lens" => {35, :mm}}})
{:error, {:invalid_metadata, ["gear", "lens"]}}

iex> Storehouse.Metadata.normalize(%{"title" => "a\0b"})
{:error, {:invalid_metadata, ["title"]}}