package secrets import ( "context" "errors" "fmt" ) // ErrNotFound is returned when an ID could not be found. var ErrNotFound = errors.New("not found") // Store is used to persist and retrieve secrets. If a Store serializes a // payload it will do so using JSON. type Store interface { // Set stores the secret payload of the given ID. Set(context.Context, ID, any) error // Get retrieves the secret of the given ID, setting it into the given // pointer value, or returns ErrNotFound. Get(context.Context, any, ID) error } // GetSetFunctions returns a Get/Set function pair for the given ID and payload // type. func GetSetFunctions[T any]( id ID, ) ( func(context.Context, Store) (T, error), // Get func(context.Context, Store, T) error, // Set ) { var ( get = func(ctx context.Context, store Store) (T, error) { var v T err := store.Get(ctx, &v, id) return v, err } set = func(ctx context.Context, store Store, v T) error { return store.Set(ctx, id, v) } ) return get, set } // MultiSet will call Set on the given Store for every key-value pair in the // given map. func MultiSet(ctx context.Context, s Store, m map[ID]any) error { var errs []error for id, payload := range m { if err := s.Set(ctx, id, payload); err != nil { errs = append(errs, fmt.Errorf("setting payload for %q: %w", id, err)) } } return errors.Join(errs...) } // MultiGet will call Get on the given Store for every key-value pair in the // given map. Each value in the map must be a pointer receiver. func MultiGet(ctx context.Context, s Store, m map[ID]any) error { var errs []error for id, into := range m { if err := s.Get(ctx, into, id); err != nil { errs = append(errs, fmt.Errorf("getting payload for %q: %w", id, err)) } } return errors.Join(errs...) }