diff --git a/.gitignore b/.gitignore
index a8bd7ea7..1d71084c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,8 @@ tmp/
*WIP
*_stringer.go
bindata/*.go
+generated_funcs.go
+generated_funcs_test.go
mgmt
mgmt.static
# crossbuild artifacts
diff --git a/Makefile b/Makefile
index 523769b5..6532ac8f 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@
# along with this program. If not, see .
SHELL = /usr/bin/env bash
-.PHONY: all art cleanart version program lang path deps run race bindata generate build build-debug crossbuild clean test gofmt yamlfmt format docs rpmbuild mkdirs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms copr tag release
+.PHONY: all art cleanart version program lang path deps run race bindata generate build build-debug crossbuild clean test gofmt yamlfmt format docs rpmbuild mkdirs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms copr tag release funcgen
.SILENT: clean bindata
# a large amount of output from this `find`, can cause `make` to be much slower!
@@ -148,7 +148,7 @@ build-debug: $(PROGRAM)
# extract os and arch from target pattern
GOOS=$(firstword $(subst -, ,$*))
GOARCH=$(lastword $(subst -, ,$*))
-build/mgmt-%: $(GO_FILES) | bindata lang
+build/mgmt-%: $(GO_FILES) | bindata lang funcgen
@echo "Building: $(PROGRAM), os/arch: $*, version: $(SVERSION)..."
@# reassigning GOOS and GOARCH to make build command copy/pastable
@# go 1.10 requires specifying the package for ldflags
@@ -166,6 +166,8 @@ clean: ## clean things up
$(MAKE) --quiet -C bindata clean
$(MAKE) --quiet -C lang/funcs clean
$(MAKE) --quiet -C lang clean
+ rm -f lang/funcs/core/generated_funcs.go || true
+ rm -f lang/funcs/core/generated_funcs_test.go || true
[ ! -e $(PROGRAM) ] || rm $(PROGRAM)
rm -f *_stringer.go # generated by `go generate`
rm -f *_mock.go # generated by `go generate`
@@ -408,4 +410,11 @@ help: ## show this help screen
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
@echo ''
+funcgen: lang/funcs/core/generated_funcs_tests.go lang/funcs/core/generated_funcs.go
+
+lang/funcs/core/generated_funcs_tests.go: lang/funcs/core/generated_funcs.go
+
+lang/funcs/core/generated_funcs.go: lang/golang2mgmt/*.go lang/funcs/core/golang2mgmt.yaml lang/golang2mgmt/templates/*.tpl
+ go run lang/golang2mgmt/*.go
+
# vim: ts=8
diff --git a/lang/funcs/core/golang2mgmt.yaml b/lang/funcs/core/golang2mgmt.yaml
new file mode 100644
index 00000000..62cd2e43
--- /dev/null
+++ b/lang/funcs/core/golang2mgmt.yaml
@@ -0,0 +1,55 @@
+# This file is to be used by github.com/purpleidea/mgmt/lang/golang2mgmt
+# to generate mgmt functions
+functions:
+- mgmtName: to_upper
+ mgmtPackage: strings
+ help: turns a string to uppercase.
+ goPackage: strings
+ goFunc: ToUpper
+ args: [{name: a, type: string}]
+ return: [{type: string}]
+ tests:
+ - args: [{type: string, value: "Hello"}]
+ return: [{type: string, value: "HELLO"}]
+ - args: [{type: string, value: "HELLO 22"}]
+ return: [{type: string, value: "HELLO 22"}]
+- mgmtName: trim
+ mgmtPackage: strings
+ help: returns a slice of the string s with all leading and trailing Unicode code points contained in cutset removed..
+ goPackage: strings
+ goFunc: Trim
+ args: [{name: s, type: string}, {name: cutset, type: string}]
+ return: [{type: string}]
+ tests:
+ - args: [{type: string, value: "??Hello.."}, {type: string, value: "?."}]
+ return: [{type: string, value: "Hello"}]
+- mgmtName: trim_left
+ mgmtPackage: strings
+ help: returns a slice of the string s with all leading Unicode code points contained in cutset removed.
+ goPackage: strings
+ goFunc: TrimLeft
+ args: [{name: s, type: string}, {name: cutset, type: string}]
+ return: [{type: string}]
+ tests:
+ - args: [{type: string, value: "??Hello.."}, {type: string, value: "?."}]
+ return: [{type: string, value: "Hello.."}]
+- mgmtName: trim_space
+ mgmtPackage: strings
+ help: returns a slice of the string s, with all leading and trailing white space removed, as defined by Unicode.
+ goPackage: strings
+ goFunc: TrimSpace
+ args: [{name: s, type: string}]
+ return: [{type: string}]
+ tests:
+ - args: [{type: string, value: "Hello 2 "}]
+ return: [{type: string, value: "Hello 2"}]
+- mgmtName: trim_right
+ mgmtPackage: strings
+ help: returns a slice of the string s with all trailing Unicode code points contained in cutset removed.
+ goPackage: strings
+ goFunc: TrimRight
+ args: [{name: s, type: string}, {name: cutset, type: string}]
+ return: [{type: string}]
+ tests:
+ - args: [{type: string, value: "??Hello.."}, {type: string, value: "?."}]
+ return: [{type: string, value: "??Hello"}]
diff --git a/lang/golang2mgmt/config.go b/lang/golang2mgmt/config.go
new file mode 100644
index 00000000..2921d4b8
--- /dev/null
+++ b/lang/golang2mgmt/config.go
@@ -0,0 +1,66 @@
+// Mgmt
+// Copyright (C) 2013-2018+ James Shubin and the project contributors
+// Written by James Shubin and the project contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package main
+
+import (
+ "fmt"
+)
+
+type config struct {
+ Functions functions `yaml:"functions"`
+}
+
+type functions []function
+
+type testarg struct {
+ Name string `yaml:"name,omitempty"`
+ Type string `yaml:"type"`
+ Value string `yaml:"value"`
+}
+
+type arg struct {
+ Name string `yaml:"name,omitempty"`
+ Type string `yaml:"type"`
+}
+
+// ToMcl prints the arg signature as expected by mcl.
+func (obj *arg) ToMcl() (string, error) {
+ if obj.Type == "string" {
+ if obj.Name != "" {
+ return fmt.Sprintf("%s str", obj.Name), nil
+ }
+ return "str", nil
+ }
+ return "", fmt.Errorf("cannot convert %v to mcl", obj)
+}
+
+// ToGo prints the arg signature as expected by golang.
+func (obj *arg) ToGo() (string, error) {
+ if obj.Type == "string" {
+ return "Str", nil
+ }
+ return "", fmt.Errorf("cannot convert %v to go", obj)
+}
+
+// ToTestInput prints the arg signature as expected by tests.
+func (obj *arg) ToTestInput() (string, error) {
+ if obj.Type == "string" {
+ return fmt.Sprintf("&types.StrValue{V: %s}", obj.Name), nil
+ }
+ return "", fmt.Errorf("cannot convert %v to test input", obj)
+}
diff --git a/lang/golang2mgmt/func.go b/lang/golang2mgmt/func.go
new file mode 100644
index 00000000..2c34287e
--- /dev/null
+++ b/lang/golang2mgmt/func.go
@@ -0,0 +1,181 @@
+// Mgmt
+// Copyright (C) 2013-2018+ James Shubin and the project contributors
+// Written by James Shubin and the project contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "log"
+ "os"
+ "path/filepath"
+ "strings"
+ "text/template"
+)
+
+type function struct {
+ MgmtPackage string `yaml:"mgmtPackage"`
+ MgmtName string `yaml:"mgmtName"`
+ Help string `yaml:"help"`
+ GoPackage string `yaml:"goPackage"`
+ GoFunc string `yaml:"goFunc"`
+ Args []arg `yaml:"args"`
+ Return []arg `yaml:"return"`
+ Tests []functest `yaml:"tests"`
+}
+
+type functest struct {
+ Args []testarg `yaml:"args"`
+ Expect []testarg `yaml:"return"`
+}
+
+type templateInput struct {
+ Func function
+ MgmtPackage string
+}
+
+func parseFuncs(c config, path, templates string) error {
+ templateFiles, err := filepath.Glob(templates)
+ if err != nil {
+ return err
+ }
+ for _, tpl := range templateFiles {
+ log.Printf("Generating %s", tpl)
+ err = generateTemplate(c, path, tpl)
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func generateTemplate(c config, path, templateFile string) error {
+ log.Printf("Reading %s", templateFile)
+ basename := filepath.Base(templateFile)
+ tplFile, err := ioutil.ReadFile(templateFile)
+ if err != nil {
+ return err
+ }
+ t, err := template.New(basename).Parse(string(tplFile))
+ if err != nil {
+ return err
+ }
+ finalName := strings.TrimSuffix(basename, ".tpl")
+ finalPath := filepath.Join(path, finalName)
+ log.Printf("Writing %s", finalPath)
+ finalFile, err := os.Create(finalPath)
+ if err != nil {
+ return err
+ }
+ if err = t.Execute(finalFile, c); err != nil {
+ return err
+ }
+ return nil
+}
+
+// MakeGoArgs translates the func args to go args.
+func (obj *function) MakeGoArgs() (string, error) {
+ var args []string
+ for i, a := range obj.Args {
+ gol, err := a.ToGo()
+ if err != nil {
+ return "", err
+ }
+ args = append(args, fmt.Sprintf("input[%d].%s()", i, gol))
+ }
+ return strings.Join(args, ", "), nil
+}
+
+// Signature generates the mcl signature of the function.
+func (obj *function) Signature() (string, error) {
+ var args []string
+ for _, a := range obj.Args {
+ mcl, err := a.ToMcl()
+ if err != nil {
+ return "", err
+ }
+ args = append(args, mcl)
+ }
+ var returns []string
+ for _, a := range obj.Return {
+ mcl, err := a.ToMcl()
+ if err != nil {
+ return "", err
+ }
+ returns = append(returns, mcl)
+ }
+ return fmt.Sprintf("func(%s) %s", strings.Join(args, ", "), returns[0]), nil
+}
+
+// MakeGoReturn returns the golang signature of the return.
+func (obj *function) MakeGoReturn() (string, error) {
+ return obj.Return[0].ToGo()
+}
+
+// MakeGoTypeReturn returns the mcl signature of the return.
+func (obj *function) MakeGoTypeReturn() string {
+ return obj.Return[0].Type
+}
+
+// MakeTestSign returns the signature of the test.
+func (obj *function) MakeTestSign() string {
+ var args []string
+ for i, a := range obj.Args {
+ var nextSign string
+ if i+1 < len(obj.Args) {
+ nextSign = obj.Args[i+1].Type
+ } else {
+ nextSign = obj.MakeGoTypeReturn()
+ }
+ if nextSign == a.Type {
+ args = append(args, a.Name)
+ } else {
+ args = append(args, fmt.Sprintf("%s %s", a.Name, a.Type))
+ }
+ }
+ args = append(args, fmt.Sprintf("expected %s", obj.MakeGoTypeReturn()))
+ return strings.Join(args, ", ")
+}
+
+// TestInput generated a string that can be passed as test input.
+func (obj *function) TestInput() (string, error) {
+ var values []string
+ for _, i := range obj.Args {
+ tti, err := i.ToTestInput()
+ if err != nil {
+ return "", err
+ }
+ values = append(values, tti)
+ }
+ return fmt.Sprintf("[]types.Value{%s}", strings.Join(values, ", ")), nil
+}
+
+// MakeTestArgs generates a string that can be passed a test arguments.
+func (obj *functest) MakeTestArgs() string {
+ var values []string
+ for _, i := range obj.Args {
+ if i.Type == "string" {
+ values = append(values, fmt.Sprintf(`"%s"`, i.Value))
+ }
+ }
+ for _, i := range obj.Expect {
+ if i.Type == "string" {
+ values = append(values, fmt.Sprintf(`"%s"`, i.Value))
+ }
+ }
+ return strings.Join(values, ", ")
+}
diff --git a/lang/golang2mgmt/main.go b/lang/golang2mgmt/main.go
new file mode 100644
index 00000000..3973d7e1
--- /dev/null
+++ b/lang/golang2mgmt/main.go
@@ -0,0 +1,41 @@
+// Mgmt
+// Copyright (C) 2013-2018+ James Shubin and the project contributors
+// Written by James Shubin and the project contributors
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+
+package main
+
+import (
+ "flag"
+ "log"
+)
+
+var (
+ pkg = flag.String("package", "lang/funcs/core", "path to the package")
+ filename = flag.String("filename", "golang2mgmt.yaml", "path to the config")
+ templates = flag.String("templates", "lang/golang2mgmt/templates/*.tpl", "path to the templates")
+)
+
+func main() {
+ flag.Parse()
+ if *pkg == "" {
+ log.Fatalf("No package passed!")
+ }
+
+ err := parsePkg(*pkg, *filename, *templates)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
diff --git a/lang/funcs/core/strings/trim_space_func.go b/lang/golang2mgmt/pkg.go
similarity index 64%
rename from lang/funcs/core/strings/trim_space_func.go
rename to lang/golang2mgmt/pkg.go
index 64c7e3a0..1b8c3854 100644
--- a/lang/funcs/core/strings/trim_space_func.go
+++ b/lang/golang2mgmt/pkg.go
@@ -15,22 +15,31 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-package corestrings
+package main
import (
- "strings"
+ "io/ioutil"
+ "log"
+ "path/filepath"
- "github.com/purpleidea/mgmt/lang/funcs/simple"
- "github.com/purpleidea/mgmt/lang/types"
+ yaml "gopkg.in/yaml.v2"
)
-func init() {
- simple.ModuleRegister(moduleName, "trim_space", &types.FuncValue{
- T: types.NewType("func(a str) str"),
- V: func(input []types.Value) (types.Value, error) {
- return &types.StrValue{
- V: strings.TrimSpace(input[0].Str()),
- }, nil
- },
- })
+func parsePkg(path, filename, templates string) error {
+ var c config
+ filePath := filepath.Join(path, filename)
+ log.Printf("Reading %s", filePath)
+ cfgFile, err := ioutil.ReadFile(filePath)
+ if err != nil {
+ return err
+ }
+ err = yaml.UnmarshalStrict(cfgFile, &c)
+ if err != nil {
+ return err
+ }
+ err = parseFuncs(c, path, templates)
+ if err != nil {
+ return err
+ }
+ return nil
}
diff --git a/lang/funcs/core/strings/to_upper_func.go b/lang/golang2mgmt/templates/generated_funcs.go.tpl
similarity index 66%
rename from lang/funcs/core/strings/to_upper_func.go
rename to lang/golang2mgmt/templates/generated_funcs.go.tpl
index 06c149c5..0864518a 100644
--- a/lang/funcs/core/strings/to_upper_func.go
+++ b/lang/golang2mgmt/templates/generated_funcs.go.tpl
@@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-package corestrings
+package core
import (
"strings"
@@ -25,15 +25,17 @@ import (
)
func init() {
- simple.ModuleRegister(moduleName, "to_upper", &types.FuncValue{
- T: types.NewType("func(a str) str"),
- V: ToUpper,
+{{ range $i, $func := .Functions }} simple.ModuleRegister("{{$func.MgmtPackage}}", "{{$func.MgmtName}}", &types.FuncValue{
+ T: types.NewType("{{$func.Signature}}"),
+ V: {{$func.GoFunc}},
})
+{{ end }}
}
-
-// ToUpper turns a string to uppercase.
-func ToUpper(input []types.Value) (types.Value, error) {
- return &types.StrValue{
- V: strings.ToUpper(input[0].Str()),
+{{ range $i, $func := .Functions }}
+// {{$func.GoFunc}} {{$func.Help}}
+func {{$func.GoFunc}}(input []types.Value) (types.Value, error) {
+ return &types.{{$func.MakeGoReturn}}Value{
+ V: {{$func.GoPackage}}.{{$func.GoFunc}}({{$func.MakeGoArgs}}),
}, nil
}
+{{ end -}}
diff --git a/lang/funcs/core/strings/to_upper_func_test.go b/lang/golang2mgmt/templates/generated_funcs_test.go.tpl
similarity index 66%
rename from lang/funcs/core/strings/to_upper_func_test.go
rename to lang/golang2mgmt/templates/generated_funcs_test.go.tpl
index 77a1c7a6..e4000337 100644
--- a/lang/funcs/core/strings/to_upper_func_test.go
+++ b/lang/golang2mgmt/templates/generated_funcs_test.go.tpl
@@ -15,30 +15,27 @@
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
-package corestrings
+package core
import (
"testing"
"github.com/purpleidea/mgmt/lang/types"
)
-
-func testToUpper(t *testing.T, input, expected string) {
- inputStr := &types.StrValue{V: input}
- value, err := ToUpper([]types.Value{inputStr})
+{{ range $i, $func := .Functions }}
+func test{{$func.GoFunc}}(t *testing.T, {{$func.MakeTestSign}}) {
+ value, err := {{$func.GoFunc}}({{$func.TestInput}})
if err != nil {
t.Error(err)
return
}
- if value.Str() != expected {
- t.Errorf("Invalid output, expected %s, got %s", expected, value.Str())
+ if value.{{$func.MakeGoReturn}}() != expected {
+ t.Errorf("invalid output, expected %s, got %s", expected, value.{{$func.MakeGoReturn}}())
}
}
-
-func TestToUpperSimple(t *testing.T) {
- testToUpper(t, "Hello", "HELLO")
-}
-
-func TestToUpperSameString(t *testing.T) {
- testToUpper(t, "HELLO 22", "HELLO 22")
+{{ range $index, $test := $func.Tests }}
+func Test{{$func.GoFunc}}{{$index}}(t *testing.T) {
+ test{{$func.GoFunc}}(t, {{.MakeTestArgs}})
}
+{{ end -}}
+{{ end -}}