Skip to content

Commit

Permalink
option to scan subdirectories + fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ihadeed committed Nov 19, 2019
1 parent a5f2169 commit f01eedf
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 30 deletions.
19 changes: 14 additions & 5 deletions cmd/gots/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ func main() {
p := parser.New(&parser.Config{
BaseDir: ctx.String("dir"),
OutFileName: o,
Subdirs: ctx.Bool("sub-dir"),
})

p.Run()
Expand All @@ -43,13 +44,21 @@ func main() {
},
Flags: []cli.Flag{
&cli.StringFlag{
Name: "dir, d",
Usage: "Base directory to lookup exportable definitions",
Value: wd,
Name: "dir",
Aliases: []string{"d"},
Usage: "Base directory to lookup exportable definitions",
Value: wd,
},
&cli.StringFlag{
Name: "outfile, o",
Usage: "Output file. If not specified, stdout will be used.",
Name: "outfile",
Aliases: []string{"o"},
Usage: "Output file. If not specified, stdout will be used.",
},
&cli.BoolFlag{
Name: "sub-dirs",
Aliases: []string{"s"},
Usage: "Scan sub-directories as well",
Value: false,
},
},
},
Expand Down
4 changes: 2 additions & 2 deletions parser/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package parser

type Config struct {
BaseDir string
OutFileName string
BaseDir, OutFileName string
Subdirs bool
}
99 changes: 89 additions & 10 deletions parser/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import (
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"log"
"path/filepath"
)

func (p *Parser) parse() {
Expand All @@ -13,14 +15,46 @@ func (p *Parser) parse() {
var pkgs map[string]*ast.Package
var err error

if pkgs, err = parser.ParseDir(fset, p.BaseDir, nil, parser.ParseComments); err != nil {
log.Panicf("unable to parse base directory: %s\n", err.Error())
pkgIndex := make(map[string]string)

var scanDir func(path string)

scanDir = func(path string) {
contents, err := ioutil.ReadDir(path)

if err != nil {
log.Panicf("unable to read directory %s: %s\n", path, err.Error())
}

for _, it := range contents {
if it.IsDir() {
scanDir(filepath.Join(path, it.Name()))
}
}

if pkgs, err = parser.ParseDir(fset, path, nil, parser.PackageClauseOnly); err != nil {
log.Panicf("unable to scan directory %s: %s\n", path, err.Error())
} else {
for k := range pkgs {
pkgIndex[k] = path
}
}
}

for k := range pkgs {
for fk := range pkgs[k].Files {
p.wg.Add(1)
go p.parseFile(pkgs[k].Files[fk])
scanDir(p.BaseDir)

p.pkgIndex = pkgIndex

for _, v := range pkgIndex {
if pkgs, err = parser.ParseDir(fset, v, nil, parser.ParseComments); err != nil {
log.Panicf("unable to parse base directory: %s\n", err.Error())
}

for pk := range pkgs {
for fk := range pkgs[pk].Files {
p.wg.Add(1)
go p.parseFile(pkgs[pk].Files[fk])
}
}
}
}
Expand Down Expand Up @@ -83,9 +117,32 @@ func (p *Parser) parseFile(file *ast.File) {

func (p *Parser) parseConst(spec *ast.ValueSpec) {
c := &Constant{
Name: spec.Names[0].Name,
Type: parseType(spec.Type),
Value: spec.Values[0].(*ast.BasicLit).Value,
Name: spec.Names[0].Name,
}

if spec.Type != nil {
c.Type = parseType(spec.Type)
c.Value = spec.Values[0].(*ast.BasicLit).Value
} else {
switch spec.Values[0].(type) {
case *ast.CallExpr:
if val, ok := spec.Values[0].(*ast.CallExpr).Args[0].(*ast.BasicLit); ok {
c.Type = parseTypeFromKind(val.Kind)
c.Value = val.Value
} else {
panic("Unhandled case")
}
case *ast.BasicLit:
v := spec.Values[0].(*ast.BasicLit)
c.Type = parseTypeFromKind(v.Kind)
c.Value = v.Value
default:
panic("Unhandled case")
}
}

if c.Value == "" {
panic("Unhandled case")
}

p.cMtx.Lock()
Expand Down Expand Up @@ -150,6 +207,28 @@ func (p *Parser) parseTypeSpec(spec *ast.TypeSpec) {
p.parseStruct(spec)
case *ast.Ident:
t := parseType(spec.Type)
p.tMtx.Lock()
defer p.tMtx.Unlock()
p.types = append(p.types, &TypeDef{
Name: spec.Name.Name,
Type: t,
})
return
case *ast.InterfaceType:
return
case *ast.SelectorExpr:
st := spec.Type.(*ast.SelectorExpr)
t := "any"
if xv, ok := st.X.(*ast.Ident); ! ok {
panic("unhandled case")
} else {
p.pMtx.Lock()
defer p.pMtx.Unlock()
if _, ok := p.pkgIndex[xv.Name]; ok {
t = st.Sel.Name
}
}

p.tMtx.Lock()
defer p.tMtx.Unlock()
p.types = append(p.types, &TypeDef{
Expand All @@ -158,6 +237,6 @@ func (p *Parser) parseTypeSpec(spec *ast.TypeSpec) {
})
return
default:
panic("??")
panic(spec.Type)
}
}
3 changes: 3 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ type Parser struct {
cMtx sync.RWMutex
constants []*Constant

pMtx sync.Mutex
pkgIndex map[string]string

tsw *typescript.Writer
}

Expand Down
42 changes: 29 additions & 13 deletions parser/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package parser
import (
"errors"
"go/ast"
"go/token"
"regexp"
"strings"
)
Expand All @@ -13,8 +14,8 @@ type tag struct {
}

var jsonTagRgx = regexp.MustCompile(`(?i)json:"([a-z0-9_-]+),?(omitempty)?"`)
var gotsTagRgx = regexp.MustCompile(`(?i)gots:"([a-z0-9_,:]+)"`)
var gotsInnerTagRgx = regexp.MustCompile(`(?i)(name|type|optional):?([a-z0-9_]+)?`)
var gotsTagRgx = regexp.MustCompile(`(?i)gots:"([a-z0-9_,:\[\]]+)"`)
var gotsInnerTagRgx = regexp.MustCompile(`(?i)(name|type|optional):?([a-z0-9_\[\]]+)?`)

var errJsonTagNotPresent = errors.New("json tag not present")
var errJsonIgnored = errors.New("field is ignored")
Expand Down Expand Up @@ -58,27 +59,42 @@ func parseTags(val string) (*tag, error) {
return t, nil
}

func parseTypeFromKind(t token.Token) string {
switch t {
case token.INT, token.FLOAT:
return "number"
case token.STRING:
return "string"
default:
panic("???")
}
}

func parseTypeFromName(n string) string {
switch n {
case "string":
return "string"
case "uint8", "uint16", "uint32", "uint64", "uint", "int8", "int16", "int32", "int64", "int", "float32", "float64":
return "number"
case "bool":
return "boolean"
default:
return n
}
}

func parseType(t ast.Expr) string {
switch t.(type) {
case *ast.Ident:
switch t.(*ast.Ident).Name {
case "string":
return "string"
case "uint8", "uint16", "uint32", "uint64", "uint", "int8", "int16", "int32", "int64", "int", "float32", "float64":
return "number"
case "bool":
return "boolean"
default:
return t.(*ast.Ident).Name
}
return parseTypeFromName(t.(*ast.Ident).Name)
case *ast.StarExpr:
return parseType(t.(*ast.StarExpr).X)
case *ast.MapType:
tt := t.(*ast.MapType)
k, v := parseType(tt.Key), parseType(tt.Value)
return strings.Join([]string{"{[key:", k, "]:", v, "}"}, "")
case *ast.ArrayType:
return strings.Join([]string{"[]", parseType(t.(*ast.ArrayType).Elt)}, "")
return strings.Join([]string{parseType(t.(*ast.ArrayType).Elt), "[]"}, "")
case *ast.SelectorExpr, *ast.InterfaceType:
return "any"
default:
Expand Down

0 comments on commit f01eedf

Please sign in to comment.