@@ -13,6 +13,7 @@ package validators
1313
1414import (
1515 "fmt"
16+ "math"
1617 "regexp"
1718 "strings"
1819
@@ -192,3 +193,79 @@ func (v *AllowedEnumValidator) Validate(
192193 return v .checkValues (t .Values , t .Path , allowedSet , allowedList , caseSensitive , allowNull , rule .ErrorMessage )
193194 })
194195}
196+
197+ // RangeValidator implements the Validator interface for 'range' type.
198+ type RangeValidator struct {}
199+
200+ // Validate checks if the variables specified in the rule match the provided range pattern.
201+ // This function focuses on the predicate and uses IterateRuleTargets from targets.go to resolve targets.
202+ func (r * RangeValidator ) Validate (
203+ bp config.Blueprint ,
204+ mod config.Module ,
205+ rule modulereader.ValidationRule ,
206+ group config.Group ,
207+ modIdx int ) error {
208+
209+ modPath := config .Root .Groups .At (bp .GroupIndex (group .Name )).Modules .At (modIdx ).Source
210+ // Extract min
211+
212+ min , err := parseIntInput (rule .Inputs , "min" , math .MinInt )
213+ if err != nil {
214+ return config.BpError {
215+ Err : fmt .Errorf (
216+ "validation rule for module %q: %v" , mod .ID , err ),
217+ Path : modPath ,
218+ }
219+ }
220+ // Extract max
221+ max , err := parseIntInput (rule .Inputs , "max" , math .MaxInt )
222+ if err != nil {
223+ return config.BpError {
224+ Err : fmt .Errorf (
225+ "validation rule for module %q: %v" , mod .ID , err ),
226+ Path : modPath ,
227+ }
228+ }
229+
230+ // helper: validate flattened cty.Values against range, returning first error
231+ validateValues := func (values []cty.Value , path config.Path ) error {
232+ for _ , val := range values {
233+ if val .IsNull () || ! val .IsKnown () {
234+ continue
235+ }
236+ var checkValue int
237+ var isList bool
238+ if val .Type ().IsListType () {
239+ checkValue = val .LengthInt ()
240+ isList = true
241+ } else if val .Type () == cty .Number {
242+ f , _ := val .AsBigFloat ().Float64 ()
243+ checkValue = int (f )
244+ } else {
245+ return config.BpError {
246+ Err : fmt .Errorf ("range validator only supports numbers or lists, not %s" , val .Type ().FriendlyName ()),
247+ Path : path ,
248+ }
249+ }
250+ effectiveMin := min
251+ if isList {
252+ effectiveMin = 0 // Default min is 0 for lists if not specified
253+ }
254+
255+ if checkValue < effectiveMin || checkValue > max {
256+ msg := rule .ErrorMessage
257+ if msg == "" {
258+ msg = fmt .Sprintf ("value %d is out of range [%d, %d]" , checkValue , effectiveMin , max )
259+ }
260+ return config.BpError {Err : fmt .Errorf ("error: %s" , msg ), Path : path }
261+ }
262+ }
263+ return nil
264+ }
265+
266+ // iterate targets using shared logic
267+ err = IterateRuleTargets (bp , mod , rule , group , modIdx , func (t Target ) error {
268+ return validateValues (t .Values , t .Path )
269+ })
270+ return err
271+ }
0 commit comments