resolver.go

 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}