github.com/slspeek/camlistore_namedsearch@v0.0.0-20140519202248-ed6f70f7721a/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) RequiredInt64(key string) int64 { 178 return jc.int64(key, nil) 179 } 180 181 func (jc Obj) OptionalInt64(key string, def int64) int64 { 182 return jc.int64(key, &def) 183 } 184 185 func (jc Obj) int64(key string, def *int64) int64 { 186 jc.noteKnownKey(key) 187 ei, ok := jc[key] 188 if !ok { 189 if def != nil { 190 return *def 191 } 192 jc.appendError(fmt.Errorf("Missing required config key %q (integer)", key)) 193 return 0 194 } 195 b, ok := ei.(float64) 196 if !ok { 197 jc.appendError(fmt.Errorf("Expected config key %q to be a number", key)) 198 return 0 199 } 200 return int64(b) 201 } 202 203 func (jc Obj) RequiredList(key string) []string { 204 return jc.requiredList(key, true) 205 } 206 207 func (jc Obj) OptionalList(key string) []string { 208 return jc.requiredList(key, false) 209 } 210 211 func (jc Obj) requiredList(key string, required bool) []string { 212 jc.noteKnownKey(key) 213 ei, ok := jc[key] 214 if !ok { 215 if required { 216 jc.appendError(fmt.Errorf("Missing required config key %q (list of strings)", key)) 217 } 218 return nil 219 } 220 eil, ok := ei.([]interface{}) 221 if !ok { 222 jc.appendError(fmt.Errorf("Expected config key %q to be a list, not %T", key, ei)) 223 return nil 224 } 225 sl := make([]string, len(eil)) 226 for i, ei := range eil { 227 s, ok := ei.(string) 228 if !ok { 229 jc.appendError(fmt.Errorf("Expected config key %q index %d to be a string, not %T", key, i, ei)) 230 return nil 231 } 232 sl[i] = s 233 } 234 return sl 235 } 236 237 func (jc Obj) noteKnownKey(key string) { 238 _, ok := jc["_knownkeys"] 239 if !ok { 240 jc["_knownkeys"] = make(map[string]bool) 241 } 242 jc["_knownkeys"].(map[string]bool)[key] = true 243 } 244 245 func (jc Obj) appendError(err error) { 246 ei, ok := jc["_errors"] 247 if ok { 248 jc["_errors"] = append(ei.([]error), err) 249 } else { 250 jc["_errors"] = []error{err} 251 } 252 } 253 254 // UnknownKeys returns the keys from the config that have not yet been discovered by one of the RequiredT or OptionalT calls. 255 func (jc Obj) UnknownKeys() []string { 256 ei, ok := jc["_knownkeys"] 257 var known map[string]bool 258 if ok { 259 known = ei.(map[string]bool) 260 } 261 var unknown []string 262 for k, _ := range jc { 263 if ok && known[k] { 264 continue 265 } 266 if strings.HasPrefix(k, "_") { 267 // Permit keys with a leading underscore as a 268 // form of comments. 269 continue 270 } 271 unknown = append(unknown, k) 272 } 273 sort.Strings(unknown) 274 return unknown 275 } 276 277 func (jc Obj) Validate() error { 278 unknown := jc.UnknownKeys() 279 for _, k := range unknown { 280 jc.appendError(fmt.Errorf("Unknown key %q", k)) 281 } 282 283 ei, ok := jc["_errors"] 284 if !ok { 285 return nil 286 } 287 errList := ei.([]error) 288 if len(errList) == 1 { 289 return errList[0] 290 } 291 strs := make([]string, 0) 292 for _, v := range errList { 293 strs = append(strs, v.Error()) 294 } 295 return fmt.Errorf("Multiple errors: " + strings.Join(strs, ", ")) 296 }