...
1 package internal
2
3 import (
4 "errors"
5 "fmt"
6 "sync"
7 )
8
9
10 var ErrNotSupported = errors.New("not supported")
11
12
13 type UnsupportedFeatureError struct {
14
15
16 MinimumVersion Version
17
18
19 Name string
20 }
21
22 func (ufe *UnsupportedFeatureError) Error() string {
23 if ufe.MinimumVersion.Unspecified() {
24 return fmt.Sprintf("%s not supported", ufe.Name)
25 }
26 return fmt.Sprintf("%s not supported (requires >= %s)", ufe.Name, ufe.MinimumVersion)
27 }
28
29
30 func (ufe *UnsupportedFeatureError) Is(target error) bool {
31 return target == ErrNotSupported
32 }
33
34 type featureTest struct {
35 sync.RWMutex
36 successful bool
37 result error
38 }
39
40
41
42
43
44
45
46
47
48 type FeatureTestFn func() error
49
50
51
52
53
54
55
56 func FeatureTest(name, version string, fn FeatureTestFn) func() error {
57 ft := new(featureTest)
58 return func() error {
59 ft.RLock()
60 if ft.successful {
61 defer ft.RUnlock()
62 return ft.result
63 }
64 ft.RUnlock()
65 ft.Lock()
66 defer ft.Unlock()
67
68
69
70
71 if ft.successful {
72 return ft.result
73 }
74 err := fn()
75 switch {
76 case errors.Is(err, ErrNotSupported):
77 v, err := NewVersion(version)
78 if err != nil {
79 return err
80 }
81
82 ft.result = &UnsupportedFeatureError{
83 MinimumVersion: v,
84 Name: name,
85 }
86 fallthrough
87
88 case err == nil:
89 ft.successful = true
90
91 default:
92
93
94
95 return fmt.Errorf("detect support for %s: %w", name, err)
96 }
97
98 return ft.result
99 }
100 }
101
View as plain text