50 lines
1.4 KiB
Text
50 lines
1.4 KiB
Text
# domains/framework/ontology/constraints.ncl
|
|
# Shared mode constraints for Library, Service, and Tool projects.
|
|
# Projects import via:
|
|
# let c = import "framework/ontology/constraints.ncl" in
|
|
|
|
let _non_empty_steps = std.contract.custom (
|
|
fun label =>
|
|
fun value =>
|
|
if std.array.length value.steps == 0 then
|
|
'Error {
|
|
message = "Mode '%{value.id}': steps must not be empty — a mode with no steps is passive documentation, not an executable contract"
|
|
}
|
|
else
|
|
'Ok value
|
|
) in
|
|
|
|
let _valid_trigger = std.contract.custom (
|
|
fun label =>
|
|
fun value =>
|
|
if std.string.length value.trigger == 0 then
|
|
'Error {
|
|
message = "Mode '%{value.id}': trigger must not be empty — it identifies how this mode is invoked"
|
|
}
|
|
else
|
|
'Ok value
|
|
) in
|
|
|
|
let _non_empty_cmds = std.contract.custom (
|
|
fun label =>
|
|
fun value =>
|
|
let bad = value.steps
|
|
|> std.array.filter (fun s =>
|
|
std.record.has_field "cmd" s
|
|
&& std.string.length (std.string.trim s.cmd) == 0
|
|
)
|
|
|> std.array.map (fun s => s.id)
|
|
in
|
|
if std.array.length bad > 0 then
|
|
'Error {
|
|
message = "Mode '%{value.id}': steps with empty cmd: %{std.string.join ", " bad}"
|
|
}
|
|
else
|
|
'Ok value
|
|
) in
|
|
|
|
{
|
|
NonEmptySteps = _non_empty_steps,
|
|
ValidTrigger = _valid_trigger,
|
|
NonEmptyCmds = _non_empty_cmds,
|
|
}
|