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