6. Constraints and findings
Contents of DESL 0.1
Constraints say what makes a document valid. They are written in CEL, evaluated by the runtime and by headless validators, and reported to the user as findings. A document with findings is still opened, shown and saved.
| Property | Type | Description |
|---|---|---|
rules |
Constraint[] | The constraints, in order. |
defaults |
{severity, timing} |
Defaults for every rule: a severity, and when rules are evaluated, from "live" (after every transaction), "save" (before a document is written) and "explicit" (when the user asks). Default severity "error", timing ["live", "save"]. |
blockSaveOn |
"never", "error" |
Whether a document with errors may not be saved. Default "never". |
doc |
Doc |
| Property | Type | Description |
|---|---|---|
id |
identifier | Required, unique. |
code |
qualified identifier | The code every finding of the rule reports (knowledge.no-title). |
label, doc |
Its name and explanation, shown with every finding. | |
scope |
"document" or TypeRefs |
What the rule is evaluated for: once for the document, or once per element of the types, with self bound to it. Default "document". |
when |
Expression | The rule is evaluated only where this holds. |
rule |
Expression → bool | Required. The condition that must hold. |
severity |
"error", "warning", "info", "hint", or {cel} |
Default from defaults. |
message |
Message | The finding's text. |
target |
Expression | The element or elements the finding is attached to. Default self, or the document for scope: "document". |
enforcement |
"report", "prevent", "prevent-and-report" |
prevent: a transaction that would make the rule false for an element it held for is rolled back, with the message as its refusal. Default "report". |
enabled |
bool | Default true. |
{ "id": "severalTitles", "code": "knowledge.several-titles", "scope": ["Property"], "severity": "error",
"rule": "self.positionIn(document.elementsOfType('Property').filter(p, p.title)) == 0",
"message": "Only one property can be the title; this one is not used as the title." }
The rule holds for the first title property only, so every later one is a finding.
Evaluating constraints, and reading a document, produce findings. A finding carries:
| Property | Type | Description |
|---|---|---|
constraint |
constraint id or built-in id | The rule that raised it. Required. |
code |
qualified identifier | The rule's code, when it has one. |
severity |
"error", "warning", "info", "hint" |
Required. |
message |
string | The rule's Message, evaluated. Required. |
target |
element or elements | What the finding is about; the document for a rule scoped to it. |
location |
SourceLocation | Where in the file it is. |
subject |
string | A display string naming something that is not an element, such as an unreadable entry. |
Every finding MUST carry at least one of target, location and subject. A runtime MUST show findings in a list, where choosing one reveals its element or goes to its location, and beside the element they target.
A SourceLocation is {file, line?, column?, length?}: file relative to the document's own folder with / separators, line and column 1-based, and column and length counted in Unicode code points. When a finding targets an element, its location is where the reader recorded that element.
Order. Findings MUST be listed: first those the reader raised (std.unparseable, std.unreadableEntry, std.missingId, std.duplicateId) in reading order; then the other built-ins, in the order of the table in 6.5; then declared constraints in the order of rules, each by its scope elements in document order.
Parse failure. A reader that cannot parse a body MUST report exactly one std.unparseable finding, at the parser's line and column when it has them, and no other finding located in that body; no constraint is evaluated over it. Whether the document then opens empty or read-only is FBL's (FBL section 9).
A constraint with enforcement prevent or prevent-and-report MUST refuse a transaction that would make it false for an element it held for, before anything is written, and MUST show its message, evaluated in the working state, as the refusal. A refused transaction changes nothing.
The metamodel and the reader raise these by themselves. They behave as declared constraints; their texts are worded by the runtime.
| Id | Raised for | Severity |
|---|---|---|
std.required |
an attribute with required: true that has no value |
error |
std.facets |
a value outside min, max, minLength, maxLength or pattern, or an enumeration value no value of the enumeration has |
error |
std.references |
a reference that names no element | error |
std.containment |
a child whose type its parent's children, or its slot, does not allow |
error |
std.unparseable |
the reader, once per body that cannot be parsed (6.3) | error |
std.unreadableEntry |
the reader, per entry of a parsed body that cannot become an element: an unknown key, a value that does not convert, an entry in no known form; the entry is kept as written | warning |
std.missingId |
the reader or runtime, per element without a usable id, or whose derived id cannot be computed (5.3.4) | warning |
std.duplicateId |
the reader or runtime, per element whose id equals that of an element earlier in reading order (5.3.4) | warning |
A reader MUST report the reader's four as they occur, and MUST NOT fail to open a document because of any of them.