From 26a1d45f8c70ac91767e5b161aecf0bd949d8976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=2E=20Garz=C3=A3o?= Date: Thu, 17 Jul 2025 21:56:22 -0300 Subject: [PATCH 1/4] feat: nin (not in) validation string --- validgen/if_code.go | 2 +- validgen/parser_validation.go | 1 + validgen/test_elements.go | 37 ++++++++++++++++++++++++----------- 3 files changed, 28 insertions(+), 12 deletions(-) diff --git a/validgen/if_code.go b/validgen/if_code.go index 2d840cd..8a3983c 100644 --- a/validgen/if_code.go +++ b/validgen/if_code.go @@ -28,7 +28,7 @@ func IfCode(fieldName, fieldValidation, fieldType string) (string, error) { booleanCondition := "" for _, roperand := range testElements.rightOperands { if booleanCondition != "" { - booleanCondition += " || " + booleanCondition += " " + testElements.concatOperator + " " } booleanCondition += fmt.Sprintf("%s %s %s", testElements.leftOperand, testElements.operator, roperand) diff --git a/validgen/parser_validation.go b/validgen/parser_validation.go index 66df7b9..0f64e11 100644 --- a/validgen/parser_validation.go +++ b/validgen/parser_validation.go @@ -34,6 +34,7 @@ func ParserValidation(fieldValidation string) (*Validation, error) { "neq": ONE_VALUE, "neq_ignore_case": ONE_VALUE, "in": MANY_VALUES, + "nin": MANY_VALUES, } validation, values, err := parserValidationString(fieldValidation) diff --git a/validgen/test_elements.go b/validgen/test_elements.go index f0b6ec3..4850807 100644 --- a/validgen/test_elements.go +++ b/validgen/test_elements.go @@ -8,15 +8,18 @@ import ( ) const ( - EqIgnoreCaseTag = "eq_ignore_case" - NeqIgnoreCaseTag = "neq_ignore_case" + EqIgnoreCaseOp = "eq_ignore_case" + NeqIgnoreCaseOp = "neq_ignore_case" + InOp = "in" + NotInOp = "nin" ) type TestElements struct { - leftOperand string - operator string - rightOperands []string - errorMessage string + leftOperand string + operator string + rightOperands []string + concatOperator string + errorMessage string } func GetTestElements(fieldName, fieldValidation, fieldType string) (TestElements, error) { @@ -41,6 +44,7 @@ func GetTestElements(fieldName, fieldValidation, fieldType string) (TestElements "neq,string": {"{{.Name}}", "!=", `"{{.Target}}"`, "{{.Name}} must not be equal to '{{.Target}}'"}, "neq_ignore_case,string": {"types.ToLower({{.Name}})", "!=", `"{{.Target}}"`, "{{.Name}} must not be equal to '{{.Target}}'"}, "in,string": {"{{.Name}}", "==", `"{{.Target}}"`, "{{.Name}} must be one of {{.Targets}}"}, + "nin,string": {"{{.Name}}", "!=", `"{{.Target}}"`, "{{.Name}} must not be one of {{.Targets}}"}, } validation, err := ParserValidation(fieldValidation) @@ -53,7 +57,7 @@ func GetTestElements(fieldName, fieldValidation, fieldType string) (TestElements return TestElements{}, types.NewValidationError("unsupported validation %s type %s", fieldValidation, fieldType) } - if validation.Operation == EqIgnoreCaseTag || validation.Operation == NeqIgnoreCaseTag { + if validation.Operation == EqIgnoreCaseOp || validation.Operation == NeqIgnoreCaseOp { for i := range validation.Values { validation.Values[i] = strings.ToLower(validation.Values[i]) } @@ -76,16 +80,27 @@ func GetTestElements(fieldName, fieldValidation, fieldType string) (TestElements } } + var concatOperator string + switch validation.Operation { + case InOp: + concatOperator = "||" + case NotInOp: + concatOperator = "&&" + default: + concatOperator = "" + } + targetValues = strings.TrimSpace(targetValues) errorMsg := condition.errorMessage errorMsg = replaceNameAndTargetWithoutPrefix(errorMsg, fieldName, targetValue) errorMsg = replaceTargetInErrors(errorMsg, targetValue, targetValues) return TestElements{ - leftOperand: replaceNameAndTargetWithPrefix(condition.loperand, fieldName, targetValue), - operator: condition.operator, - rightOperands: roperands, - errorMessage: errorMsg, + leftOperand: replaceNameAndTargetWithPrefix(condition.loperand, fieldName, targetValue), + operator: condition.operator, + rightOperands: roperands, + concatOperator: concatOperator, + errorMessage: errorMsg, }, nil } From c5df4dfc15dda367c84aaf4d6f7b3c2608b8aa30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=2E=20Garz=C3=A3o?= Date: Thu, 17 Jul 2025 21:56:56 -0300 Subject: [PATCH 2/4] test: nin (not in) validation --- README.md | 2 +- tests/endtoend/string.go | 4 +++ tests/endtoend/stringtype_validator.go | 4 +++ validgen/get_test_elements_string_test.go | 32 +++++++++++++++++------ validgen/if_code_test.go | 13 +++++++++ 5 files changed, 46 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 68feb09..3c00958 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ The following table shows the validations and possible types, where "I" means "I | max | I | - | - | W | W | W | W | W | | min | I | - | - | W | W | W | W | W | | in | I | W | W | W | W | W | - | W | -| nin | W | W | W | W | W | W | - | W | +| nin | I | W | W | W | W | W | - | W | | required | I | W | W | W | W | W | W | W | # Steps to run the unit tests diff --git a/tests/endtoend/string.go b/tests/endtoend/string.go index fc876a7..9407109 100644 --- a/tests/endtoend/string.go +++ b/tests/endtoend/string.go @@ -11,6 +11,7 @@ type StringType struct { FieldNeq string `validate:"neq=cba"` FieldNeqIC string `validate:"neq_ignore_case=YeS"` FieldIn string `validate:"in=ab bc cd"` + FieldNotIn string `validate:"nin=xx yy zz"` } func string_tests() { @@ -25,6 +26,7 @@ func string_tests() { FieldNeq: "cba", FieldNeqIC: "yeS", FieldIn: "abc", + FieldNotIn: "zz", } expectedMsgErrors = []string{ "FieldReq is required", @@ -35,6 +37,7 @@ func string_tests() { "FieldNeq must not be equal to 'cba'", "FieldNeqIC must not be equal to 'yes'", "FieldIn must be one of 'ab' 'bc' 'cd'", + "FieldNotIn must not be one of 'xx' 'yy' 'zz'", } errs = StringTypeValidate(v) if !expectedMsgErrorsOk(errs, expectedMsgErrors) { @@ -50,6 +53,7 @@ func string_tests() { FieldNeq: "ops", FieldNeqIC: "No", FieldIn: "bc", + FieldNotIn: "xy", } expectedMsgErrors = nil errs = StringTypeValidate(v) diff --git a/tests/endtoend/stringtype_validator.go b/tests/endtoend/stringtype_validator.go index 755d74b..4f31129 100644 --- a/tests/endtoend/stringtype_validator.go +++ b/tests/endtoend/stringtype_validator.go @@ -45,5 +45,9 @@ func StringTypeValidate(obj *StringType) []error { errs = append(errs, types.NewValidationError("FieldIn must be one of 'ab' 'bc' 'cd'")) } + if !(obj.FieldNotIn != "xx" && obj.FieldNotIn != "yy" && obj.FieldNotIn != "zz") { + errs = append(errs, types.NewValidationError("FieldNotIn must not be one of 'xx' 'yy' 'zz'")) + } + return errs } diff --git a/validgen/get_test_elements_string_test.go b/validgen/get_test_elements_string_test.go index aab1c7d..dc4f4a6 100644 --- a/validgen/get_test_elements_string_test.go +++ b/validgen/get_test_elements_string_test.go @@ -126,10 +126,11 @@ func TestGetTestElementsWithStringFields(t *testing.T) { fieldValidation: "in=a b c", }, want: TestElements{ - leftOperand: "obj.InField", - operator: "==", - rightOperands: []string{`"a"`, `"b"`, `"c"`}, - errorMessage: "InField must be one of 'a' 'b' 'c'", + leftOperand: "obj.InField", + operator: "==", + rightOperands: []string{`"a"`, `"b"`, `"c"`}, + concatOperator: "||", + errorMessage: "InField must be one of 'a' 'b' 'c'", }, }, { @@ -139,10 +140,25 @@ func TestGetTestElementsWithStringFields(t *testing.T) { fieldValidation: "in=' a ' ' b ' ' c '", }, want: TestElements{ - leftOperand: "obj.InField", - operator: "==", - rightOperands: []string{`" a "`, `" b "`, `" c "`}, - errorMessage: "InField must be one of ' a ' ' b ' ' c '", + leftOperand: "obj.InField", + operator: "==", + rightOperands: []string{`" a "`, `" b "`, `" c "`}, + concatOperator: "||", + errorMessage: "InField must be one of ' a ' ' b ' ' c '", + }, + }, + { + name: "NotIn string with spaces", + args: args{ + fieldName: "NotInField", + fieldValidation: "nin=a b c", + }, + want: TestElements{ + leftOperand: "obj.NotInField", + operator: "!=", + rightOperands: []string{`"a"`, `"b"`, `"c"`}, + concatOperator: "&&", + errorMessage: "NotInField must not be one of 'a' 'b' 'c'", }, }, } diff --git a/validgen/if_code_test.go b/validgen/if_code_test.go index 0bb9aa7..4c81c59 100644 --- a/validgen/if_code_test.go +++ b/validgen/if_code_test.go @@ -51,6 +51,19 @@ func TestIfCode(t *testing.T) { if !(obj.strField == "a" || obj.strField == "b" || obj.strField == "c") { errs = append(errs, types.NewValidationError("strField must be one of 'a' 'b' 'c'")) } +`, + }, + { + name: "if code with string and not in", + args: args{ + fieldName: "strField", + fieldType: "string", + fieldValidation: "nin=a b c", + }, + want: ` + if !(obj.strField != "a" && obj.strField != "b" && obj.strField != "c") { + errs = append(errs, types.NewValidationError("strField must not be one of 'a' 'b' 'c'")) + } `, }, } From d71415dcc325f73313b92d6af4e587f651e1c122 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=2E=20Garz=C3=A3o?= Date: Fri, 18 Jul 2025 18:58:56 -0300 Subject: [PATCH 3/4] fix: check for concat operator when as more 1 operator --- validgen/if_code.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/validgen/if_code.go b/validgen/if_code.go index 8a3983c..c3ca217 100644 --- a/validgen/if_code.go +++ b/validgen/if_code.go @@ -25,6 +25,10 @@ func IfCode(fieldName, fieldValidation, fieldType string) (string, error) { return "", fmt.Errorf("field %s: %w", fieldName, err) } + if len(testElements.rightOperands) > 1 && testElements.concatOperator == "" { + return "", fmt.Errorf("missed concat operator") + } + booleanCondition := "" for _, roperand := range testElements.rightOperands { if booleanCondition != "" { From 1e9b43d10a0d68734742230f5e65344ebeb1b0c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20S=2E=20Garz=C3=A3o?= Date: Fri, 18 Jul 2025 19:08:30 -0300 Subject: [PATCH 4/4] refact: move missed concat operator to get test elements --- validgen/if_code.go | 4 ---- validgen/test_elements.go | 4 ++++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/validgen/if_code.go b/validgen/if_code.go index c3ca217..8a3983c 100644 --- a/validgen/if_code.go +++ b/validgen/if_code.go @@ -25,10 +25,6 @@ func IfCode(fieldName, fieldValidation, fieldType string) (string, error) { return "", fmt.Errorf("field %s: %w", fieldName, err) } - if len(testElements.rightOperands) > 1 && testElements.concatOperator == "" { - return "", fmt.Errorf("missed concat operator") - } - booleanCondition := "" for _, roperand := range testElements.rightOperands { if booleanCondition != "" { diff --git a/validgen/test_elements.go b/validgen/test_elements.go index 7c8faa4..0f3cafe 100644 --- a/validgen/test_elements.go +++ b/validgen/test_elements.go @@ -91,6 +91,10 @@ func GetTestElements(fieldName, fieldValidation, fieldType string) (TestElements concatOperator = "" } + if len(roperands) > 1 && concatOperator == "" { + return TestElements{}, types.NewValidationError("missed concat operator") + } + targetValues = strings.TrimSpace(targetValues) errorMsg := condition.errorMessage errorMsg = replaceNameAndTargetWithoutPrefix(errorMsg, fieldName, targetValue)