
Child.pudu
Pudu96 lines4.0 KB
1/** @Validation.Rules.Child.Module — composes nested validator failures */2module PuduLangValidator.Rules.Child34import Std.List as List5import PuduLangValidator.Result as Validation6import PuduLangValidator.Rule as Rule7import PuduLangValidator.Selection as Selection8import PuduLangValidator.Validator as Validator91011export fn setValidator[T, V](name: Str, select: fn(T) -> V, child: Validator.Validator[V]) -> Rule.Rule[T] {12 let groups = childGroups(&child)13 Rule.Rule{name: name, ruleSets: groups, run: fn(root: T, sets: Array[Str], includeDefault: Bool, properties: Array[Str], data: Rule.ContextData) -> Array[Validation.Failure] {14 let options = Validator.Options{properties: requested(name, &properties), ruleSets: sets, includeDefault: includeDefault}15 prefix(name, &Validator.validateWithData(&child, select(root), options, data).errors)16 } }17}181920export fn setOptionalValidator[T, V](21 name: Str, select: fn(T) -> Option[V], child: Validator.Validator[V]22) -> Rule.Rule[T] {23 let groups = childGroups(&child)24 Rule.Rule{name: name, ruleSets: groups, run: fn(root: T, sets: Array[Str], includeDefault: Bool, properties: Array[Str], data: Rule.ContextData) -> Array[Validation.Failure] {25 match select(root) {26 case Some(value) => {27 let options = Validator.Options{properties: requested(name, &properties), ruleSets: sets, includeDefault: includeDefault}28 prefix(name, &Validator.validateWithData(&child, value, options, data).errors)29 }30 case None => []31 }32 } }33}343536export fn forEachValidator[T, V](37 name: Str, select: fn(T) -> Array[V], child: Validator.Validator[V]38) -> Rule.Rule[T] {39 let groups = childGroups(&child)40 Rule.Rule{name: name, ruleSets: groups, run: fn(root: T, sets: Array[Str], includeDefault: Bool, properties: Array[Str], data: Rule.ContextData) -> Array[Validation.Failure] {41 var failures: Array[Validation.Failure] = []42 var index = 043 for value in select(root) {44 let path = name + "[" + show(index) + "]"45 if reaches(name, path, &properties) {46 let options = Validator.Options{properties: requested(path, &properties), ruleSets: sets, includeDefault: includeDefault}47 for failure in prefix(path, &Validator.validateWithData(&child, value, options, data).errors) {48 failures = failures.push(failure)49 }50 }51 index = index + 152 }53 failures54 } }55}565758fn childGroups[V](child: &Validator.Validator[V]) -> Array[Str] {59 var groups: Array[Str] = []60 for rule in child.rules {61 for name in rule.ruleSets {62 if !groups.contains(name) { groups = groups.push(name) }63 }64 }65 if List.any(&child.rules, |rule: Rule.Rule[V]| rule.ruleSets.isEmpty()) { groups = groups.push("default") }66 groups67}686970fn requested(name: Str, properties: &Array[Str]) -> Array[Str] {71 if name.isEmpty() { return *properties }72 var selected: Array[Str] = []73 for wanted in *properties {74 if Selection.covers(wanted, name) { return [] }75 if let Some(relative) = Selection.relative(wanted, name) { selected = selected.push(relative) }76 }77 selected78}798081fn reaches(name: Str, path: Str, properties: &Array[Str]) -> Bool {82 if properties.isEmpty() { return true }83 for wanted in *properties {84 if Selection.overlaps(wanted, path) { return true }85 }86 false87}888990fn prefix(name: Str, failures: &Array[Validation.Failure]) -> Array[Validation.Failure] {91 failures.map(|failure: Validation.Failure| Validation.Failure {92 ..failure,93 propertyName: if name.isEmpty() { failure.propertyName } else if failure.propertyName.isEmpty() { name } else { name + "." + failure.propertyName }94 })95}96