Alternatives
Alternatives allow you to define multiple possible node-rules definitions for the same node. They are typically used when a configuration element may appear in different forms, such as different types or distinct structural layouts.
Alternatives are created by defining a section list instead of a regular section. Each entry in the section list represents one alternative.
Important
Alternatives are evaluated linearly and without backtracking.
An alternative is considered fulfilled as soon as its own constraints are valid. Validation of child nodes happens afterwards.
If an alternative matches at the top level but a child node fails validation, the entire document validation fails immediately. No further alternatives are considered.
This behavior is intentional and must be taken into account when designing alternative definitions.
*[main.interface]*
type: "text"
default: "localhost"
*[main.interface]*
type: "section"
[.address]
type: "text"
default: "localhost"
[.protocol]
type: "text"
default: "https"
[.port]
type: "integer"
default: 443
[main]
interface: "10.120.14.17"
[main.interface]
address: "10.120.14.17"
protocol: "http"
port: 80
Rules for Alternatives
Section List Required: Alternatives must be defined using a section list.
Each section list entry represents one complete alternative.
*[app.service]* type: "integer" *[app.service]* type: "text" in: "http", "https", "smtp", "smtps"
Complete Definitions: Each alternative must be a complete and valid node-rules definition.
*[app.threads]* type: "integer" minimum: 1 maximum: 100 *[app.threads]* minimum: 20 # ERROR: Missing "type"
Order of Definition: Alternatives must be evaluated strictly in the order in which they are defined.
*[server.initial_response]* type: "text" starts: "response:{" ends: "}" *[server.initial_response]* type: "text" starts: "response:"
First Match Wins: The first alternative that fulfills all of its own constraints is selected. Even if later alternatives would also match, they are not considered.
[server] initial_response: "response:{demo}" # Matches the first alternative
Missing Required Node: If a node defined using alternatives is required but missing, the error message must list all valid types declared by the alternatives that match the nodes version constraints.
Example
The 'app.service' value is missing. It must be an Integer or Text value.
Error Handling When No Alternative Matches: If no alternative fulfills all constraints, validators must apply the following algorithm:
Scan all alternatives for matching
typeandversionconstraints.If none match, raise an error listing all valid types.
Example
The 'app.service' must be an Integer or Text value.
If exactly one alternative matches, handle errors as for a regular node-rules definition.
If multiple alternatives match, use the first matching alternative for error reporting.
Defaults: If a
defaultvalue is defined in any alternative and the node is missing, the first default encountered is used.Validators must raise an error if multiple alternatives define a
default.*[app.service]* type: "integer" *[app.service]* type: "text" default: "https"
[app] # "service" defaults to "https"
Optionality: If
is_optionalis present in any alternative, the node is considered optional.Validators must raise an error if multiple alternatives define
is_optional.Validators must raise an error if
is_optionalis not defined in the first alternative.
*[app.service]* type: "integer" is_optional: yes *[app.service]* type: "text"
Sections in Alternatives: When alternatives define sections, their child node-rules are validated individually after the alternative has been selected.
An alternative is considered fulfilled if its own constraints are valid, even if required child nodes are missing.
*[app.screen]* type: "section" [app.screen.size] type: "integer" *[app.screen]* # Does not work as expected type: "section" [app.screen.width] type: "integer" *[app.screen]* type: "text"
[app.screen] width: 10 # ERROR: Missing "size"
Alternatives that differ only in their child layout must be distinguished by constraints at the same level, such as
version.*[app.screen]* type: "section" version: 1 [app.screen.size] type: "integer" *[app.screen]* type: "section" version: 2 [app.screen.width] type: "integer" *[app.screen]* type: "text"
# Assuming version 2 is in effect: [app.screen] width: 10
Design Rationale
Alternatives are evaluated linearly without backtracking. This model is predictable, efficient to implement, and avoids ambiguous validation outcomes that would otherwise arise from structural lookahead.