lang: Refactor most functions to support modules

This is a giant refactor to move functions into a hierarchial module
layout. While this isn't entirely implemented yet, it should work
correctly once all the import bits have landed. What's broken at the
moment is the template function, which currently doesn't understand the
period separator.
This commit is contained in:
James Shubin
2018-11-10 22:19:28 -05:00
parent c32183eb70
commit 046b21b907
55 changed files with 400 additions and 106 deletions

View File

@@ -20,10 +20,18 @@ package funcs
import (
"fmt"
"strings"
"github.com/purpleidea/mgmt/lang/interfaces"
)
const (
// ModuleSep is the character used for the module scope separation. For
// example when using `fmt.printf` or `math.sin` this is the char used.
// It is included here for convenience when importing this package.
ModuleSep = interfaces.ModuleSep
)
// registeredFuncs is a global map of all possible funcs which can be used. You
// should never touch this map directly. Use methods like Register instead. It
// includes implementations which also satisfy PolyFunc as well.
@@ -32,15 +40,33 @@ var registeredFuncs = make(map[string]func() interfaces.Func) // must initialize
// Register takes a func and its name and makes it available for use. It is
// commonly called in the init() method of the func at program startup. There is
// no matching Unregister function. You may also register functions which
// satisfy the PolyFunc interface.
// satisfy the PolyFunc interface. To register a function which lives in a
// module, you must join the module name to the function name with the ModuleSep
// character. It is defined as a const and is probably the period character.
func Register(name string, fn func() interfaces.Func) {
if _, exists := registeredFuncs[name]; exists {
panic(fmt.Sprintf("a func named %s is already registered", name))
}
// can't contain more than one period in a row
if strings.Index(name, ModuleSep+ModuleSep) >= 0 {
panic(fmt.Sprintf("a func named %s is invalid", name))
}
// can't start or end with a period
if strings.HasPrefix(name, ModuleSep) || strings.HasSuffix(name, ModuleSep) {
panic(fmt.Sprintf("a func named %s is invalid", name))
}
//gob.Register(fn())
registeredFuncs[name] = fn
}
// ModuleRegister is exactly like Register, except that it registers within a
// named module.
func ModuleRegister(module, name string, fn func() interfaces.Func) {
Register(module+ModuleSep+name, fn)
}
// Lookup returns a pointer to the function's struct. It may be convertible to a
// PolyFunc if the particular function implements those additional methods.
func Lookup(name string) (interfaces.Func, error) {
@@ -50,3 +76,21 @@ func Lookup(name string) (interfaces.Func, error) {
}
return f(), nil
}
// LookupPrefix returns a map of names to functions that start with a module
// prefix. This search automatically adds the period separator. So if you want
// functions in the `fmt` package, search for `fmt`, not `fmt.` and it will find
// all the correctly registered functions. This removes that prefix from the
// result in the map keys that it returns.
func LookupPrefix(prefix string) (map[string]interfaces.Func, error) {
result := make(map[string]interfaces.Func)
for name, f := range registeredFuncs {
sep := prefix + ModuleSep
if !strings.HasPrefix(name, sep) {
continue
}
s := strings.TrimPrefix(name, sep) // TODO: is it okay to remove the prefix?
result[s] = f() // build
}
return result, nil
}