github.com/abolfazlbeh/zhycan@v0.0.0-20230819144214-24cf38237387/internal/config/wrapper.go (about)

     1  package config
     2  
     3  import (
     4  	"bytes"
     5  	"github.com/fsnotify/fsnotify"
     6  	"github.com/spf13/viper"
     7  	"log"
     8  	"sync"
     9  	"time"
    10  )
    11  
    12  // MARK: ViperWrapper
    13  
    14  // ViperWrapper object
    15  type ViperWrapper struct {
    16  	Instance            *viper.Viper
    17  	ConfigPath          []string
    18  	ConfigName          string
    19  	ConfigEnvPrefix     string
    20  	ConfigResourcePlace string
    21  	lastModified        time.Time
    22  	wg                  sync.WaitGroup
    23  	lock                sync.Mutex
    24  }
    25  
    26  // MARK: Public Methods
    27  
    28  // Load - It creates new instance of Viper and load config file base on ConfigName
    29  func (w *ViperWrapper) Load() error {
    30  	w.wg.Add(1)
    31  	defer w.wg.Done()
    32  
    33  	w.Instance = viper.New()
    34  	for _, path := range w.ConfigPath {
    35  		w.Instance.AddConfigPath(path)
    36  	}
    37  	w.Instance.SetConfigName(w.ConfigName)
    38  	err := w.Instance.ReadInConfig()
    39  	if err != nil {
    40  		return err
    41  	}
    42  
    43  	// Get env variables and bind them if exist in config file
    44  	env, exist := w.Get("env", true)
    45  	if env != nil && exist {
    46  		envArray := make([]string, len(env.([]interface{})))
    47  		for i, v := range env.([]interface{}) {
    48  			envArray[i] = v.(string)
    49  		}
    50  
    51  		if len(envArray) > 0 {
    52  			w.Instance.SetEnvPrefix(w.ConfigEnvPrefix)
    53  
    54  			for _, e := range envArray {
    55  				_ = w.Instance.BindEnv(e)
    56  			}
    57  		}
    58  	}
    59  
    60  	return nil
    61  }
    62  
    63  // LoadFromRemote - loads the configs from the remote server
    64  func (w *ViperWrapper) LoadFromRemote(data []byte) error {
    65  	w.wg.Add(1)
    66  	defer w.wg.Done()
    67  
    68  	if w.Instance == nil {
    69  		w.Instance = viper.New()
    70  	}
    71  
    72  	err := w.Instance.ReadConfig(bytes.NewBuffer(data))
    73  	if err != nil {
    74  		return err
    75  	}
    76  
    77  	// Get env variables and bind them if exist in config file
    78  	env, exist := w.Get("env", true)
    79  	if env != nil && exist {
    80  		envArray := make([]string, len(env.([]interface{})))
    81  		for i, v := range env.([]interface{}) {
    82  			envArray[i] = v.(string)
    83  		}
    84  
    85  		if len(envArray) > 0 {
    86  			w.Instance.SetEnvPrefix(w.ConfigEnvPrefix)
    87  
    88  			for _, e := range envArray {
    89  				_ = w.Instance.BindEnv(e)
    90  			}
    91  		}
    92  	}
    93  
    94  	return nil
    95  }
    96  
    97  // RegisterChangeCallback - get function and call it when config file changed
    98  func (w *ViperWrapper) RegisterChangeCallback(fn func() interface{}) {
    99  	w.wg.Wait()
   100  
   101  	w.Instance.WatchConfig()
   102  	w.Instance.OnConfigChange(func(e fsnotify.Event) {
   103  		log.Println(w.ConfigName, "Config file changed: ", e.Name)
   104  
   105  		if fn != nil {
   106  			fn()
   107  		}
   108  	})
   109  }
   110  
   111  // Get method - returns value base on key
   112  func (w *ViperWrapper) Get(key string, bypass bool) (interface{}, bool) {
   113  	if !bypass {
   114  		w.wg.Wait()
   115  	}
   116  
   117  	w.lock.Lock()
   118  	defer w.lock.Unlock()
   119  
   120  	exist := w.Instance.InConfig(key)
   121  	return w.Instance.Get(key), exist
   122  }
   123  
   124  // Set method - set value by given key and write it back to file
   125  func (w *ViperWrapper) Set(key string, value interface{}, bypass bool) error {
   126  	if !bypass {
   127  		w.wg.Wait()
   128  	}
   129  
   130  	w.lock.Lock()
   131  	defer w.lock.Unlock()
   132  
   133  	w.Instance.Set(key, value)
   134  	return w.Instance.SafeWriteConfig()
   135  }