package entity import ( "fmt" "sync" ) // Resolver is an interface to find an Entity from its Id type Resolver interface { Resolve(id Id) (Interface, error) } // Resolvers is a collection of Resolver, for different type of Entity type Resolvers map[Interface]Resolver // Resolve use the appropriate sub-resolver for the given type and find the Entity matching the Id. func Resolve[T Interface](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]Interface } func NewCachedResolver(resolver Resolver) *CachedResolver { return &CachedResolver{ resolver: resolver, entities: make(map[Id]Interface), } } func (c *CachedResolver) Resolve(id Id) (Interface, 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(nil) // ResolverFunc is a helper to morph a function resolver into a Resolver type ResolverFunc func(id Id) (Interface, error) func (fn ResolverFunc) Resolve(id Id) (Interface, error) { return fn(id) } // MakeResolver create a resolver able to return the given entities. func MakeResolver(entities ...Interface) Resolver { return ResolverFunc(func(id Id) (Interface, error) { for _, entity := range entities { if entity.Id() == id { return entity, nil } } return nil, fmt.Errorf("entity not found") }) }