github.com/kovansky/hugo@v0.92.3-0.20220224232819-63076e4ff19f/tpl/internal/go_templates/texttemplate/funcs.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package template 6 7 import ( 8 "bytes" 9 "errors" 10 "fmt" 11 "io" 12 "net/url" 13 "reflect" 14 "strings" 15 "sync" 16 "unicode" 17 "unicode/utf8" 18 ) 19 20 // FuncMap is the type of the map defining the mapping from names to functions. 21 // Each function must have either a single return value, or two return values of 22 // which the second has type error. In that case, if the second (error) 23 // return value evaluates to non-nil during execution, execution terminates and 24 // Execute returns that error. 25 // 26 // When template execution invokes a function with an argument list, that list 27 // must be assignable to the function's parameter types. Functions meant to 28 // apply to arguments of arbitrary type can use parameters of type interface{} or 29 // of type reflect.Value. Similarly, functions meant to return a result of arbitrary 30 // type can return interface{} or reflect.Value. 31 type FuncMap map[string]interface{} 32 33 // builtins returns the FuncMap. 34 // It is not a global variable so the linker can dead code eliminate 35 // more when this isn't called. See golang.org/issue/36021. 36 // TODO: revert this back to a global map once golang.org/issue/2559 is fixed. 37 func builtins() FuncMap { 38 return FuncMap{ 39 "and": and, 40 "call": call, 41 "html": HTMLEscaper, 42 "index": index, 43 "safeIndex": safeIndex, 44 "slice": slice, 45 "js": JSEscaper, 46 "len": length, 47 "not": not, 48 "or": or, 49 "print": fmt.Sprint, 50 "printf": fmt.Sprintf, 51 "println": fmt.Sprintln, 52 "urlquery": URLQueryEscaper, 53 54 // Comparisons 55 "eq": eq, // == 56 "ge": ge, // >= 57 "gt": gt, // > 58 "le": le, // <= 59 "lt": lt, // < 60 "ne": ne, // != 61 } 62 } 63 64 var builtinFuncsOnce struct { 65 sync.Once 66 v map[string]reflect.Value 67 } 68 69 // builtinFuncsOnce lazily computes & caches the builtinFuncs map. 70 // TODO: revert this back to a global map once golang.org/issue/2559 is fixed. 71 func builtinFuncs() map[string]reflect.Value { 72 builtinFuncsOnce.Do(func() { 73 builtinFuncsOnce.v = createValueFuncs(builtins()) 74 }) 75 return builtinFuncsOnce.v 76 } 77 78 // createValueFuncs turns a FuncMap into a map[string]reflect.Value 79 func createValueFuncs(funcMap FuncMap) map[string]reflect.Value { 80 m := make(map[string]reflect.Value) 81 addValueFuncs(m, funcMap) 82 return m 83 } 84 85 // addValueFuncs adds to values the functions in funcs, converting them to reflect.Values. 86 func addValueFuncs(out map[string]reflect.Value, in FuncMap) { 87 for name, fn := range in { 88 if !goodName(name) { 89 panic(fmt.Errorf("function name %q is not a valid identifier", name)) 90 } 91 v := reflect.ValueOf(fn) 92 if v.Kind() != reflect.Func { 93 panic("value for " + name + " not a function") 94 } 95 if !goodFunc(v.Type()) { 96 panic(fmt.Errorf("can't install method/function %q with %d results", name, v.Type().NumOut())) 97 } 98 out[name] = v 99 } 100 } 101 102 // addFuncs adds to values the functions in funcs. It does no checking of the input - 103 // call addValueFuncs first. 104 func addFuncs(out, in FuncMap) { 105 for name, fn := range in { 106 out[name] = fn 107 } 108 } 109 110 // goodFunc reports whether the function or method has the right result signature. 111 func goodFunc(typ reflect.Type) bool { 112 // We allow functions with 1 result or 2 results where the second is an error. 113 switch { 114 case typ.NumOut() == 1: 115 return true 116 case typ.NumOut() == 2 && typ.Out(1) == errorType: 117 return true 118 } 119 return false 120 } 121 122 // goodName reports whether the function name is a valid identifier. 123 func goodName(name string) bool { 124 if name == "" { 125 return false 126 } 127 for i, r := range name { 128 switch { 129 case r == '_': 130 case i == 0 && !unicode.IsLetter(r): 131 return false 132 case !unicode.IsLetter(r) && !unicode.IsDigit(r): 133 return false 134 } 135 } 136 return true 137 } 138 139 // findFunction looks for a function in the template, and global map. 140 func findFunction(name string, tmpl *Template) (reflect.Value, bool) { 141 if tmpl != nil && tmpl.common != nil { 142 tmpl.muFuncs.RLock() 143 defer tmpl.muFuncs.RUnlock() 144 if fn := tmpl.execFuncs[name]; fn.IsValid() { 145 return fn, true 146 } 147 } 148 if fn := builtinFuncs()[name]; fn.IsValid() { 149 return fn, true 150 } 151 return reflect.Value{}, false 152 } 153 154 // prepareArg checks if value can be used as an argument of type argType, and 155 // converts an invalid value to appropriate zero if possible. 156 func prepareArg(value reflect.Value, argType reflect.Type) (reflect.Value, error) { 157 if !value.IsValid() { 158 if !canBeNil(argType) { 159 return reflect.Value{}, fmt.Errorf("value is nil; should be of type %s", argType) 160 } 161 value = reflect.Zero(argType) 162 } 163 if value.Type().AssignableTo(argType) { 164 return value, nil 165 } 166 if intLike(value.Kind()) && intLike(argType.Kind()) && value.Type().ConvertibleTo(argType) { 167 value = value.Convert(argType) 168 return value, nil 169 } 170 return reflect.Value{}, fmt.Errorf("value has type %s; should be %s", value.Type(), argType) 171 } 172 173 func intLike(typ reflect.Kind) bool { 174 switch typ { 175 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 176 return true 177 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 178 return true 179 } 180 return false 181 } 182 183 // indexArg checks if a reflect.Value can be used as an index, and converts it to int if possible. 184 func indexArg(index reflect.Value, cap int) (int, error) { 185 var x int64 186 switch index.Kind() { 187 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 188 x = index.Int() 189 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 190 x = int64(index.Uint()) 191 case reflect.Invalid: 192 return 0, fmt.Errorf("cannot index slice/array with nil") 193 default: 194 return 0, fmt.Errorf("cannot index slice/array with type %s", index.Type()) 195 } 196 if x < 0 || int(x) < 0 || int(x) > cap { 197 return 0, fmt.Errorf("index out of range: %d", x) 198 } 199 return int(x), nil 200 } 201 202 // Indexing. 203 204 // index returns the result of indexing its first argument by the following 205 // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each 206 // indexed item must be a map, slice, or array. 207 func index(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { 208 item = indirectInterface(item) 209 if !item.IsValid() { 210 return reflect.Value{}, fmt.Errorf("index of untyped nil") 211 } 212 for _, index := range indexes { 213 index = indirectInterface(index) 214 var isNil bool 215 if item, isNil = indirect(item); isNil { 216 return reflect.Value{}, fmt.Errorf("index of nil pointer") 217 } 218 switch item.Kind() { 219 case reflect.Array, reflect.Slice, reflect.String: 220 x, err := indexArg(index, item.Len()) 221 if err != nil { 222 return reflect.Value{}, err 223 } 224 item = item.Index(x) 225 case reflect.Map: 226 index, err := prepareArg(index, item.Type().Key()) 227 if err != nil { 228 return reflect.Value{}, err 229 } 230 if x := item.MapIndex(index); x.IsValid() { 231 item = x 232 } else { 233 item = reflect.Zero(item.Type().Elem()) 234 } 235 case reflect.Invalid: 236 // the loop holds invariant: item.IsValid() 237 panic("unreachable") 238 default: 239 return reflect.Value{}, fmt.Errorf("can't index item of type %s", item.Type()) 240 } 241 } 242 return item, nil 243 } 244 245 // safeIndex returns the result of indexing its first argument by the following 246 // arguments. Thus "index x 1 2 3" is, in Go syntax, x[1][2][3]. Each 247 // indexed item must be a map, slice, or array. Doesn't return errors, just empty value. 248 func safeIndex(item reflect.Value, indexes ...reflect.Value) reflect.Value { 249 item, err := index(item, indexes...) 250 if err != nil { 251 return reflect.Value{} 252 } 253 254 return item 255 } 256 257 // Slicing. 258 259 // slice returns the result of slicing its first argument by the remaining 260 // arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2], while "slice x" 261 // is x[:], "slice x 1" is x[1:], and "slice x 1 2 3" is x[1:2:3]. The first 262 // argument must be a string, slice, or array. 263 func slice(item reflect.Value, indexes ...reflect.Value) (reflect.Value, error) { 264 item = indirectInterface(item) 265 if !item.IsValid() { 266 return reflect.Value{}, fmt.Errorf("slice of untyped nil") 267 } 268 if len(indexes) > 3 { 269 return reflect.Value{}, fmt.Errorf("too many slice indexes: %d", len(indexes)) 270 } 271 var cap int 272 switch item.Kind() { 273 case reflect.String: 274 if len(indexes) == 3 { 275 return reflect.Value{}, fmt.Errorf("cannot 3-index slice a string") 276 } 277 cap = item.Len() 278 case reflect.Array, reflect.Slice: 279 cap = item.Cap() 280 default: 281 return reflect.Value{}, fmt.Errorf("can't slice item of type %s", item.Type()) 282 } 283 // set default values for cases item[:], item[i:]. 284 idx := [3]int{0, item.Len()} 285 for i, index := range indexes { 286 x, err := indexArg(index, cap) 287 if err != nil { 288 return reflect.Value{}, err 289 } 290 idx[i] = x 291 } 292 // given item[i:j], make sure i <= j. 293 if idx[0] > idx[1] { 294 return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[0], idx[1]) 295 } 296 if len(indexes) < 3 { 297 return item.Slice(idx[0], idx[1]), nil 298 } 299 // given item[i:j:k], make sure i <= j <= k. 300 if idx[1] > idx[2] { 301 return reflect.Value{}, fmt.Errorf("invalid slice index: %d > %d", idx[1], idx[2]) 302 } 303 return item.Slice3(idx[0], idx[1], idx[2]), nil 304 } 305 306 // Length 307 308 // length returns the length of the item, with an error if it has no defined length. 309 func length(item reflect.Value) (int, error) { 310 item, isNil := indirect(item) 311 if isNil { 312 return 0, fmt.Errorf("len of nil pointer") 313 } 314 switch item.Kind() { 315 case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: 316 return item.Len(), nil 317 } 318 return 0, fmt.Errorf("len of type %s", item.Type()) 319 } 320 321 // Function invocation 322 323 // call returns the result of evaluating the first argument as a function. 324 // The function must return 1 result, or 2 results, the second of which is an error. 325 func call(fn reflect.Value, args ...reflect.Value) (reflect.Value, error) { 326 fn = indirectInterface(fn) 327 if !fn.IsValid() { 328 return reflect.Value{}, fmt.Errorf("call of nil") 329 } 330 typ := fn.Type() 331 if typ.Kind() != reflect.Func { 332 return reflect.Value{}, fmt.Errorf("non-function of type %s", typ) 333 } 334 if !goodFunc(typ) { 335 return reflect.Value{}, fmt.Errorf("function called with %d args; should be 1 or 2", typ.NumOut()) 336 } 337 numIn := typ.NumIn() 338 var dddType reflect.Type 339 if typ.IsVariadic() { 340 if len(args) < numIn-1 { 341 return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want at least %d", len(args), numIn-1) 342 } 343 dddType = typ.In(numIn - 1).Elem() 344 } else { 345 if len(args) != numIn { 346 return reflect.Value{}, fmt.Errorf("wrong number of args: got %d want %d", len(args), numIn) 347 } 348 } 349 argv := make([]reflect.Value, len(args)) 350 for i, arg := range args { 351 arg = indirectInterface(arg) 352 // Compute the expected type. Clumsy because of variadics. 353 argType := dddType 354 if !typ.IsVariadic() || i < numIn-1 { 355 argType = typ.In(i) 356 } 357 358 var err error 359 if argv[i], err = prepareArg(arg, argType); err != nil { 360 return reflect.Value{}, fmt.Errorf("arg %d: %s", i, err) 361 } 362 } 363 return safeCall(fn, argv) 364 } 365 366 // safeCall runs fun.Call(args), and returns the resulting value and error, if 367 // any. If the call panics, the panic value is returned as an error. 368 func safeCall(fun reflect.Value, args []reflect.Value) (val reflect.Value, err error) { 369 defer func() { 370 if r := recover(); r != nil { 371 if e, ok := r.(error); ok { 372 err = e 373 } else { 374 err = fmt.Errorf("%v", r) 375 } 376 } 377 }() 378 ret := fun.Call(args) 379 if len(ret) == 2 && !ret[1].IsNil() { 380 return ret[0], ret[1].Interface().(error) 381 } 382 return ret[0], nil 383 } 384 385 // Boolean logic. 386 387 func truth(arg reflect.Value) bool { 388 t, _ := isTrue(indirectInterface(arg)) 389 return t 390 } 391 392 // and computes the Boolean AND of its arguments, returning 393 // the first false argument it encounters, or the last argument. 394 func and(arg0 reflect.Value, args ...reflect.Value) reflect.Value { 395 if !truth(arg0) { 396 return arg0 397 } 398 for i := range args { 399 arg0 = args[i] 400 if !truth(arg0) { 401 break 402 } 403 } 404 return arg0 405 } 406 407 // or computes the Boolean OR of its arguments, returning 408 // the first true argument it encounters, or the last argument. 409 func or(arg0 reflect.Value, args ...reflect.Value) reflect.Value { 410 if truth(arg0) { 411 return arg0 412 } 413 for i := range args { 414 arg0 = args[i] 415 if truth(arg0) { 416 break 417 } 418 } 419 return arg0 420 } 421 422 // not returns the Boolean negation of its argument. 423 func not(arg reflect.Value) bool { 424 return !truth(arg) 425 } 426 427 // Comparison. 428 429 // TODO: Perhaps allow comparison between signed and unsigned integers. 430 431 var ( 432 errBadComparisonType = errors.New("invalid type for comparison") 433 errBadComparison = errors.New("incompatible types for comparison") 434 errNoComparison = errors.New("missing argument for comparison") 435 ) 436 437 type kind int 438 439 const ( 440 invalidKind kind = iota 441 boolKind 442 complexKind 443 intKind 444 floatKind 445 stringKind 446 uintKind 447 ) 448 449 func basicKind(v reflect.Value) (kind, error) { 450 switch v.Kind() { 451 case reflect.Bool: 452 return boolKind, nil 453 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 454 return intKind, nil 455 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 456 return uintKind, nil 457 case reflect.Float32, reflect.Float64: 458 return floatKind, nil 459 case reflect.Complex64, reflect.Complex128: 460 return complexKind, nil 461 case reflect.String: 462 return stringKind, nil 463 } 464 return invalidKind, errBadComparisonType 465 } 466 467 // eq evaluates the comparison a == b || a == c || ... 468 func eq(arg1 reflect.Value, arg2 ...reflect.Value) (bool, error) { 469 arg1 = indirectInterface(arg1) 470 if arg1 != zero { 471 if t1 := arg1.Type(); !t1.Comparable() { 472 return false, fmt.Errorf("uncomparable type %s: %v", t1, arg1) 473 } 474 } 475 if len(arg2) == 0 { 476 return false, errNoComparison 477 } 478 k1, _ := basicKind(arg1) 479 for _, arg := range arg2 { 480 arg = indirectInterface(arg) 481 k2, _ := basicKind(arg) 482 truth := false 483 if k1 != k2 { 484 // Special case: Can compare integer values regardless of type's sign. 485 switch { 486 case k1 == intKind && k2 == uintKind: 487 truth = arg1.Int() >= 0 && uint64(arg1.Int()) == arg.Uint() 488 case k1 == uintKind && k2 == intKind: 489 truth = arg.Int() >= 0 && arg1.Uint() == uint64(arg.Int()) 490 default: 491 return false, errBadComparison 492 } 493 } else { 494 switch k1 { 495 case boolKind: 496 truth = arg1.Bool() == arg.Bool() 497 case complexKind: 498 truth = arg1.Complex() == arg.Complex() 499 case floatKind: 500 truth = arg1.Float() == arg.Float() 501 case intKind: 502 truth = arg1.Int() == arg.Int() 503 case stringKind: 504 truth = arg1.String() == arg.String() 505 case uintKind: 506 truth = arg1.Uint() == arg.Uint() 507 default: 508 if arg == zero { 509 truth = arg1 == arg 510 } else { 511 if t2 := arg.Type(); !t2.Comparable() { 512 return false, fmt.Errorf("uncomparable type %s: %v", t2, arg) 513 } 514 truth = arg1.Interface() == arg.Interface() 515 } 516 } 517 } 518 if truth { 519 return true, nil 520 } 521 } 522 return false, nil 523 } 524 525 // ne evaluates the comparison a != b. 526 func ne(arg1, arg2 reflect.Value) (bool, error) { 527 // != is the inverse of ==. 528 equal, err := eq(arg1, arg2) 529 return !equal, err 530 } 531 532 // lt evaluates the comparison a < b. 533 func lt(arg1, arg2 reflect.Value) (bool, error) { 534 arg1 = indirectInterface(arg1) 535 k1, err := basicKind(arg1) 536 if err != nil { 537 return false, err 538 } 539 arg2 = indirectInterface(arg2) 540 k2, err := basicKind(arg2) 541 if err != nil { 542 return false, err 543 } 544 truth := false 545 if k1 != k2 { 546 // Special case: Can compare integer values regardless of type's sign. 547 switch { 548 case k1 == intKind && k2 == uintKind: 549 truth = arg1.Int() < 0 || uint64(arg1.Int()) < arg2.Uint() 550 case k1 == uintKind && k2 == intKind: 551 truth = arg2.Int() >= 0 && arg1.Uint() < uint64(arg2.Int()) 552 default: 553 return false, errBadComparison 554 } 555 } else { 556 switch k1 { 557 case boolKind, complexKind: 558 return false, errBadComparisonType 559 case floatKind: 560 truth = arg1.Float() < arg2.Float() 561 case intKind: 562 truth = arg1.Int() < arg2.Int() 563 case stringKind: 564 truth = arg1.String() < arg2.String() 565 case uintKind: 566 truth = arg1.Uint() < arg2.Uint() 567 default: 568 panic("invalid kind") 569 } 570 } 571 return truth, nil 572 } 573 574 // le evaluates the comparison <= b. 575 func le(arg1, arg2 reflect.Value) (bool, error) { 576 // <= is < or ==. 577 lessThan, err := lt(arg1, arg2) 578 if lessThan || err != nil { 579 return lessThan, err 580 } 581 return eq(arg1, arg2) 582 } 583 584 // gt evaluates the comparison a > b. 585 func gt(arg1, arg2 reflect.Value) (bool, error) { 586 // > is the inverse of <=. 587 lessOrEqual, err := le(arg1, arg2) 588 if err != nil { 589 return false, err 590 } 591 return !lessOrEqual, nil 592 } 593 594 // ge evaluates the comparison a >= b. 595 func ge(arg1, arg2 reflect.Value) (bool, error) { 596 // >= is the inverse of <. 597 lessThan, err := lt(arg1, arg2) 598 if err != nil { 599 return false, err 600 } 601 return !lessThan, nil 602 } 603 604 // HTML escaping. 605 606 var ( 607 htmlQuot = []byte(""") // shorter than """ 608 htmlApos = []byte("'") // shorter than "'" and apos was not in HTML until HTML5 609 htmlAmp = []byte("&") 610 htmlLt = []byte("<") 611 htmlGt = []byte(">") 612 htmlNull = []byte("\uFFFD") 613 ) 614 615 // HTMLEscape writes to w the escaped HTML equivalent of the plain text data b. 616 func HTMLEscape(w io.Writer, b []byte) { 617 last := 0 618 for i, c := range b { 619 var html []byte 620 switch c { 621 case '\000': 622 html = htmlNull 623 case '"': 624 html = htmlQuot 625 case '\'': 626 html = htmlApos 627 case '&': 628 html = htmlAmp 629 case '<': 630 html = htmlLt 631 case '>': 632 html = htmlGt 633 default: 634 continue 635 } 636 w.Write(b[last:i]) 637 w.Write(html) 638 last = i + 1 639 } 640 w.Write(b[last:]) 641 } 642 643 // HTMLEscapeString returns the escaped HTML equivalent of the plain text data s. 644 func HTMLEscapeString(s string) string { 645 // Avoid allocation if we can. 646 if !strings.ContainsAny(s, "'\"&<>\000") { 647 return s 648 } 649 var b bytes.Buffer 650 HTMLEscape(&b, []byte(s)) 651 return b.String() 652 } 653 654 // HTMLEscaper returns the escaped HTML equivalent of the textual 655 // representation of its arguments. 656 func HTMLEscaper(args ...interface{}) string { 657 return HTMLEscapeString(evalArgs(args)) 658 } 659 660 // JavaScript escaping. 661 662 var ( 663 jsLowUni = []byte(`\u00`) 664 hex = []byte("0123456789ABCDEF") 665 666 jsBackslash = []byte(`\\`) 667 jsApos = []byte(`\'`) 668 jsQuot = []byte(`\"`) 669 jsLt = []byte(`\u003C`) 670 jsGt = []byte(`\u003E`) 671 jsAmp = []byte(`\u0026`) 672 jsEq = []byte(`\u003D`) 673 ) 674 675 // JSEscape writes to w the escaped JavaScript equivalent of the plain text data b. 676 func JSEscape(w io.Writer, b []byte) { 677 last := 0 678 for i := 0; i < len(b); i++ { 679 c := b[i] 680 681 if !jsIsSpecial(rune(c)) { 682 // fast path: nothing to do 683 continue 684 } 685 w.Write(b[last:i]) 686 687 if c < utf8.RuneSelf { 688 // Quotes, slashes and angle brackets get quoted. 689 // Control characters get written as \u00XX. 690 switch c { 691 case '\\': 692 w.Write(jsBackslash) 693 case '\'': 694 w.Write(jsApos) 695 case '"': 696 w.Write(jsQuot) 697 case '<': 698 w.Write(jsLt) 699 case '>': 700 w.Write(jsGt) 701 case '&': 702 w.Write(jsAmp) 703 case '=': 704 w.Write(jsEq) 705 default: 706 w.Write(jsLowUni) 707 t, b := c>>4, c&0x0f 708 w.Write(hex[t : t+1]) 709 w.Write(hex[b : b+1]) 710 } 711 } else { 712 // Unicode rune. 713 r, size := utf8.DecodeRune(b[i:]) 714 if unicode.IsPrint(r) { 715 w.Write(b[i : i+size]) 716 } else { 717 fmt.Fprintf(w, "\\u%04X", r) 718 } 719 i += size - 1 720 } 721 last = i + 1 722 } 723 w.Write(b[last:]) 724 } 725 726 // JSEscapeString returns the escaped JavaScript equivalent of the plain text data s. 727 func JSEscapeString(s string) string { 728 // Avoid allocation if we can. 729 if strings.IndexFunc(s, jsIsSpecial) < 0 { 730 return s 731 } 732 var b bytes.Buffer 733 JSEscape(&b, []byte(s)) 734 return b.String() 735 } 736 737 func jsIsSpecial(r rune) bool { 738 switch r { 739 case '\\', '\'', '"', '<', '>', '&', '=': 740 return true 741 } 742 return r < ' ' || utf8.RuneSelf <= r 743 } 744 745 // JSEscaper returns the escaped JavaScript equivalent of the textual 746 // representation of its arguments. 747 func JSEscaper(args ...interface{}) string { 748 return JSEscapeString(evalArgs(args)) 749 } 750 751 // URLQueryEscaper returns the escaped value of the textual representation of 752 // its arguments in a form suitable for embedding in a URL query. 753 func URLQueryEscaper(args ...interface{}) string { 754 return url.QueryEscape(evalArgs(args)) 755 } 756 757 // evalArgs formats the list of arguments into a string. It is therefore equivalent to 758 // fmt.Sprint(args...) 759 // except that each argument is indirected (if a pointer), as required, 760 // using the same rules as the default string evaluation during template 761 // execution. 762 func evalArgs(args []interface{}) string { 763 ok := false 764 var s string 765 // Fast path for simple common case. 766 if len(args) == 1 { 767 s, ok = args[0].(string) 768 } 769 if !ok { 770 for i, arg := range args { 771 a, ok := printableValue(reflect.ValueOf(arg)) 772 if ok { 773 args[i] = a 774 } // else let fmt do its thing 775 } 776 s = fmt.Sprint(args...) 777 } 778 return s 779 }