Skip to content

Commit

Permalink
bake: get string implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidGamba committed Jun 9, 2024
1 parent 4f467eb commit 30d8777
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 44 deletions.
99 changes: 81 additions & 18 deletions bake/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,17 @@ package main

import (
"bytes"
"context"
"fmt"
"go/ast"
"go/parser"
"go/printer"
"go/token"
"regexp"
"strconv"
"strings"

"github.com/DavidGamba/go-getoptions"
"github.com/davecgh/go-spew/spew"
"golang.org/x/tools/go/packages"
)
Expand Down Expand Up @@ -165,7 +169,11 @@ func PrintAst(dir string) error {
// opt.String("hello", "world")
// opt.String("hola", "mundo")
// return func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
func ListAst(dir string) error {
func LoadAst(ctx context.Context, opt *getoptions.GetOpt, dir string) error {
// Regex for description: fn-name - description
re := regexp.MustCompile(`^\w\S+ -`)
ot := NewOptTree(opt)

for p, err := range parsedFiles(dir) {
if err != nil {
return err
Expand All @@ -178,11 +186,14 @@ func ListAst(dir string) error {
case *ast.FuncDecl:
if x.Name.IsExported() {
name := x.Name.Name
description := x.Doc.Text()
var buf bytes.Buffer
printer.Fprint(&buf, p.fset, x.Type)
Logger.Printf("file: %s\n", p.file)
Logger.Printf("type: %s, name: %s, desc: %s\n", buf.String(), name, strings.TrimSpace(description))
description := strings.TrimSpace(x.Doc.Text())
// var buf bytes.Buffer
// printer.Fprint(&buf, p.fset, x.Type)
// Logger.Printf("file: %s\n", p.file)
// Logger.Printf("type: %s, name: %s, desc: %s\n", buf.String(), name, description)

// Expect function of type:
// func Name(opt *getoptions.GetOpt) getoptions.CommandFn

// Check Params
// Expect opt *getoptions.GetOpt
Expand All @@ -194,7 +205,7 @@ func ListAst(dir string) error {
name := param.Names[0].Name
var buf bytes.Buffer
printer.Fprint(&buf, p.fset, param.Type)
Logger.Printf("name: %s, %s\n", name, buf.String())
// Logger.Printf("name: %s, %s\n", name, buf.String())
if buf.String() != "*getoptions.GetOpt" {
return false
}
Expand All @@ -209,25 +220,39 @@ func ListAst(dir string) error {
for _, result := range x.Type.Results.List {
var buf bytes.Buffer
printer.Fprint(&buf, p.fset, result.Type)
Logger.Printf("result: %s\n", buf.String())
// Logger.Printf("result: %s\n", buf.String())
if buf.String() != "getoptions.CommandFn" {
return false
}
}

// TODO: The yield probably goes here
// Add function to OptTree
if description != "" {
// Logger.Printf("description '%s'\n", description)
if re.MatchString(description) {
// Get first word from string
name = strings.Split(description, " ")[0]
description = strings.TrimPrefix(description, name+" -")
description = strings.TrimSpace(description)
}
} else {
name = camelToKebab(name)
}
cmd := ot.AddCommand(name, description)

// Check for Expressions of opt type
ast.Inspect(n, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.BlockStmt:
for i, stmt := range x.List {
for _, stmt := range x.List {
var buf bytes.Buffer
printer.Fprint(&buf, p.fset, stmt)
// We are expecting the expression before the return function
_, ok := stmt.(*ast.ReturnStmt)
if ok {
return false
}
Logger.Printf("i: %d\n", i)
Logger.Printf("stmt: %s\n", buf.String())
exprStmt, ok := stmt.(*ast.ExprStmt)
if !ok {
Expand All @@ -254,7 +279,7 @@ func ListAst(dir string) error {

switch fun.Sel.Name {
case "String":
handleString(optFieldName, n)
handleString(cmd, optFieldName, n)
}

return false
Expand All @@ -273,23 +298,32 @@ func ListAst(dir string) error {
return nil
}

func handleString(optFieldName string, n ast.Node) error {
func handleString(cmd *getoptions.GetOpt, optFieldName string, n ast.Node) error {
x := n.(*ast.CallExpr)
name := ""
defaultValue := ""
mfns := []getoptions.ModifyFn{}
// Check for args
for i, arg := range x.Args {
Logger.Printf("i: %d, arg: %T\n", i, arg)
if i == 0 {
// First argument is the Name
Logger.Printf("Name: %s\n", arg.(*ast.BasicLit).Value)
// Logger.Printf("Name: '%s'\n", arg.(*ast.BasicLit).Value)
var err error
name, err = strconv.Unquote(arg.(*ast.BasicLit).Value)
if err != nil {
name = arg.(*ast.BasicLit).Value
}
} else if i == 1 {
// Second argument is the Default
Logger.Printf("Default: %s\n", arg.(*ast.BasicLit).Value)
// Logger.Printf("Default: '%s'\n", arg.(*ast.BasicLit).Value)
var err error
defaultValue, err = strconv.Unquote(arg.(*ast.BasicLit).Value)
if err != nil {
defaultValue = arg.(*ast.BasicLit).Value
}
} else {
// Remaining arguments are option modifiers
// Start with support for:
// Description
// ValidValues
// Alias

callE, ok := arg.(*ast.CallExpr)
if !ok {
Expand All @@ -311,10 +345,39 @@ func handleString(optFieldName string, n ast.Node) error {
// TODO: SetCalled function receives a bool
continue
}
values := []string{}
for _, arg := range callE.Args {
Logger.Printf("Value: %s\n", arg.(*ast.BasicLit).Value)
value, err := strconv.Unquote(arg.(*ast.BasicLit).Value)
if err != nil {
value = arg.(*ast.BasicLit).Value
}
values = append(values, value)
}
switch fun.Sel.Name {
case "Alias":
mfns = append(mfns, cmd.Alias(values...))
case "ArgName":
if len(values) > 0 {
mfns = append(mfns, cmd.ArgName(values[0]))
}
case "Description":
if len(values) > 0 {
mfns = append(mfns, cmd.Description(values[0]))
}
case "GetEnv":
if len(values) > 0 {
mfns = append(mfns, cmd.GetEnv(values[0]))
}
case "Required":
mfns = append(mfns, cmd.Required(values...))
case "SuggestedValues":
mfns = append(mfns, cmd.SuggestedValues(values...))
case "ValidValues":
mfns = append(mfns, cmd.ValidValues(values...))
}
}
}
cmd.String(name, defaultValue, mfns...)
return nil
}
22 changes: 19 additions & 3 deletions bake/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ import (
"github.com/DavidGamba/go-getoptions"
)

var inputArgs []string

var Logger = log.New(os.Stderr, "", log.LstdFlags)

func main() {
Expand Down Expand Up @@ -47,6 +49,13 @@ func program(args []string) int {
// return 1
// }

inputArgs = args[1:]
err = LoadAst(ctx, opt, dir)
if err != nil {
fmt.Fprintf(os.Stderr, "ERROR: %s\n", err)
return 1
}

b := opt.NewCommand("_bake", "")

bld := b.NewCommand("list-descriptions", "lists descriptions")
Expand All @@ -56,7 +65,7 @@ func program(args []string) int {
bast.SetCommandFn(ShowASTRun(dir))

bastList := b.NewCommand("list-ast", "list parsed ast")
bastList.SetCommandFn(ListASTRun(dir))
bastList.SetCommandFn(LoadASTRun(dir))

opt.HelpCommand("help", opt.Alias("?"))
remaining, err := opt.Parse(args[1:])
Expand Down Expand Up @@ -130,6 +139,13 @@ func (ot *OptTree) AddCommand(name, description string) *getoptions.GetOpt {
Children: make(map[string]*OptNode),
}
node = node.Children[key]
if len(keys) == i+1 {
cmd.SetCommandFn(func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
// TODO: Run os.exec call to the built binary with keys as the arguments
fmt.Printf("Running %v\n", inputArgs)
return nil
})
}
}
return cmd
}
Expand Down Expand Up @@ -170,9 +186,9 @@ func ShowASTRun(dir string) getoptions.CommandFn {
}
}

func ListASTRun(dir string) getoptions.CommandFn {
func LoadASTRun(dir string) getoptions.CommandFn {
return func(ctx context.Context, opt *getoptions.GetOpt, args []string) error {
err := ListAst(dir)
err := LoadAst(ctx, opt, dir)
if err != nil {
return fmt.Errorf("failed to inspect package: %w", err)
}
Expand Down
23 changes: 0 additions & 23 deletions bake/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,8 @@ import (
"os"
"path/filepath"
"plugin"
"reflect"
"regexp"
"strings"
"unsafe"

"github.com/DavidGamba/go-getoptions"
"github.com/DavidGamba/go-getoptions/dag"
Expand Down Expand Up @@ -92,24 +90,3 @@ func loadOptFns(ctx context.Context, plug *plugin.Plugin, opt *getoptions.GetOpt
}
return nil
}

// https://github.com/golang/go/issues/17823
type Plug struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]any
}

func inspectPlugin(p *plugin.Plugin) {
pl := (*Plug)(unsafe.Pointer(p))

Logger.Printf("Plugin %s exported symbols (%d): \n", pl.pluginpath, len(pl.syms))

for name, pointers := range pl.syms {
Logger.Printf("symbol: %s, pointer: %v, type: %v\n", name, pointers, reflect.TypeOf(pointers))
if _, ok := pointers.(func(*getoptions.GetOpt) getoptions.CommandFn); ok {
fmt.Printf("name: %s\n", name)
}
}
}

0 comments on commit 30d8777

Please sign in to comment.