code.gitea.io/gitea@v1.22.3/modules/setting/config/value.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package config
     5  
     6  import (
     7  	"context"
     8  	"sync"
     9  
    10  	"code.gitea.io/gitea/modules/json"
    11  	"code.gitea.io/gitea/modules/log"
    12  	"code.gitea.io/gitea/modules/util"
    13  )
    14  
    15  type CfgSecKey struct {
    16  	Sec, Key string
    17  }
    18  
    19  type Value[T any] struct {
    20  	mu sync.RWMutex
    21  
    22  	cfgSecKey CfgSecKey
    23  	dynKey    string
    24  
    25  	def, value T
    26  	revision   int
    27  }
    28  
    29  func (value *Value[T]) parse(key, valStr string) (v T) {
    30  	v = value.def
    31  	if valStr != "" {
    32  		if err := json.Unmarshal(util.UnsafeStringToBytes(valStr), &v); err != nil {
    33  			log.Error("Unable to unmarshal json config for key %q, err: %v", key, err)
    34  		}
    35  	}
    36  	return v
    37  }
    38  
    39  func (value *Value[T]) Value(ctx context.Context) (v T) {
    40  	dg := GetDynGetter()
    41  	if dg == nil {
    42  		// this is an edge case: the database is not initialized but the system setting is going to be used
    43  		// it should panic to avoid inconsistent config values (from config / system setting) and fix the code
    44  		panic("no config dyn value getter")
    45  	}
    46  
    47  	rev := dg.GetRevision(ctx)
    48  
    49  	// if the revision in database doesn't change, use the last value
    50  	value.mu.RLock()
    51  	if rev == value.revision {
    52  		v = value.value
    53  		value.mu.RUnlock()
    54  		return v
    55  	}
    56  	value.mu.RUnlock()
    57  
    58  	// try to parse the config and cache it
    59  	var valStr *string
    60  	if dynVal, has := dg.GetValue(ctx, value.dynKey); has {
    61  		valStr = &dynVal
    62  	} else if cfgVal, has := GetCfgSecKeyGetter().GetValue(value.cfgSecKey.Sec, value.cfgSecKey.Key); has {
    63  		valStr = &cfgVal
    64  	}
    65  	if valStr == nil {
    66  		v = value.def
    67  	} else {
    68  		v = value.parse(value.dynKey, *valStr)
    69  	}
    70  
    71  	value.mu.Lock()
    72  	value.value = v
    73  	value.revision = rev
    74  	value.mu.Unlock()
    75  	return v
    76  }
    77  
    78  func (value *Value[T]) DynKey() string {
    79  	return value.dynKey
    80  }
    81  
    82  func (value *Value[T]) WithDefault(def T) *Value[T] {
    83  	value.def = def
    84  	return value
    85  }
    86  
    87  func (value *Value[T]) WithFileConfig(cfgSecKey CfgSecKey) *Value[T] {
    88  	value.cfgSecKey = cfgSecKey
    89  	return value
    90  }
    91  
    92  func ValueJSON[T any](dynKey string) *Value[T] {
    93  	return &Value[T]{dynKey: dynKey}
    94  }