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  	       '&lt;&#39;&amp;&quot;&gt;'
   209  	*/
   210  
   211  	text = html.EscapeString(text)
   212  	text = strings.NewReplacer(
   213  		`“`, "&ldquo;",
   214  		`”`, "&rdquo;",
   215  		` `, "&nbsp;",
   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('&lt;&#39;&amp;&quot;&gt;')
   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  }