github.com/astaxie/beego@v1.12.3/templatefunc.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain 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, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package beego 16 17 import ( 18 "errors" 19 "fmt" 20 "html" 21 "html/template" 22 "net/url" 23 "reflect" 24 "regexp" 25 "strconv" 26 "strings" 27 "time" 28 ) 29 30 const ( 31 formatTime = "15:04:05" 32 formatDate = "2006-01-02" 33 formatDateTime = "2006-01-02 15:04:05" 34 formatDateTimeT = "2006-01-02T15:04:05" 35 ) 36 37 // Substr returns the substr from start to length. 38 func Substr(s string, start, length int) string { 39 bt := []rune(s) 40 if start < 0 { 41 start = 0 42 } 43 if start > len(bt) { 44 start = start % len(bt) 45 } 46 var end int 47 if (start + length) > (len(bt) - 1) { 48 end = len(bt) 49 } else { 50 end = start + length 51 } 52 return string(bt[start:end]) 53 } 54 55 // HTML2str returns escaping text convert from html. 56 func HTML2str(html string) string { 57 58 re := regexp.MustCompile(`\<[\S\s]+?\>`) 59 html = re.ReplaceAllStringFunc(html, strings.ToLower) 60 61 //remove STYLE 62 re = regexp.MustCompile(`\<style[\S\s]+?\</style\>`) 63 html = re.ReplaceAllString(html, "") 64 65 //remove SCRIPT 66 re = regexp.MustCompile(`\<script[\S\s]+?\</script\>`) 67 html = re.ReplaceAllString(html, "") 68 69 re = regexp.MustCompile(`\<[\S\s]+?\>`) 70 html = re.ReplaceAllString(html, "\n") 71 72 re = regexp.MustCompile(`\s{2,}`) 73 html = re.ReplaceAllString(html, "\n") 74 75 return strings.TrimSpace(html) 76 } 77 78 // DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat" 79 func DateFormat(t time.Time, layout string) (datestring string) { 80 datestring = t.Format(layout) 81 return 82 } 83 84 // DateFormat pattern rules. 85 var datePatterns = []string{ 86 // year 87 "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 88 "y", "06", //A two digit representation of a year Examples: 99 or 03 89 90 // month 91 "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 92 "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 93 "M", "Jan", // A short textual representation of a month, three letters Jan through Dec 94 "F", "January", // A full textual representation of a month, such as January or March January through December 95 96 // day 97 "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 98 "j", "2", // Day of the month without leading zeros 1 to 31 99 100 // week 101 "D", "Mon", // A textual representation of a day, three letters Mon through Sun 102 "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday 103 104 // time 105 "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 106 "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 107 "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 108 "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 109 110 "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm 111 "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM 112 113 "i", "04", // Minutes with leading zeros 00 to 59 114 "s", "05", // Seconds, with leading zeros 00 through 59 115 116 // time zone 117 "T", "MST", 118 "P", "-07:00", 119 "O", "-0700", 120 121 // RFC 2822 122 "r", time.RFC1123Z, 123 } 124 125 // DateParse Parse Date use PHP time format. 126 func DateParse(dateString, format string) (time.Time, error) { 127 replacer := strings.NewReplacer(datePatterns...) 128 format = replacer.Replace(format) 129 return time.ParseInLocation(format, dateString, time.Local) 130 } 131 132 // Date takes a PHP like date func to Go's time format. 133 func Date(t time.Time, format string) string { 134 replacer := strings.NewReplacer(datePatterns...) 135 format = replacer.Replace(format) 136 return t.Format(format) 137 } 138 139 // Compare is a quick and dirty comparison function. It will convert whatever you give it to strings and see if the two values are equal. 140 // Whitespace is trimmed. Used by the template parser as "eq". 141 func Compare(a, b interface{}) (equal bool) { 142 equal = false 143 if strings.TrimSpace(fmt.Sprintf("%v", a)) == strings.TrimSpace(fmt.Sprintf("%v", b)) { 144 equal = true 145 } 146 return 147 } 148 149 // CompareNot !Compare 150 func CompareNot(a, b interface{}) (equal bool) { 151 return !Compare(a, b) 152 } 153 154 // NotNil the same as CompareNot 155 func NotNil(a interface{}) (isNil bool) { 156 return CompareNot(a, nil) 157 } 158 159 // GetConfig get the Appconfig 160 func GetConfig(returnType, key string, defaultVal interface{}) (value interface{}, err error) { 161 switch returnType { 162 case "String": 163 value = AppConfig.String(key) 164 case "Bool": 165 value, err = AppConfig.Bool(key) 166 case "Int": 167 value, err = AppConfig.Int(key) 168 case "Int64": 169 value, err = AppConfig.Int64(key) 170 case "Float": 171 value, err = AppConfig.Float(key) 172 case "DIY": 173 value, err = AppConfig.DIY(key) 174 default: 175 err = errors.New("config keys must be of type String, Bool, Int, Int64, Float, or DIY") 176 } 177 178 if err != nil { 179 if reflect.TypeOf(returnType) != reflect.TypeOf(defaultVal) { 180 err = errors.New("defaultVal type does not match returnType") 181 } else { 182 value, err = defaultVal, nil 183 } 184 } else if reflect.TypeOf(value).Kind() == reflect.String { 185 if value == "" { 186 if reflect.TypeOf(defaultVal).Kind() != reflect.String { 187 err = errors.New("defaultVal type must be a String if the returnType is a String") 188 } else { 189 value = defaultVal.(string) 190 } 191 } 192 } 193 194 return 195 } 196 197 // Str2html Convert string to template.HTML type. 198 func Str2html(raw string) template.HTML { 199 return template.HTML(raw) 200 } 201 202 // Htmlquote returns quoted html string. 203 func Htmlquote(text string) string { 204 //HTML编码为实体符号 205 /* 206 Encodes `text` for raw use in HTML. 207 >>> htmlquote("<'&\\">") 208 '<'&">' 209 */ 210 211 text = html.EscapeString(text) 212 text = strings.NewReplacer( 213 `“`, "“", 214 `”`, "”", 215 ` `, " ", 216 ).Replace(text) 217 218 return strings.TrimSpace(text) 219 } 220 221 // Htmlunquote returns unquoted html string. 222 func Htmlunquote(text string) string { 223 //实体符号解释为HTML 224 /* 225 Decodes `text` that's HTML quoted. 226 >>> htmlunquote('<'&">') 227 '<\\'&">' 228 */ 229 230 text = html.UnescapeString(text) 231 232 return strings.TrimSpace(text) 233 } 234 235 // URLFor returns url string with another registered controller handler with params. 236 // usage: 237 // 238 // URLFor(".index") 239 // print URLFor("index") 240 // router /login 241 // print URLFor("login") 242 // print URLFor("login", "next","/"") 243 // router /profile/:username 244 // print UrlFor("profile", ":username","John Doe") 245 // result: 246 // / 247 // /login 248 // /login?next=/ 249 // /user/John%20Doe 250 // 251 // more detail http://beego.me/docs/mvc/controller/urlbuilding.md 252 func URLFor(endpoint string, values ...interface{}) string { 253 return BeeApp.Handlers.URLFor(endpoint, values...) 254 } 255 256 // AssetsJs returns script tag with src string. 257 func AssetsJs(text string) template.HTML { 258 259 text = "<script src=\"" + text + "\"></script>" 260 261 return template.HTML(text) 262 } 263 264 // AssetsCSS returns stylesheet link tag with src string. 265 func AssetsCSS(text string) template.HTML { 266 267 text = "<link href=\"" + text + "\" rel=\"stylesheet\" />" 268 269 return template.HTML(text) 270 } 271 272 // ParseForm will parse form values to struct via tag. 273 // Support for anonymous struct. 274 func parseFormToStruct(form url.Values, objT reflect.Type, objV reflect.Value) error { 275 for i := 0; i < objT.NumField(); i++ { 276 fieldV := objV.Field(i) 277 if !fieldV.CanSet() { 278 continue 279 } 280 281 fieldT := objT.Field(i) 282 if fieldT.Anonymous && fieldT.Type.Kind() == reflect.Struct { 283 err := parseFormToStruct(form, fieldT.Type, fieldV) 284 if err != nil { 285 return err 286 } 287 continue 288 } 289 290 tags := strings.Split(fieldT.Tag.Get("form"), ",") 291 var tag string 292 if len(tags) == 0 || len(tags[0]) == 0 { 293 tag = fieldT.Name 294 } else if tags[0] == "-" { 295 continue 296 } else { 297 tag = tags[0] 298 } 299 300 formValues := form[tag] 301 var value string 302 if len(formValues) == 0 { 303 defaultValue := fieldT.Tag.Get("default") 304 if defaultValue != "" { 305 value = defaultValue 306 } else { 307 continue 308 } 309 } 310 if len(formValues) == 1 { 311 value = formValues[0] 312 if value == "" { 313 continue 314 } 315 } 316 317 switch fieldT.Type.Kind() { 318 case reflect.Bool: 319 if strings.ToLower(value) == "on" || strings.ToLower(value) == "1" || strings.ToLower(value) == "yes" { 320 fieldV.SetBool(true) 321 continue 322 } 323 if strings.ToLower(value) == "off" || strings.ToLower(value) == "0" || strings.ToLower(value) == "no" { 324 fieldV.SetBool(false) 325 continue 326 } 327 b, err := strconv.ParseBool(value) 328 if err != nil { 329 return err 330 } 331 fieldV.SetBool(b) 332 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 333 x, err := strconv.ParseInt(value, 10, 64) 334 if err != nil { 335 return err 336 } 337 fieldV.SetInt(x) 338 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: 339 x, err := strconv.ParseUint(value, 10, 64) 340 if err != nil { 341 return err 342 } 343 fieldV.SetUint(x) 344 case reflect.Float32, reflect.Float64: 345 x, err := strconv.ParseFloat(value, 64) 346 if err != nil { 347 return err 348 } 349 fieldV.SetFloat(x) 350 case reflect.Interface: 351 fieldV.Set(reflect.ValueOf(value)) 352 case reflect.String: 353 fieldV.SetString(value) 354 case reflect.Struct: 355 switch fieldT.Type.String() { 356 case "time.Time": 357 var ( 358 t time.Time 359 err error 360 ) 361 if len(value) >= 25 { 362 value = value[:25] 363 t, err = time.ParseInLocation(time.RFC3339, value, time.Local) 364 } else if strings.HasSuffix(strings.ToUpper(value), "Z") { 365 t, err = time.ParseInLocation(time.RFC3339, value, time.Local) 366 } else if len(value) >= 19 { 367 if strings.Contains(value, "T") { 368 value = value[:19] 369 t, err = time.ParseInLocation(formatDateTimeT, value, time.Local) 370 } else { 371 value = value[:19] 372 t, err = time.ParseInLocation(formatDateTime, value, time.Local) 373 } 374 } else if len(value) >= 10 { 375 if len(value) > 10 { 376 value = value[:10] 377 } 378 t, err = time.ParseInLocation(formatDate, value, time.Local) 379 } else if len(value) >= 8 { 380 if len(value) > 8 { 381 value = value[:8] 382 } 383 t, err = time.ParseInLocation(formatTime, value, time.Local) 384 } 385 if err != nil { 386 return err 387 } 388 fieldV.Set(reflect.ValueOf(t)) 389 } 390 case reflect.Slice: 391 if fieldT.Type == sliceOfInts { 392 formVals := form[tag] 393 fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf(int(1))), len(formVals), len(formVals))) 394 for i := 0; i < len(formVals); i++ { 395 val, err := strconv.Atoi(formVals[i]) 396 if err != nil { 397 return err 398 } 399 fieldV.Index(i).SetInt(int64(val)) 400 } 401 } else if fieldT.Type == sliceOfStrings { 402 formVals := form[tag] 403 fieldV.Set(reflect.MakeSlice(reflect.SliceOf(reflect.TypeOf("")), len(formVals), len(formVals))) 404 for i := 0; i < len(formVals); i++ { 405 fieldV.Index(i).SetString(formVals[i]) 406 } 407 } 408 } 409 } 410 return nil 411 } 412 413 // ParseForm will parse form values to struct via tag. 414 func ParseForm(form url.Values, obj interface{}) error { 415 objT := reflect.TypeOf(obj) 416 objV := reflect.ValueOf(obj) 417 if !isStructPtr(objT) { 418 return fmt.Errorf("%v must be a struct pointer", obj) 419 } 420 objT = objT.Elem() 421 objV = objV.Elem() 422 423 return parseFormToStruct(form, objT, objV) 424 } 425 426 var sliceOfInts = reflect.TypeOf([]int(nil)) 427 var sliceOfStrings = reflect.TypeOf([]string(nil)) 428 429 var unKind = map[reflect.Kind]bool{ 430 reflect.Uintptr: true, 431 reflect.Complex64: true, 432 reflect.Complex128: true, 433 reflect.Array: true, 434 reflect.Chan: true, 435 reflect.Func: true, 436 reflect.Map: true, 437 reflect.Ptr: true, 438 reflect.Slice: true, 439 reflect.Struct: true, 440 reflect.UnsafePointer: true, 441 } 442 443 // RenderForm will render object to form html. 444 // obj must be a struct pointer. 445 func RenderForm(obj interface{}) template.HTML { 446 objT := reflect.TypeOf(obj) 447 objV := reflect.ValueOf(obj) 448 if !isStructPtr(objT) { 449 return template.HTML("") 450 } 451 objT = objT.Elem() 452 objV = objV.Elem() 453 454 var raw []string 455 for i := 0; i < objT.NumField(); i++ { 456 fieldV := objV.Field(i) 457 if !fieldV.CanSet() || unKind[fieldV.Kind()] { 458 continue 459 } 460 461 fieldT := objT.Field(i) 462 463 label, name, fType, id, class, ignored, required := parseFormTag(fieldT) 464 if ignored { 465 continue 466 } 467 468 raw = append(raw, renderFormField(label, name, fType, fieldV.Interface(), id, class, required)) 469 } 470 return template.HTML(strings.Join(raw, "</br>")) 471 } 472 473 // renderFormField returns a string containing HTML of a single form field. 474 func renderFormField(label, name, fType string, value interface{}, id string, class string, required bool) string { 475 if id != "" { 476 id = " id=\"" + id + "\"" 477 } 478 479 if class != "" { 480 class = " class=\"" + class + "\"" 481 } 482 483 requiredString := "" 484 if required { 485 requiredString = " required" 486 } 487 488 if isValidForInput(fType) { 489 return fmt.Sprintf(`%v<input%v%v name="%v" type="%v" value="%v"%v>`, label, id, class, name, fType, value, requiredString) 490 } 491 492 return fmt.Sprintf(`%v<%v%v%v name="%v"%v>%v</%v>`, label, fType, id, class, name, requiredString, value, fType) 493 } 494 495 // isValidForInput checks if fType is a valid value for the `type` property of an HTML input element. 496 func isValidForInput(fType string) bool { 497 validInputTypes := strings.Fields("text password checkbox radio submit reset hidden image file button search email url tel number range date month week time datetime datetime-local color") 498 for _, validType := range validInputTypes { 499 if fType == validType { 500 return true 501 } 502 } 503 return false 504 } 505 506 // parseFormTag takes the stuct-tag of a StructField and parses the `form` value. 507 // returned are the form label, name-property, type and wether the field should be ignored. 508 func parseFormTag(fieldT reflect.StructField) (label, name, fType string, id string, class string, ignored bool, required bool) { 509 tags := strings.Split(fieldT.Tag.Get("form"), ",") 510 label = fieldT.Name + ": " 511 name = fieldT.Name 512 fType = "text" 513 ignored = false 514 id = fieldT.Tag.Get("id") 515 class = fieldT.Tag.Get("class") 516 517 required = false 518 requiredField := fieldT.Tag.Get("required") 519 if requiredField != "-" && requiredField != "" { 520 required, _ = strconv.ParseBool(requiredField) 521 } 522 523 switch len(tags) { 524 case 1: 525 if tags[0] == "-" { 526 ignored = true 527 } 528 if len(tags[0]) > 0 { 529 name = tags[0] 530 } 531 case 2: 532 if len(tags[0]) > 0 { 533 name = tags[0] 534 } 535 if len(tags[1]) > 0 { 536 fType = tags[1] 537 } 538 case 3: 539 if len(tags[0]) > 0 { 540 name = tags[0] 541 } 542 if len(tags[1]) > 0 { 543 fType = tags[1] 544 } 545 if len(tags[2]) > 0 { 546 label = tags[2] 547 } 548 } 549 550 return 551 } 552 553 func isStructPtr(t reflect.Type) bool { 554 return t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Struct 555 } 556 557 // go1.2 added template funcs. begin 558 var ( 559 errBadComparisonType = errors.New("invalid type for comparison") 560 errBadComparison = errors.New("incompatible types for comparison") 561 errNoComparison = errors.New("missing argument for comparison") 562 ) 563 564 type kind int 565 566 const ( 567 invalidKind kind = iota 568 boolKind 569 complexKind 570 intKind 571 floatKind 572 stringKind 573 uintKind 574 ) 575 576 func basicKind(v reflect.Value) (kind, error) { 577 switch v.Kind() { 578 case reflect.Bool: 579 return boolKind, nil 580 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 581 return intKind, nil 582 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 583 return uintKind, nil 584 case reflect.Float32, reflect.Float64: 585 return floatKind, nil 586 case reflect.Complex64, reflect.Complex128: 587 return complexKind, nil 588 case reflect.String: 589 return stringKind, nil 590 } 591 return invalidKind, errBadComparisonType 592 } 593 594 // eq evaluates the comparison a == b || a == c || ... 595 func eq(arg1 interface{}, arg2 ...interface{}) (bool, error) { 596 v1 := reflect.ValueOf(arg1) 597 k1, err := basicKind(v1) 598 if err != nil { 599 return false, err 600 } 601 if len(arg2) == 0 { 602 return false, errNoComparison 603 } 604 for _, arg := range arg2 { 605 v2 := reflect.ValueOf(arg) 606 k2, err := basicKind(v2) 607 if err != nil { 608 return false, err 609 } 610 if k1 != k2 { 611 return false, errBadComparison 612 } 613 truth := false 614 switch k1 { 615 case boolKind: 616 truth = v1.Bool() == v2.Bool() 617 case complexKind: 618 truth = v1.Complex() == v2.Complex() 619 case floatKind: 620 truth = v1.Float() == v2.Float() 621 case intKind: 622 truth = v1.Int() == v2.Int() 623 case stringKind: 624 truth = v1.String() == v2.String() 625 case uintKind: 626 truth = v1.Uint() == v2.Uint() 627 default: 628 panic("invalid kind") 629 } 630 if truth { 631 return true, nil 632 } 633 } 634 return false, nil 635 } 636 637 // ne evaluates the comparison a != b. 638 func ne(arg1, arg2 interface{}) (bool, error) { 639 // != is the inverse of ==. 640 equal, err := eq(arg1, arg2) 641 return !equal, err 642 } 643 644 // lt evaluates the comparison a < b. 645 func lt(arg1, arg2 interface{}) (bool, error) { 646 v1 := reflect.ValueOf(arg1) 647 k1, err := basicKind(v1) 648 if err != nil { 649 return false, err 650 } 651 v2 := reflect.ValueOf(arg2) 652 k2, err := basicKind(v2) 653 if err != nil { 654 return false, err 655 } 656 if k1 != k2 { 657 return false, errBadComparison 658 } 659 truth := false 660 switch k1 { 661 case boolKind, complexKind: 662 return false, errBadComparisonType 663 case floatKind: 664 truth = v1.Float() < v2.Float() 665 case intKind: 666 truth = v1.Int() < v2.Int() 667 case stringKind: 668 truth = v1.String() < v2.String() 669 case uintKind: 670 truth = v1.Uint() < v2.Uint() 671 default: 672 panic("invalid kind") 673 } 674 return truth, nil 675 } 676 677 // le evaluates the comparison <= b. 678 func le(arg1, arg2 interface{}) (bool, error) { 679 // <= is < or ==. 680 lessThan, err := lt(arg1, arg2) 681 if lessThan || err != nil { 682 return lessThan, err 683 } 684 return eq(arg1, arg2) 685 } 686 687 // gt evaluates the comparison a > b. 688 func gt(arg1, arg2 interface{}) (bool, error) { 689 // > is the inverse of <=. 690 lessOrEqual, err := le(arg1, arg2) 691 if err != nil { 692 return false, err 693 } 694 return !lessOrEqual, nil 695 } 696 697 // ge evaluates the comparison a >= b. 698 func ge(arg1, arg2 interface{}) (bool, error) { 699 // >= is the inverse of <. 700 lessThan, err := lt(arg1, arg2) 701 if err != nil { 702 return false, err 703 } 704 return !lessThan, nil 705 } 706 707 // MapGet getting value from map by keys 708 // usage: 709 // Data["m"] = M{ 710 // "a": 1, 711 // "1": map[string]float64{ 712 // "c": 4, 713 // }, 714 // } 715 // 716 // {{ map_get m "a" }} // return 1 717 // {{ map_get m 1 "c" }} // return 4 718 func MapGet(arg1 interface{}, arg2 ...interface{}) (interface{}, error) { 719 arg1Type := reflect.TypeOf(arg1) 720 arg1Val := reflect.ValueOf(arg1) 721 722 if arg1Type.Kind() == reflect.Map && len(arg2) > 0 { 723 // check whether arg2[0] type equals to arg1 key type 724 // if they are different, make conversion 725 arg2Val := reflect.ValueOf(arg2[0]) 726 arg2Type := reflect.TypeOf(arg2[0]) 727 if arg2Type.Kind() != arg1Type.Key().Kind() { 728 // convert arg2Value to string 729 var arg2ConvertedVal interface{} 730 arg2String := fmt.Sprintf("%v", arg2[0]) 731 732 // convert string representation to any other type 733 switch arg1Type.Key().Kind() { 734 case reflect.Bool: 735 arg2ConvertedVal, _ = strconv.ParseBool(arg2String) 736 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 737 arg2ConvertedVal, _ = strconv.ParseInt(arg2String, 0, 64) 738 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 739 arg2ConvertedVal, _ = strconv.ParseUint(arg2String, 0, 64) 740 case reflect.Float32, reflect.Float64: 741 arg2ConvertedVal, _ = strconv.ParseFloat(arg2String, 64) 742 case reflect.String: 743 arg2ConvertedVal = arg2String 744 default: 745 arg2ConvertedVal = arg2Val.Interface() 746 } 747 arg2Val = reflect.ValueOf(arg2ConvertedVal) 748 } 749 750 storedVal := arg1Val.MapIndex(arg2Val) 751 752 if storedVal.IsValid() { 753 var result interface{} 754 755 switch arg1Type.Elem().Kind() { 756 case reflect.Bool: 757 result = storedVal.Bool() 758 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 759 result = storedVal.Int() 760 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 761 result = storedVal.Uint() 762 case reflect.Float32, reflect.Float64: 763 result = storedVal.Float() 764 case reflect.String: 765 result = storedVal.String() 766 default: 767 result = storedVal.Interface() 768 } 769 770 // if there is more keys, handle this recursively 771 if len(arg2) > 1 { 772 return MapGet(result, arg2[1:]...) 773 } 774 return result, nil 775 } 776 return nil, nil 777 778 } 779 return nil, nil 780 }