Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,12 @@ import (
)

var (
outputPath string
inputPath string
parseVendors []string
vendorsPath string
exitError bool
outputPath string
inputPath string
parseVendors []string
vendorsPath string
externalTypes string
exitError bool
)

// RootCmd represents the root command
Expand All @@ -26,7 +27,7 @@ var RootCmd = &cobra.Command{
Long: `Parse comments in code to generate an OpenAPI documentation`,
Run: func(cmd *cobra.Command, args []string) {
spec := docparser.NewOpenAPI()
spec.Parse(inputPath, parseVendors, vendorsPath, exitError)
spec.Parse(inputPath, parseVendors, vendorsPath, externalTypes, exitError)
d, err := yaml.Marshal(&spec)
if err != nil {
log.Fatalf("error: %v", err)
Expand All @@ -49,5 +50,6 @@ func init() {
RootCmd.Flags().StringVar(&inputPath, "path", ".", "The Folder to parse")
RootCmd.Flags().StringArrayVar(&parseVendors, "parse-vendors", []string{}, "Give the vendor to parse")
RootCmd.Flags().StringVar(&vendorsPath, "vendors-path", "vendor", "Give the vendor path")
RootCmd.Flags().StringVar(&externalTypes, "types", "types-formats.tsv", "External types csv file")
RootCmd.Flags().BoolVar(&exitError, "exit-error", false, "When an error occurs on parsing, exit with a code > 0")
}
28 changes: 17 additions & 11 deletions docparser/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,8 @@ type content struct {

func validatePath(path string, parseVendors []string) bool {
// vendoring path
if strings.Contains(path, "vendor") {
dir, fn := filepath.Split(path)
if strings.Contains(dir, "vendor") {
found := false
for _, vendorPath := range parseVendors {
if strings.Contains(path, vendorPath) {
Expand All @@ -257,21 +258,26 @@ func validatePath(path string, parseVendors []string) bool {
}

// not golang file
if !strings.HasSuffix(path, ".go") {
if !strings.HasSuffix(fn, ".go") {
return false
}

// dot file
if strings.HasPrefix(path, ".") {
if strings.HasPrefix(fn, ".") {
return false
}

// _ file
if strings.HasPrefix(fn, "_") {
return false
}

return true
}

func (spec *openAPI) Parse(path string, parseVendors []string, vendorsPath string, exitNonZeroOnError bool) {
func (spec *openAPI) Parse(path string, parseVendors []string, vendorsPath string, externalTypes string, exitNonZeroOnError bool) {
// fset := token.NewFileSet() // positions are relative to fset

_ = TsvLoadTypes(externalTypes)
walker := func(path string, f os.FileInfo, err error) error {
if validatePath(path, parseVendors) {
astFile, _ := parseFile(path)
Expand All @@ -286,13 +292,13 @@ func (spec *openAPI) Parse(path string, parseVendors []string, vendorsPath strin
return nil
}

err := filepath.Walk(path, walker)
if err != nil {
os.Exit(1)
for _, pt := range strings.Split(path, ",") {
if err := filepath.Walk(pt, walker); err != nil {
os.Exit(1)
}
}

err = filepath.Walk(vendorsPath, walker)
if err != nil {
if err := filepath.Walk(vendorsPath, walker); err != nil {
os.Exit(1)
}

Expand Down Expand Up @@ -421,7 +427,7 @@ func (spec *openAPI) parseMaps(mp *ast.MapType) (*schema, []error) {

// only map[string]
if i, ok := mp.Key.(*ast.Ident); ok {
t, _, err := parseIdentProperty(i)
t, _, err := parseIdentProperty(i, nil)
if err != nil {
errors = append(errors, BuildError{
Err: err,
Expand Down
29 changes: 24 additions & 5 deletions docparser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,11 @@ func parseJSONTag(field *ast.Field) (j jsonTagInfo, err error) {
}
return j, nil
}

func parseNamedType(gofile *ast.File, expr ast.Expr, sel *ast.Ident) (*schema, error) {
p := schema{}
switch ftpe := expr.(type) {
case *ast.Ident: // simple value
t, format, err := parseIdentProperty(ftpe)
t, format, err := parseIdentProperty(ftpe, sel)
if err != nil {
p.Ref = "#/components/schemas/"
if sel != nil {
Expand Down Expand Up @@ -164,7 +163,11 @@ func parseNamedType(gofile *ast.File, expr ast.Expr, sel *ast.Ident) (*schema, e
case *ast.MapType:
k, kerr := parseNamedType(gofile, ftpe.Key, sel)
v, verr := parseNamedType(gofile, ftpe.Value, sel)
if kerr != nil || verr != nil || k.Type != "string" {
if kerr != nil ||
verr != nil ||
(k.Type != "string" && k.Type != "integer") {
k, kerr := parseNamedType(gofile, ftpe.Key, sel)
_, _ = k, kerr
// keys can only be of type string
return nil, fmt.Errorf("expr (%s) not yet unsupported", expr)
}
Expand All @@ -182,7 +185,7 @@ func parseNamedType(gofile *ast.File, expr ast.Expr, sel *ast.Ident) (*schema, e
}

// https://swagger.io/specification/#dataTypes
func parseIdentProperty(expr *ast.Ident) (t, format string, err error) {
func parseIdentProperty(expr *ast.Ident, sel *ast.Ident) (t, format string, err error) {
switch expr.Name {
case "string":
t = "string"
Expand Down Expand Up @@ -210,7 +213,23 @@ func parseIdentProperty(expr *ast.Ident) (t, format string, err error) {
t = "string"
format = "binary"
default:
t = expr.Name
if nil != sel {
name := expr.Name + "." + sel.Name
if tp, ok := externalTypesMap[name]; ok {
t = tp.Type
format = tp.Format
break
} else if tp, ok := externalTypesMap[sel.Name]; ok {
t = tp.Type
format = tp.Format
break
} else if tp, ok := externalTypesMap[expr.Name]; ok {
t = tp.Type
format = tp.Format
break
}
}
//t = expr.Name
err = fmt.Errorf("Can't set the type %s", expr.Name)
}
return t, format, err
Expand Down
77 changes: 77 additions & 0 deletions docparser/tsv.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package docparser

import (
"io/ioutil"
"os"
"strings"
)

type ExtTyp struct {
Type string
Format string
}

var externalTypesMap = map[string]ExtTyp{}

func TsvLoadTypes(fn string) error {
if fi, e := os.Stat(fn); nil != e || !fi.Mode().IsRegular() {
return nil
}
buf, e := ioutil.ReadFile(fn)
if nil != e {
return e
}
d, e := TsvParse(string(buf))
if nil != e {
return e
}
for _, line := range d {
if name, ok := line["name"]; ok {
externalTypesMap[name] = ExtTyp{Type: line["type"], Format: line["format"]}
}
}
return nil
}

func TsvParse(text string) ([]map[string]string, error) {
var dats []map[string]string
lines := strings.Split(text, "\n")
var recs [][]string
for _, l := range lines {
lax := strings.Split(l, "#")
l = lax[0]
if len(strings.TrimSpace(l)) == 0 {
continue
}
la := strings.Split(l, "\t")
recs = append(recs, la)
}

var keys []string
for _, rec := range recs {
if len(keys) == 0 && len(rec) > 0 {
keys = rec
for i, k := range keys {
keys[i] = strings.TrimSpace(k)
}
continue
}
if len(keys) >= 0 {
dat := map[string]string{}
for i, k := range keys {
if len(rec) > i {
v := strings.TrimSpace(rec[i])
if "" != v {
dat[k] = v
}
}
}
if 0 == len(dat) {
continue
}
dats = append(dats, dat)
}
}

return dats, nil
}
Binary file added openapi-parser
Binary file not shown.
Loading