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 }