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