diff --git a/README.md b/README.md index 75f323e7..dad8f2dd 100644 --- a/README.md +++ b/README.md @@ -347,6 +347,8 @@ validators include: | Required | any | Rejects zero values of the response type | Boolean values pass straight through since the zero value (false) is a valid response | | MinLength(n) | string | Enforces that a response is at least the given length | | | MaxLength(n) | string | Enforces that a response is no longer than the given length | | +| MaxItems(n) | []OptionAnswer | Enforces that a response has no more selections of the indicated | | +| MinItems(n) | []OptionAnswer | Enforces that a response has no less selections of the indicated | | ## Help Text diff --git a/validate.go b/validate.go index ba7e1278..462dc5e7 100644 --- a/validate.go +++ b/validate.go @@ -4,6 +4,8 @@ import ( "errors" "fmt" "reflect" + + "github.com/AlecAivazis/survey/v2/core" ) // Required does not allow an empty value @@ -58,6 +60,44 @@ func MinLength(length int) Validator { } } +// MaxItems requires that the list is no longer than the specified value +func MaxItems(numberItems int) Validator { + // return a validator that checks the length of the list + return func(val interface{}) error { + if list, ok := val.([]core.OptionAnswer); ok { + // if the list is longer than the given value + if len(list) > numberItems { + // yell loudly + return fmt.Errorf("value is too long. Max items is %v", numberItems) + } + } else { + // otherwise we cannot convert the value into a list of answer and cannot enforce length + return fmt.Errorf("cannot impose the length on something other than a list of answers") + } + // the input is fine + return nil + } +} + +// MinItems requires that the list is longer or equal in length to the specified value +func MinItems(numberItems int) Validator { + // return a validator that checks the length of the list + return func(val interface{}) error { + if list, ok := val.([]core.OptionAnswer); ok { + // if the list is shorter than the given value + if len(list) < numberItems { + // yell loudly + return fmt.Errorf("value is too long. Min items is %v", numberItems) + } + } else { + // otherwise we cannot convert the value into a list of answer and cannot enforce length + return fmt.Errorf("cannot impose the length on something other than a list of answers") + } + // the input is fine + return nil + } +} + // ComposeValidators is a variadic function used to create one validator from many. func ComposeValidators(validators ...Validator) Validator { // return a validator that calls each one sequentially diff --git a/validate_test.go b/validate_test.go index 4def869a..62c4c564 100644 --- a/validate_test.go +++ b/validate_test.go @@ -3,6 +3,8 @@ package survey import ( "math/rand" "testing" + + "github.com/AlecAivazis/survey/v2/core" ) func TestRequired_canSucceedOnPrimitiveTypes(t *testing.T) { @@ -86,6 +88,40 @@ func randString(n int) string { return string(b) } +func TestMaxItems(t *testing.T) { + // the list to test + testList := []core.OptionAnswer{ + core.OptionAnswer{Value: "a", Index: 0}, + core.OptionAnswer{Value: "b", Index: 1}, + core.OptionAnswer{Value: "c", Index: 2}, + core.OptionAnswer{Value: "d", Index: 3}, + core.OptionAnswer{Value: "e", Index: 4}, + core.OptionAnswer{Value: "f", Index: 5}, + } + + // validate the list + if err := MaxItems(4)(testList); err == nil { + t.Error("No error returned with input greater than 6 items.") + } +} + +func TestMinItems(t *testing.T) { + // the list to test + testList := []core.OptionAnswer{ + core.OptionAnswer{Value: "a", Index: 0}, + core.OptionAnswer{Value: "b", Index: 1}, + core.OptionAnswer{Value: "c", Index: 2}, + core.OptionAnswer{Value: "d", Index: 3}, + core.OptionAnswer{Value: "e", Index: 4}, + core.OptionAnswer{Value: "f", Index: 5}, + } + + // validate the list + if err := MinItems(10)(testList); err == nil { + t.Error("No error returned with input less than 10 items.") + } +} + func TestMaxLength(t *testing.T) { // the string to test testStr := randString(150)