1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
package errorx
import "fmt"
// Namespace is a way go group a number of error types together, and each error type belongs to exactly one namespace.
// Namespaces may form hierarchy, with child namespaces inheriting the traits and modifiers of a parent.
// Those modifiers and traits are then passed upon all error types in the namespace.
// In formatting, a dot notation is used, for example:
//
// namespace.sub_namespace.type.subtype
//
type Namespace struct {
parent *Namespace
id uint64
name string
traits []Trait
modifiers modifiers
}
// NamespaceKey is a comparable descriptor of a Namespace.
type NamespaceKey struct {
id uint64
}
// NewNamespace defines a namespace with a name and, optionally, a number of inheritable traits.
func NewNamespace(name string, traits ...Trait) Namespace {
namespace := newNamespace(nil, name, traits...)
globalRegistry.registerNamespace(namespace)
return namespace
}
// NewSubNamespace defines a child namespace that inherits all that is defined for a parent and, optionally, adds some more.
func (n Namespace) NewSubNamespace(name string, traits ...Trait) Namespace {
namespace := newNamespace(&n, name, traits...)
globalRegistry.registerNamespace(namespace)
return namespace
}
// ApplyModifiers makes a one-time modification of defaults in error creation.
func (n Namespace) ApplyModifiers(modifiers ...TypeModifier) Namespace {
n.modifiers = n.modifiers.ReplaceWith(newTypeModifiers(modifiers...))
return n
}
// NewType creates a new type within a namespace that inherits all that is defined for namespace and, optionally, adds some more.
func (n Namespace) NewType(typeName string, traits ...Trait) *Type {
return NewType(n, typeName, traits...)
}
// Key returns a comparison key for namespace.
func (n Namespace) Key() NamespaceKey {
return NamespaceKey{
id: n.id,
}
}
// IsNamespaceOf checks whether or not an error belongs either to this namespace or some of its sub-namespaces.
func (n Namespace) IsNamespaceOf(t *Type) bool {
namespace := t.namespace
other := &namespace
for other != nil {
if n.Key() == other.Key() {
return true
}
other = other.parent
}
return false
}
// FullName returns a full name of a namespace.
func (n Namespace) FullName() string {
return n.name
}
func (n Namespace) String() string {
return n.name
}
// Parent returns the immediate parent namespace, if present.
// The use of this function outside of a system layer that handles error types (see TypeSubscriber) is a code smell.
func (n Namespace) Parent() *Namespace {
return n.parent
}
func (n Namespace) collectTraits() map[Trait]bool {
result := make(map[Trait]bool)
namespace := &n
for namespace != nil {
for _, trait := range namespace.traits {
result[trait] = true
}
namespace = namespace.parent
}
return result
}
func newNamespace(parent *Namespace, name string, traits ...Trait) Namespace {
createName := func() string {
if parent == nil {
return name
}
return fmt.Sprintf("%s.%s", parent.FullName(), name)
}
createModifiers := func() modifiers {
if parent == nil {
return noModifiers{}
}
return newInheritedModifiers(parent.modifiers)
}
namespace := Namespace{
id: nextInternalID(),
parent: parent,
name: createName(),
traits: append([]Trait(nil), traits...),
modifiers: createModifiers(),
}
return namespace
}
|