go-gun/gun/scoped.go

78 lines
2.0 KiB
Go
Raw Normal View History

2019-02-20 20:54:46 +00:00
package gun
2019-02-22 06:46:19 +00:00
import (
"context"
2019-02-22 09:23:14 +00:00
"errors"
2019-02-25 04:23:15 +00:00
"fmt"
2019-02-22 06:46:19 +00:00
"sync"
)
2019-02-20 20:54:46 +00:00
type Scoped struct {
2019-02-22 09:23:14 +00:00
gun *Gun
parent *Scoped
field string
cachedParentSoul string
cachedParentSoulLock sync.RWMutex
2019-02-22 06:46:19 +00:00
2019-02-25 04:23:15 +00:00
fetchResultListeners map[<-chan *FetchResult]*fetchResultListener
fetchResultListenersLock sync.Mutex
2019-02-20 20:54:46 +00:00
2019-02-25 04:23:15 +00:00
putResultListeners map[<-chan *PutResult]*putResultListener
putResultListenersLock sync.Mutex
2019-02-20 20:54:46 +00:00
}
2019-02-22 09:23:14 +00:00
func newScoped(gun *Gun, parent *Scoped, field string) *Scoped {
2019-02-22 06:46:19 +00:00
return &Scoped{
2019-02-25 04:23:15 +00:00
gun: gun,
parent: parent,
field: field,
fetchResultListeners: map[<-chan *FetchResult]*fetchResultListener{},
putResultListeners: map[<-chan *PutResult]*putResultListener{},
2019-02-22 06:46:19 +00:00
}
}
2019-02-22 09:23:14 +00:00
var ErrNotObject = errors.New("Scoped value not an object")
2019-02-25 04:23:15 +00:00
var ErrLookupOnTopLevel = errors.New("Cannot do put/lookup on top level")
2019-02-22 09:23:14 +00:00
// Empty string if doesn't exist, ErrNotObject if self or parent not an object
func (s *Scoped) Soul(ctx context.Context) (string, error) {
2019-02-25 04:23:15 +00:00
if cachedSoul := s.cachedSoul(); cachedSoul != "" {
return cachedSoul, nil
} else if r := s.FetchOne(ctx); r.Err != nil {
2019-02-22 21:40:55 +00:00
return "", r.Err
} else if !r.ValueExists {
2019-02-22 09:23:14 +00:00
return "", nil
2019-02-22 21:40:55 +00:00
} else if rel, ok := r.Value.(ValueRelation); !ok {
2019-02-22 09:23:14 +00:00
return "", ErrNotObject
2019-02-25 04:23:15 +00:00
} else if !s.setCachedSoul(rel) {
return "", fmt.Errorf("Concurrent soul cache set")
2019-02-22 09:23:14 +00:00
} else {
return string(rel), nil
}
2019-02-20 20:54:46 +00:00
}
2019-02-22 06:46:19 +00:00
2019-02-25 04:23:15 +00:00
func (s *Scoped) cachedSoul() string {
s.cachedParentSoulLock.RLock()
defer s.cachedParentSoulLock.RUnlock()
return s.cachedParentSoul
2019-02-22 06:46:19 +00:00
}
2019-02-25 04:23:15 +00:00
func (s *Scoped) setCachedSoul(val ValueRelation) bool {
s.cachedParentSoulLock.Lock()
defer s.cachedParentSoulLock.Unlock()
if s.cachedParentSoul != "" {
return false
2019-02-22 06:46:19 +00:00
}
2019-02-25 04:23:15 +00:00
s.cachedParentSoul = string(val)
return true
2019-02-22 06:46:19 +00:00
}
func (s *Scoped) Scoped(ctx context.Context, key string, children ...string) *Scoped {
2019-02-22 09:23:14 +00:00
ret := newScoped(s.gun, s, key)
for _, child := range children {
ret = newScoped(s.gun, ret, child)
}
return ret
2019-02-22 06:46:19 +00:00
}