Skip to content

Commit

Permalink
feat: struct recycle check
Browse files Browse the repository at this point in the history
  • Loading branch information
alovn committed May 9, 2022
1 parent 4e3f6b6 commit e8fffa0
Show file tree
Hide file tree
Showing 5 changed files with 111 additions and 68 deletions.
30 changes: 28 additions & 2 deletions examples/docs/apis-greeter.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,43 @@ GET /greeter
{ //object(main.Response), 通用返回结果
"code": 0, //int, 返回状态码
"data": { //object(main.TestData)
"Int": 123, //int
"Map2": { //object(main.TestData2)
"abc": null //object
"abc": { //object(main.TestData2)
"MyAge2": 123, //int
"MyTitle2": "abc" //string, 标题2
}
},
"Map3": { //object(main.TestData2)
"abc": { //object(main.TestData2)
"MyAge2": 123, //int
"MyTitle2": "abc" //string, 标题2
}
},
"Map4": { //object(main.Node)
"123": { //object(main.Node)
"Name": "abc", //string
"Nodes": { //object(main.Node)
"abc": null //object
}
}
},
"MyFloat32": 1.23, //float32
"MyFloat64": 1.23, //float64
"MyInt": 123, //int
"MyIntArray": [ //array[int]
123
],
"MyIntData": 123, //int
"MyInts": [ //array[int]
123
],
"MyTestData2Array": [ //array[main.TestData2]
{ //object(main.TestData2)
"MyAge2": 123, //int
"MyTitle2": "abc" //string, 标题2
}
],
"Nodes": { //object(main.Node)
"abc": { //object(main.Node)
"Name": "abc", //string
Expand All @@ -43,7 +68,8 @@ GET /greeter
"data2": { //object(main.TestData2)
"MyAge2": 123, //int
"MyTitle2": "abc" //string, 标题2
}
},
"my_title": "abc" //string, 标题
},
"msg": "返回消息" //string, 返回文本消息
}
Expand Down
29 changes: 15 additions & 14 deletions examples/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,21 @@ type Node struct {
Nodes map[string]Node
}
type TestData struct {
// MyTitle string `json:"my_title,omitempty"` //标题
Data2 *TestData2 `json:"data2,omitempty"`
// MyIntData int
// MyFloat64 float64
// MyFloat32 float32
// MyIntArray []int
// MyTestData2Array []TestData2
// Int *int
// MyInt MyInt
MyInts []MyInt
Map Map `json:"amap"`
Map2 Map2
Map3 map[string]TestData2
Nodes map[string]Node
MyTitle string `json:"my_title,omitempty"` //标题
Data2 *TestData2 `json:"data2,omitempty"`
MyIntData int
MyFloat64 float64
MyFloat32 float32
MyIntArray []int
MyTestData2Array []TestData2
Int *int
MyInt MyInt
MyInts []MyInt
Map Map `json:"amap"`
Map2 Map2
Map3 map[string]TestData2
Nodes map[string]Node
Map4 map[int]Node
}

type Request struct {
Expand Down
9 changes: 2 additions & 7 deletions operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (operation *Operation) ParseRequestComment(commentLine string, astFile *ast
case IsGolangPrimitiveType(refType):
return nil
default:
schema, err := operation.parser.getTypeSchema(refType, astFile, nil)
schema, err := operation.parser.getTypeSchema(refType, astFile, nil, nil)
if err != nil {
return err
}
Expand Down Expand Up @@ -160,12 +160,7 @@ func (operation *Operation) ParseRequestComment(commentLine string, astFile *ast
// ParseResponseComment parses comment for given `response` comment string.
func (operation *Operation) ParseResponseComment(commentLine string, astFile *ast.File) error {
operation.parser.clearStructStack()
fmt.Println(commentLine)
matches := responsePattern.FindStringSubmatch(commentLine)
// for i, m := range matches {
// fmt.Println(i, m)
// }
// fmt.Println(commentLine, len(matches))
if len(matches) != 4 && len(matches) != 3 {
return nil
}
Expand Down Expand Up @@ -205,7 +200,7 @@ func (operation *Operation) parseObject(refType string, astFile *ast.File) (*Typ
case strings.Contains(refType, "{"):
return operation.parseCombinedObject(refType, astFile)
default:
schema, err := operation.parser.getTypeSchema(refType, astFile, nil)
schema, err := operation.parser.getTypeSchema(refType, astFile, nil, nil)
if err != nil {
return nil, err
}
Expand Down
85 changes: 44 additions & 41 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,7 @@ func fullTypeName(pkgName, typeName string) string {
return typeName
}

func (parser *Parser) getTypeSchema(typeName string, file *ast.File, field *ast.Field) (*TypeSchema, error) {
func (parser *Parser) getTypeSchema(typeName string, file *ast.File, field *ast.Field, parentSchema *TypeSchema) (*TypeSchema, error) {
if IsGolangPrimitiveType(typeName) {
name := field.Names[0].Name
isOmitempty, fieldName := getFieldName(name, field, "json")
Expand All @@ -367,6 +367,7 @@ func (parser *Parser) getTypeSchema(typeName string, file *ast.File, field *ast.
Validate: getValidateTagValue(field),
Tags: getParameterTags(field),
Required: getRequiredTagValue(field),
Parent: parentSchema,
}, nil
}

Expand All @@ -376,7 +377,7 @@ func (parser *Parser) getTypeSchema(typeName string, file *ast.File, field *ast.
}
fmt.Println("typeSpecDef", typeSpecDef.Name())

schema, err := parser.ParseDefinition(typeSpecDef, field)
schema, err := parser.ParseDefinition(typeSpecDef, field, parentSchema)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -407,22 +408,21 @@ func (parser *Parser) clearStructStack() {

// ParseDefinition parses given type spec that corresponds to the type under
// given name and package
func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef, field *ast.Field) (*TypeSchema, error) {
func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef, field *ast.Field, parentSchema *TypeSchema) (*TypeSchema, error) {
typeName := typeSpecDef.FullName()
refTypeName := TypeDocName(typeName, typeSpecDef.TypeSpec)

if parser.isInStructStack(typeSpecDef) {
fmt.Printf("Skipping '%s', recursion detected.", typeName)
schema := &TypeSchema{
if parentSchema != nil && parentSchema.isInTypeChain(typeSpecDef) {
fmt.Printf("Skipping '%s', recursion detected.\n", typeName)
return &TypeSchema{
Name: refTypeName,
FieldName: refTypeName,
FullName: typeName,
Type: OBJECT,
Example: NULL,
PkgPath: typeSpecDef.PkgPath,
}
parser.clearStructStack()
return schema, nil
Parent: parentSchema,
}, nil
}

parser.structStack = append(parser.structStack, typeSpecDef)
Expand All @@ -434,35 +434,36 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef, field *ast.Field
switch expr := typeSpecDef.TypeSpec.Type.(type) {
// type Foo struct {...}
case *ast.StructType:
schema, err := parser.parseStruct(typeSpecDef.File, expr.Fields)
schema, err := parser.parseStruct(typeSpecDef, typeSpecDef.File, expr.Fields, parentSchema)
if err != nil {
return nil, err
}
schema.Name = typeSpecDef.Name()
schema.FullName = typeSpecDef.FullName()
return schema, err
case *ast.Ident:
return parser.getTypeSchema(expr.Name, typeSpecDef.File, field)
return parser.getTypeSchema(expr.Name, typeSpecDef.File, field, parentSchema)
case *ast.MapType:
if keyIdent, ok := expr.Key.(*ast.Ident); ok {
if IsGolangPrimitiveType(keyIdent.Name) {
example := getFieldExample(keyIdent.Name, nil)
if _, ok := expr.Value.(*ast.InterfaceType); ok {
return &TypeSchema{Type: OBJECT, Properties: nil}, nil
}
schema, err := parser.parseTypeExpr(typeSpecDef.File, field, expr.Value)
arrSchama := &TypeSchema{
Name: example,
Type: OBJECT,
FieldName: example,
Parent: parentSchema,
Properties: map[string]*TypeSchema{},
}
schema, err := parser.parseTypeExpr(typeSpecDef.File, field, expr.Value, arrSchama)
if err != nil {
return nil, err
}
return &TypeSchema{
Name: example,
Type: OBJECT,
FieldName: example,
FullName: schema.FullName,
Properties: map[string]*TypeSchema{
strings.Trim(example, "\""): schema,
},
}, err
arrSchama.FullName = schema.FullName
arrSchama.Properties[strings.Trim(example, "\"")] = schema
return arrSchama, nil
}
}

Expand All @@ -479,39 +480,40 @@ func (parser *Parser) ParseDefinition(typeSpecDef *TypeSpecDef, field *ast.Field
return &sch, nil
}

func (parser *Parser) parseTypeExpr(file *ast.File, field *ast.Field, typeExpr ast.Expr) (*TypeSchema, error) {
func (parser *Parser) parseTypeExpr(file *ast.File, field *ast.Field, typeExpr ast.Expr, parentSchama *TypeSchema) (*TypeSchema, error) {
switch expr := typeExpr.(type) {
// type Foo interface{}
case *ast.InterfaceType:
return &TypeSchema{
Type: ANY,
Example: "null",
Parent: parentSchama,
}, nil

// type Foo struct {...}
case *ast.StructType:
return parser.parseStruct(file, expr.Fields)
return parser.parseStruct(nil, file, expr.Fields, parentSchama)

// type Foo Baz
case *ast.Ident:
return parser.getTypeSchema(expr.Name, file, field)
return parser.getTypeSchema(expr.Name, file, field, parentSchama)

// type Foo *Baz
case *ast.StarExpr:
return parser.parseTypeExpr(file, field, expr.X)
return parser.parseTypeExpr(file, field, expr.X, parentSchama)

// type Foo pkg.Bar
case *ast.SelectorExpr:
if xIdent, ok := expr.X.(*ast.Ident); ok {
return parser.getTypeSchema(fullTypeName(xIdent.Name, expr.Sel.Name), file, field)
return parser.getTypeSchema(fullTypeName(xIdent.Name, expr.Sel.Name), file, field, parentSchama)
}
// type Foo []Baz
case *ast.ArrayType:
itemSchema, err := parser.parseTypeExpr(file, field, expr.Elt)
itemSchema, err := parser.parseTypeExpr(file, field, expr.Elt, parentSchama)
if err != nil {
return nil, err
}
return &TypeSchema{Type: "array", IsArray: true, ArraySchema: itemSchema}, nil
return &TypeSchema{Type: "array", IsArray: true, ArraySchema: itemSchema, Parent: parentSchama}, nil
// type Foo map[string]Bar
case *ast.MapType:
if keyIdent, ok := expr.Key.(*ast.Ident); ok {
Expand All @@ -520,7 +522,7 @@ func (parser *Parser) parseTypeExpr(file *ast.File, field *ast.Field, typeExpr a
if _, ok := expr.Value.(*ast.InterfaceType); ok {
return &TypeSchema{Type: OBJECT, Properties: nil}, nil
}
schema, err := parser.parseTypeExpr(file, field, expr.Value)
schema, err := parser.parseTypeExpr(file, field, expr.Value, parentSchama)
if err != nil {
return nil, err
}
Expand All @@ -546,37 +548,38 @@ func (parser *Parser) parseTypeExpr(file *ast.File, field *ast.Field, typeExpr a
return &TypeSchema{Type: OBJECT}, nil
}

func (parser *Parser) parseStruct(file *ast.File, fields *ast.FieldList) (*TypeSchema, error) {
properties := make(map[string]*TypeSchema)
// parser.clearStructStack() //warning
func (parser *Parser) parseStruct(typeSpecDef *TypeSpecDef, file *ast.File, fields *ast.FieldList, parentSchama *TypeSchema) (*TypeSchema, error) {
structSchema := &TypeSchema{
Name: file.Name.Name,
Type: OBJECT,
typeSpecDef: typeSpecDef,
Parent: parentSchama,
Properties: map[string]*TypeSchema{},
}
for _, field := range fields.List {
if len(field.Names) != 1 {
return nil, errors.New("error len(field.Names) != 1")
}
name := field.Names[0].Name
schema, err := parser.parseStructField(file, field)
schema, err := parser.parseStructField(file, field, structSchema)
if err != nil {
return nil, err
}
schema.Name = name
isOmitempty, fieldName := getFieldName(field.Names[0].Name, field, "json")
schema.FieldName = fieldName
schema.IsOmitempty = isOmitempty
properties[schema.FieldName] = schema
structSchema.Properties[schema.FieldName] = schema
}
return &TypeSchema{
Name: file.Name.Name,
Type: OBJECT,
Properties: properties,
}, nil
return structSchema, nil
}

func (parser *Parser) parseStructField(file *ast.File, field *ast.Field) (*TypeSchema, error) {
func (parser *Parser) parseStructField(file *ast.File, field *ast.Field, parentSchama *TypeSchema) (*TypeSchema, error) {
name := field.Names[0].Name
if !ast.IsExported(name) {
return nil, nil
}
return parser.parseTypeExpr(file, field, field.Type)
return parser.parseTypeExpr(file, field, field.Type, parentSchama)
// isArray, typeName, err := getFieldType(field.Type)
// if err != nil {
// return nil, err
Expand Down
Loading

0 comments on commit e8fffa0

Please sign in to comment.