github.com/ijc/docker-app@v0.6.1-0.20181012090447-c7ca8bc483fb/types/settings/load.go (about)

     1  package settings
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"io/ioutil"
     8  
     9  	"github.com/docker/app/internal/yaml"
    10  	"github.com/pkg/errors"
    11  )
    12  
    13  // Load loads the given data in settings
    14  func Load(data []byte, ops ...func(*Options)) (Settings, error) {
    15  	options := &Options{}
    16  	for _, op := range ops {
    17  		op(options)
    18  	}
    19  
    20  	r := bytes.NewReader(data)
    21  	s := make(map[interface{}]interface{})
    22  	decoder := yaml.NewDecoder(r)
    23  	if err := decoder.Decode(&s); err != nil {
    24  		if err == io.EOF {
    25  			return Settings{}, nil
    26  		}
    27  		return nil, errors.Wrap(err, "failed to read settings")
    28  	}
    29  	converted, err := convertToStringKeysRecursive(s, "")
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  	settings := converted.(map[string]interface{})
    34  	if options.prefix != "" {
    35  		settings = map[string]interface{}{
    36  			options.prefix: settings,
    37  		}
    38  	}
    39  	return settings, nil
    40  }
    41  
    42  // LoadMultiple loads multiple data in settings
    43  func LoadMultiple(datas [][]byte, ops ...func(*Options)) (Settings, error) {
    44  	m := Settings(map[string]interface{}{})
    45  	for _, data := range datas {
    46  		settings, err := Load(data, ops...)
    47  		if err != nil {
    48  			return nil, err
    49  		}
    50  		m, err = Merge(m, settings)
    51  		if err != nil {
    52  			return nil, err
    53  		}
    54  	}
    55  	return m, nil
    56  }
    57  
    58  // LoadFile loads a file (path) in settings (i.e. flatten map)
    59  func LoadFile(path string, ops ...func(*Options)) (Settings, error) {
    60  	data, err := ioutil.ReadFile(path)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  	return Load(data, ops...)
    65  }
    66  
    67  // LoadFiles loads multiple path in settings, merging them.
    68  func LoadFiles(paths []string, ops ...func(*Options)) (Settings, error) {
    69  	m := Settings(map[string]interface{}{})
    70  	for _, path := range paths {
    71  		settings, err := LoadFile(path, ops...)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		m, err = Merge(m, settings)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  	}
    80  	return m, nil
    81  }
    82  
    83  // from cli
    84  func convertToStringKeysRecursive(value interface{}, keyPrefix string) (interface{}, error) {
    85  	if mapping, ok := value.(map[interface{}]interface{}); ok {
    86  		dict := make(map[string]interface{})
    87  		for key, entry := range mapping {
    88  			str, ok := key.(string)
    89  			if !ok {
    90  				return nil, formatInvalidKeyError(keyPrefix, key)
    91  			}
    92  			var newKeyPrefix string
    93  			if keyPrefix == "" {
    94  				newKeyPrefix = str
    95  			} else {
    96  				newKeyPrefix = fmt.Sprintf("%s.%s", keyPrefix, str)
    97  			}
    98  			convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
    99  			if err != nil {
   100  				return nil, err
   101  			}
   102  			dict[str] = convertedEntry
   103  		}
   104  		return dict, nil
   105  	}
   106  	if list, ok := value.([]interface{}); ok {
   107  		var convertedList []interface{}
   108  		for index, entry := range list {
   109  			newKeyPrefix := fmt.Sprintf("%s[%d]", keyPrefix, index)
   110  			convertedEntry, err := convertToStringKeysRecursive(entry, newKeyPrefix)
   111  			if err != nil {
   112  				return nil, err
   113  			}
   114  			convertedList = append(convertedList, convertedEntry)
   115  		}
   116  		return convertedList, nil
   117  	}
   118  	return value, nil
   119  }
   120  
   121  func formatInvalidKeyError(keyPrefix string, key interface{}) error {
   122  	var location string
   123  	if keyPrefix == "" {
   124  		location = "at top level"
   125  	} else {
   126  		location = fmt.Sprintf("in %s", keyPrefix)
   127  	}
   128  	return errors.Errorf("Non-string key %s: %#v", location, key)
   129  }