2020-07-22 14:50:08 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2020-12-12 13:28:58 +00:00
package web
2020-07-22 14:50:08 +00:00
import (
"errors"
"fmt"
"html"
"html/template"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
"time"
)
const (
formatTime = "15:04:05"
formatDate = "2006-01-02"
formatDateTime = "2006-01-02 15:04:05"
formatDateTimeT = "2006-01-02T15:04:05"
)
// Substr returns the substr from start to length.
func Substr ( s string , start , length int ) string {
bt := [ ] rune ( s )
if start < 0 {
start = 0
}
if start > len ( bt ) {
start = start % len ( bt )
}
var end int
if ( start + length ) > ( len ( bt ) - 1 ) {
end = len ( bt )
} else {
end = start + length
}
return string ( bt [ start : end ] )
}
// HTML2str returns escaping text convert from html.
func HTML2str ( html string ) string {
re := regexp . MustCompile ( ` \<[\S\s]+?\> ` )
html = re . ReplaceAllStringFunc ( html , strings . ToLower )
2020-12-12 13:28:58 +00:00
// remove STYLE
2020-07-22 14:50:08 +00:00
re = regexp . MustCompile ( ` \<style[\S\s]+?\</style\> ` )
html = re . ReplaceAllString ( html , "" )
2020-12-12 13:28:58 +00:00
// remove SCRIPT
2020-07-22 14:50:08 +00:00
re = regexp . MustCompile ( ` \<script[\S\s]+?\</script\> ` )
html = re . ReplaceAllString ( html , "" )
re = regexp . MustCompile ( ` \<[\S\s]+?\> ` )
html = re . ReplaceAllString ( html , "\n" )
re = regexp . MustCompile ( ` \s { 2,} ` )
html = re . ReplaceAllString ( html , "\n" )
return strings . TrimSpace ( html )
}
// DateFormat takes a time and a layout string and returns a string with the formatted date. Used by the template parser as "dateformat"
func DateFormat ( t time . Time , layout string ) ( datestring string ) {
datestring = t . Format ( layout )
return
}
// DateFormat pattern rules.
var datePatterns = [ ] string {
// year
"Y" , "2006" , // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
2020-12-12 13:28:58 +00:00
"y" , "06" , // A two digit representation of a year Examples: 99 or 03
2020-07-22 14:50:08 +00:00
// month
"m" , "01" , // Numeric representation of a month, with leading zeros 01 through 12
"n" , "1" , // Numeric representation of a month, without leading zeros 1 through 12
"M" , "Jan" , // A short textual representation of a month, three letters Jan through Dec
"F" , "January" , // A full textual representation of a month, such as January or March January through December
// day
"d" , "02" , // Day of the month, 2 digits with leading zeros 01 to 31
"j" , "2" , // Day of the month without leading zeros 1 to 31
// week
"D" , "Mon" , // A textual representation of a day, three letters Mon through Sun
"l" , "Monday" , // A full textual representation of the day of the week Sunday through Saturday
// time
"g" , "3" , // 12-hour format of an hour without leading zeros 1 through 12
"G" , "15" , // 24-hour format of an hour without leading zeros 0 through 23
"h" , "03" , // 12-hour format of an hour with leading zeros 01 through 12
"H" , "15" , // 24-hour format of an hour with leading zeros 00 through 23
"a" , "pm" , // Lowercase Ante meridiem and Post meridiem am or pm
"A" , "PM" , // Uppercase Ante meridiem and Post meridiem AM or PM
"i" , "04" , // Minutes with leading zeros 00 to 59
"s" , "05" , // Seconds, with leading zeros 00 through 59
// time zone
"T" , "MST" ,
"P" , "-07:00" ,
"O" , "-0700" ,
// RFC 2822
"r" , time . RFC1123Z ,
}
// DateParse Parse Date use PHP time format.
func DateParse ( dateString , format string ) ( time . Time , error ) {
replacer := strings . NewReplacer ( datePatterns ... )
format = replacer . Replace ( format )
return time . ParseInLocation ( format , dateString , time . Local )
}
// Date takes a PHP like date func to Go's time format.
func Date ( t time . Time , format string ) string {
replacer := strings . NewReplacer ( datePatterns ... )
format = replacer . Replace ( format )
return t . Format ( format )
}
// 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.
// Whitespace is trimmed. Used by the template parser as "eq".
func Compare ( a , b interface { } ) ( equal bool ) {
equal = false
if strings . TrimSpace ( fmt . Sprintf ( "%v" , a ) ) == strings . TrimSpace ( fmt . Sprintf ( "%v" , b ) ) {
equal = true
}
return
}
// CompareNot !Compare
func CompareNot ( a , b interface { } ) ( equal bool ) {
return ! Compare ( a , b )
}
// NotNil the same as CompareNot
func NotNil ( a interface { } ) ( isNil bool ) {
return CompareNot ( a , nil )
}
// GetConfig get the Appconfig
func GetConfig ( returnType , key string , defaultVal interface { } ) ( value interface { } , err error ) {
switch returnType {
case "String" :
2020-12-12 13:28:58 +00:00
value , err = AppConfig . String ( key )
2020-07-22 14:50:08 +00:00
case "Bool" :
2020-10-13 14:32:41 +00:00
value , err = AppConfig . Bool ( key )
2020-07-22 14:50:08 +00:00
case "Int" :
2020-10-13 14:32:41 +00:00
value , err = AppConfig . Int ( key )
2020-07-22 14:50:08 +00:00
case "Int64" :
2020-10-13 14:32:41 +00:00
value , err = AppConfig . Int64 ( key )
2020-07-22 14:50:08 +00:00
case "Float" :
2020-10-13 14:32:41 +00:00
value , err = AppConfig . Float ( key )
2020-07-22 14:50:08 +00:00
case "DIY" :
2020-10-13 14:32:41 +00:00
value , err = AppConfig . DIY ( key )
2020-07-22 14:50:08 +00:00
default :
err = errors . New ( "config keys must be of type String, Bool, Int, Int64, Float, or DIY" )
}
if err != nil {
if reflect . TypeOf ( returnType ) != reflect . TypeOf ( defaultVal ) {
err = errors . New ( "defaultVal type does not match returnType" )
} else {
value , err = defaultVal , nil
}
} else if reflect . TypeOf ( value ) . Kind ( ) == reflect . String {
if value == "" {
if reflect . TypeOf ( defaultVal ) . Kind ( ) != reflect . String {
err = errors . New ( "defaultVal type must be a String if the returnType is a String" )
} else {
value = defaultVal . ( string )
}
}
}
return
}
// Str2html Convert string to template.HTML type.
func Str2html ( raw string ) template . HTML {
return template . HTML ( raw )
}
// Htmlquote returns quoted html string.
func Htmlquote ( text string ) string {
2020-12-12 13:28:58 +00:00
// HTML编码为实体符号
2020-07-22 14:50:08 +00:00
/ *
Encodes ` text ` for raw use in HTML .
>> > htmlquote ( "<'&\\" > " )
' & lt ; & # 39 ; & amp ; & quot ; & gt ; '
* /
text = html . EscapeString ( text )
text = strings . NewReplacer (
` “ ` , "“" ,
` ” ` , "”" ,
` ` , " " ,
) . Replace ( text )
return strings . TrimSpace ( text )
}
// Htmlunquote returns unquoted html string.
func Htmlunquote ( text string ) string {
2020-12-12 13:28:58 +00:00
// 实体符号解释为HTML
2020-07-22 14:50:08 +00:00
/ *
Decodes ` text ` that ' s HTML quoted .
>> > htmlunquote ( ' & lt ; & # 39 ; & amp ; & quot ; & gt ; ' )
' < \ \ ' & " > '
* /
text = html . UnescapeString ( text )
return strings . TrimSpace ( text )
}
// URLFor returns url string with another registered controller handler with params.
// usage:
//
// URLFor(".index")
// print URLFor("index")
// router /login
// print URLFor("login")
// print URLFor("login", "next","/"")
// router /profile/:username
// print UrlFor("profile", ":username","John Doe")
// result:
// /
// /login
// /login?next=/
// /user/John%20Doe
//
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md
func URLFor ( endpoint string , values ... interface { } ) string {
return BeeApp . Handlers . URLFor ( endpoint , values ... )
}
// AssetsJs returns script tag with src string.
func AssetsJs ( text string ) template . HTML {
text = "<script src=\"" + text + "\"></script>"
return template . HTML ( text )
}
// AssetsCSS returns stylesheet link tag with src string.
func AssetsCSS ( text string ) template . HTML {
text = "<link href=\"" + text + "\" rel=\"stylesheet\" />"
return template . HTML ( text )
}
// ParseForm will parse form values to struct via tag.
// Support for anonymous struct.
func parseFormToStruct ( form url . Values , objT reflect . Type , objV reflect . Value ) error {
for i := 0 ; i < objT . NumField ( ) ; i ++ {
fieldV := objV . Field ( i )
if ! fieldV . CanSet ( ) {
continue
}
fieldT := objT . Field ( i )
if fieldT . Anonymous && fieldT . Type . Kind ( ) == reflect . Struct {
err := parseFormToStruct ( form , fieldT . Type , fieldV )
if err != nil {
return err
}
continue
}
tags := strings . Split ( fieldT . Tag . Get ( "form" ) , "," )
var tag string
if len ( tags ) == 0 || len ( tags [ 0 ] ) == 0 {
tag = fieldT . Name
} else if tags [ 0 ] == "-" {
continue
} else {
tag = tags [ 0 ]
}
formValues := form [ tag ]
var value string
if len ( formValues ) == 0 {
defaultValue := fieldT . Tag . Get ( "default" )
if defaultValue != "" {
value = defaultValue
} else {
continue
}
}
if len ( formValues ) == 1 {
value = formValues [ 0 ]
if value == "" {
continue
}
}
switch fieldT . Type . Kind ( ) {
case reflect . Bool :
if strings . ToLower ( value ) == "on" || strings . ToLower ( value ) == "1" || strings . ToLower ( value ) == "yes" {
fieldV . SetBool ( true )
continue
}
if strings . ToLower ( value ) == "off" || strings . ToLower ( value ) == "0" || strings . ToLower ( value ) == "no" {
fieldV . SetBool ( false )
continue
}
b , err := strconv . ParseBool ( value )
if err != nil {
return err
}
fieldV . SetBool ( b )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
x , err := strconv . ParseInt ( value , 10 , 64 )
if err != nil {
return err
}
fieldV . SetInt ( x )
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 :
x , err := strconv . ParseUint ( value , 10 , 64 )
if err != nil {
return err
}
fieldV . SetUint ( x )
case reflect . Float32 , reflect . Float64 :
x , err := strconv . ParseFloat ( value , 64 )
if err != nil {
return err
}
fieldV . SetFloat ( x )
case reflect . Interface :
fieldV . Set ( reflect . ValueOf ( value ) )
case reflect . String :
fieldV . SetString ( value )
case reflect . Struct :
switch fieldT . Type . String ( ) {
case "time.Time" :
var (
t time . Time
err error
)
if len ( value ) >= 25 {
value = value [ : 25 ]
t , err = time . ParseInLocation ( time . RFC3339 , value , time . Local )
} else if strings . HasSuffix ( strings . ToUpper ( value ) , "Z" ) {
2020-12-12 13:28:58 +00:00
t , err = time . ParseInLocation ( time . RFC3339 , value , time . Local )
2020-07-22 14:50:08 +00:00
} else if len ( value ) >= 19 {
if strings . Contains ( value , "T" ) {
value = value [ : 19 ]
t , err = time . ParseInLocation ( formatDateTimeT , value , time . Local )
} else {
value = value [ : 19 ]
t , err = time . ParseInLocation ( formatDateTime , value , time . Local )
}
} else if len ( value ) >= 10 {
if len ( value ) > 10 {
value = value [ : 10 ]
}
t , err = time . ParseInLocation ( formatDate , value , time . Local )
} else if len ( value ) >= 8 {
if len ( value ) > 8 {
value = value [ : 8 ]
}
t , err = time . ParseInLocation ( formatTime , value , time . Local )
}
if err != nil {
return err
}
fieldV . Set ( reflect . ValueOf ( t ) )
}
case reflect . Slice :
if fieldT . Type == sliceOfInts {
formVals := form [ tag ]
fieldV . Set ( reflect . MakeSlice ( reflect . SliceOf ( reflect . TypeOf ( int ( 1 ) ) ) , len ( formVals ) , len ( formVals ) ) )
for i := 0 ; i < len ( formVals ) ; i ++ {
val , err := strconv . Atoi ( formVals [ i ] )
if err != nil {
return err
}
fieldV . Index ( i ) . SetInt ( int64 ( val ) )
}
} else if fieldT . Type == sliceOfStrings {
formVals := form [ tag ]
fieldV . Set ( reflect . MakeSlice ( reflect . SliceOf ( reflect . TypeOf ( "" ) ) , len ( formVals ) , len ( formVals ) ) )
for i := 0 ; i < len ( formVals ) ; i ++ {
fieldV . Index ( i ) . SetString ( formVals [ i ] )
}
}
}
}
return nil
}
// ParseForm will parse form values to struct via tag.
func ParseForm ( form url . Values , obj interface { } ) error {
objT := reflect . TypeOf ( obj )
objV := reflect . ValueOf ( obj )
if ! isStructPtr ( objT ) {
return fmt . Errorf ( "%v must be a struct pointer" , obj )
}
objT = objT . Elem ( )
objV = objV . Elem ( )
return parseFormToStruct ( form , objT , objV )
}
var sliceOfInts = reflect . TypeOf ( [ ] int ( nil ) )
var sliceOfStrings = reflect . TypeOf ( [ ] string ( nil ) )
var unKind = map [ reflect . Kind ] bool {
reflect . Uintptr : true ,
reflect . Complex64 : true ,
reflect . Complex128 : true ,
reflect . Array : true ,
reflect . Chan : true ,
reflect . Func : true ,
reflect . Map : true ,
reflect . Ptr : true ,
reflect . Slice : true ,
reflect . Struct : true ,
reflect . UnsafePointer : true ,
}
// RenderForm will render object to form html.
// obj must be a struct pointer.
func RenderForm ( obj interface { } ) template . HTML {
objT := reflect . TypeOf ( obj )
objV := reflect . ValueOf ( obj )
if ! isStructPtr ( objT ) {
return template . HTML ( "" )
}
objT = objT . Elem ( )
objV = objV . Elem ( )
var raw [ ] string
for i := 0 ; i < objT . NumField ( ) ; i ++ {
fieldV := objV . Field ( i )
if ! fieldV . CanSet ( ) || unKind [ fieldV . Kind ( ) ] {
continue
}
fieldT := objT . Field ( i )
label , name , fType , id , class , ignored , required := parseFormTag ( fieldT )
if ignored {
continue
}
raw = append ( raw , renderFormField ( label , name , fType , fieldV . Interface ( ) , id , class , required ) )
}
return template . HTML ( strings . Join ( raw , "</br>" ) )
}
// renderFormField returns a string containing HTML of a single form field.
func renderFormField ( label , name , fType string , value interface { } , id string , class string , required bool ) string {
if id != "" {
id = " id=\"" + id + "\""
}
if class != "" {
class = " class=\"" + class + "\""
}
requiredString := ""
if required {
requiredString = " required"
}
if isValidForInput ( fType ) {
return fmt . Sprintf ( ` %v<input%v%v name="%v" type="%v" value="%v"%v> ` , label , id , class , name , fType , value , requiredString )
}
return fmt . Sprintf ( ` %v<%v%v%v name="%v"%v>%v</%v> ` , label , fType , id , class , name , requiredString , value , fType )
}
// isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
func isValidForInput ( fType string ) bool {
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" )
for _ , validType := range validInputTypes {
if fType == validType {
return true
}
}
return false
}
// parseFormTag takes the stuct-tag of a StructField and parses the `form` value.
// returned are the form label, name-property, type and wether the field should be ignored.
func parseFormTag ( fieldT reflect . StructField ) ( label , name , fType string , id string , class string , ignored bool , required bool ) {
tags := strings . Split ( fieldT . Tag . Get ( "form" ) , "," )
label = fieldT . Name + ": "
name = fieldT . Name
fType = "text"
ignored = false
id = fieldT . Tag . Get ( "id" )
class = fieldT . Tag . Get ( "class" )
required = false
requiredField := fieldT . Tag . Get ( "required" )
if requiredField != "-" && requiredField != "" {
required , _ = strconv . ParseBool ( requiredField )
}
switch len ( tags ) {
case 1 :
if tags [ 0 ] == "-" {
ignored = true
}
if len ( tags [ 0 ] ) > 0 {
name = tags [ 0 ]
}
case 2 :
if len ( tags [ 0 ] ) > 0 {
name = tags [ 0 ]
}
if len ( tags [ 1 ] ) > 0 {
fType = tags [ 1 ]
}
case 3 :
if len ( tags [ 0 ] ) > 0 {
name = tags [ 0 ]
}
if len ( tags [ 1 ] ) > 0 {
fType = tags [ 1 ]
}
if len ( tags [ 2 ] ) > 0 {
label = tags [ 2 ]
}
}
return
}
func isStructPtr ( t reflect . Type ) bool {
return t . Kind ( ) == reflect . Ptr && t . Elem ( ) . Kind ( ) == reflect . Struct
}
// go1.2 added template funcs. begin
var (
errBadComparisonType = errors . New ( "invalid type for comparison" )
errBadComparison = errors . New ( "incompatible types for comparison" )
errNoComparison = errors . New ( "missing argument for comparison" )
)
type kind int
const (
invalidKind kind = iota
boolKind
complexKind
intKind
floatKind
stringKind
uintKind
)
func basicKind ( v reflect . Value ) ( kind , error ) {
switch v . Kind ( ) {
case reflect . Bool :
return boolKind , nil
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
return intKind , nil
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr :
return uintKind , nil
case reflect . Float32 , reflect . Float64 :
return floatKind , nil
case reflect . Complex64 , reflect . Complex128 :
return complexKind , nil
case reflect . String :
return stringKind , nil
}
return invalidKind , errBadComparisonType
}
// eq evaluates the comparison a == b || a == c || ...
func eq ( arg1 interface { } , arg2 ... interface { } ) ( bool , error ) {
v1 := reflect . ValueOf ( arg1 )
k1 , err := basicKind ( v1 )
if err != nil {
return false , err
}
if len ( arg2 ) == 0 {
return false , errNoComparison
}
for _ , arg := range arg2 {
v2 := reflect . ValueOf ( arg )
k2 , err := basicKind ( v2 )
if err != nil {
return false , err
}
if k1 != k2 {
return false , errBadComparison
}
truth := false
switch k1 {
case boolKind :
truth = v1 . Bool ( ) == v2 . Bool ( )
case complexKind :
truth = v1 . Complex ( ) == v2 . Complex ( )
case floatKind :
truth = v1 . Float ( ) == v2 . Float ( )
case intKind :
truth = v1 . Int ( ) == v2 . Int ( )
case stringKind :
truth = v1 . String ( ) == v2 . String ( )
case uintKind :
truth = v1 . Uint ( ) == v2 . Uint ( )
default :
panic ( "invalid kind" )
}
if truth {
return true , nil
}
}
return false , nil
}
// ne evaluates the comparison a != b.
func ne ( arg1 , arg2 interface { } ) ( bool , error ) {
// != is the inverse of ==.
equal , err := eq ( arg1 , arg2 )
return ! equal , err
}
// lt evaluates the comparison a < b.
func lt ( arg1 , arg2 interface { } ) ( bool , error ) {
v1 := reflect . ValueOf ( arg1 )
k1 , err := basicKind ( v1 )
if err != nil {
return false , err
}
v2 := reflect . ValueOf ( arg2 )
k2 , err := basicKind ( v2 )
if err != nil {
return false , err
}
if k1 != k2 {
return false , errBadComparison
}
truth := false
switch k1 {
case boolKind , complexKind :
return false , errBadComparisonType
case floatKind :
truth = v1 . Float ( ) < v2 . Float ( )
case intKind :
truth = v1 . Int ( ) < v2 . Int ( )
case stringKind :
truth = v1 . String ( ) < v2 . String ( )
case uintKind :
truth = v1 . Uint ( ) < v2 . Uint ( )
default :
panic ( "invalid kind" )
}
return truth , nil
}
// le evaluates the comparison <= b.
func le ( arg1 , arg2 interface { } ) ( bool , error ) {
// <= is < or ==.
lessThan , err := lt ( arg1 , arg2 )
if lessThan || err != nil {
return lessThan , err
}
return eq ( arg1 , arg2 )
}
// gt evaluates the comparison a > b.
func gt ( arg1 , arg2 interface { } ) ( bool , error ) {
// > is the inverse of <=.
lessOrEqual , err := le ( arg1 , arg2 )
if err != nil {
return false , err
}
return ! lessOrEqual , nil
}
// ge evaluates the comparison a >= b.
func ge ( arg1 , arg2 interface { } ) ( bool , error ) {
// >= is the inverse of <.
lessThan , err := lt ( arg1 , arg2 )
if err != nil {
return false , err
}
return ! lessThan , nil
}
// MapGet getting value from map by keys
// usage:
// Data["m"] = M{
// "a": 1,
// "1": map[string]float64{
// "c": 4,
// },
// }
//
// {{ map_get m "a" }} // return 1
// {{ map_get m 1 "c" }} // return 4
func MapGet ( arg1 interface { } , arg2 ... interface { } ) ( interface { } , error ) {
arg1Type := reflect . TypeOf ( arg1 )
arg1Val := reflect . ValueOf ( arg1 )
if arg1Type . Kind ( ) == reflect . Map && len ( arg2 ) > 0 {
// check whether arg2[0] type equals to arg1 key type
// if they are different, make conversion
arg2Val := reflect . ValueOf ( arg2 [ 0 ] )
arg2Type := reflect . TypeOf ( arg2 [ 0 ] )
if arg2Type . Kind ( ) != arg1Type . Key ( ) . Kind ( ) {
// convert arg2Value to string
var arg2ConvertedVal interface { }
arg2String := fmt . Sprintf ( "%v" , arg2 [ 0 ] )
// convert string representation to any other type
switch arg1Type . Key ( ) . Kind ( ) {
case reflect . Bool :
arg2ConvertedVal , _ = strconv . ParseBool ( arg2String )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
arg2ConvertedVal , _ = strconv . ParseInt ( arg2String , 0 , 64 )
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr :
arg2ConvertedVal , _ = strconv . ParseUint ( arg2String , 0 , 64 )
case reflect . Float32 , reflect . Float64 :
arg2ConvertedVal , _ = strconv . ParseFloat ( arg2String , 64 )
case reflect . String :
arg2ConvertedVal = arg2String
default :
arg2ConvertedVal = arg2Val . Interface ( )
}
arg2Val = reflect . ValueOf ( arg2ConvertedVal )
}
storedVal := arg1Val . MapIndex ( arg2Val )
if storedVal . IsValid ( ) {
var result interface { }
switch arg1Type . Elem ( ) . Kind ( ) {
case reflect . Bool :
result = storedVal . Bool ( )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
result = storedVal . Int ( )
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr :
result = storedVal . Uint ( )
case reflect . Float32 , reflect . Float64 :
result = storedVal . Float ( )
case reflect . String :
result = storedVal . String ( )
default :
result = storedVal . Interface ( )
}
// if there is more keys, handle this recursively
if len ( arg2 ) > 1 {
return MapGet ( result , arg2 [ 1 : ] ... )
}
return result , nil
}
return nil , nil
}
return nil , nil
}