Checks - ConfigMate/configmate GitHub Wiki

Checks in ConfigMate

Overview

Checks are a cornerstone feature of ConfigMate. They are defined using ConfigMate Check Language (CMCL), a restricted programmatic language. The most important element in checks are functions which are tied to field types. To see what types are supported and what functions are available for each type, see Types and Functions.

CMCL: Syntax and Usage

Components of CMCL

  • Type-Specific Functions: Each field type has its own set of validation functions.
  • Logical Operators: Include &&, ||, and !.
  • Control Structures: Provide conditional logic (if-elseif-else) and iteration (foreach).

Writing Checks

Basic Structure

The basic form of a check is a simple expression. A simple expression can be a function expression or a field expression. A function expression starts by invoking a function. A field expression starts with a reference to another field in the configuration file. Any function we invoke in the beginning of a function expression will be invoked on the field being defined. The first funciton in a field expression is invoked on the referenced field. We can then chain more functions to the expression, and each function will be invoked on the result of the previous function.

To illustrate, consider the following example of a check defined for a field of type int:

eq(3)

This is a function expression. It invokes the equality function (eq) on the int field, and passes the value 3 as an argument to the function. Here we are comparing the value of the field to the value 3. Now, consider another field of type int exists and it's called F. We can write a check for the first field that compares it to F by writing:

eq(F)

Here, the argument to the equality function is a field expression. The field expression is simply a reference to the field F. The equality function will check that the first field is equal to the value of F. Now if F was of type float, we could write:

eq(F.toInt())

We can write function expressions as field expressions by using the keyword this. The keyword this refers to the field being defined. So, the following two expressions are equivalent:

eq(3)
this.eq(3)

The use of the keyword this is necessary when we want to reference the field being defined inside a field expression that referenced another field. For example, consider the following check:

F.eq(this)

A valid check must result in a boolean; a true result will be considered a passing check and a false one a failed one.

Logical Operators

Logical operators can be used to combine multiple expressions. The operators && and || are supported, and they have the same semantics as in most programming languages. The operator ! is also supported, and it negates the result of the expressions it is applied to.

Grouping Expressions

We can group expressions using parentheses. This allows us to control the order in which expressions are evaluated.

Control Structures

Control structures allow us to write more complex checks.

Conditional Logic

The if-elseif-else structure allows us to write checks where the desired state or behavior of a field varies with respect to the value of other expressions. For example, consider the following check:

if (F.eq(3)) {
    eq(5)
}

Iteration

The foreach structure allows us to iterate over a list of fields and perform checks on each of them. The syntax of the foreach structure is as follows:

foreach (var : list) {
    // Check that uses var
}

Where var is the name of the variable that will be used to reference each element in the list, and list is the list of fields to iterate over. The list must be a field name.

This is another case where the keyword this is necessary. Consider we are defining a field of type list and we want to perform some check on each of its elements. We can write:

foreach (e : this) {
    // Check that uses e
}

Examples

  • For a 'host' field:

    reachable()
    
  • For an 'int' field:

    range(25, 100)
    
  • For a 'list' field:

    len().gte(3)
    
    foreach(s : this) { s.reachable() }
    
  • For a 'string' field:

    regex('^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$')
    
  • For a 'file' field:

    exists()
    
    isDir()
    
    perms().eq("0644")
    
⚠️ **GitHub.com Fallback** ⚠️