github.com/blend/go-sdk@v1.20220411.3/logger/scopes.go (about) 1 /* 2 3 Copyright (c) 2022 - Present. Blend Labs, Inc. All rights reserved 4 Use of this source code is governed by a MIT license that can be found in the LICENSE file. 5 6 */ 7 8 package logger 9 10 import ( 11 "path/filepath" 12 "strings" 13 14 "github.com/blend/go-sdk/stringutil" 15 ) 16 17 // NewScopes yields a scope set from a list of scopes. 18 // 19 // Each scope should be path formatted, e.g. `foo/bar/baz` and can include 20 // wildcard segments for glob matching, e.g. `foo/*/baz`. 21 // 22 // A single `*` is interpretted as `All` and will match any scope path. 23 func NewScopes(scopes ...string) *Scopes { 24 scopeSet := &Scopes{ 25 scopes: make(map[string]bool), 26 } 27 for _, rawScope := range scopes { 28 parsedScope := strings.ToLower(strings.TrimSpace(rawScope)) 29 if parsedScope == ScopeAll { 30 scopeSet.all = true 31 continue 32 } 33 if strings.HasPrefix(parsedScope, "-") { 34 scopeSet.scopes[strings.TrimPrefix(parsedScope, "-")] = false 35 } else { 36 scopeSet.scopes[parsedScope] = true 37 } 38 } 39 return scopeSet 40 } 41 42 // ScopesAll returns a preset scopes with the all flag flipped. 43 func ScopesAll() *Scopes { 44 return &Scopes{scopes: make(map[string]bool), all: true} 45 } 46 47 // ScopesNone returns a preset empty scopes. 48 func ScopesNone() *Scopes { 49 return &Scopes{scopes: make(map[string]bool)} 50 } 51 52 // Scopes is a set of scopes. 53 type Scopes struct { 54 all bool 55 scopes map[string]bool 56 } 57 58 // Enable enables a set of scopes. 59 // 60 // The scopes should be given in filepath form, e.g. `foo/bar/*`. 61 func (s *Scopes) Enable(scopes ...string) { 62 for _, scope := range scopes { 63 s.scopes[strings.ToLower(strings.TrimSpace(scope))] = true 64 } 65 } 66 67 // Disable disables a set of scopes. 68 // 69 // The scopes should be given in filepath form, e.g. `foo/bar/*`. 70 func (s *Scopes) Disable(scopes ...string) { 71 for _, scope := range scopes { 72 s.scopes[strings.ToLower(strings.TrimSpace(scope))] = false 73 } 74 } 75 76 // SetAll flips the `all` bit on the flag set to true. 77 // 78 // Note: flags that are explicitly disabled will remain disabled. 79 func (s *Scopes) SetAll() { 80 s.all = true 81 } 82 83 // All returns if the all bit is flipped to true. 84 func (s *Scopes) All() bool { 85 return s.all 86 } 87 88 // SetNone disables the `all` bit, and empties the scopes 89 // set, resulting in calls to `None()` to return true. 90 // 91 // You should view this method as a way to reset or zero a scopes set. 92 func (s *Scopes) SetNone() { 93 s.all = false 94 s.scopes = make(map[string]bool) 95 } 96 97 // None returns if the all bit is set to false, and 98 // there are no scope specific overrides. 99 // 100 // It is functionally equivalent to an `IsZero()` method. 101 func (s *Scopes) None() bool { 102 return !s.all && len(s.scopes) == 0 103 } 104 105 // IsEnabled returns if a given logger scope is enabled. 106 func (s Scopes) IsEnabled(scopePath ...string) bool { 107 scopeJoined := filepath.Join(scopePath...) 108 if s.all { 109 // check if we explicitly disabled the scope 110 return !s.isScopeExplicitlyDisabled(scopeJoined) 111 } 112 // we treat empty scopes as `none` 113 if len(s.scopes) == 0 { 114 return false 115 } 116 // if there is no scope, assumed to pass 117 if len(scopeJoined) == 0 { 118 return true 119 } 120 // return if we either explicitly enabled or disabled the scope 121 // with path globs in the scopes map. 122 return s.isScopeEnabled(scopeJoined) 123 } 124 125 // String returns a string representation of the scopes. 126 func (s Scopes) String() string { 127 return strings.Join(s.Scopes(), ", ") 128 } 129 130 // Scopes returns an array of scopes. 131 func (s Scopes) Scopes() []string { 132 var scopes []string 133 if s.all { 134 scopes = []string{ScopeAll} 135 } 136 for key, enabled := range s.scopes { 137 if key != FlagAll { 138 if enabled { 139 if !s.all { 140 scopes = append(scopes, string(key)) 141 } 142 } else { 143 scopes = append(scopes, "-"+string(key)) 144 } 145 } 146 } 147 return scopes 148 } 149 150 // 151 // internal helpers 152 // 153 154 // isScopeEnabled returns if a scopePath is enabled strictly by 155 // a lookup to the underlying scopes map. 156 func (s Scopes) isScopeEnabled(scopePath string) bool { 157 for pattern, enabled := range s.scopes { 158 if s.matches(scopePath, pattern) { 159 return enabled 160 } 161 } 162 // no matching entry is a failure. 163 return false 164 } 165 166 // isScopeDisabled returns if a scopePath is explicitly disabled 167 // that is, has a matching glob in the scopes map that is set to false. 168 // 169 // it is differentiated from `isScopeEnabled` 170 func (s Scopes) isScopeExplicitlyDisabled(subj string) bool { 171 for pattern, enabled := range s.scopes { 172 if !enabled && s.matches(subj, pattern) { 173 return true 174 } 175 } 176 // if we didn't find a matching scope, assume it's not disabled 177 return false 178 } 179 180 func (s Scopes) matches(subj, pattern string) (output bool) { 181 output = stringutil.Glob(subj, pattern) 182 return 183 }