The one place this engine turns an SCXML duration designation - <send delay>'s attribute string, or a delayexpr result - into milliseconds.
Wraps Predicator.Duration rather than reimplementing CSS2-style duration
parsing.
The delegation is deliberate rather than incidental: one duration vocabulary
across the platform, with Predicator.Duration.parse/1 (whole-string, no
partial consumption, :error on junk) and to_milliseconds/1 as its only
implementation, so a duration means the same thing in a <send delay> as in
any expression predicator evaluates. Everything below is a consequence of
that, not a second look at it.
The unit set is a superset, delegated as-is
The SCXML schema's delay/delayexpr pattern
(\d*(\.\d+)?(ms|s|m|h|d)) recognizes five units: ms, s, m
(minutes), h, d. Predicator.Duration.parse/1 recognizes eight:
y, mo, w, d, h, m, s, ms - the same five plus y
(years), mo (months), and w (weeks). Every unit the two sets share
means the same thing in both (m is minutes, not months, in both), so
predicator's set is a strict superset of the schema's, and this module
delegates it whole rather than re-restricting parsing to the schema's five.
A document that writes delay="2w" gets a working two-week delay; nothing
here rejects it for being outside the schema pattern, since accepting more
than the schema requires is never a conformance violation.
The leading-dot concession
The SCXML schema pattern above allows a bare leading dot - ".5s" is a
schema-valid delay value - but Predicator.Duration.parse/1 does not:
parse(".5s") is :error while parse("1.5s") is {:ok, ...} (verified
against deps/predicator/lib/predicator/duration.ex's own doctests).
normalize_leading_dot/1 is the one-line concession this schema pattern
requires and predicator 8.0 deliberately does not make. The split is by
agreement rather than by omission: ".5s" is schema-valid SCXML but not a
duration predicator means to accept, so pre-normalizing it stays this
engine's job and does not become a widening of predicator's own grammar.
So it rewrites a leading . to 0. before parsing, and ".5s" and
"0.5s" parse identically. This is the only rewrite this module performs;
every other character reaches Predicator.Duration.parse/1 untouched.
Summary
Types
An SCXML duration designation: the raw delay string, or a native predicator duration value off delayexpr.
Functions
Rewrites a leading . to 0. (".5s" -> "0.5s") - the concession the
SCXML schema's \d*(\.\d+)?(ms|s|m|h|d) pattern requires and
Predicator.Duration.parse/1 does not make on its own. A string with no
leading dot passes through unchanged.
Resolves an SCXML duration designation to whole milliseconds.
Types
@type value() :: binary() | Predicator.Types.duration()
An SCXML duration designation: the raw delay string, or a native predicator duration value off delayexpr.
Functions
Rewrites a leading . to 0. (".5s" -> "0.5s") - the concession the
SCXML schema's \d*(\.\d+)?(ms|s|m|h|d) pattern requires and
Predicator.Duration.parse/1 does not make on its own. A string with no
leading dot passes through unchanged.
@spec to_ms(value :: value()) :: {:ok, non_neg_integer()} | {:error, term()}
Resolves an SCXML duration designation to whole milliseconds.
Accepts either a delay-style string (parsed via
Predicator.Duration.parse/1, after normalize_leading_dot/1) or a
native Predicator.Duration value straight off delayexpr (a computed
duration such as 2s + backoff needs no parsing - delayexpr already
evaluated it to a duration map). A string that fails to parse - a bad
unit, a sub-millisecond fractional remainder, or anything else
Predicator.Duration.parse/1 refuses - is {:error, {:invalid_delay, value}}; the caller is responsible for treating that as an argument
failure (ADR-0036 governs <send>'s own case).