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  }