1// Copyright 2014 Unknwon 2// 3// Licensed under the Apache License, Version 2.0 (the "License"): you may 4// not use this file except in compliance with the License. You may obtain 5// a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12// License for the specific language governing permissions and limitations 13// under the License. 14 15package ini 16 17import ( 18 "bytes" 19 "errors" 20 "fmt" 21 "reflect" 22 "strings" 23 "time" 24 "unicode" 25) 26 27// NameMapper represents a ini tag name mapper. 28type NameMapper func(string) string 29 30// Built-in name getters. 31var ( 32 // AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. 33 AllCapsUnderscore NameMapper = func(raw string) string { 34 newstr := make([]rune, 0, len(raw)) 35 for i, chr := range raw { 36 if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { 37 if i > 0 { 38 newstr = append(newstr, '_') 39 } 40 } 41 newstr = append(newstr, unicode.ToUpper(chr)) 42 } 43 return string(newstr) 44 } 45 // TitleUnderscore converts to format title_underscore. 46 TitleUnderscore NameMapper = func(raw string) string { 47 newstr := make([]rune, 0, len(raw)) 48 for i, chr := range raw { 49 if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { 50 if i > 0 { 51 newstr = append(newstr, '_') 52 } 53 chr -= ('A' - 'a') 54 } 55 newstr = append(newstr, chr) 56 } 57 return string(newstr) 58 } 59) 60 61func (s *Section) parseFieldName(raw, actual string) string { 62 if len(actual) > 0 { 63 return actual 64 } 65 if s.f.NameMapper != nil { 66 return s.f.NameMapper(raw) 67 } 68 return raw 69} 70 71func parseDelim(actual string) string { 72 if len(actual) > 0 { 73 return actual 74 } 75 return "," 76} 77 78var reflectTime = reflect.TypeOf(time.Now()).Kind() 79 80// setSliceWithProperType sets proper values to slice based on its type. 81func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { 82 var strs []string 83 if allowShadow { 84 strs = key.StringsWithShadows(delim) 85 } else { 86 strs = key.Strings(delim) 87 } 88 89 numVals := len(strs) 90 if numVals == 0 { 91 return nil 92 } 93 94 var vals interface{} 95 var err error 96 97 sliceOf := field.Type().Elem().Kind() 98 switch sliceOf { 99 case reflect.String: 100 vals = strs 101 case reflect.Int: 102 vals, err = key.parseInts(strs, true, false) 103 case reflect.Int64: 104 vals, err = key.parseInt64s(strs, true, false) 105 case reflect.Uint: 106 vals, err = key.parseUints(strs, true, false) 107 case reflect.Uint64: 108 vals, err = key.parseUint64s(strs, true, false) 109 case reflect.Float64: 110 vals, err = key.parseFloat64s(strs, true, false) 111 case reflectTime: 112 vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) 113 default: 114 return fmt.Errorf("unsupported type '[]%s'", sliceOf) 115 } 116 if err != nil && isStrict { 117 return err 118 } 119 120 slice := reflect.MakeSlice(field.Type(), numVals, numVals) 121 for i := 0; i < numVals; i++ { 122 switch sliceOf { 123 case reflect.String: 124 slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) 125 case reflect.Int: 126 slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) 127 case reflect.Int64: 128 slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) 129 case reflect.Uint: 130 slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) 131 case reflect.Uint64: 132 slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) 133 case reflect.Float64: 134 slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) 135 case reflectTime: 136 slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) 137 } 138 } 139 field.Set(slice) 140 return nil 141} 142 143func wrapStrictError(err error, isStrict bool) error { 144 if isStrict { 145 return err 146 } 147 return nil 148} 149 150// setWithProperType sets proper value to field based on its type, 151// but it does not return error for failing parsing, 152// because we want to use default value that is already assigned to strcut. 153func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { 154 switch t.Kind() { 155 case reflect.String: 156 if len(key.String()) == 0 { 157 return nil 158 } 159 field.SetString(key.String()) 160 case reflect.Bool: 161 boolVal, err := key.Bool() 162 if err != nil { 163 return wrapStrictError(err, isStrict) 164 } 165 field.SetBool(boolVal) 166 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 167 durationVal, err := key.Duration() 168 // Skip zero value 169 if err == nil && int64(durationVal) > 0 { 170 field.Set(reflect.ValueOf(durationVal)) 171 return nil 172 } 173 174 intVal, err := key.Int64() 175 if err != nil { 176 return wrapStrictError(err, isStrict) 177 } 178 field.SetInt(intVal) 179 // byte is an alias for uint8, so supporting uint8 breaks support for byte 180 case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: 181 durationVal, err := key.Duration() 182 // Skip zero value 183 if err == nil && uint64(durationVal) > 0 { 184 field.Set(reflect.ValueOf(durationVal)) 185 return nil 186 } 187 188 uintVal, err := key.Uint64() 189 if err != nil { 190 return wrapStrictError(err, isStrict) 191 } 192 field.SetUint(uintVal) 193 194 case reflect.Float32, reflect.Float64: 195 floatVal, err := key.Float64() 196 if err != nil { 197 return wrapStrictError(err, isStrict) 198 } 199 field.SetFloat(floatVal) 200 case reflectTime: 201 timeVal, err := key.Time() 202 if err != nil { 203 return wrapStrictError(err, isStrict) 204 } 205 field.Set(reflect.ValueOf(timeVal)) 206 case reflect.Slice: 207 return setSliceWithProperType(key, field, delim, allowShadow, isStrict) 208 default: 209 return fmt.Errorf("unsupported type '%s'", t) 210 } 211 return nil 212} 213 214func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool) { 215 opts := strings.SplitN(tag, ",", 3) 216 rawName = opts[0] 217 if len(opts) > 1 { 218 omitEmpty = opts[1] == "omitempty" 219 } 220 if len(opts) > 2 { 221 allowShadow = opts[2] == "allowshadow" 222 } 223 return rawName, omitEmpty, allowShadow 224} 225 226func (s *Section) mapTo(val reflect.Value, isStrict bool) error { 227 if val.Kind() == reflect.Ptr { 228 val = val.Elem() 229 } 230 typ := val.Type() 231 232 for i := 0; i < typ.NumField(); i++ { 233 field := val.Field(i) 234 tpField := typ.Field(i) 235 236 tag := tpField.Tag.Get("ini") 237 if tag == "-" { 238 continue 239 } 240 241 rawName, _, allowShadow := parseTagOptions(tag) 242 fieldName := s.parseFieldName(tpField.Name, rawName) 243 if len(fieldName) == 0 || !field.CanSet() { 244 continue 245 } 246 247 isAnonymous := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous 248 isStruct := tpField.Type.Kind() == reflect.Struct 249 if isAnonymous { 250 field.Set(reflect.New(tpField.Type.Elem())) 251 } 252 253 if isAnonymous || isStruct { 254 if sec, err := s.f.GetSection(fieldName); err == nil { 255 if err = sec.mapTo(field, isStrict); err != nil { 256 return fmt.Errorf("error mapping field(%s): %v", fieldName, err) 257 } 258 continue 259 } 260 } 261 262 if key, err := s.GetKey(fieldName); err == nil { 263 delim := parseDelim(tpField.Tag.Get("delim")) 264 if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { 265 return fmt.Errorf("error mapping field(%s): %v", fieldName, err) 266 } 267 } 268 } 269 return nil 270} 271 272// MapTo maps section to given struct. 273func (s *Section) MapTo(v interface{}) error { 274 typ := reflect.TypeOf(v) 275 val := reflect.ValueOf(v) 276 if typ.Kind() == reflect.Ptr { 277 typ = typ.Elem() 278 val = val.Elem() 279 } else { 280 return errors.New("cannot map to non-pointer struct") 281 } 282 283 return s.mapTo(val, false) 284} 285 286// MapTo maps section to given struct in strict mode, 287// which returns all possible error including value parsing error. 288func (s *Section) StrictMapTo(v interface{}) error { 289 typ := reflect.TypeOf(v) 290 val := reflect.ValueOf(v) 291 if typ.Kind() == reflect.Ptr { 292 typ = typ.Elem() 293 val = val.Elem() 294 } else { 295 return errors.New("cannot map to non-pointer struct") 296 } 297 298 return s.mapTo(val, true) 299} 300 301// MapTo maps file to given struct. 302func (f *File) MapTo(v interface{}) error { 303 return f.Section("").MapTo(v) 304} 305 306// MapTo maps file to given struct in strict mode, 307// which returns all possible error including value parsing error. 308func (f *File) StrictMapTo(v interface{}) error { 309 return f.Section("").StrictMapTo(v) 310} 311 312// MapTo maps data sources to given struct with name mapper. 313func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { 314 cfg, err := Load(source, others...) 315 if err != nil { 316 return err 317 } 318 cfg.NameMapper = mapper 319 return cfg.MapTo(v) 320} 321 322// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, 323// which returns all possible error including value parsing error. 324func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { 325 cfg, err := Load(source, others...) 326 if err != nil { 327 return err 328 } 329 cfg.NameMapper = mapper 330 return cfg.StrictMapTo(v) 331} 332 333// MapTo maps data sources to given struct. 334func MapTo(v, source interface{}, others ...interface{}) error { 335 return MapToWithMapper(v, nil, source, others...) 336} 337 338// StrictMapTo maps data sources to given struct in strict mode, 339// which returns all possible error including value parsing error. 340func StrictMapTo(v, source interface{}, others ...interface{}) error { 341 return StrictMapToWithMapper(v, nil, source, others...) 342} 343 344// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. 345func reflectSliceWithProperType(key *Key, field reflect.Value, delim string) error { 346 slice := field.Slice(0, field.Len()) 347 if field.Len() == 0 { 348 return nil 349 } 350 351 var buf bytes.Buffer 352 sliceOf := field.Type().Elem().Kind() 353 for i := 0; i < field.Len(); i++ { 354 switch sliceOf { 355 case reflect.String: 356 buf.WriteString(slice.Index(i).String()) 357 case reflect.Int, reflect.Int64: 358 buf.WriteString(fmt.Sprint(slice.Index(i).Int())) 359 case reflect.Uint, reflect.Uint64: 360 buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) 361 case reflect.Float64: 362 buf.WriteString(fmt.Sprint(slice.Index(i).Float())) 363 case reflectTime: 364 buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) 365 default: 366 return fmt.Errorf("unsupported type '[]%s'", sliceOf) 367 } 368 buf.WriteString(delim) 369 } 370 key.SetValue(buf.String()[:buf.Len()-1]) 371 return nil 372} 373 374// reflectWithProperType does the opposite thing as setWithProperType. 375func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string) error { 376 switch t.Kind() { 377 case reflect.String: 378 key.SetValue(field.String()) 379 case reflect.Bool: 380 key.SetValue(fmt.Sprint(field.Bool())) 381 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 382 key.SetValue(fmt.Sprint(field.Int())) 383 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 384 key.SetValue(fmt.Sprint(field.Uint())) 385 case reflect.Float32, reflect.Float64: 386 key.SetValue(fmt.Sprint(field.Float())) 387 case reflectTime: 388 key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) 389 case reflect.Slice: 390 return reflectSliceWithProperType(key, field, delim) 391 default: 392 return fmt.Errorf("unsupported type '%s'", t) 393 } 394 return nil 395} 396 397// CR: copied from encoding/json/encode.go with modifications of time.Time support. 398// TODO: add more test coverage. 399func isEmptyValue(v reflect.Value) bool { 400 switch v.Kind() { 401 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 402 return v.Len() == 0 403 case reflect.Bool: 404 return !v.Bool() 405 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 406 return v.Int() == 0 407 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 408 return v.Uint() == 0 409 case reflect.Float32, reflect.Float64: 410 return v.Float() == 0 411 case reflect.Interface, reflect.Ptr: 412 return v.IsNil() 413 case reflectTime: 414 t, ok := v.Interface().(time.Time) 415 return ok && t.IsZero() 416 } 417 return false 418} 419 420func (s *Section) reflectFrom(val reflect.Value) error { 421 if val.Kind() == reflect.Ptr { 422 val = val.Elem() 423 } 424 typ := val.Type() 425 426 for i := 0; i < typ.NumField(); i++ { 427 field := val.Field(i) 428 tpField := typ.Field(i) 429 430 tag := tpField.Tag.Get("ini") 431 if tag == "-" { 432 continue 433 } 434 435 opts := strings.SplitN(tag, ",", 2) 436 if len(opts) == 2 && opts[1] == "omitempty" && isEmptyValue(field) { 437 continue 438 } 439 440 fieldName := s.parseFieldName(tpField.Name, opts[0]) 441 if len(fieldName) == 0 || !field.CanSet() { 442 continue 443 } 444 445 if (tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous) || 446 (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { 447 // Note: The only error here is section doesn't exist. 448 sec, err := s.f.GetSection(fieldName) 449 if err != nil { 450 // Note: fieldName can never be empty here, ignore error. 451 sec, _ = s.f.NewSection(fieldName) 452 } 453 454 // Add comment from comment tag 455 if len(sec.Comment) == 0 { 456 sec.Comment = tpField.Tag.Get("comment") 457 } 458 459 if err = sec.reflectFrom(field); err != nil { 460 return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) 461 } 462 continue 463 } 464 465 // Note: Same reason as secion. 466 key, err := s.GetKey(fieldName) 467 if err != nil { 468 key, _ = s.NewKey(fieldName, "") 469 } 470 471 // Add comment from comment tag 472 if len(key.Comment) == 0 { 473 key.Comment = tpField.Tag.Get("comment") 474 } 475 476 if err = reflectWithProperType(tpField.Type, key, field, parseDelim(tpField.Tag.Get("delim"))); err != nil { 477 return fmt.Errorf("error reflecting field (%s): %v", fieldName, err) 478 } 479 480 } 481 return nil 482} 483 484// ReflectFrom reflects secion from given struct. 485func (s *Section) ReflectFrom(v interface{}) error { 486 typ := reflect.TypeOf(v) 487 val := reflect.ValueOf(v) 488 if typ.Kind() == reflect.Ptr { 489 typ = typ.Elem() 490 val = val.Elem() 491 } else { 492 return errors.New("cannot reflect from non-pointer struct") 493 } 494 495 return s.reflectFrom(val) 496} 497 498// ReflectFrom reflects file from given struct. 499func (f *File) ReflectFrom(v interface{}) error { 500 return f.Section("").ReflectFrom(v) 501} 502 503// ReflectFrom reflects data sources from given struct with name mapper. 504func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { 505 cfg.NameMapper = mapper 506 return cfg.ReflectFrom(v) 507} 508 509// ReflectFrom reflects data sources from given struct. 510func ReflectFrom(cfg *File, v interface{}) error { 511 return ReflectFromWithMapper(cfg, v, nil) 512} 513