1 // Copyright (c) Microsoft Corporation.
2 // Licensed under the MIT license.
3 4 package options
5 6 import (
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.
14 type 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.
21 func 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".
33 func 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
46 type callOption func(any) error
47 48 func (c callOption) Do(a any) error {
49 return c(a)
50 }
51 52 func (callOption) callOption() {}
53