asteris-llc/converge

View on GitHub
load/registry/registry.go

Summary

Maintainability
A
0 mins
Test Coverage
// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package registry

import (
    "fmt"
    "reflect"
)

// Registry for importable types
type Registry struct {
    forward map[string]reflect.Type
    reverse map[reflect.Type]string
}

// New creates a new Registry
func New() *Registry {
    return &Registry{
        map[string]reflect.Type{},
        map[reflect.Type]string{},
    }
}

// Register a new type by import name
func (r *Registry) Register(name string, i interface{}, reverse ...interface{}) error {
    if _, present := r.forward[name]; present {
        return fmt.Errorf("%q already registered", name)
    }

    r.forward[name] = reflect.TypeOf(i)

    var err error
    for _, rev := range append(reverse, i) {
        if err = r.RegisterReverse(rev, name); err != nil {
            break
        }
    }
    return err
}

// RegisterReverse registers a name in reverse
func (r *Registry) RegisterReverse(i interface{}, name string) error {
    t := reflect.TypeOf(i)

    if _, present := r.reverse[t]; present {
        return nil
    }

    r.reverse[t] = name
    return nil
}

// NewByName creates a new value by the name it was registered under. If no
// type was registered at the given name, the second value will be false
func (r *Registry) NewByName(name string) (interface{}, bool) {
    t, present := r.forward[name]
    if !present {
        return nil, false
    }

    var val reflect.Value
    if t.Kind() == reflect.Ptr {
        val = reflect.New(t.Elem())
    } else {
        val = reflect.New(t)
    }

    // send the interface...
    if val.CanInterface() {
        return val.Interface(), true
    }

    // ...but if we can't, return false
    return nil, false
}

// NameForType retrieves the name registered for a type. If no name was
// registered for the given type, the second value will be false
func (r *Registry) NameForType(i interface{}) (string, bool) {
    name, present := r.reverse[reflect.TypeOf(i)]
    return name, present
}

// package-global API
var registry *Registry

// Register a type in the global registry
func Register(name string, i interface{}, reverse ...interface{}) {
    if err := registry.Register(name, i, reverse...); err != nil {
        panic(err)
    }
}

// RegisterReverse registers a name in the global registry
func RegisterReverse(i interface{}, name string) {
    if err := registry.RegisterReverse(i, name); err != nil {
        panic(err)
    }
}

// NewByName creates a new value by the name it was registered under. If no
// type was registered at the given name, the second value will be false
func NewByName(name string) (interface{}, bool) {
    return registry.NewByName(name)
}

// NameForType retrieves the name registered for a type. If no name was
// registered for the given type, the second value will be false
func NameForType(i interface{}) (string, bool) {
    return registry.NameForType(i)
}

func init() {
    registry = New()
}