gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/config/context.go (about)

     1  package config
     2  
     3  import (
     4  	"gitee.com/sy_183/go-common/container"
     5  	"gitee.com/sy_183/go-common/lock"
     6  	"gitee.com/sy_183/go-common/log"
     7  	"gitee.com/sy_183/go-common/option"
     8  	"sync"
     9  	"sync/atomic"
    10  )
    11  
    12  type Option[C any] option.CustomOption[*Context[C]]
    13  
    14  func ErrorCallback[C any](callback func(err *Error)) Option[C] {
    15  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    16  		ctx.errorCallback = callback
    17  	})
    18  }
    19  
    20  func LoggerErrorCallback(logger *log.Logger) func(err *Error) {
    21  	return func(err *Error) {
    22  		switch err.Type {
    23  		case ParseError:
    24  			logger.Fatal("加载配置失败", log.Error(err))
    25  		case CheckError:
    26  			logger.Fatal("检查配置失败", log.Error(err))
    27  		case ReloadParseError:
    28  			logger.ErrorWith("重新加载配置失败", err)
    29  		case ReloadCheckError:
    30  			logger.ErrorWith("重新加载配置检查失败", err)
    31  		}
    32  	}
    33  }
    34  
    35  func AddBytes[C any](bs []byte, typ Type) Option[C] {
    36  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    37  		ctx.AddBytes(bs, typ)
    38  	})
    39  }
    40  
    41  func SetBytes[C any](bs []byte, typ Type) Option[C] {
    42  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    43  		ctx.SetBytes(bs, typ)
    44  	})
    45  }
    46  
    47  func AddFile[C any](path string, typ *Type) Option[C] {
    48  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    49  		ctx.AddFile(path, typ)
    50  	})
    51  }
    52  
    53  func SetFile[C any](path string, typ *Type) Option[C] {
    54  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    55  		ctx.SetFile(path, typ)
    56  	})
    57  }
    58  
    59  func AddFilePrefix[C any](prefix string, types ...Type) Option[C] {
    60  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    61  		ctx.AddFilePrefix(prefix, types...)
    62  	})
    63  }
    64  
    65  func SetFilePrefix[C any](prefix string, types ...Type) Option[C] {
    66  	return option.Custom[*Context[C]](func(ctx *Context[C]) {
    67  		ctx.SetFilePrefix(prefix, types...)
    68  	})
    69  }
    70  
    71  type (
    72  	OnConfigReloaded[C any]    func(oc, nc *C)
    73  	ConfigReloadChecker[C any] func(oc, nc *C) error
    74  )
    75  
    76  type Context[C any] struct {
    77  	Parser
    78  
    79  	newConfig   func() *C
    80  	config      atomic.Pointer[C]
    81  	initializer sync.Once
    82  
    83  	errorCallback func(err *Error)
    84  
    85  	configReloadedCallbackId atomic.Uint64
    86  	configReloadCheckerId    atomic.Uint64
    87  
    88  	configReloadedCallbacks       *container.LinkedMap[uint64, OnConfigReloaded[C]]
    89  	configReloadedCallbacksLocker sync.Mutex
    90  	configReloadCheckers          *container.LinkedMap[uint64, ConfigReloadChecker[C]]
    91  	configReloadCheckersLocker    sync.Mutex
    92  }
    93  
    94  func NewContext[C any](newConfig func() *C, options ...Option[C]) *Context[C] {
    95  	ctx := &Context[C]{
    96  		newConfig:               newConfig,
    97  		configReloadedCallbacks: container.NewLinkedMap[uint64, OnConfigReloaded[C]](0, 0),
    98  		configReloadCheckers:    container.NewLinkedMap[uint64, ConfigReloadChecker[C]](0, 0),
    99  	}
   100  	for _, opt := range options {
   101  		opt.Apply(ctx)
   102  	}
   103  	return ctx
   104  }
   105  
   106  func (c *Context[C]) RegisterConfigReloadedCallback(callback OnConfigReloaded[C]) uint64 {
   107  	return lock.LockGet(&c.configReloadedCallbacksLocker, func() uint64 {
   108  		id := c.configReloadedCallbackId.Add(1)
   109  		c.configReloadedCallbacks.LoadOrStore(id, callback)
   110  		return id
   111  	})
   112  }
   113  
   114  func (c *Context[C]) UnregisterConfigReloadedCallback(id uint64) {
   115  	lock.LockDo(&c.configReloadedCallbacksLocker, func() {
   116  		c.configReloadedCallbacks.Remove(id)
   117  	})
   118  }
   119  
   120  func (c *Context[C]) RegisterConfigReloadChecker(checker ConfigReloadChecker[C]) uint64 {
   121  	return lock.LockGet(&c.configReloadCheckersLocker, func() uint64 {
   122  		id := c.configReloadCheckerId.Add(1)
   123  		c.configReloadCheckers.LoadOrStore(id, checker)
   124  		return id
   125  	})
   126  }
   127  
   128  func (c *Context[C]) UnregisterConfigReloadChecker(id uint64) {
   129  	lock.LockDo(&c.configReloadCheckersLocker, func() {
   130  		c.configReloadCheckers.Remove(id)
   131  	})
   132  }
   133  
   134  func (c *Context[C]) configReloaded(oc, nc *C) {
   135  	lock.LockDo(&c.configReloadedCallbacksLocker, func() {
   136  		for entry := c.configReloadedCallbacks.HeadEntry(); entry != nil; entry = entry.Next() {
   137  			entry.Value()(oc, nc)
   138  		}
   139  	})
   140  }
   141  
   142  func (c *Context[C]) configReloadCheck(oc, nc *C) error {
   143  	return lock.LockGet(&c.configReloadCheckersLocker, func() error {
   144  		for entry := c.configReloadCheckers.HeadEntry(); entry != nil; entry = entry.Next() {
   145  			if err := entry.Value()(oc, nc); err != nil {
   146  				return err
   147  			}
   148  		}
   149  		return nil
   150  	})
   151  }
   152  
   153  func (c *Context[C]) Config() *C {
   154  	return c.config.Load()
   155  }
   156  
   157  func (c *Context[C]) InitConfig() error {
   158  	return c.ReloadConfig()
   159  }
   160  
   161  func (c *Context[C]) ReloadConfig() error {
   162  	oc := c.Config()
   163  	var nc *C
   164  	if c.newConfig == nil {
   165  		nc = new(C)
   166  	} else {
   167  		nc = c.newConfig()
   168  	}
   169  	if err := c.Parser.Unmarshal(nc); err != nil {
   170  		return err
   171  	}
   172  	if err := c.configReloadCheck(oc, nc); err != nil {
   173  		return err
   174  	}
   175  	c.config.Store(nc)
   176  	c.configReloaded(oc, nc)
   177  	return nil
   178  }