Statifier.Machine.Identity (Statifier v2.0.0)

Copy Markdown View Source

A chart's identity: a content hash taken over the SCXML source Statifier.compile/2 received, plus an optional embedder-supplied name and version. Statifier.compile/2 stamps one onto every Statifier.Machine.t() it produces (Statifier.Machine.identity/1).

The hash is taken over the source binary, not the compiled %Statifier.Machine{} term: the full argument for that choice lives in ADR-0052 (a term-level hash would vary with compiler internals - numbering order, expression-compilation output - that carry no meaning to a host comparing chart revisions, while two byte-identical documents must always agree regardless of what the compiler does with them).

Two t() values are the same chart only when matches?/2 says so - ==/2 on the struct is deliberately not the public comparison, since a future field addition here should not silently change what "the same chart" means at every existing call site.

Summary

Functions

The version tag to_binary/1 writes and from_binary/1 checks. A bare integer, so a future format change is a version bump here rather than an inference from the blob's shape.

Decodes a to_binary/1 envelope back into a t().

Whether a and b name the same chart identity. Total: nil on either side answers false, never true - two unidentified charts are not the same chart, and treating nil == nil as a match would be exactly the silent misread this module exists to prevent.

Hashes source and carries opts[:chart_name]/opts[:chart_version] alongside the hash.

Encodes identity as a tagged, versioned binary envelope, so a host can store an identity beside the source it retained for it.

Types

t()

@type t() :: %Statifier.Machine.Identity{
  content_hash: String.t(),
  name: String.t() | nil,
  version: String.t() | nil
}

Functions

format_version()

@spec format_version() :: pos_integer()

The version tag to_binary/1 writes and from_binary/1 checks. A bare integer, so a future format change is a version bump here rather than an inference from the blob's shape.

from_binary(blob)

@spec from_binary(blob :: binary()) ::
  {:ok, t()}
  | {:error, :not_a_statifier_blob}
  | {:error, {:unsupported_format_version, term()}}

Decodes a to_binary/1 envelope back into a t().

Returns {:error, :not_a_statifier_blob} for anything that is not this module's tagged envelope - a foreign term_to_binary blob, garbage bytes, or a well-formed envelope whose payload is not a t() - and {:error, {:unsupported_format_version, version}} when the envelope is recognizably this module's own but carries a format version this build does not know how to read.

matches?(a, b)

@spec matches?(a :: t() | nil, b :: t() | nil) :: boolean()

Whether a and b name the same chart identity. Total: nil on either side answers false, never true - two unidentified charts are not the same chart, and treating nil == nil as a match would be exactly the silent misread this module exists to prevent.

of_source(source, opts \\ [])

@spec of_source(source :: binary(), opts :: keyword()) :: t()

Hashes source and carries opts[:chart_name]/opts[:chart_version] alongside the hash.

opts is Statifier.compile/2's whole option keyword list, passed through unfiltered - this function reads only its own two keys and ignores every other one (invoke_content_markup: true included). That is intentional: narrowing this to a filtered keyword list would just move the coupling to this call site instead of removing it.

to_binary(identity)

@spec to_binary(identity :: t()) :: binary()

Encodes identity as a tagged, versioned binary envelope, so a host can store an identity beside the source it retained for it.