package entity import ( "fmt" "sync" ) // Resolved is a minimal interface on which Resolver operates on. // Notably, this operates on Entity and Excerpt in the cache. type Resolved interface { // Id returns the object identifier. Id() Id } // Resolver is an interface to find an Entity from its Id type Resolver interface { Resolve(id Id) (Resolved, error) } // Resolvers is a collection of Resolver, for different type of Entity type Resolvers map[Resolved]Resolver // Resolve use the appropriate sub-resolver for the given type and find the Entity matching the Id. func Resolve[T Resolved](rs Resolvers, id Id) (T, error) { var zero T for t, resolver := range rs { switch t.(type) { case T: val, err := resolver.(Resolver).Resolve(id) if err != nil { return zero, err } return val.(T), nil } } return zero, fmt.Errorf("unknown type to resolve") } var _ Resolver = &CachedResolver{} // CachedResolver is a resolver ensuring that loading is done only once through another Resolver. type CachedResolver struct { resolver Resolver mu sync.RWMutex entities map[Id]Resolved } func NewCachedResolver(resolver Resolver) *CachedResolver { return &CachedResolver{ resolver: resolver, entities: make(map[Id]Resolved), } } func (c *CachedResolver) Resolve(id Id) (Resolved, error) { c.mu.RLock() if i, ok := c.entities[id]; ok { c.mu.RUnlock() return i, nil } c.mu.RUnlock() c.mu.Lock() defer c.mu.Unlock() i, err := c.resolver.Resolve(id) if err != nil { return nil, err } c.entities[id] = i return i, nil } var _ Resolver = ResolverFunc[Resolved](nil) // ResolverFunc is a helper to morph a function resolver into a Resolver type ResolverFunc[EntityT Resolved] func(id Id) (EntityT, error) func (fn ResolverFunc[EntityT]) Resolve(id Id) (Resolved, error) { return fn(id) } // MakeResolver create a resolver able to return the given entities. func MakeResolver(entities ...Resolved) Resolver { return ResolverFunc[Resolved](func(id Id) (Resolved, error) { for _, entity := range entities { if entity.Id() == id { return entity, nil } } return nil, fmt.Errorf("entity not found") }) }