resolver.go

 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}