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 switch t.(type) {
28 case T:
29 val, err := resolver.(Resolver).Resolve(id)
30 if err != nil {
31 return zero, err
32 }
33 return val.(T), nil
34 }
35 }
36 return zero, fmt.Errorf("unknown type to resolve")
37}
38
39var _ Resolver = &CachedResolver{}
40
41// CachedResolver is a resolver ensuring that loading is done only once through another Resolver.
42type CachedResolver struct {
43 resolver Resolver
44 mu sync.RWMutex
45 entities map[Id]Resolved
46}
47
48func NewCachedResolver(resolver Resolver) *CachedResolver {
49 return &CachedResolver{
50 resolver: resolver,
51 entities: make(map[Id]Resolved),
52 }
53}
54
55func (c *CachedResolver) Resolve(id Id) (Resolved, error) {
56 c.mu.RLock()
57 if i, ok := c.entities[id]; ok {
58 c.mu.RUnlock()
59 return i, nil
60 }
61 c.mu.RUnlock()
62
63 c.mu.Lock()
64 defer c.mu.Unlock()
65
66 i, err := c.resolver.Resolve(id)
67 if err != nil {
68 return nil, err
69 }
70 c.entities[id] = i
71 return i, nil
72}
73
74var _ Resolver = ResolverFunc[Resolved](nil)
75
76// ResolverFunc is a helper to morph a function resolver into a Resolver
77type ResolverFunc[EntityT Resolved] func(id Id) (EntityT, error)
78
79func (fn ResolverFunc[EntityT]) Resolve(id Id) (Resolved, error) {
80 return fn(id)
81}
82
83// MakeResolver create a resolver able to return the given entities.
84func MakeResolver(entities ...Resolved) Resolver {
85 return ResolverFunc[Resolved](func(id Id) (Resolved, error) {
86 for _, entity := range entities {
87 if entity.Id() == id {
88 return entity, nil
89 }
90 }
91 return nil, fmt.Errorf("entity not found")
92 })
93}