diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index dfe162e..9670d18 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -23,7 +23,6 @@ validgen/ │ ├── parser_validation.go # Validation tag parsing │ └── test_elements.go # Test condition building ├── types/ # Common types and error handling -├── _examples/ # Example usage and test cases └── tests/ # Integration, unit, and benchmark tests ``` @@ -157,7 +156,7 @@ Follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/ ### Documentation Standards - **Generated code comments**: Include generation warning in all generated files - **Function documentation**: Document validation functions if exported -- **Example usage**: Provide clear examples in `_examples/` directory +- **Example usage**: Examples live in github.com/opencodeco/validgen-samples ## CLI Usage Patterns diff --git a/README.md b/README.md index b4b478d..a32d308 100644 --- a/README.md +++ b/README.md @@ -207,60 +207,19 @@ cd validgen make endtoendtests ``` -## Steps to run the examples +## Examples -All examples are in the `_examples` folder. +Examples live in [opencodeco/validgen-samples](https://github.com/opencodeco/validgen-samples). -### Steps to run test01 +That repository is a separate Go module. It shows the ValidGen CLI, the generated import of `github.com/opencodeco/validgen/types`, and a caller importing the package that owns the structs. -Test01 aims to be a case where all the files are in the same package (in this case, the main package). +Build the CLI from this repository with `make build`. The binary is `bin/validgen`. It takes one path and writes `validator__.go` next to the structs. ```bash -# Runs validgen to generate structs validator code -./bin/validgen _examples/test01 +./bin/validgen /path/to/package ``` -After that the file `user_validator.go` will be generated. This file contains UserValidate function that is responsible to check if User object has a valid content. - -```bash -# Execute the test -cd _examples/test01 -go run . -``` - -### Steps to run test02 - -Test02 aims to be an example where the structs to be validated are in another package (structsinpkg in this test). - -```bash -# Runs validgen to generate structs validator code -./bin/validgen _examples/test02 -``` - -After that the file `user_validator.go` will be generated. This file contains UserValidate function that is responsible to check if User object has a valid content. - -```bash -# Execute the test -cd _examples/test02 -go run . -``` - -### Steps to run test03 - -Test03 aims to be an example where the structs to be validated use min and max tags. - -```bash -# Runs validgen to generate structs validator code -./bin/validgen _examples/test03 -``` - -After that the file `user_validator.go` will be generated. This file contains UserValidate function that is responsible to check if User object has a valid content. - -```bash -# Execute the test -cd _examples/test03 -go run . -``` +Pass `-unmarshal-json` to also generate `UnmarshalJSON` methods. The samples repository shows that flag on `signup`. ## Steps to run the benchmark tests comparing ValidGen and Validator diff --git a/_examples/email_test/test_email.go b/_examples/email_test/test_email.go deleted file mode 100644 index fc49005..0000000 --- a/_examples/email_test/test_email.go +++ /dev/null @@ -1,87 +0,0 @@ -package main - -import ( - "fmt" -) - -type User struct { - Email1 string `valid:"required,email"` - Email2 string `valid:"email"` -} - -func main() { - // Test case 1: Empty required email (should fail) - u1 := &User{ - Email1: "", - Email2: "", - } - if errs := UserValidate(u1); len(errs) > 0 { - fmt.Printf("User1: %+v Errors: ", u1) - for _, err := range errs { - fmt.Printf("%s; ", err) - } - fmt.Println() - } else { - fmt.Printf("User1: %+v is valid\n", u1) - } - - // Test case 2: Invalid required email (should fail) - u2 := &User{ - Email1: "invalid.email", - Email2: "", - } - if errs := UserValidate(u2); len(errs) > 0 { - fmt.Printf("User2: %+v Errors: ", u2) - for _, err := range errs { - fmt.Printf("%s; ", err) - } - fmt.Println() - } else { - fmt.Printf("User2: %+v is valid\n", u2) - } - - // Test case 3: Valid required email, empty optional email (should pass) - u3 := &User{ - Email1: "valid@example.com", - Email2: "", - } - if errs := UserValidate(u3); len(errs) > 0 { - fmt.Printf("User3: %+v Errors: ", u3) - for _, err := range errs { - fmt.Printf("%s; ", err) - } - fmt.Println() - } else { - fmt.Printf("User3: %+v is valid\n", u3) - } - - // Test case 4: Valid required email, valid optional email (should pass) - u4 := &User{ - Email1: "user@domain.com", - Email2: "optional@test.org", - } - if errs := UserValidate(u4); len(errs) > 0 { - fmt.Printf("User4: %+v Errors: ", u4) - for _, err := range errs { - fmt.Printf("%s; ", err) - } - fmt.Println() - } else { - fmt.Printf("User4: %+v is valid\n", u4) - } - - // Test case 5: Valid required email, invalid optional email (should fail) - u5 := &User{ - Email1: "user@domain.com", - Email2: "invalid.email", - } - if errs := UserValidate(u5); len(errs) > 0 { - fmt.Printf("User5: %+v Errors: ", u5) - for _, err := range errs { - fmt.Printf("%s; ", err) - } - fmt.Println() - } else { - fmt.Printf("User5: %+v is valid\n", u5) - } -} diff --git a/_examples/email_test/validator__.go b/_examples/email_test/validator__.go deleted file mode 100755 index 8b8d211..0000000 --- a/_examples/email_test/validator__.go +++ /dev/null @@ -1,22 +0,0 @@ -// Code generated by ValidGen. DO NOT EDIT. - -//nolint:all -package main - -import ( - "github.com/opencodeco/validgen/types" -) - -func UserValidate(obj *User) []error { - var errs []error - if !(obj.Email1 != "") { - errs = append(errs, types.NewValidationError("Email1 is required")) - } - if !(types.IsValidEmail(obj.Email1)) { - errs = append(errs, types.NewValidationError("Email1 must be a valid email")) - } - if !(types.IsValidEmail(obj.Email2)) { - errs = append(errs, types.NewValidationError("Email2 must be a valid email")) - } - return errs -} diff --git a/_examples/test01/test01.go b/_examples/test01/test01.go deleted file mode 100644 index 92fd52d..0000000 --- a/_examples/test01/test01.go +++ /dev/null @@ -1,37 +0,0 @@ -package main - -import ( - "fmt" -) - -type User struct { - FirstName string `valid:"required"` - LastName string `valid:"required"` - Age uint8 `valid:"required"` -} - -type NoValidTag struct { - Name string - Address string -} - -func main() { - u1 := &User{} - if err := UserValidate(u1); err != nil { - fmt.Printf("User: %+v Error: %s\n", u1, err) - } else { - fmt.Printf("User: %+v is valid\n", u1) - } - - u2 := &User{ - FirstName: "First", - LastName: "Last", - Age: 18, - } - - if err := UserValidate(u2); err != nil { - fmt.Printf("User: %+v Error: %s\n", u2, err) - } else { - fmt.Printf("User: %+v is valid\n", u2) - } -} diff --git a/_examples/test01/validator__.go b/_examples/test01/validator__.go deleted file mode 100755 index 0d6eb7c..0000000 --- a/_examples/test01/validator__.go +++ /dev/null @@ -1,22 +0,0 @@ -// Code generated by ValidGen. DO NOT EDIT. - -//nolint:all -package main - -import ( - "github.com/opencodeco/validgen/types" -) - -func UserValidate(obj *User) []error { - var errs []error - if !(obj.FirstName != "") { - errs = append(errs, types.NewValidationError("FirstName is required")) - } - if !(obj.LastName != "") { - errs = append(errs, types.NewValidationError("LastName is required")) - } - if !(obj.Age != 0) { - errs = append(errs, types.NewValidationError("Age is required")) - } - return errs -} diff --git a/_examples/test02/structsinpkg/mystructs.go b/_examples/test02/structsinpkg/mystructs.go deleted file mode 100644 index 8220612..0000000 --- a/_examples/test02/structsinpkg/mystructs.go +++ /dev/null @@ -1,12 +0,0 @@ -package structsinpkg - -type User struct { - FirstName string `valid:"required"` - LastName string `valid:"required"` - Age uint8 `valid:"required"` -} - -type NoValidTag struct { - Name string - Address string -} diff --git a/_examples/test02/structsinpkg/validator__.go b/_examples/test02/structsinpkg/validator__.go deleted file mode 100755 index c19e1f8..0000000 --- a/_examples/test02/structsinpkg/validator__.go +++ /dev/null @@ -1,22 +0,0 @@ -// Code generated by ValidGen. DO NOT EDIT. - -//nolint:all -package structsinpkg - -import ( - "github.com/opencodeco/validgen/types" -) - -func UserValidate(obj *User) []error { - var errs []error - if !(obj.FirstName != "") { - errs = append(errs, types.NewValidationError("FirstName is required")) - } - if !(obj.LastName != "") { - errs = append(errs, types.NewValidationError("LastName is required")) - } - if !(obj.Age != 0) { - errs = append(errs, types.NewValidationError("Age is required")) - } - return errs -} diff --git a/_examples/test02/test02.go b/_examples/test02/test02.go deleted file mode 100644 index f21e87c..0000000 --- a/_examples/test02/test02.go +++ /dev/null @@ -1,28 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/opencodeco/validgen/_examples/test02/structsinpkg" -) - -func main() { - u1 := &structsinpkg.User{} - if err := structsinpkg.UserValidate(u1); err != nil { - fmt.Printf("User: %+v Error: %s\n", u1, err) - } else { - fmt.Printf("User: %+v is valid\n", u1) - } - - u2 := &structsinpkg.User{ - FirstName: "First", - LastName: "Last", - Age: 18, - } - - if err := structsinpkg.UserValidate(u2); err != nil { - fmt.Printf("User: %+v Error: %s\n", u2, err) - } else { - fmt.Printf("User: %+v is valid\n", u2) - } -} diff --git a/_examples/test03/test03.go b/_examples/test03/test03.go deleted file mode 100644 index 98dc57f..0000000 --- a/_examples/test03/test03.go +++ /dev/null @@ -1,54 +0,0 @@ -package main - -import ( - "fmt" -) - -type User struct { - FirstName string `valid:"required"` - LastName string `valid:"required"` - Age uint8 `valid:"gte=18,lte=130"` - UserName string `valid:"min=5,max=10"` - Optional string -} - -func main() { - u1 := &User{ - FirstName: "", - LastName: "", - Age: 135, - UserName: "abc", - } - - if err := UserValidate(u1); err != nil { - fmt.Printf("User: %+v Error: %s\n", u1, err) - } else { - fmt.Printf("User: %+v is valid\n", u1) - } - - u2 := &User{ - FirstName: "", - LastName: "", - Age: 135, - UserName: "mylongusername", - } - - if err := UserValidate(u2); err != nil { - fmt.Printf("User: %+v Error: %s\n", u2, err) - } else { - fmt.Printf("User: %+v is valid\n", u2) - } - - u3 := &User{ - FirstName: "First", - LastName: "Last", - Age: 49, - UserName: "myusername", - } - - if err := UserValidate(u3); err != nil { - fmt.Printf("User: %+v Error: %s\n", u3, err) - } else { - fmt.Printf("User: %+v is valid\n", u3) - } -} diff --git a/_examples/test03/validator__.go b/_examples/test03/validator__.go deleted file mode 100755 index 92e74ad..0000000 --- a/_examples/test03/validator__.go +++ /dev/null @@ -1,31 +0,0 @@ -// Code generated by ValidGen. DO NOT EDIT. - -//nolint:all -package main - -import ( - "github.com/opencodeco/validgen/types" -) - -func UserValidate(obj *User) []error { - var errs []error - if !(obj.FirstName != "") { - errs = append(errs, types.NewValidationError("FirstName is required")) - } - if !(obj.LastName != "") { - errs = append(errs, types.NewValidationError("LastName is required")) - } - if !(obj.Age >= 18) { - errs = append(errs, types.NewValidationError("Age must be >= 18")) - } - if !(obj.Age <= 130) { - errs = append(errs, types.NewValidationError("Age must be <= 130")) - } - if !(len(obj.UserName) >= 5) { - errs = append(errs, types.NewValidationError("UserName length must be >= 5")) - } - if !(len(obj.UserName) <= 10) { - errs = append(errs, types.NewValidationError("UserName length must be <= 10")) - } - return errs -} diff --git a/docs/internals.md b/docs/internals.md index b2e2716..cb93b9d 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -156,7 +156,7 @@ Imports kept on the generated package are the struct file's imports whose local `Writer` renders `fileValidatorTpl` for each package. The file starts with `// Code generated by ValidGen. DO NOT EDIT.` and then `//nolint:all`, declares the source package, and prints `ValidatorFuncCode` then `UnmarshalJSONCode` for each struct in the package map. `go/format` formats the buffer. The output path is `Path + "/validator__.go"`, and the file mode is `os.ModePerm`. -The same path is rewritten on every run. Structs in one package and directory share that file. Structs with no `valid` tag are absent from it. `_examples/test01` shows the shape. `User` has `valid` tags and `UserValidate` is written. `NoValidTag` is parsed, printed, and omitted from `validator__.go`. +The same path is rewritten on every run. Structs in one package and directory share that file. A struct with a `valid` or `validate` tag gets a `Validate` function in that file. A struct with neither tag is parsed, printed, and omitted. ## TestGen diff --git a/testgen/README.md b/testgen/README.md index dfc798a..60577b6 100644 --- a/testgen/README.md +++ b/testgen/README.md @@ -101,7 +101,6 @@ These groups already have tests beside the code they check. A generator that rea - Operation checks in `internal/analyzer/operations/operations_test.go`. The functions are `TestOperationsIsValid`, `TestOperationsIsValidByType`, `TestOperationsIsFieldOperation`, and `TestOperationsArgsCount`. - Condition-table checks in `internal/codegenerator/get_test_elements_*_test.go`. Each case stores the condition string and the error string passed to `DefineTestElements`. - Parser checks in `internal/parser/parser_test.go`. They compare parsed structs with source text. -- Examples under `_examples/`. Field-operation rows in the four generated suites wait on integer field operations in issue #78. Complex ordering tags stay rejected, and `dive` in issue #7 is separate work.