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() {}