github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/utils/config/file_config.go (about)

     1  // Copyright 2019 Dolthub, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package config
    16  
    17  import (
    18  	"encoding/json"
    19  	"errors"
    20  	"os"
    21  	"path/filepath"
    22  
    23  	"github.com/dolthub/dolt/go/libraries/utils/filesys"
    24  )
    25  
    26  // FileConfig is backed by a file in the filesystem.
    27  type FileConfig struct {
    28  	// The path of the config file in the filesystem
    29  	Path string
    30  
    31  	fs         filesys.ReadWriteFS
    32  	properties map[string]string
    33  }
    34  
    35  var _ ReadableConfig = &FileConfig{}
    36  var _ WritableConfig = &FileConfig{}
    37  var _ ReadWriteConfig = &FileConfig{}
    38  
    39  // NewFileConfig creates a new empty config and writes it to a newly created file.  If a file already exists at this
    40  // location it will be overwritten. If a directory does not exist where this file should live, it will be created.
    41  func NewFileConfig(path string, fs filesys.ReadWriteFS, properties map[string]string) (*FileConfig, error) {
    42  	dir := filepath.Dir(path)
    43  	err := fs.MkDirs(dir)
    44  
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	fc := &FileConfig{path, fs, properties}
    50  	err = fc.write()
    51  
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	return fc, err
    57  }
    58  
    59  // FromFile reads configuration from a file on the given filesystem.  Calls to SetStrings will result in this file
    60  // being updated.
    61  func FromFile(path string, fs filesys.ReadWriteFS) (*FileConfig, error) {
    62  	data, err := fs.ReadFile(path)
    63  
    64  	if err != nil {
    65  		return nil, err
    66  	}
    67  
    68  	properties := make(map[string]string)
    69  	err = json.Unmarshal(data, &properties)
    70  
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return &FileConfig{path, fs, properties}, nil
    76  }
    77  
    78  // GetString retrieves a string from the cached config state
    79  func (fc *FileConfig) GetString(k string) (string, error) {
    80  	if val, ok := fc.properties[k]; ok {
    81  		return val, nil
    82  	}
    83  
    84  	return "", ErrConfigParamNotFound
    85  }
    86  
    87  // GetString retrieves a string from the cached config state
    88  func (fc *FileConfig) GetStringOrDefault(k, defStr string) string {
    89  	if val, ok := fc.properties[k]; ok {
    90  		return val
    91  	}
    92  
    93  	return defStr
    94  }
    95  
    96  // SetStrings will set the value of configuration parameters in memory, and persist any changes to the backing file.
    97  func (fc *FileConfig) SetStrings(updates map[string]string) error {
    98  	modified := false
    99  	for k, v := range updates {
   100  		if val, ok := fc.properties[k]; !ok || val != v {
   101  			fc.properties[k] = v
   102  			modified = true
   103  		}
   104  	}
   105  
   106  	if modified == false {
   107  		return nil
   108  	}
   109  
   110  	return fc.write()
   111  }
   112  
   113  // Iter will perform a callback for ech value in a config until all values have been exhausted or until the
   114  // callback returns true indicating that it should stop.
   115  func (fc *FileConfig) Iter(cb func(string, string) (stop bool)) {
   116  	for k, v := range fc.properties {
   117  		stop := cb(k, v)
   118  
   119  		if stop {
   120  			break
   121  		}
   122  	}
   123  }
   124  
   125  func (fc *FileConfig) write() error {
   126  	data, err := json.Marshal(fc.properties)
   127  
   128  	if err != nil {
   129  		return err
   130  	}
   131  
   132  	return fc.fs.WriteFile(fc.Path, data, os.ModePerm)
   133  }
   134  
   135  // Unset removes a configuration parameter from the config
   136  func (fc *FileConfig) Unset(params []string) error {
   137  	for _, param := range params {
   138  		if _, ok := fc.properties[param]; !ok {
   139  			return errors.New("key does not exist on this configuration")
   140  		}
   141  		delete(fc.properties, param)
   142  
   143  	}
   144  
   145  	return fc.write()
   146  }
   147  
   148  // Size returns the number of properties contained within the config
   149  func (fc *FileConfig) Size() int {
   150  	return len(fc.properties)
   151  }