github.com/legenove/viper@v1.7.5/parsers.go (about)

     1  package viper
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"strings"
     9  
    10  	"github.com/hashicorp/hcl"
    11  	"github.com/hashicorp/hcl/hcl/printer"
    12  	jsoniter "github.com/json-iterator/go"
    13  	"github.com/magiconair/properties"
    14  	"github.com/pelletier/go-toml"
    15  	"github.com/spf13/afero"
    16  	"github.com/subosito/gotenv"
    17  	"gopkg.in/ini.v1"
    18  	"gopkg.in/yaml.v3"
    19  )
    20  
    21  var SupportedParsers map[string]Parser
    22  var SupportedExts map[string][]string
    23  
    24  type Parser interface {
    25  	UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error // Unmarshal a Reader into a map.
    26  	MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error   // Marshal a map into Writer.
    27  }
    28  
    29  // add support parser
    30  func AddParser(parser Parser, names ...string) {
    31  	for _, n := range names {
    32  		SupportedParsers[n] = parser
    33  		SupportedExts[n] = names
    34  	}
    35  }
    36  
    37  func parserInit() {
    38  	SupportedParsers = make(map[string]Parser, 32)
    39  	SupportedExts = make(map[string][]string, 32)
    40  	AddParser(&JSONParser{}, "json")
    41  	AddParser(&TOMLParser{}, "toml")
    42  	AddParser(&YAMLParser{}, "yaml", "yml")
    43  	AddParser(&PROPSParser{}, "properties", "props", "prop")
    44  	AddParser(&HCLParser{}, "hcl")
    45  	AddParser(&DOTENVParser{}, "dotenv", "env")
    46  	AddParser(&INIParser{}, "ini")
    47  }
    48  
    49  func parserReset() {
    50  	parserInit()
    51  }
    52  
    53  // json parser
    54  type JSONParser struct {
    55  }
    56  
    57  func (pp *JSONParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
    58  	buf := new(bytes.Buffer)
    59  	buf.ReadFrom(in)
    60  	return jsoniter.Unmarshal(buf.Bytes(), &c)
    61  }
    62  func (pp *JSONParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
    63  	b, err := json.MarshalIndent(c, "", "  ")
    64  	if err != nil {
    65  		return err
    66  	}
    67  	_, err = f.WriteString(string(b))
    68  	return err
    69  }
    70  
    71  // toml parser
    72  type TOMLParser struct {
    73  }
    74  
    75  func (pp *TOMLParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
    76  	buf := new(bytes.Buffer)
    77  	buf.ReadFrom(in)
    78  	tree, err := toml.LoadReader(buf)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	tmap := tree.ToMap()
    83  	for k, v := range tmap {
    84  		c[k] = v
    85  	}
    86  	return nil
    87  }
    88  func (pp *TOMLParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
    89  	t, err := toml.TreeFromMap(c)
    90  	if err != nil {
    91  		return err
    92  	}
    93  	s := t.String()
    94  	if _, err := f.WriteString(s); err != nil {
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  // yaml parser
   101  type YAMLParser struct {
   102  }
   103  
   104  func (pp *YAMLParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
   105  	buf := new(bytes.Buffer)
   106  	buf.ReadFrom(in)
   107  	return yaml.Unmarshal(buf.Bytes(), &c)
   108  }
   109  func (pp *YAMLParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
   110  	b, err := yaml.Marshal(c)
   111  	if err != nil {
   112  		return err
   113  	}
   114  	if _, err = f.WriteString(string(b)); err != nil {
   115  		return err
   116  	}
   117  	return nil
   118  }
   119  
   120  // ini parser
   121  type INIParser struct {
   122  }
   123  
   124  func (pp *INIParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
   125  	buf := new(bytes.Buffer)
   126  	buf.ReadFrom(in)
   127  	cfg := ini.Empty()
   128  	err := cfg.Append(buf.Bytes())
   129  	if err != nil {
   130  		return err
   131  	}
   132  	sections := cfg.Sections()
   133  	for i := 0; i < len(sections); i++ {
   134  		section := sections[i]
   135  		keys := section.Keys()
   136  		for j := 0; j < len(keys); j++ {
   137  			key := keys[j]
   138  			value := cfg.Section(section.Name()).Key(key.Name()).String()
   139  			c[section.Name()+"."+key.Name()] = value
   140  		}
   141  	}
   142  	return nil
   143  }
   144  func (pp *INIParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
   145  	keys := v.AllKeys()
   146  	cfg := ini.Empty()
   147  	ini.PrettyFormat = false
   148  	for i := 0; i < len(keys); i++ {
   149  		key := keys[i]
   150  		lastSep := strings.LastIndex(key, ".")
   151  		sectionName := key[:(lastSep)]
   152  		keyName := key[(lastSep + 1):]
   153  		if sectionName == "default" {
   154  			sectionName = ""
   155  		}
   156  		cfg.Section(sectionName).Key(keyName).SetValue(v.Get(key).(string))
   157  	}
   158  	cfg.WriteTo(f)
   159  	return nil
   160  }
   161  
   162  // hcl parser
   163  type HCLParser struct {
   164  }
   165  
   166  func (pp *HCLParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
   167  	buf := new(bytes.Buffer)
   168  	buf.ReadFrom(in)
   169  
   170  	obj, err := hcl.Parse(buf.String())
   171  	if err != nil {
   172  		return err
   173  	}
   174  	if err = hcl.DecodeObject(&c, obj); err != nil {
   175  		return err
   176  	}
   177  	return nil
   178  }
   179  func (pp *HCLParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
   180  	b, err := json.Marshal(c)
   181  	if err != nil {
   182  		return err
   183  	}
   184  	ast, err := hcl.Parse(string(b))
   185  	if err != nil {
   186  		return err
   187  	}
   188  	err = printer.Fprint(f, ast.Node)
   189  	if err != nil {
   190  		return err
   191  	}
   192  	return nil
   193  }
   194  
   195  // dot env parser
   196  type DOTENVParser struct {
   197  }
   198  
   199  func (pp *DOTENVParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
   200  	buf := new(bytes.Buffer)
   201  	buf.ReadFrom(in)
   202  
   203  	env, err := gotenv.StrictParse(buf)
   204  	if err != nil {
   205  		return err
   206  	}
   207  	for k, v := range env {
   208  		c[k] = v
   209  	}
   210  
   211  	return nil
   212  }
   213  func (pp *DOTENVParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
   214  	lines := []string{}
   215  	for _, key := range v.AllKeys() {
   216  		envName := strings.ToUpper(strings.Replace(key, ".", "_", -1))
   217  		val := v.Get(key)
   218  		lines = append(lines, fmt.Sprintf("%v=%v", envName, val))
   219  	}
   220  	s := strings.Join(lines, "\n")
   221  	if _, err := f.WriteString(s); err != nil {
   222  		return err
   223  	}
   224  	return nil
   225  }
   226  
   227  // props parser
   228  type PROPSParser struct {
   229  }
   230  
   231  func (pp *PROPSParser) UnmarshalReader(v *Viper, in io.Reader, c map[string]interface{}) error {
   232  	buf := new(bytes.Buffer)
   233  	buf.ReadFrom(in)
   234  
   235  	v.properties = properties.NewProperties()
   236  	var err error
   237  	if v.properties, err = properties.Load(buf.Bytes(), properties.UTF8); err != nil {
   238  		return ConfigParseError{err}
   239  	}
   240  	for _, key := range v.properties.Keys() {
   241  		value, _ := v.properties.Get(key)
   242  		// recursively build nested maps
   243  		path := strings.Split(key, ".")
   244  		lastKey := strings.ToLower(path[len(path)-1])
   245  		deepestMap := deepSearch(c, path[0:len(path)-1])
   246  		// set innermost value
   247  		deepestMap[lastKey] = value
   248  	}
   249  	return nil
   250  }
   251  func (pp *PROPSParser) MarshalWriter(v *Viper, f afero.File, c map[string]interface{}) error {
   252  	if v.properties == nil {
   253  		v.properties = properties.NewProperties()
   254  	}
   255  	p := v.properties
   256  	for _, key := range v.AllKeys() {
   257  		_, _, err := p.Set(key, v.GetString(key))
   258  		if err != nil {
   259  			return err
   260  		}
   261  	}
   262  	_, err := p.WriteComment(f, "#", properties.UTF8)
   263  	if err != nil {
   264  		return err
   265  	}
   266  	return nil
   267  }