github.com/safing/portbase@v0.19.5/config/perspective.go (about) 1 package config 2 3 import ( 4 "fmt" 5 6 "github.com/safing/portbase/log" 7 ) 8 9 // Perspective is a view on configuration data without interfering with the configuration system. 10 type Perspective struct { 11 config map[string]*perspectiveOption 12 } 13 14 type perspectiveOption struct { 15 option *Option 16 valueCache *valueCache 17 } 18 19 // NewPerspective parses the given config and returns it as a new perspective. 20 func NewPerspective(config map[string]interface{}) (*Perspective, error) { 21 // flatten config structure 22 config = Flatten(config) 23 24 perspective := &Perspective{ 25 config: make(map[string]*perspectiveOption), 26 } 27 var firstErr error 28 var errCnt int 29 30 optionsLock.RLock() 31 optionsLoop: 32 for key, option := range options { 33 // get option key from config 34 configValue, ok := config[key] 35 if !ok { 36 continue 37 } 38 // migrate value 39 configValue = migrateValue(option, configValue) 40 // validate value 41 valueCache, err := validateValue(option, configValue) 42 if err != nil { 43 errCnt++ 44 if firstErr == nil { 45 firstErr = err 46 } 47 continue optionsLoop 48 } 49 50 // add to perspective 51 perspective.config[key] = &perspectiveOption{ 52 option: option, 53 valueCache: valueCache, 54 } 55 } 56 optionsLock.RUnlock() 57 58 if firstErr != nil { 59 if errCnt > 0 { 60 return perspective, fmt.Errorf("encountered %d errors, first was: %w", errCnt, firstErr) 61 } 62 return perspective, firstErr 63 } 64 65 return perspective, nil 66 } 67 68 func (p *Perspective) getPerspectiveValueCache(name string, requestedType OptionType) *valueCache { 69 // get option 70 pOption, ok := p.config[name] 71 if !ok { 72 // check if option exists at all 73 if _, err := GetOption(name); err != nil { 74 log.Errorf("config: request for unregistered option: %s", name) 75 } 76 return nil 77 } 78 79 // check type 80 if requestedType != pOption.option.OptType && requestedType != optTypeAny { 81 log.Errorf("config: bad type: requested %s as %s, but is %s", name, getTypeName(requestedType), getTypeName(pOption.option.OptType)) 82 return nil 83 } 84 85 // check release level 86 if pOption.option.ReleaseLevel > getReleaseLevel() { 87 return nil 88 } 89 90 return pOption.valueCache 91 } 92 93 // Has returns whether the given option is set in the perspective. 94 func (p *Perspective) Has(name string) bool { 95 valueCache := p.getPerspectiveValueCache(name, optTypeAny) 96 return valueCache != nil 97 } 98 99 // GetAsString returns a function that returns the wanted string with high performance. 100 func (p *Perspective) GetAsString(name string) (value string, ok bool) { 101 valueCache := p.getPerspectiveValueCache(name, OptTypeString) 102 if valueCache != nil { 103 return valueCache.stringVal, true 104 } 105 return "", false 106 } 107 108 // GetAsStringArray returns a function that returns the wanted string with high performance. 109 func (p *Perspective) GetAsStringArray(name string) (value []string, ok bool) { 110 valueCache := p.getPerspectiveValueCache(name, OptTypeStringArray) 111 if valueCache != nil { 112 return valueCache.stringArrayVal, true 113 } 114 return nil, false 115 } 116 117 // GetAsInt returns a function that returns the wanted int with high performance. 118 func (p *Perspective) GetAsInt(name string) (value int64, ok bool) { 119 valueCache := p.getPerspectiveValueCache(name, OptTypeInt) 120 if valueCache != nil { 121 return valueCache.intVal, true 122 } 123 return 0, false 124 } 125 126 // GetAsBool returns a function that returns the wanted int with high performance. 127 func (p *Perspective) GetAsBool(name string) (value bool, ok bool) { 128 valueCache := p.getPerspectiveValueCache(name, OptTypeBool) 129 if valueCache != nil { 130 return valueCache.boolVal, true 131 } 132 return false, false 133 }