github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/pkg/jsonconfig/jsonconfig.go (about) 1 /* 2 Copyright 2011 Google Inc. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package jsonconfig defines a helper type for JSON objects to be 18 // used for configuration. 19 package jsonconfig 20 21 import ( 22 "fmt" 23 "sort" 24 "strconv" 25 "strings" 26 ) 27 28 // Obj is a JSON configuration map. 29 type Obj map[string]interface{} 30 31 // Reads json config data from the specified open file, expanding 32 // all expressions 33 func ReadFile(configPath string) (Obj, error) { 34 var c ConfigParser 35 return c.ReadFile(configPath) 36 } 37 38 func (jc Obj) RequiredObject(key string) Obj { 39 return jc.obj(key, false) 40 } 41 42 func (jc Obj) OptionalObject(key string) Obj { 43 return jc.obj(key, true) 44 } 45 46 func (jc Obj) obj(key string, optional bool) Obj { 47 jc.noteKnownKey(key) 48 ei, ok := jc[key] 49 if !ok { 50 if optional { 51 return make(Obj) 52 } 53 jc.appendError(fmt.Errorf("Missing required config key %q (object)", key)) 54 return make(Obj) 55 } 56 m, ok := ei.(map[string]interface{}) 57 if !ok { 58 jc.appendError(fmt.Errorf("Expected config key %q to be an object, not %T", key, ei)) 59 return make(Obj) 60 } 61 return Obj(m) 62 } 63 64 func (jc Obj) RequiredString(key string) string { 65 return jc.string(key, nil) 66 } 67 68 func (jc Obj) OptionalString(key, def string) string { 69 return jc.string(key, &def) 70 } 71 72 func (jc Obj) string(key string, def *string) string { 73 jc.noteKnownKey(key) 74 ei, ok := jc[key] 75 if !ok { 76 if def != nil { 77 return *def 78 } 79 jc.appendError(fmt.Errorf("Missing required config key %q (string)", key)) 80 return "" 81 } 82 s, ok := ei.(string) 83 if !ok { 84 jc.appendError(fmt.Errorf("Expected config key %q to be a string", key)) 85 return "" 86 } 87 return s 88 } 89 90 func (jc Obj) RequiredStringOrObject(key string) interface{} { 91 return jc.stringOrObject(key, true) 92 } 93 94 func (jc Obj) OptionalStringOrObject(key string) interface{} { 95 return jc.stringOrObject(key, false) 96 } 97 98 func (jc Obj) stringOrObject(key string, required bool) interface{} { 99 jc.noteKnownKey(key) 100 ei, ok := jc[key] 101 if !ok { 102 if !required { 103 return nil 104 } 105 jc.appendError(fmt.Errorf("Missing required config key %q (string or object)", key)) 106 return "" 107 } 108 if _, ok := ei.(map[string]interface{}); ok { 109 return ei 110 } 111 if _, ok := ei.(string); ok { 112 return ei 113 } 114 jc.appendError(fmt.Errorf("Expected config key %q to be a string or object", key)) 115 return "" 116 } 117 118 func (jc Obj) RequiredBool(key string) bool { 119 return jc.bool(key, nil) 120 } 121 122 func (jc Obj) OptionalBool(key string, def bool) bool { 123 return jc.bool(key, &def) 124 } 125 126 func (jc Obj) bool(key string, def *bool) bool { 127 jc.noteKnownKey(key) 128 ei, ok := jc[key] 129 if !ok { 130 if def != nil { 131 return *def 132 } 133 jc.appendError(fmt.Errorf("Missing required config key %q (boolean)", key)) 134 return false 135 } 136 switch v := ei.(type) { 137 case bool: 138 return v 139 case string: 140 b, err := strconv.ParseBool(v) 141 if err != nil { 142 jc.appendError(fmt.Errorf("Config key %q has bad boolean format %q", key, v)) 143 } 144 return b 145 default: 146 jc.appendError(fmt.Errorf("Expected config key %q to be a boolean", key)) 147 return false 148 } 149 } 150 151 func (jc Obj) RequiredInt(key string) int { 152 return jc.int(key, nil) 153 } 154 155 func (jc Obj) OptionalInt(key string, def int) int { 156 return jc.int(key, &def) 157 } 158 159 func (jc Obj) int(key string, def *int) int { 160 jc.noteKnownKey(key) 161 ei, ok := jc[key] 162 if !ok { 163 if def != nil { 164 return *def 165 } 166 jc.appendError(fmt.Errorf("Missing required config key %q (integer)", key)) 167 return 0 168 } 169 b, ok := ei.(float64) 170 if !ok { 171 jc.appendError(fmt.Errorf("Expected config key %q to be a number", key)) 172 return 0 173 } 174 return int(b) 175 } 176 177 func (jc Obj) RequiredList(key string) []string { 178 return jc.requiredList(key, true) 179 } 180 181 func (jc Obj) OptionalList(key string) []string { 182 return jc.requiredList(key, false) 183 } 184 185 func (jc Obj) requiredList(key string, required bool) []string { 186 jc.noteKnownKey(key) 187 ei, ok := jc[key] 188 if !ok { 189 if required { 190 jc.appendError(fmt.Errorf("Missing required config key %q (list of strings)", key)) 191 } 192 return nil 193 } 194 eil, ok := ei.([]interface{}) 195 if !ok { 196 jc.appendError(fmt.Errorf("Expected config key %q to be a list, not %T", key, ei)) 197 return nil 198 } 199 sl := make([]string, len(eil)) 200 for i, ei := range eil { 201 s, ok := ei.(string) 202 if !ok { 203 jc.appendError(fmt.Errorf("Expected config key %q index %d to be a string, not %T", key, i, ei)) 204 return nil 205 } 206 sl[i] = s 207 } 208 return sl 209 } 210 211 func (jc Obj) noteKnownKey(key string) { 212 _, ok := jc["_knownkeys"] 213 if !ok { 214 jc["_knownkeys"] = make(map[string]bool) 215 } 216 jc["_knownkeys"].(map[string]bool)[key] = true 217 } 218 219 func (jc Obj) appendError(err error) { 220 ei, ok := jc["_errors"] 221 if ok { 222 jc["_errors"] = append(ei.([]error), err) 223 } else { 224 jc["_errors"] = []error{err} 225 } 226 } 227 228 // UnknownKeys returns the keys from the config that have not yet been discovered by one of the RequiredT or OptionalT calls. 229 func (jc Obj) UnknownKeys() []string { 230 ei, ok := jc["_knownkeys"] 231 var known map[string]bool 232 if ok { 233 known = ei.(map[string]bool) 234 } 235 var unknown []string 236 for k, _ := range jc { 237 if ok && known[k] { 238 continue 239 } 240 if strings.HasPrefix(k, "_") { 241 // Permit keys with a leading underscore as a 242 // form of comments. 243 continue 244 } 245 unknown = append(unknown, k) 246 } 247 sort.Strings(unknown) 248 return unknown 249 } 250 251 func (jc Obj) Validate() error { 252 unknown := jc.UnknownKeys() 253 for _, k := range unknown { 254 jc.appendError(fmt.Errorf("Unknown key %q", k)) 255 } 256 257 ei, ok := jc["_errors"] 258 if !ok { 259 return nil 260 } 261 errList := ei.([]error) 262 if len(errList) == 1 { 263 return errList[0] 264 } 265 strs := make([]string, 0) 266 for _, v := range errList { 267 strs = append(strs, v.Error()) 268 } 269 return fmt.Errorf("Multiple errors: " + strings.Join(strs, ", ")) 270 }