Defaults and Optionality

Validation Rules provide two distinct mechanisms for handling missing elements in a parsed configuration document:

  • Defaults — supply a predefined value when a scalar value or value list is missing.

  • Optionality — explicitly mark a node as optional so it may be omitted entirely, along with all of its child nodes.

While both mechanisms address missing data, they serve different purposes and have different semantics.

Feature

Defaults

Optionality

Applies to

Scalar values, Value lists

Any node (values, lists, sections)

Purpose

Supply a predefined value if the node is missing

Allow the node (and its children) to be omitted entirely

Child Nodes

Defaults affect only the node itself

Optionality cascades to all child nodes

Constraints

Default values bypass constraints (except type)

Constraints apply only if the node exists

Interaction

Must not be combined with is_optional

Must not be combined with default

Common Use

Avoid repetitive program logic for common values

Support optional features or configuration sections

[api.host]
type: "text"
default: "127.0.0.1"

[api.port]
type: "integer"
default: 9000

[client]
type: "section"
is_optional: yes

[client.name]
type: "text"
[api]  # The "api" section is required
# "host" and "port" use their default values
# The "client" section is optional

Design Rationale

Defaults are intentionally limited to scalar values and value lists to keep their behavior simple, predictable, and easy to implement.

While it might appear useful to provide defaults for entire sections or complete configurations, doing so would blur the boundary between validation and configuration derivation. Applications that require a default configuration should provide one explicitly (for example, by shipping a base configuration file or embedding defaults in code) and then validate user-provided overrides.

Defaults in Validation Rules are therefore designed as a lightweight, node-level convenience. They eliminate repetitive application logic while also serving as machine-readable documentation of commonly used values.

Rules for Defaults

  1. Default Field: A node-rules definition may specify a default value that is used if the node is missing from the configuration document.

    [api.host]
    type: "text"
    default: "127.0.0.1"
    
  2. Allowed Node Types: Defaults may only be defined for scalar values and value lists.

    [article.tags]
    type: "ValueList"
    default: "article", "news"
    
    [article.tags.vr_entry]
    type: "text"
    minimum: 1
    maximum: 60
    
  3. Type Matching: A default value must match the declared type of the node-rules definition.

    [api.port]
    type: "integer"
    default: 9000
    
  4. Constraints Ignored: Default values are not validated against local constraints, except for the type constraint.

    This allows placeholder or empty values to be supplied even if they would not be valid as explicit user input.

    [server.name]
    type: "text"
    minimum: 1
    default: ""   # Allowed, even though the minimum length is 1
    
  5. No Combination with Optionality: A node-rules definition must not combine default with is_optional.

    Validators must report this as an error.

    [server.name]
    type: "text"
    is_optional: yes
    default: "example"  # ERROR: default and is_optional must not be combined
    

Rules for Optional Nodes

  1. Optional Flag: Any node may be marked as optional by setting is_optional: yes.

    [client]
    type: "section"
    is_optional: yes
    
    [client.name]
    type: "text"
    
  2. Optionality Includes Child Nodes: If an optional node does not exist, none of its child nodes may exist either.

    Even if child nodes are normally required, they are only validated if the parent node itself exists.

    # An empty document is valid because "client" is optional
    
  3. No Defaults for Missing Optional Nodes: Defaults are not applied when an optional node is missing.

    Child nodes are neither created nor validated unless the optional parent node exists.

    [client]
    type: "section"
    is_optional: yes
    
    [client.name]
    type: "text"
    default: "unknown"
    
    # Empty document — "client" does not exist,
    # so "client.name" is not created.
    
    [client]
    # "client" exists, so "client.name" is created
    # with the default value "unknown"