Visualize.Chart.Limits (Visualize v0.4.1)

Copy Markdown View Source

Sanity limits on a document read from elsewhere (spec/14 §9.2, D-137).

Every reader — Visualize.Chart.from_map/2, from_json/2 and Visualize.Chart.Fragment.from_json/2 — applies these, the defaults when no limits: is given. They bound what reading and rendering a document can be made to cost; they are sanity limits, not a render budget.

LimitDefaultBounds
depth64how deeply maps and lists nest, a use site's local and vars included
items100_000the entries one list or map holds — a design holds no rows of its own, so this bounds every list a row could be held inline in
ticks1_000an axis's tick count
iterations10_000a :force step's ticks
counts1_000_000bins, samples, n, width, and a grid's size

The defaults are the system's sanity values, and the host holds the number. A host loading untrusted input knows its inputs and tightens the limits at its layer: Visualize.Chart.from_json(json, limits: [items: 5_000, depth: 24]); a limit it does not name keeps its default.

A fault is {path, {:limit, name, max, given}}, the validator's shape (spec/14 §10.1), so Visualize.Chart.Validator.format/1 words it:

iex> Visualize.Chart.Limits.check(%{version: 2, frames: %{main: %{axes: [%{scale: :x, ticks: 5000}]}}})
{:error, [{[:frames, :main, :axes, 0, :ticks], {:limit, :ticks, 1000, 5000}}]}

iex> Visualize.Chart.Validator.format({[:frames, :main, :axes, 0, :ticks], {:limit, :ticks, 1000, 5000}})
"frames.main.axes[0].ticks: asks for 5000 ticks; the limit ticks is 1000"

Summary

Types

A limit's name.

t()

The limits a reader applies, one positive integer per name.

Functions

:ok, or every depth, items, ticks, iterations and counts fault of a design map at the current version, structure/2's first; limits as new/1 takes them.

Every ticks, iterations and counts fault of a design map at the current version, found by walking it with the schema, ordered by path. A variable is not a literal of the document and is not checked.

The system's sanity values (spec/14 §9.2).

The limits a reader applies: given merged over defaults/0.

Every depth and items fault of any term — a design, a fragment, a parsed JSON document with string keys — ordered by path.

Types

name()

@type name() :: :depth | :items | :ticks | :iterations | :counts

A limit's name.

t()

@type t() :: %{required(name()) => pos_integer()}

The limits a reader applies, one positive integer per name.

Functions

check(design, limits \\ [])

@spec check(term(), keyword() | map()) ::
  :ok | {:error, [Visualize.Chart.Validator.error(), ...]}

:ok, or every depth, items, ticks, iterations and counts fault of a design map at the current version, structure/2's first; limits as new/1 takes them.

counts(design, limits)

@spec counts(term(), t()) :: [Visualize.Chart.Validator.error()]

Every ticks, iterations and counts fault of a design map at the current version, found by walking it with the schema, ordered by path. A variable is not a literal of the document and is not checked.

defaults()

@spec defaults() :: t()

The system's sanity values (spec/14 §9.2).

new(given)

@spec new(keyword() | map() | t()) :: t()

The limits a reader applies: given merged over defaults/0.

Raises ArgumentError for a name that is not a limit or a value that is not a positive integer.

iex> Visualize.Chart.Limits.new(items: 10).items
10

structure(term, limits)

@spec structure(term(), t()) :: [Visualize.Chart.Validator.error()]

Every depth and items fault of any term — a design, a fragment, a parsed JSON document with string keys — ordered by path.

The document itself is depth 1. A struct is a leaf, but a %Visualize.Chart.Use{}, whose local and vars hold fragments. Past the depth limit a branch is reported once, at the first map or list beyond it, and not walked further.