Files
mgmt/lang/funcs/vars/vars.go

154 lines
6.0 KiB
Go

// Mgmt
// Copyright (C) 2013-2024+ James Shubin and the project contributors
// Written by James Shubin <james@shubin.ca> 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 <https://www.gnu.org/licenses/>.
//
// Additional permission under GNU GPL version 3 section 7
//
// If you modify this program, or any covered work, by linking or combining it
// with embedded mcl code and modules (and that the embedded mcl code and
// modules which link with this program, contain a copy of their source code in
// the authoritative form) containing parts covered by the terms of any other
// license, the licensors of this program grant you additional permission to
// convey the resulting work. Furthermore, the licensors of this program grant
// the original author, James Shubin, additional permission to update this
// additional permission if he deems it necessary to achieve the goals of this
// additional permission.
// Package vars provides a framework for language vars.
package vars
import (
"fmt"
"strings"
"github.com/purpleidea/mgmt/lang/interfaces"
)
const (
// ConstNamespace is the string prefix for all top-level built-in vars.
ConstNamespace = "const"
// ResourceNamespace is the string prefix for all top-level resource
// specific built-in vars, that exist under the ConstNamespace header.
ResourceNamespace = "res"
)
// Value is a shortcut to using this type.
// XXX: Eventually we might get rid of this entirely and use types.Value instead
// of interfaces.Var which seems to be unnecessary at the moment.
type Value = interfaces.Var
// registeredVars is a global map of all possible vars which can be used. You
// should never touch this map directly. Use methods like Register instead.
var registeredVars = make(map[string]func() interfaces.Var) // must initialize
// Register takes a var and its name and makes it available for use. It is
// commonly called in the init() method of the var at program startup. There is
// no matching Unregister function.
func Register(name string, fn func() interfaces.Var) {
if _, ok := registeredVars[name]; ok {
panic(fmt.Sprintf("a var named %s is already registered", name))
}
//gob.Register(fn())
registeredVars[name] = fn
}
// ModuleRegister is exactly like Register, except that it registers within a
// named module. This is a helper function.
func ModuleRegister(module, name string, v func() interfaces.Var) {
Register(module+interfaces.ModuleSep+name, v)
}
// resourceConstHelper is a helper function to manage the const topology.
func resourceConstHelper(kind, param, field string) string {
// const.res.file.state.exists = "exists"
// TODO: should it be: const.res.file.params.state.exists = "exists" ?
chunks := []string{
ConstNamespace,
ResourceNamespace,
kind,
param,
field,
}
//return ConstNamespace + interfaces.ModuleSep + ResourceNamespace + interfaces.ModuleSep + kind + interfaces.ModuleSep + param + interfaces.ModuleSep + field
return strings.Join(chunks, interfaces.ModuleSep) // cleaner code
}
// RegisterResourceParam registers a single const param for a resource. You
// might prefer to use RegisterResourceParams instead.
func RegisterResourceParam(kind, param, field string, value func() interfaces.Var) {
Register(resourceConstHelper(kind, param, field), value)
}
// RegisterResourceParams registers a map of const params for a resource. The
// params mapping keys are the param name and the param field name. Finally, the
// value is the specific type value for that constant.
func RegisterResourceParams(kind string, params map[string]map[string]func() interfaces.Var) {
for param, mapping := range params {
for field, value := range mapping {
Register(resourceConstHelper(kind, param, field), value)
}
}
}
// Lookup returns a pointer to the var implementation.
func Lookup(name string) (interfaces.Var, error) {
f, exists := registeredVars[name]
if !exists {
return nil, fmt.Errorf("not found")
}
return f(), nil
}
// LookupPrefix returns a map of names to vars that start with a module prefix.
// This search automatically adds the period separator. So if you want vars in
// the `const` prefix, search for `const`, not `const.` and it will find all the
// correctly registered vars. This removes that prefix from the result in the
// map keys that it returns. If you search for an empty prefix, then this will
// return all the top-level functions that aren't in a module.
func LookupPrefix(prefix string) map[string]func() interfaces.Var {
result := make(map[string]func() interfaces.Var)
for name, f := range registeredVars {
// requested top-level vars, and no module separators...
if prefix == "" {
if !strings.Contains(name, interfaces.ModuleSep) {
result[name] = f // copy
}
continue
}
sep := prefix + interfaces.ModuleSep
if !strings.HasPrefix(name, sep) {
continue
}
s := strings.TrimPrefix(name, sep) // remove the prefix
result[s] = f // copy
}
return result
}
// Map returns a map from all registered var names to a function to return that
// one. We return a copy of our internal registered var store so that this
// result can be manipulated safely. We return the vars that produce the Var
// interface because we might use this result to create multiple vars, and each
// one might need to have its own unique memory address to work properly.
func Map() map[string]func() interfaces.Var {
m := make(map[string]func() interfaces.Var)
for name, fn := range registeredVars { // copy
m[name] = fn
}
return m
}