From 859b5d7ea3b8f660ac68d9aea5a53d25a9a7422c Mon Sep 17 00:00:00 2001 From: Matthew Holt Date: Tue, 26 Mar 2019 12:00:54 -0600 Subject: Initial commit --- modules.go | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 modules.go (limited to 'modules.go') diff --git a/modules.go b/modules.go new file mode 100644 index 0000000..1c3e231 --- /dev/null +++ b/modules.go @@ -0,0 +1,110 @@ +package caddy2 + +import ( + "fmt" + "sort" + "strings" + "sync" +) + +// Module is a module. +type Module struct { + Name string + New func() (interface{}, error) +} + +func (m Module) String() string { return m.Name } + +// RegisterModule registers a module. +func RegisterModule(mod Module) error { + modulesMu.Lock() + defer modulesMu.Unlock() + + if _, ok := modules[mod.Name]; ok { + return fmt.Errorf("module already registered: %s", mod.Name) + } + modules[mod.Name] = mod + return nil +} + +// GetModule returns a module by name. +func GetModule(name string) (Module, error) { + modulesMu.Lock() + defer modulesMu.Unlock() + + m, ok := modules[name] + if !ok { + return Module{}, fmt.Errorf("module not registered: %s", name) + } + return m, nil +} + +// GetModules returns all modules in the given scope/namespace. +// For example, a scope of "foo" returns modules named "foo.bar", +// "foo.lor", but not "bar", "foo.bar.lor", etc. An empty scope +// returns top-level modules, for example "foo" or "bar". Partial +// scopes are not matched (i.e. scope "foo.ba" does not match +// name "foo.bar"). +// +// Because modules are registered to a map, the returned slice +// will be sorted to keep it deterministic. +func GetModules(scope string) []Module { + modulesMu.Lock() + defer modulesMu.Unlock() + + scopeParts := strings.Split(scope, ".") + + // handle the special case of an empty scope, which + // should match only the top-level modules + if len(scopeParts) == 1 && scopeParts[0] == "" { + scopeParts = []string{} + } + + var mods []Module +iterateModules: + for name, m := range modules { + modParts := strings.Split(name, ".") + + // match only the next level of nesting + if len(modParts) != len(scopeParts)+1 { + continue + } + + // specified parts must be exact matches + for i := range scopeParts { + if modParts[i] != scopeParts[i] { + continue iterateModules + } + } + + mods = append(mods, m) + } + + // make return value deterministic + sort.Slice(mods, func(i, j int) bool { + return mods[i].Name < mods[j].Name + }) + + return mods +} + +// Modules returns the names of all registered modules +// in ascending lexicographical order. +func Modules() []string { + modulesMu.Lock() + defer modulesMu.Unlock() + + var names []string + for name := range modules { + names = append(names, name) + } + + sort.Strings(names) + + return names +} + +var ( + modules = make(map[string]Module) + modulesMu sync.Mutex +) -- cgit v1.2.3