/
ast.go
117 lines (103 loc) · 2.75 KB
/
ast.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
// Copyright 2020 The searKing Author. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"go/ast"
"go/token"
"strings"
"unicode"
"github.com/searKing/golang/go/reflect"
strings_ "github.com/searKing/golang/go/strings"
)
const (
TagValidator = "validate"
)
func isPublicName(name string) bool {
for _, c := range name {
return unicode.IsUpper(c)
}
return false
}
// genDecl processes one declaration clause.
func (f *File) genDecl(node ast.Node) bool {
decl, ok := node.(*ast.GenDecl)
// Token must be in IMPORT, CONST, TYPE, VAR
if !ok || decl.Tok != token.TYPE {
// We only care about const|var declarations.
return true
}
// The trimmedStructName of the type of the constants or variables we are declaring.
// Can change if this is a multi-element declaration.
typ := ""
// Loop over the elements of the declaration. Each element is a ValueSpec:
// a list of names possibly followed by a type, possibly followed by structs.
// If the type and value are both missing, we carry down the type (and value,
// but the "go/types" package takes care of that).
for _, spec := range decl.Specs {
tspec := spec.(*ast.TypeSpec) // Guaranteed to succeed as this is TYPE.
typ = tspec.Name.Name
sExpr, ok := tspec.Type.(*ast.StructType)
if !ok {
continue
}
if typ != f.typeInfo.Name && !f.typeInfo.any {
// This is not the type we're looking for.
continue
}
if sExpr.Fields.NumFields() <= 0 {
panic(fmt.Errorf("%s has no Fields", typ))
}
v := Struct{
StructType: typ,
}
if c := tspec.Comment; f.lineComment && c != nil && len(c.List) == 1 {
v.Comment = strings.TrimSpace(c.Text())
} else {
v.Comment = strings_.SnakeCase(strings.TrimPrefix(typ, f.trimPrefix))
}
for _, field := range sExpr.Fields.List {
fieldName := ""
if len(field.Names) != 0 { // pick first exported Name
for _, field := range field.Names {
if !*flagSkipPrivateFields || isPublicName(field.Name) {
fieldName = field.Name
break
}
}
} else { // anonymous field
ident, ok := field.Type.(*ast.Ident)
if !ok {
continue
}
if !*flagSkipAnonymousFields {
fieldName = ident.Name
}
}
// nothing to process, continue with next line
if fieldName == "" {
continue
}
if field.Tag == nil {
field.Tag = &ast.BasicLit{}
}
tags, err := reflect.ParseAstStructTag(field.Tag.Value)
if err != nil {
panic(err)
}
if _, has := tags.Get(TagValidator); !has {
// ignore this field
continue
}
v.Fields = append(v.Fields, StructField{
FieldName: fieldName,
})
}
if len(v.Fields) == 0 {
continue
}
f.structs = append(f.structs, v)
}
return false
}