HW09 is completed
parent
960977d60c
commit
c683ffa4e9
|
@ -1,5 +1,177 @@
|
|||
package main
|
||||
|
||||
func main() {
|
||||
// Place your code here
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type TemplateVars struct {
|
||||
StructName string
|
||||
FieldName string
|
||||
FieldType string
|
||||
ValidatorValue string
|
||||
IsSlice bool
|
||||
}
|
||||
|
||||
const str string = "string"
|
||||
|
||||
func main() {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.WriteString(validatorHeader)
|
||||
fset := token.NewFileSet()
|
||||
//node, err := parser.ParseFile(fset, os.Args[1], nil, parser.ParseComments)
|
||||
node, err := parser.ParseFile(fset, "models/models.go", nil, parser.ParseComments)
|
||||
if err != nil {
|
||||
log.Fatal("не удалось открыть файл", err.Error())
|
||||
}
|
||||
// Перебираем ноды
|
||||
for _, f := range node.Decls {
|
||||
gd, ok := f.(*ast.GenDecl)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
for _, spec := range gd.Specs {
|
||||
// Ищем структуры
|
||||
t, ok := spec.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
s, ok := t.Type.(*ast.StructType)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
// Если все проверки пройдены, это структура
|
||||
// Итерируем ее поля
|
||||
|
||||
tmplV := TemplateVars{StructName: t.Name.Name}
|
||||
z := template.Must(template.New("validatorFunctionHeader").Parse(validatorFunctionHeader))
|
||||
if z.Execute(buf, tmplV) != nil {
|
||||
log.Fatal("ошибка сборки шаблона:", err)
|
||||
}
|
||||
err = iterStructFields(s, buf)
|
||||
if err != nil {
|
||||
log.Fatal("ошибка в ходе перебора полей структуры:", err.Error())
|
||||
}
|
||||
buf.WriteString(validatorFunctionFooter)
|
||||
}
|
||||
}
|
||||
buf.WriteString(validatorFunctions)
|
||||
f, err := os.Create("models/models_validation_generated.go")
|
||||
if err != nil {
|
||||
log.Fatal("не удалось создать/открыть файл:", err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if f.Close() != nil {
|
||||
panic("Не удается захлопнуть файл!")
|
||||
}
|
||||
}()
|
||||
_, err = buf.WriteTo(f)
|
||||
if err != nil {
|
||||
log.Fatal("не удалось записать буфер в файл:", err.Error())
|
||||
}
|
||||
// Тут нужно открыть файл, записать в него байтбуфер и закрыть файд
|
||||
}
|
||||
|
||||
func iterStructFields(s *ast.StructType, buf io.Writer) error {
|
||||
for _, field := range s.Fields.List {
|
||||
// Рекурсивный вызов, в случае если поле является структурой
|
||||
//switch field.Type.(type) {
|
||||
//case *ast.StructType:
|
||||
// if err:=iterStructFields(field.Type.(*ast.StructType), buf); err!=nil { log.Fatal("Структура не распарсилась в рекурсии:", err.Error()) }
|
||||
//}
|
||||
if len(field.Names) == 0 {
|
||||
continue
|
||||
}
|
||||
// Достаем тэг поля
|
||||
if field.Tag == nil {
|
||||
continue
|
||||
}
|
||||
tag, ok := getTagString(field, "validate")
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
//Ищем комбинации
|
||||
for _, comb := range strings.Split(tag, "|") {
|
||||
k := strings.Split(comb, ":")
|
||||
|
||||
isSlice, fieldType := getFieldType(field)
|
||||
tmplV := TemplateVars{FieldName: field.Names[0].Name, FieldType: fieldType, ValidatorValue: k[1], IsSlice: isSlice}
|
||||
|
||||
z := &template.Template{}
|
||||
switch k[0] {
|
||||
case "min":
|
||||
z = template.Must(template.New("validatorMin").Parse(validatorMin))
|
||||
case "max":
|
||||
z = template.Must(template.New("validatorMax").Parse(validatorMax))
|
||||
case "in":
|
||||
switch fieldType {
|
||||
case "int":
|
||||
z = template.Must(template.New("validatorInInt").Parse(validatorInInt))
|
||||
case str:
|
||||
z = template.Must(template.New("validatorInStr").Parse(validatorInStr))
|
||||
}
|
||||
case "len":
|
||||
z = template.Must(template.New("validatorLen").Parse(validatorLen))
|
||||
case "regexp":
|
||||
z = template.Must(template.New("validatorRegexp").Parse(validatorRegexp))
|
||||
default:
|
||||
log.Fatal("Неизвестный параметр тега validate")
|
||||
}
|
||||
err := z.Execute(buf, tmplV)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getTagString(f *ast.Field, tag string) (string, bool) {
|
||||
t := reflect.StructTag(f.Tag.Value[1 : len(f.Tag.Value)-1])
|
||||
v := t.Get(tag)
|
||||
if v == "" {
|
||||
return "", false
|
||||
}
|
||||
return v, true
|
||||
}
|
||||
|
||||
func getFieldType(field *ast.Field) (bool, string) {
|
||||
var fieldSlice bool
|
||||
var fieldType string
|
||||
switch field.Type.(type) {
|
||||
case *ast.Ident:
|
||||
fieldSlice = false
|
||||
fieldType = field.Type.(*ast.Ident).Name
|
||||
if field.Type.(*ast.Ident).Obj != nil {
|
||||
t, ok := field.Type.(*ast.Ident).Obj.Decl.(*ast.TypeSpec)
|
||||
if !ok {
|
||||
return false, ""
|
||||
}
|
||||
s, ok := t.Type.(*ast.Ident)
|
||||
if !ok {
|
||||
return false, ""
|
||||
}
|
||||
fieldType = s.Name
|
||||
}
|
||||
case *ast.ArrayType:
|
||||
fieldSlice = true
|
||||
fieldType = field.Type.(*ast.ArrayType).Elt.(*ast.Ident).Name
|
||||
}
|
||||
switch {
|
||||
case strings.Contains(fieldType, "int"):
|
||||
return fieldSlice, "int"
|
||||
case strings.Contains(fieldType, "float"):
|
||||
return fieldSlice, "float"
|
||||
case fieldType == str:
|
||||
return fieldSlice, str
|
||||
}
|
||||
return false, ""
|
||||
}
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
package main
|
||||
|
||||
const validatorHeader string = `// Code generated by cool go-validate tool; DO NOT EDIT.
|
||||
package models
|
||||
|
||||
import (
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ValidationError struct {
|
||||
Name string
|
||||
Error error
|
||||
}
|
||||
`
|
||||
|
||||
const validatorFunctionHeader string = `
|
||||
func (u {{.StructName}}) Validate() ([]ValidationError, error) {
|
||||
res := []ValidationError{}
|
||||
var err error
|
||||
var ok bool`
|
||||
|
||||
const validatorFunctionFooter string = `
|
||||
log.Println(ok)
|
||||
return res, err
|
||||
}
|
||||
`
|
||||
|
||||
const validatorLen string = `
|
||||
if ok, err = valLen({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]string{string(u.{{.FieldName}})}{{- end}},{{.ValidatorValue}}); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf("len:{{.ValidatorValue}}")})}`
|
||||
const validatorMin string = `
|
||||
if ok, err = valMin({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]int{int(u.{{.FieldName}})}{{- end}},{{.ValidatorValue}}); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf("min:{{.ValidatorValue}}")})}`
|
||||
const validatorMax string = `
|
||||
if ok, err = valMax({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]int{int(u.{{.FieldName}})}{{- end}},{{.ValidatorValue}}); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf("max:{{.ValidatorValue}}")})}`
|
||||
const validatorRegexp string = `
|
||||
if ok, err = valRegexp({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]string{string(u.{{.FieldName}})}{{- end}},` + "`{{.ValidatorValue}}`" + `); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf(` + "`regexp:{{.ValidatorValue}}`" + `)})}`
|
||||
const validatorInStr string = `
|
||||
if ok, err = valInString({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]string{string(u.{{.FieldName}})}{{- end}},` + "`{{.ValidatorValue}}`" + `); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf(` + "`in:{{.ValidatorValue}}`" + `)})}`
|
||||
const validatorInInt string = `
|
||||
if ok, err = valInInt({{if .IsSlice}}u.{{.FieldName}}{{- else}}[]int{int(u.{{.FieldName}})}{{- end}},` + "`{{.ValidatorValue}}`" + `); !ok { res=append(res,ValidationError{"{{.FieldName}}",fmt.Errorf("in:{{.ValidatorValue}}")})}`
|
||||
|
||||
const validatorFunctions string = `
|
||||
|
||||
func valLen(s []string, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range s {
|
||||
if len(v)>n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valMin(i []int, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range i {
|
||||
if v<n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valMax(i []int, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range i {
|
||||
if v>n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valRegexp(s []string, r string) (bool,error) {
|
||||
res := true
|
||||
var err error
|
||||
rg := regexp.MustCompile(r)
|
||||
for _,v :=range s {
|
||||
if !rg.MatchString(v) {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,err
|
||||
}
|
||||
|
||||
func valInString(s []string, r string) (bool,error) {
|
||||
i := false
|
||||
for _,k :=range s {
|
||||
i = false
|
||||
for _, v := range strings.Split(r, ",") {
|
||||
if k == v {
|
||||
i = true
|
||||
}
|
||||
}
|
||||
if !i { break }
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func valInInt(s []int, r string) (bool,error) {
|
||||
i := false
|
||||
var err error
|
||||
var m int
|
||||
for _,k :=range s {
|
||||
i = false
|
||||
for _, v := range strings.Split(r, ",") {
|
||||
m,err=strconv.Atoi(v)
|
||||
if k == m { i = true }
|
||||
}
|
||||
if !i { break }
|
||||
}
|
||||
return i, err
|
||||
}
|
||||
`
|
|
@ -1,4 +1,4 @@
|
|||
module github.com/fixme_my_friend/hw09_generator_of_validators
|
||||
module github.com/tiburon-777/HW_OTUS/hw09_generator_of_validators
|
||||
|
||||
go 1.14
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
package models
|
||||
|
||||
//go:generate go-validate models.go
|
||||
type UserRole string
|
||||
|
||||
// NOTE: Several struct specs in one type declaration are allowed
|
||||
// NOTE: Several struct specs in one type declaration are allowed.
|
||||
type (
|
||||
User struct {
|
||||
ID string `json:"id" validate:"len:36"`
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
// Code generated by cool go-validate tool; DO NOT EDIT.
|
||||
package models
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ValidationError struct {
|
||||
Name string
|
||||
Error error
|
||||
}
|
||||
|
||||
func (u User) Validate() ([]ValidationError, error) {
|
||||
res := []ValidationError{}
|
||||
var err error
|
||||
var ok bool
|
||||
if ok, err = valLen([]string{string(u.ID)},36); !ok { res=append(res,ValidationError{"ID",fmt.Errorf("len:36")})}
|
||||
if ok, err = valMin([]int{int(u.Age)},18); !ok { res=append(res,ValidationError{"Age",fmt.Errorf("min:18")})}
|
||||
if ok, err = valMax([]int{int(u.Age)},50); !ok { res=append(res,ValidationError{"Age",fmt.Errorf("max:50")})}
|
||||
if ok, err = valRegexp([]string{string(u.Email)},`^\w+@\w+\.\w+$`); !ok { res=append(res,ValidationError{"Email",fmt.Errorf(`regexp:^\w+@\w+\.\w+$`)})}
|
||||
if ok, err = valInString([]string{string(u.Role)},`admin,stuff`); !ok { res=append(res,ValidationError{"Role",fmt.Errorf(`in:admin,stuff`)})}
|
||||
if ok, err = valLen(u.Phones,11); !ok { res=append(res,ValidationError{"Phones",fmt.Errorf("len:11")})}
|
||||
log.Println(ok)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (u App) Validate() ([]ValidationError, error) {
|
||||
res := []ValidationError{}
|
||||
var err error
|
||||
var ok bool
|
||||
if ok, err = valLen([]string{string(u.Version)},5); !ok { res=append(res,ValidationError{"Version",fmt.Errorf("len:5")})}
|
||||
log.Println(ok)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (u Token) Validate() ([]ValidationError, error) {
|
||||
res := []ValidationError{}
|
||||
var err error
|
||||
var ok bool
|
||||
log.Println(ok)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (u Response) Validate() ([]ValidationError, error) {
|
||||
res := []ValidationError{}
|
||||
var err error
|
||||
var ok bool
|
||||
if ok, err = valInInt([]int{int(u.Code)},`200,404,500`); !ok { res=append(res,ValidationError{"Code",fmt.Errorf("in:200,404,500")})}
|
||||
log.Println(ok)
|
||||
return res, err
|
||||
}
|
||||
|
||||
|
||||
func valLen(s []string, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range s {
|
||||
if len(v)>n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valMin(i []int, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range i {
|
||||
if v<n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valMax(i []int, n int) (bool,error) {
|
||||
res := true
|
||||
for _,v :=range i {
|
||||
if v>n {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,nil
|
||||
}
|
||||
|
||||
func valRegexp(s []string, r string) (bool,error) {
|
||||
res := true
|
||||
var err error
|
||||
rg := regexp.MustCompile(r)
|
||||
for _,v :=range s {
|
||||
if !rg.MatchString(v) {
|
||||
res=false
|
||||
break
|
||||
}
|
||||
}
|
||||
return res,err
|
||||
}
|
||||
|
||||
func valInString(s []string, r string) (bool,error) {
|
||||
i := false
|
||||
for _,k :=range s {
|
||||
i = false
|
||||
for _, v := range strings.Split(r, ",") {
|
||||
if k == v {
|
||||
i = true
|
||||
}
|
||||
}
|
||||
if !i { break }
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func valInInt(s []int, r string) (bool,error) {
|
||||
i := false
|
||||
var err error
|
||||
var m int
|
||||
for _,k :=range s {
|
||||
i = false
|
||||
for _, v := range strings.Split(r, ",") {
|
||||
m,err=strconv.Atoi(v)
|
||||
if k == m { i = true }
|
||||
}
|
||||
if !i { break }
|
||||
}
|
||||
return i, err
|
||||
}
|
Loading…
Reference in New Issue