2024-04-03 17:03:45 +00:00
|
|
|
// Package fieldcollection contains a map[string]any with accessor
|
|
|
|
// methods to derive them into different formats
|
2021-11-20 20:30:58 +00:00
|
|
|
package fieldcollection
|
|
|
|
|
|
|
|
import (
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
2024-04-03 17:03:45 +00:00
|
|
|
// ErrValueNotSet signalizes the value does not exist in the map
|
|
|
|
ErrValueNotSet = errors.New("specified value not found")
|
|
|
|
// ErrValueMismatch signalizes the value has a different data type
|
2021-11-20 20:30:58 +00:00
|
|
|
ErrValueMismatch = errors.New("specified value has different format")
|
|
|
|
)
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
type (
|
|
|
|
// FieldCollection holds a map with integrated locking and can
|
|
|
|
// therefore used in multiple Go-routines concurrently
|
|
|
|
FieldCollection struct {
|
|
|
|
data map[string]any
|
|
|
|
lock sync.RWMutex
|
|
|
|
}
|
|
|
|
)
|
2021-11-20 20:30:58 +00:00
|
|
|
|
|
|
|
// NewFieldCollection creates a new FieldCollection with empty data store
|
|
|
|
func NewFieldCollection() *FieldCollection {
|
2024-04-03 17:03:45 +00:00
|
|
|
return &FieldCollection{data: make(map[string]any)}
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FieldCollectionFromData is a wrapper around NewFieldCollection and SetFromData
|
2024-04-03 17:03:45 +00:00
|
|
|
//
|
|
|
|
//revive:disable-next-line:exported
|
|
|
|
func FieldCollectionFromData(data map[string]any) *FieldCollection {
|
2021-11-20 20:30:58 +00:00
|
|
|
o := NewFieldCollection()
|
|
|
|
o.SetFromData(data)
|
|
|
|
return o
|
|
|
|
}
|
|
|
|
|
|
|
|
// Clone is a wrapper around n.SetFromData(o.Data())
|
|
|
|
func (f *FieldCollection) Clone() *FieldCollection {
|
|
|
|
out := new(FieldCollection)
|
|
|
|
out.SetFromData(f.Data())
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Data creates a map-copy of the data stored inside the FieldCollection
|
2024-04-03 17:03:45 +00:00
|
|
|
func (f *FieldCollection) Data() map[string]any {
|
2021-11-20 20:30:58 +00:00
|
|
|
if f == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
f.lock.RLock()
|
|
|
|
defer f.lock.RUnlock()
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
out := make(map[string]any)
|
2021-11-20 20:30:58 +00:00
|
|
|
for k := range f.data {
|
|
|
|
out[k] = f.data[k]
|
|
|
|
}
|
|
|
|
|
|
|
|
return out
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expect takes a list of keys and returns an error with all non-found names
|
|
|
|
func (f *FieldCollection) Expect(keys ...string) error {
|
|
|
|
if len(keys) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if f == nil || f.data == nil {
|
|
|
|
return errors.New("uninitialized field collection")
|
|
|
|
}
|
|
|
|
|
|
|
|
f.lock.RLock()
|
|
|
|
defer f.lock.RUnlock()
|
|
|
|
|
|
|
|
var missing []string
|
|
|
|
|
|
|
|
for _, k := range keys {
|
|
|
|
if _, ok := f.data[k]; !ok {
|
|
|
|
missing = append(missing, k)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(missing) > 0 {
|
|
|
|
return errors.Errorf("missing key(s) %s", strings.Join(missing, ", "))
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasAll takes a list of keys and returns whether all of them exist inside the FieldCollection
|
|
|
|
func (f *FieldCollection) HasAll(keys ...string) bool {
|
|
|
|
return f.Expect(keys...) == nil
|
|
|
|
}
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
// Get retrieves the value of a key as "any" type or returns an error
|
|
|
|
// in case the field is not set
|
|
|
|
func (f *FieldCollection) Get(name string) (any, error) {
|
2021-11-20 20:30:58 +00:00
|
|
|
if f == nil || f.data == nil {
|
2024-04-03 17:03:45 +00:00
|
|
|
return nil, errors.New("uninitialized field collection")
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f.lock.RLock()
|
|
|
|
defer f.lock.RUnlock()
|
|
|
|
|
|
|
|
v, ok := f.data[name]
|
|
|
|
if !ok {
|
2024-04-03 17:03:45 +00:00
|
|
|
return nil, ErrValueNotSet
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
return v, nil
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
// Keys returns a list of all known keys
|
|
|
|
func (f *FieldCollection) Keys() (keys []string) {
|
2021-11-20 20:30:58 +00:00
|
|
|
f.lock.RLock()
|
|
|
|
defer f.lock.RUnlock()
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
for k := range f.data {
|
|
|
|
keys = append(keys, k)
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
2024-04-03 17:03:45 +00:00
|
|
|
return keys
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Set sets a single key to specified value
|
2024-04-03 17:03:45 +00:00
|
|
|
func (f *FieldCollection) Set(key string, value any) {
|
2021-11-20 20:30:58 +00:00
|
|
|
f.lock.Lock()
|
|
|
|
defer f.lock.Unlock()
|
|
|
|
|
|
|
|
if f.data == nil {
|
2024-04-03 17:03:45 +00:00
|
|
|
f.data = make(map[string]any)
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
f.data[key] = value
|
|
|
|
}
|
|
|
|
|
|
|
|
// SetFromData takes a map of data and copies all data into the FieldCollection
|
2024-04-03 17:03:45 +00:00
|
|
|
func (f *FieldCollection) SetFromData(data map[string]any) {
|
2021-11-20 20:30:58 +00:00
|
|
|
f.lock.Lock()
|
|
|
|
defer f.lock.Unlock()
|
|
|
|
|
|
|
|
if f.data == nil {
|
2024-04-03 17:03:45 +00:00
|
|
|
f.data = make(map[string]any)
|
2021-11-20 20:30:58 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for key, value := range data {
|
|
|
|
f.data[key] = value
|
|
|
|
}
|
|
|
|
}
|