Skip to content

Commit

Permalink
feat: process the next type after go generate
Browse files Browse the repository at this point in the history
In case if `-type` arg is ommited, this tool
now processes the next type after `go:generate` comment
calling this tool.
  • Loading branch information
g4s8 committed Dec 14, 2023
1 parent 84461c1 commit c4b93c3
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 10 deletions.
2 changes: 1 addition & 1 deletion _examples/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package main

//go:generate go run ../ -output config.md -type Config
//go:generate go run ../ -output config.md
type Config struct {
// Host name to listen on.
Host string `env:"HOST,required"`
Expand Down
51 changes: 48 additions & 3 deletions inspector.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ type inspectorOutput interface {
type inspector struct {
typeName string
out inspectorOutput
execLine int

lines []int
pendingType bool
}

func newInspector(typeName string, out inspectorOutput) *inspector {
return &inspector{typeName: typeName, out: out}
func newInspector(typeName string, out inspectorOutput, execLine int) *inspector {
return &inspector{typeName: typeName, out: out, execLine: execLine}
}

func (i *inspector) inspectFile(fileName string) error {
Expand All @@ -27,6 +31,9 @@ func (i *inspector) inspectFile(fileName string) error {
if err != nil {
return fmt.Errorf("parse file: %w", err)
}
// get a lines to position map for the file.
f := fset.File(file.Pos())
i.lines = f.Lines()
i.inspect(file)
return nil
}
Expand All @@ -37,10 +44,45 @@ func (i *inspector) inspect(node ast.Node) {

func (i *inspector) Visit(n ast.Node) ast.Visitor {
switch t := n.(type) {
case *ast.Comment:
// if type name is not specified we should process the next type
// declaration after the comment with go:generate
// which causes this command to be executed.
if i.typeName != "" {
return nil
}
if !t.Pos().IsValid() {
return nil
}
var line int
for l, pos := range i.lines {
if token.Pos(pos) > t.Pos() {
break
}
// $GOLINE env var is 1-based.
line = l + 1
}
if line != i.execLine {
return nil
}
if !strings.HasPrefix(t.Text, "//go:generate") {
return nil
}

i.pendingType = true
return nil
case *ast.TypeSpec:
if t.Name == nil && t.Name.Name != i.typeName {
var generate bool
if i.typeName != "" && t.Name != nil && t.Name.Name == i.typeName {
generate = true
}
if i.typeName == "" && i.pendingType {
generate = true
}
if !generate {
return i
}

if st, ok := t.Type.(*ast.StructType); ok {
for _, field := range st.Fields.List {
if field.Tag != nil {
Expand All @@ -53,6 +95,9 @@ func (i *inspector) Visit(n ast.Node) ast.Visitor {
}
}
}
// reset pending type flag event if this type
// is not processable (e.g. interface type).
i.pendingType = false
}
return i
}
Expand Down
20 changes: 14 additions & 6 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"os"
"strconv"
)

func main() {
Expand All @@ -17,14 +18,21 @@ func main() {
flag.Usage()
os.Exit(1)
}
if typeName == "" {
flag.Usage()
os.Exit(1)
}

inputFileName := os.Getenv("GOFILE")
if inputFileName == "" {
fatal("No input file specified")
fatal("No input file specified, this tool should be called by go generate")
}

var execLine int
if e := os.Getenv("GOLINE"); e != "" {
i, err := strconv.Atoi(e)
if err != nil {
fatal("Invalid line number specified, this tool should be called by go generate")
}
execLine = i
} else {
fatal("No line number specified, this tool should be called by go generate")
}

outputFile, err := os.Create(outputFileName)
Expand All @@ -46,7 +54,7 @@ func main() {
}
}()

insp := newInspector(typeName, output)
insp := newInspector(typeName, output, execLine)
if err := insp.inspectFile(inputFileName); err != nil {
fatalf("inspect file: %v", err)
}
Expand Down

0 comments on commit c4b93c3

Please sign in to comment.