1package entity
2
3import (
4 "fmt"
5 "sync"
6)
7
8// Resolver is an interface to find an Entity from its Id
9type Resolver interface {
10 Resolve(id Id) (Interface, error)
11}
12
13// Resolvers is a collection of Resolver, for different type of Entity
14type Resolvers map[Interface]Resolver
15
16// Resolve use the appropriate sub-resolver for the given type and find the Entity matching the Id.
17func Resolve[T Interface](rs Resolvers, id Id) (T, error) {
18 var zero T
19 for t, resolver := range rs {
20 switch t.(type) {
21 case T:
22 val, err := resolver.(Resolver).Resolve(id)
23 if err != nil {
24 return zero, err
25 }
26 return val.(T), nil
27 }
28 }
29 return zero, fmt.Errorf("unknown type to resolve")
30}
31
32var _ Resolver = &CachedResolver{}
33
34// CachedResolver is a resolver ensuring that loading is done only once through another Resolver.
35type CachedResolver struct {
36 resolver Resolver
37 mu sync.RWMutex
38 entities map[Id]Interface
39}
40
41func NewCachedResolver(resolver Resolver) *CachedResolver {
42 return &CachedResolver{
43 resolver: resolver,
44 entities: make(map[Id]Interface),
45 }
46}
47
48func (c *CachedResolver) Resolve(id Id) (Interface, error) {
49 c.mu.RLock()
50 if i, ok := c.entities[id]; ok {
51 c.mu.RUnlock()
52 return i, nil
53 }
54 c.mu.RUnlock()
55
56 c.mu.Lock()
57 defer c.mu.Unlock()
58
59 i, err := c.resolver.Resolve(id)
60 if err != nil {
61 return nil, err
62 }
63 c.entities[id] = i
64 return i, nil
65}
66
67var _ Resolver = ResolverFunc(nil)
68
69// ResolverFunc is a helper to morph a function resolver into a Resolver
70type ResolverFunc func(id Id) (Interface, error)
71
72func (fn ResolverFunc) Resolve(id Id) (Interface, error) {
73 return fn(id)
74}