options.go

 1// Copyright (c) Microsoft Corporation.
 2// Licensed under the MIT license.
 3
 4package options
 5
 6import (
 7	"errors"
 8	"fmt"
 9)
10
11// CallOption implements an optional argument to a method call. See
12// https://blog.devgenius.io/go-call-option-that-can-be-used-with-multiple-methods-6c81734f3dbe
13// for an explanation of the usage pattern.
14type CallOption interface {
15	Do(any) error
16	callOption()
17}
18
19// ApplyOptions applies all the callOptions to options. options must be a pointer to a struct and
20// callOptions must be a list of objects that implement CallOption.
21func ApplyOptions[O, C any](options O, callOptions []C) error {
22	for _, o := range callOptions {
23		if t, ok := any(o).(CallOption); !ok {
24			return fmt.Errorf("unexpected option type %T", o)
25		} else if err := t.Do(options); err != nil {
26			return err
27		}
28	}
29	return nil
30}
31
32// NewCallOption returns a new CallOption whose Do() method calls function "f".
33func NewCallOption(f func(any) error) CallOption {
34	if f == nil {
35		// This isn't a practical concern because only an MSAL maintainer can get
36		// us here, by implementing a do-nothing option. But if someone does that,
37		// the below ensures the method invoked with the option returns an error.
38		return callOption(func(any) error {
39			return errors.New("invalid option: missing implementation")
40		})
41	}
42	return callOption(f)
43}
44
45// callOption is an adapter for a function to a CallOption
46type callOption func(any) error
47
48func (c callOption) Do(a any) error {
49	return c(a)
50}
51
52func (callOption) callOption() {}