2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// 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
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// 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.
2013-12-12 14:25:08 +00:00
package beego
import (
"errors"
"fmt"
2018-06-13 07:43:01 +00:00
"html"
2013-12-12 14:25:08 +00:00
"html/template"
"net/url"
"reflect"
"regexp"
"strconv"
"strings"
"time"
)
2017-02-27 06:43:16 +00:00
const (
2017-05-19 01:22:27 +00:00
formatTime = "15:04:05"
formatDate = "2006-01-02"
formatDateTime = "2006-01-02 15:04:05"
formatDateTimeT = "2006-01-02T15:04:05"
2017-02-27 06:43:16 +00:00
)
2013-12-21 05:19:24 +00:00
// Substr returns the substr from start to length.
2013-12-12 14:25:08 +00:00
func Substr ( s string , start , length int ) string {
bt := [ ] rune ( s )
if start < 0 {
start = 0
}
2014-08-05 15:52:06 +00:00
if start > len ( bt ) {
start = start % len ( bt )
}
2013-12-12 14:25:08 +00:00
var end int
if ( start + length ) > ( len ( bt ) - 1 ) {
end = len ( bt )
} else {
end = start + length
}
return string ( bt [ start : end ] )
}
2015-09-08 15:41:41 +00:00
// HTML2str returns escaping text convert from html.
func HTML2str ( html string ) string {
2013-12-12 14:25:08 +00:00
2019-01-26 11:13:53 +00:00
re := regexp . MustCompile ( ` \<[\S\s]+?\> ` )
2017-03-17 17:45:30 +00:00
html = re . ReplaceAllStringFunc ( html , strings . ToLower )
2013-12-12 14:25:08 +00:00
2014-06-25 02:39:37 +00:00
//remove STYLE
2019-01-26 11:13:53 +00:00
re = regexp . MustCompile ( ` \<style[\S\s]+?\</style\> ` )
2017-03-17 17:45:30 +00:00
html = re . ReplaceAllString ( html , "" )
2013-12-12 14:25:08 +00:00
2014-06-25 02:39:37 +00:00
//remove SCRIPT
2019-01-26 11:13:53 +00:00
re = regexp . MustCompile ( ` \<script[\S\s]+?\</script\> ` )
2017-03-17 17:45:30 +00:00
html = re . ReplaceAllString ( html , "" )
2013-12-12 14:25:08 +00:00
2019-01-26 11:13:53 +00:00
re = regexp . MustCompile ( ` \<[\S\s]+?\> ` )
2017-03-17 17:45:30 +00:00
html = re . ReplaceAllString ( html , "\n" )
2013-12-12 14:25:08 +00:00
2019-01-26 11:13:53 +00:00
re = regexp . MustCompile ( ` \s { 2,} ` )
2017-03-17 17:45:30 +00:00
html = re . ReplaceAllString ( html , "\n" )
2013-12-12 14:25:08 +00:00
2017-03-17 17:45:30 +00:00
return strings . TrimSpace ( html )
2013-12-12 14:25:08 +00:00
}
// 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
}
2013-12-21 05:19:24 +00:00
// DateFormat pattern rules.
2013-12-21 12:44:34 +00:00
var datePatterns = [ ] string {
2013-12-12 14:25:08 +00:00
// year
"Y" , "2006" , // A full numeric representation of a year, 4 digits Examples: 1999 or 2003
2019-01-24 17:15:40 +00:00
"y" , "06" , //A two digit representation of a year Examples: 99 or 03
2013-12-12 14:25:08 +00:00
// month
2019-01-24 17:15:40 +00:00
"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
2013-12-12 14:25:08 +00:00
"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
2019-01-24 17:15:40 +00:00
"j" , "2" , // Day of the month without leading zeros 1 to 31
2013-12-12 14:25:08 +00:00
// week
2019-01-24 17:15:40 +00:00
"D" , "Mon" , // A textual representation of a day, three letters Mon through Sun
2013-12-12 14:25:08 +00:00
"l" , "Monday" , // A full textual representation of the day of the week Sunday through Saturday
// time
2019-01-24 17:15:40 +00:00
"g" , "3" , // 12-hour format of an hour without leading zeros 1 through 12
2013-12-12 14:25:08 +00:00
"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 ,
}
2015-09-08 15:41:41 +00:00
// DateParse Parse Date use PHP time format.
2013-12-12 14:25:08 +00:00
func DateParse ( dateString , format string ) ( time . Time , error ) {
2013-12-21 12:44:34 +00:00
replacer := strings . NewReplacer ( datePatterns ... )
2013-12-12 14:25:08 +00:00
format = replacer . Replace ( format )
return time . ParseInLocation ( format , dateString , time . Local )
}
2013-12-21 05:19:24 +00:00
// Date takes a PHP like date func to Go's time format.
2013-12-12 14:25:08 +00:00
func Date ( t time . Time , format string ) string {
2013-12-21 12:44:34 +00:00
replacer := strings . NewReplacer ( datePatterns ... )
2013-12-12 14:25:08 +00:00
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.
2013-12-21 05:19:24 +00:00
// Whitespace is trimmed. Used by the template parser as "eq".
2013-12-12 14:25:08 +00:00
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
}
2015-09-08 15:41:41 +00:00
// CompareNot !Compare
2015-01-05 08:38:57 +00:00
func CompareNot ( a , b interface { } ) ( equal bool ) {
2015-09-08 02:43:42 +00:00
return ! Compare ( a , b )
2015-01-05 08:38:57 +00:00
}
2015-09-08 15:41:41 +00:00
// NotNil the same as CompareNot
func NotNil ( a interface { } ) ( isNil bool ) {
2015-09-08 02:43:42 +00:00
return CompareNot ( a , nil )
2015-01-05 08:38:57 +00:00
}
2016-01-17 16:18:21 +00:00
// GetConfig get the Appconfig
func GetConfig ( returnType , key string , defaultVal interface { } ) ( value interface { } , err error ) {
2014-05-31 04:48:23 +00:00
switch returnType {
case "String" :
value = AppConfig . String ( key )
case "Bool" :
value , err = AppConfig . Bool ( key )
case "Int" :
value , err = AppConfig . Int ( key )
case "Int64" :
value , err = AppConfig . Int64 ( key )
case "Float" :
value , err = AppConfig . Float ( key )
case "DIY" :
value , err = AppConfig . DIY ( key )
default :
2019-01-22 11:09:57 +00:00
err = errors . New ( "config keys must be of type String, Bool, Int, Int64, Float, or DIY" )
2014-05-31 04:48:23 +00:00
}
if err != nil {
if reflect . TypeOf ( returnType ) != reflect . TypeOf ( defaultVal ) {
2015-09-08 15:41:41 +00:00
err = errors . New ( "defaultVal type does not match returnType" )
2014-05-31 04:48:23 +00:00
} 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
}
2015-09-08 15:41:41 +00:00
// Str2html Convert string to template.HTML type.
2013-12-12 14:25:08 +00:00
func Str2html ( raw string ) template . HTML {
return template . HTML ( raw )
}
2013-12-21 05:19:24 +00:00
// Htmlquote returns quoted html string.
2017-03-17 17:45:30 +00:00
func Htmlquote ( text string ) string {
2013-12-12 14:25:08 +00:00
//HTML编码为实体符号
/ *
Encodes ` text ` for raw use in HTML .
>> > htmlquote ( "<'&\\" > " )
' & lt ; & # 39 ; & amp ; & quot ; & gt ; '
* /
2018-06-13 07:43:01 +00:00
text = html . EscapeString ( text )
text = strings . NewReplacer (
` “ ` , "“" ,
` ” ` , "”" ,
` ` , " " ,
) . Replace ( text )
2013-12-12 14:25:08 +00:00
return strings . TrimSpace ( text )
}
2013-12-21 05:19:24 +00:00
// Htmlunquote returns unquoted html string.
2017-03-17 17:45:30 +00:00
func Htmlunquote ( text string ) string {
2013-12-12 14:25:08 +00:00
//实体符号解释为HTML
/ *
Decodes ` text ` that ' s HTML quoted .
>> > htmlunquote ( ' & lt ; & # 39 ; & amp ; & quot ; & gt ; ' )
' < \ \ ' & " > '
* /
2018-06-13 07:43:01 +00:00
text = html . UnescapeString ( text )
2013-12-12 14:25:08 +00:00
return strings . TrimSpace ( text )
}
2015-09-08 02:43:42 +00:00
// URLFor returns url string with another registered controller handler with params.
2013-12-21 05:19:24 +00:00
// usage:
2014-05-16 16:12:25 +00:00
//
2015-09-08 02:43:42 +00:00
// URLFor(".index")
// print URLFor("index")
2014-05-16 16:12:25 +00:00
// router /login
2015-09-08 02:43:42 +00:00
// print URLFor("login")
// print URLFor("login", "next","/"")
2014-05-16 16:12:25 +00:00
// router /profile/:username
// print UrlFor("profile", ":username","John Doe")
2013-12-21 05:19:24 +00:00
// result:
2013-12-12 14:25:08 +00:00
// /
// /login
// /login?next=/
// /user/John%20Doe
2014-05-16 16:12:25 +00:00
//
// more detail http://beego.me/docs/mvc/controller/urlbuilding.md
2015-09-08 02:43:42 +00:00
func URLFor ( endpoint string , values ... interface { } ) string {
2015-09-08 14:01:13 +00:00
return BeeApp . Handlers . URLFor ( endpoint , values ... )
2013-12-12 14:25:08 +00:00
}
2015-09-08 15:41:41 +00:00
// AssetsJs returns script tag with src string.
2017-03-17 17:45:30 +00:00
func AssetsJs ( text string ) template . HTML {
2013-12-12 14:25:08 +00:00
2017-03-17 17:45:30 +00:00
text = "<script src=\"" + text + "\"></script>"
2013-12-12 14:25:08 +00:00
return template . HTML ( text )
}
2015-09-08 15:41:41 +00:00
// AssetsCSS returns stylesheet link tag with src string.
2017-03-17 17:45:30 +00:00
func AssetsCSS ( text string ) template . HTML {
2013-12-12 14:25:08 +00:00
2017-03-17 17:45:30 +00:00
text = "<link href=\"" + text + "\" rel=\"stylesheet\" />"
2013-12-12 14:25:08 +00:00
return template . HTML ( text )
}
2015-09-08 15:41:41 +00:00
// ParseForm will parse form values to struct via tag.
2016-09-01 07:04:57 +00:00
// Support for anonymous struct.
func parseFormToStruct ( form url . Values , objT reflect . Type , objV reflect . Value ) error {
2013-12-12 14:25:08 +00:00
for i := 0 ; i < objT . NumField ( ) ; i ++ {
fieldV := objV . Field ( i )
if ! fieldV . CanSet ( ) {
continue
}
fieldT := objT . Field ( i )
2016-09-01 07:04:57 +00:00
if fieldT . Anonymous && fieldT . Type . Kind ( ) == reflect . Struct {
err := parseFormToStruct ( form , fieldT . Type , fieldV )
if err != nil {
return err
}
continue
}
2013-12-12 14:25:08 +00:00
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 ]
}
2019-01-25 04:00:24 +00:00
formValues := form [ tag ]
var value string
if len ( formValues ) == 0 {
2019-04-21 02:27:35 +00:00
defaultValue := fieldT . Tag . Get ( "default" )
if defaultValue != "" {
value = defaultValue
} else {
continue
}
2013-12-12 14:25:08 +00:00
}
2019-01-25 04:00:24 +00:00
if len ( formValues ) == 1 {
value = formValues [ 0 ]
if value == "" {
2019-01-24 17:15:40 +00:00
continue
}
}
2013-12-12 14:25:08 +00:00
switch fieldT . Type . Kind ( ) {
case reflect . Bool :
2014-11-04 08:19:46 +00:00
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
}
2013-12-12 14:25:08 +00:00
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 )
2014-11-04 08:19:46 +00:00
case reflect . Struct :
switch fieldT . Type . String ( ) {
case "time.Time" :
2017-02-27 06:43:16 +00:00
var (
t time . Time
err error
)
2017-03-24 23:52:43 +00:00
if len ( value ) >= 25 {
value = value [ : 25 ]
t , err = time . ParseInLocation ( time . RFC3339 , value , time . Local )
2019-05-31 07:47:21 +00:00
} else if strings . HasSuffix ( strings . ToUpper ( value ) , "Z" ) {
2020-10-10 13:34:02 +00:00
t , err = time . ParseInLocation ( time . RFC3339 , value , time . Local )
2017-03-24 23:52:43 +00:00
} else if len ( value ) >= 19 {
2017-05-19 01:22:27 +00:00
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 )
}
2017-02-27 06:43:16 +00:00
} 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 )
2014-11-04 08:19:46 +00:00
}
if err != nil {
return err
}
fieldV . Set ( reflect . ValueOf ( t ) )
}
2015-02-22 17:13:06 +00:00
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 ] )
}
}
2013-12-12 14:25:08 +00:00
}
}
return nil
}
2016-09-01 07:04:57 +00:00
// 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 )
}
2015-02-22 17:13:06 +00:00
var sliceOfInts = reflect . TypeOf ( [ ] int ( nil ) )
var sliceOfStrings = reflect . TypeOf ( [ ] string ( nil ) )
2013-12-12 14:25:08 +00:00
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 ,
}
2015-09-08 15:41:41 +00:00
// RenderForm will render object to form html.
2013-12-21 05:19:24 +00:00
// obj must be a struct pointer.
2013-12-12 14:25:08 +00:00
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 )
2014-06-29 17:19:32 +00:00
2016-06-28 22:19:58 +00:00
label , name , fType , id , class , ignored , required := parseFormTag ( fieldT )
2014-07-03 15:40:21 +00:00
if ignored {
continue
}
2013-12-12 14:25:08 +00:00
2016-06-16 00:17:50 +00:00
raw = append ( raw , renderFormField ( label , name , fType , fieldV . Interface ( ) , id , class , required ) )
2013-12-12 14:25:08 +00:00
}
return template . HTML ( strings . Join ( raw , "</br>" ) )
}
2014-06-30 08:38:32 +00:00
// renderFormField returns a string containing HTML of a single form field.
2016-06-16 00:17:50 +00:00
func renderFormField ( label , name , fType string , value interface { } , id string , class string , required bool ) string {
2014-10-20 10:59:46 +00:00
if id != "" {
2014-10-20 14:23:29 +00:00
id = " id=\"" + id + "\""
2014-10-20 10:59:46 +00:00
}
if class != "" {
2014-10-20 14:23:29 +00:00
class = " class=\"" + class + "\""
2014-10-20 10:59:46 +00:00
}
2016-06-16 00:17:50 +00:00
requiredString := ""
if required {
requiredString = " required"
}
2014-07-03 15:40:21 +00:00
if isValidForInput ( fType ) {
2016-06-16 00:17:50 +00:00
return fmt . Sprintf ( ` %v<input%v%v name="%v" type="%v" value="%v"%v> ` , label , id , class , name , fType , value , requiredString )
2014-07-03 15:40:21 +00:00
}
2014-06-29 18:30:11 +00:00
2016-06-28 23:39:09 +00:00
return fmt . Sprintf ( ` %v<%v%v%v name="%v"%v>%v</%v> ` , label , fType , id , class , name , requiredString , value , fType )
2014-06-29 18:30:11 +00:00
}
2014-06-30 08:38:32 +00:00
// isValidForInput checks if fType is a valid value for the `type` property of an HTML input element.
2014-06-29 18:30:11 +00:00
func isValidForInput ( fType string ) bool {
2014-07-03 15:40:21 +00:00
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
2014-06-29 18:30:11 +00:00
}
2014-06-29 17:19:32 +00:00
// 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.
2016-06-28 22:19:58 +00:00
func parseFormTag ( fieldT reflect . StructField ) ( label , name , fType string , id string , class string , ignored bool , required bool ) {
2014-07-03 15:40:21 +00:00
tags := strings . Split ( fieldT . Tag . Get ( "form" ) , "," )
label = fieldT . Name + ": "
name = fieldT . Name
fType = "text"
ignored = false
2014-10-20 09:49:16 +00:00
id = fieldT . Tag . Get ( "id" )
class = fieldT . Tag . Get ( "class" )
2014-07-03 15:40:21 +00:00
2016-06-28 22:19:58 +00:00
required = false
2017-04-29 01:13:28 +00:00
requiredField := fieldT . Tag . Get ( "required" )
if requiredField != "-" && requiredField != "" {
required , _ = strconv . ParseBool ( requiredField )
2016-06-28 22:19:58 +00:00
}
2014-07-03 15:40:21 +00:00
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 ]
}
}
2016-06-16 00:17:50 +00:00
return
}
2013-12-12 14:25:08 +00:00
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
}
2015-09-08 15:41:41 +00:00
// MapGet getting value from map by keys
2015-08-16 19:08:02 +00:00
// usage:
2018-08-20 20:55:50 +00:00
// Data["m"] = M{
2015-08-16 19:08:02 +00:00
// "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
2016-01-17 15:48:17 +00:00
// if they are different, make conversion
2015-08-16 19:08:02 +00:00
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 )
2015-08-16 20:18:29 +00:00
if storedVal . IsValid ( ) {
var result interface { }
switch arg1Type . Elem ( ) . Kind ( ) {
2015-09-08 02:43:42 +00:00
case reflect . Bool :
2015-08-16 20:18:29 +00:00
result = storedVal . Bool ( )
2015-09-08 02:43:42 +00:00
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
2015-08-16 20:18:29 +00:00
result = storedVal . Int ( )
2015-09-08 02:43:42 +00:00
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr :
2015-08-16 20:18:29 +00:00
result = storedVal . Uint ( )
2015-09-08 02:43:42 +00:00
case reflect . Float32 , reflect . Float64 :
2015-08-16 20:18:29 +00:00
result = storedVal . Float ( )
2015-09-08 02:43:42 +00:00
case reflect . String :
2015-08-16 20:18:29 +00:00
result = storedVal . String ( )
2015-09-08 02:43:42 +00:00
default :
2015-08-16 20:18:29 +00:00
result = storedVal . Interface ( )
}
2015-08-16 19:08:02 +00:00
2015-08-16 20:18:29 +00:00
// if there is more keys, handle this recursively
if len ( arg2 ) > 1 {
return MapGet ( result , arg2 [ 1 : ] ... )
}
2015-09-08 15:41:41 +00:00
return result , nil
2015-08-16 19:08:02 +00:00
}
return nil , nil
2015-09-08 15:41:41 +00:00
2015-08-16 19:08:02 +00:00
}
2015-09-08 15:41:41 +00:00
return nil , nil
2015-08-16 19:08:02 +00:00
}