Validation Rules

Wikipedia: Data validation
Business rules change frequently. Encoding them in Tengo scripts instead of compiled Go means rules can be updated and reloaded while the host application keeps running.

fmt  := import("fmt")
text := import("text")
rules := [
    {
        field:   "username",
        check:   func(v) { return is_string(v) && len(v) >= 3 && len(v) <= 20 },
        message: "must be 3–20 characters",
    },
    {
        field:   "age",
        check:   func(v) { return is_int(v) && v >= 13 && v <= 120 },
        message: "must be an integer between 13 and 120",
    },
    {
        field:   "email",
        check:   func(v) { return is_string(v) && text.contains(v, "@") && len(v) > 5 },
        message: "must contain @ and be at least 6 characters",
    },
    {
        field:   "score",
        check:   func(v) { return is_float(v) && v >= 0.0 && v <= 1.0 },
        message: "must be a float between 0.0 and 1.0",
    },
]

validate := func(record) {
    errs := []
    for _, rule in rules {
        val := record[rule.field]
        if is_undefined(val) || !rule.check(val) {
            errs = append(errs, {field: rule.field, msg: rule.message})
        }
    }
    return errs
}
good := {username: "alice", age: 30, email: "alice@example.com", score: 0.95}
errs := validate(good)
fmt.printf("valid record errors: %d\n", len(errs))
bad := {username: "x", age: 10, email: "notanemail", score: 1.5}
errs = validate(bad)
fmt.printf("invalid record errors: %d\n", len(errs))
for _, e in errs {
    fmt.printf("  %-12s %s\n", e.field+":", e.msg)
}

try it

valid record errors: 0
invalid record errors: 4
  username:    must be 3–20 characters
  age:         must be an integer between 13 and 120
  email:       must contain @ and be at least 6 characters
  score:       must be a float between 0.0 and 1.0
loading…