lang: funcs: Autogenerated a lot of new functions

Signed-off-by: Julien Pivotto <roidelapluie@inuits.eu>
This commit is contained in:
Julien Pivotto
2019-05-15 01:11:36 +02:00
committed by James Shubin
parent 9b4d11f220
commit 1685ee1ecb
18 changed files with 671 additions and 204 deletions

View File

@@ -18,13 +18,39 @@
package main
import (
"bytes"
"errors"
"fmt"
"io/ioutil"
"log"
"os/exec"
"path/filepath"
"regexp"
"strings"
"github.com/iancoleman/strcase"
yaml "gopkg.in/yaml.v2"
)
var (
validSignature = regexp.MustCompile(`^func (?P<name>[A-Z][a-zA-Z0-9]+)\((?P<args>([a-zA-Z]+( (string|bool|float64|int64|int))?(, )?){0,})\) (?P<return>(string|float64|int64|bool|int)|\((string|float64|int64|bool|int), error\))$`)
errExcluded = errors.New("function is excluded")
)
type golangPackages []*golangPackage
type golangPackage struct {
// Name is the name of the go package.
Name string `yaml:"name"`
// Alias is the alias of the package when imported in golang.
// e.g. import rand "os.rand"
Alias string `yaml:"alias,omitempty"`
// MgmtAlias is the name of the package inside mcl.
MgmtAlias string `yaml:"mgmtAlias,omitempty"`
// Exclude is a list of golang function names that we do not want.
Exclude []string `yaml:"exclude,omitempty"`
}
func parsePkg(path, filename, templates string) error {
var c config
filePath := filepath.Join(path, filename)
@@ -37,9 +63,173 @@ func parsePkg(path, filename, templates string) error {
if err != nil {
return err
}
err = parseFuncs(c, path, templates)
functions, err := parsePackages(c)
if err != nil {
return err
}
err = parseFuncs(c, functions, path, templates)
if err != nil {
return err
}
return nil
}
func parsePackages(c config) (functions, error) {
var funcs []function
for _, golangPackage := range c.Packages {
fn, err := golangPackage.parsefuncs()
if err != nil {
return funcs, err
}
funcs = append(funcs, fn...)
}
return funcs, nil
}
func (obj *golangPackage) parsefuncs() (functions, error) {
var funcs []function
cmd := exec.Command("go", "doc", obj.Name)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return funcs, err
}
return obj.extractFuncs(out.String(), true)
}
func (obj *golangPackage) extractFuncs(doc string, getHelp bool) (functions, error) {
var funcs []function
for _, line := range strings.Split(doc, "\n") {
if validSignature.MatchString(line) {
f, err := obj.parseFunctionLine(line, getHelp)
if err != nil && err != errExcluded {
return funcs, err
}
if f != nil {
funcs = append(funcs, *f)
}
}
}
return funcs, nil
}
func (obj *golangPackage) parseFunctionLine(line string, getHelp bool) (*function, error) {
match := validSignature.FindStringSubmatch(line)
result := make(map[string]string)
for i, name := range validSignature.SubexpNames() {
if i != 0 && name != "" {
result[name] = match[i]
}
}
name := result["name"]
for _, e := range obj.Exclude {
if e == name {
return nil, errExcluded
}
}
errorFul, err := regexp.MatchString(`, error\)$`, result["return"])
if err != nil {
return nil, err
}
returns := parseReturn(result["return"])
if len(returns) == 0 {
return nil, errExcluded
}
mgmtpackage := obj.Name
if obj.MgmtAlias != "" {
mgmtpackage = obj.MgmtAlias
}
mgmtpackage = fmt.Sprintf("golang/%s", mgmtpackage)
internalName := fmt.Sprintf("%s%s", strcase.ToCamel(strings.Replace(obj.Name, "/", "", -1)), name)
internalName = strings.Replace(internalName, "Html", "HTML", -1)
var help string
if getHelp {
help, err = obj.getHelp(name, internalName)
if err != nil {
return nil, err
}
}
return &function{
MgmtPackage: mgmtpackage,
MclName: strcase.ToSnake(name),
InternalName: internalName,
Help: help,
GolangPackage: obj,
GolangFunc: name,
Errorful: errorFul,
Args: parseArgs(result["args"]),
Return: returns,
}, nil
}
func reverseArgs(s []arg) {
last := len(s) - 1
for i := 0; i < len(s)/2; i++ {
s[i], s[last-i] = s[last-i], s[i]
}
}
func reverse(s []string) {
last := len(s) - 1
for i := 0; i < len(s)/2; i++ {
s[i], s[last-i] = s[last-i], s[i]
}
}
func parseArgs(str string) []arg {
var args []arg
s := strings.Split(str, ",")
reverse(s)
var currentType string
for _, currentArg := range s {
if currentArg == "" {
continue
}
v := strings.Split(strings.TrimSpace(currentArg), " ")
if len(v) == 2 {
currentType = v[1]
}
args = append(args, arg{Name: v[0], Type: currentType})
}
reverseArgs(args)
return args
}
func parseReturn(str string) []arg {
var returns []arg
re := regexp.MustCompile(`(int64|float64|string|bool|int)`)
t := string(re.Find([]byte(str)))
returns = append(returns, arg{Type: t})
return returns
}
func (obj *golangPackage) getHelp(function, internalName string) (string, error) {
cmd := exec.Command("go", "doc", fmt.Sprintf("%s.%s", obj.Name, function))
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
return "", err
}
var doc string
for i, line := range strings.Split(strings.TrimSpace(out.String()), "\n") {
if i > 0 {
s := strings.TrimSpace(line)
if i == 1 {
docs := strings.Split(s, " ")
docs[0] = internalName
s = strings.Join(docs, " ")
}
doc = fmt.Sprintf("%s// %s is an autogenerated function.\n", doc, s)
}
}
return doc, nil
}