morph 0.1.0
Asynchronous bridge between GUI thread and business models
Loading...
Searching...
No Matches
instance_constraints.hpp File Reference

Per-*instance* constraints on a form field: the seam by which data that lives in a database row — not in a template parameter — reaches the framework-meaningful keys of a served schema, and is checked against a submitted value by the same declaration that served it. More...

#include <cstdint>
#include <glaze/glaze.hpp>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "../attributes.hpp"
#include "../util/quantity.hpp"
#include "../util/rational.hpp"
#include "forms.hpp"

Go to the source code of this file.

Classes

struct  morph::forms::ConstraintViolation
 One violation: which field, and which declared key it broke. More...
struct  morph::forms::FieldConstraint
 The per-instance constraint declared for one field of an action. More...
class  morph::forms::InstanceConstraints
 A set of per-field constraints sourced from one instance (a database row, a tenant record, a catalogue version) — the single declaration that both decorates a served schema and checks a submitted value. More...

Enumerations

enum class  morph::forms::ConstraintViolationKind : std::uint8_t { BelowMinimum , AboveMaximum , PrecisionExceeded }
 Which framework-meaningful key a submitted value fell foul of. More...

Functions

std::string_view morph::forms::violationKindName (ConstraintViolationKind kind) noexcept
 A stable, machine-readable name for a violation kind — for log lines and model-level error messages.
template<typename A>
std::string morph::forms::instanceSchemaJson (const InstanceConstraints &constraints)
 schemaJson<A>() decorated with constraints — the served form for one instance.

Detailed Description

Per-*instance* constraints on a form field: the seam by which data that lives in a database row — not in a template parameter — reaches the framework-meaningful keys of a served schema, and is checked against a submitted value by the same declaration that served it.

schemaJson<A>() is a pure function of the compiled action type A: every key it emits is derived from A's members, so two rows that describe the same action differently — an analysis catalogue whose version 1 declares three decimal places and whose version 2 declares one — cannot be told apart by anything the framework enforces. Before this header the only way out was for the application to patch a second, app-private key beside the framework's (x-versionDecimalPlaces next to x-decimalPlaces) and re-implement the check by hand, which leaves a renderer with two keys for one concept and no way to know which to believe.

InstanceConstraints replaces that with one declaration that does both jobs:

  • decorate() rewrites the served schema so the framework's own keys carry the instance's values (x-decimalPlaces, x-minimum, x-maximum), and stamps the document-level x-instanceConstraints array naming every field whose keys came from instance data rather than from the compiled type — so a renderer can tell the two apart.
  • checkAction() / checkValue() report what a submitted value violates, against the identical declaration. Serving a bound and checking it can no longer drift apart, because there is only one place the bound is written.
What this deliberately does not do
The framework does not decide what a violation means. An out-of-range value is a rejection in one domain and a value to flag in another (a lab result outside its specification range is reportable, not refusable), so checkAction/checkValue return violations and the model applies its own policy. Nor is any of this wired into the dispatch runners: those decode an action from bytes with no access to the row a constraint lives in (see docs/spec/forms/instance_constraints.md, "Why dispatch cannot apply these automatically"). A model that decorates a schema is responsible for checking against the same constraints; the x-instanceConstraints stamp is what makes a schema that was decorated visible to a reviewer and a renderer.
Structure is still compile-time
Only values of existing keys vary per instance. Which fields exist, which are required, and the x-rules list all remain functions of the compiled type — an instance cannot add a field. See the spec's "Limitations".

Definition in file instance_constraints.hpp.

Enumeration Type Documentation

◆ ConstraintViolationKind

enum class morph::forms::ConstraintViolationKind : std::uint8_t
strong

Which framework-meaningful key a submitted value fell foul of.

Enumerator
BelowMinimum 

The value is strictly below the declared inclusive minimum.

AboveMaximum 

The value is strictly above the declared inclusive maximum.

PrecisionExceeded 

The value is not exactly representable at the declared decimalPlaces — it carries more precision than the instance says the field has.

Definition at line 66 of file instance_constraints.hpp.

Function Documentation

◆ violationKindName()

std::string_view morph::forms::violationKindName ( ConstraintViolationKind kind)
inlinenodiscardnoexcept

A stable, machine-readable name for a violation kind — for log lines and model-level error messages.

Parameters
kindThe violation kind to name.
Returns
"belowMinimum", "aboveMaximum" or "precisionExceeded".

Definition at line 94 of file instance_constraints.hpp.

◆ instanceSchemaJson()

template<typename A>
std::string morph::forms::instanceSchemaJson ( const InstanceConstraints & constraints)
nodiscard

schemaJson<A>() decorated with constraints — the served form for one instance.

Not memoised: unlike schemaJson<A>() (one cached string per compiled type), the result varies per instance, so the caller caches it if it wants caching. The compiled half is still memoised inside schemaJson<A>(), so the per-call cost is the DOM round trip, not schema generation.

Template Parameters
AAction type (a reflectable aggregate).
Parameters
constraintsThe instance's constraints.
Returns
The decorated schema text.

Definition at line 385 of file instance_constraints.hpp.