1 package builtins
2
3 import (
4 "encoding/json"
5 "fmt"
6 "math"
7 "math/rand"
8 "net/url"
9 "regexp"
10 "sort"
11 "strings"
12 "time"
13 "unicode"
14 "unicode/utf8"
15
16 "github.com/pkg/errors"
17
18 "github.com/noirbizarre/gonja/exec"
19 u "github.com/noirbizarre/gonja/utils"
20 )
21
22 func init() {
23 rand.Seed(time.Now().Unix())
24 }
25
26
27 var Filters = exec.FilterSet{
28 "abs": filterAbs,
29 "attr": filterAttr,
30 "batch": filterBatch,
31 "capitalize": filterCapitalize,
32 "center": filterCenter,
33 "d": filterDefault,
34 "default": filterDefault,
35 "dictsort": filterDictSort,
36 "e": filterEscape,
37 "escape": filterEscape,
38 "filesizeformat": filterFileSize,
39 "first": filterFirst,
40 "float": filterFloat,
41 "forceescape": filterForceEscape,
42 "format": filterFormat,
43 "groupby": filterGroupBy,
44 "indent": filterIndent,
45 "int": filterInteger,
46 "join": filterJoin,
47 "last": filterLast,
48 "length": filterLength,
49 "list": filterList,
50 "lower": filterLower,
51 "map": filterMap,
52 "max": filterMax,
53 "min": filterMin,
54 "pprint": filterPPrint,
55 "random": filterRandom,
56 "reject": filterReject,
57 "rejectattr": filterRejectAttr,
58 "replace": filterReplace,
59 "reverse": filterReverse,
60 "round": filterRound,
61 "safe": filterSafe,
62 "select": filterSelect,
63 "selectattr": filterSelectAttr,
64 "slice": filterSlice,
65 "sort": filterSort,
66 "string": filterString,
67 "striptags": filterStriptags,
68 "sum": filterSum,
69 "title": filterTitle,
70 "tojson": filterToJSON,
71 "trim": filterTrim,
72 "truncate": filterTruncate,
73 "unique": filterUnique,
74 "upper": filterUpper,
75 "urlencode": filterUrlencode,
76 "urlize": filterUrlize,
77 "wordcount": filterWordcount,
78 "wordwrap": filterWordwrap,
79 "xmlattr": filterXMLAttr,
80 }
81
82 func filterAbs(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
83 if p := params.ExpectNothing(); p.IsError() {
84 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'abs'"))
85 }
86 if in.IsInteger() {
87 asInt := in.Integer()
88 if asInt < 0 {
89 return exec.AsValue(-asInt)
90 }
91 return in
92 } else if in.IsFloat() {
93 return exec.AsValue(math.Abs(in.Float()))
94 }
95 return exec.AsValue(math.Abs(in.Float()))
96 }
97
98 func filterAttr(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
99 p := params.ExpectArgs(1)
100 if p.IsError() {
101 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'attr'"))
102 }
103 attr := p.First().String()
104 value, _ := in.Getattr(attr)
105 return value
106 }
107
108 func filterBatch(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
109 p := params.Expect(1, []*exec.KwArg{{"fill_with", nil}})
110 if p.IsError() {
111 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'batch'"))
112 }
113 size := p.First().Integer()
114 out := []*exec.Value{}
115 var row []*exec.Value
116 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
117 if math.Mod(float64(idx), float64(size)) == 0 {
118 if row != nil {
119 out = append(out, exec.AsValue(row))
120 }
121 row = []*exec.Value{}
122 }
123 row = append(row, key)
124 return true
125 }, func() {})
126 if len(row) > 0 {
127 fillWith := p.KwArgs["fill_with"]
128 if !fillWith.IsNil() {
129 for len(row) < size {
130 row = append(row, fillWith)
131 }
132 }
133 out = append(out, exec.AsValue(row))
134 }
135 return exec.AsValue(out)
136 }
137
138 func filterCapitalize(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
139 if p := params.ExpectNothing(); p.IsError() {
140 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'capitalize'"))
141 }
142 if in.Len() <= 0 {
143 return exec.AsValue("")
144 }
145 t := in.String()
146 r, size := utf8.DecodeRuneInString(t)
147 return exec.AsValue(strings.ToUpper(string(r)) + strings.ToLower(t[size:]))
148 }
149
150 func filterCenter(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
151 p := params.ExpectArgs(1)
152 if p.IsError() {
153 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'center'"))
154 }
155 width := p.First().Integer()
156 slen := in.Len()
157 if width <= slen {
158 return in
159 }
160
161 spaces := width - slen
162 left := spaces/2 + spaces%2
163 right := spaces / 2
164
165 return exec.AsValue(fmt.Sprintf("%s%s%s", strings.Repeat(" ", left),
166 in.String(), strings.Repeat(" ", right)))
167 }
168
169 func filterDefault(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
170 p := params.Expect(1, []*exec.KwArg{{"boolean", false}})
171 if p.IsError() {
172 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'default'"))
173 }
174 defaultVal := p.First()
175 falsy := p.KwArgs["boolean"]
176 if falsy.Bool() && (in.IsError() || !in.IsTrue()) {
177 return defaultVal
178 } else if in.IsError() || in.IsNil() {
179 return defaultVal
180 }
181 return in
182 }
183
184 func sortByKey(in *exec.Value, caseSensitive bool, reverse bool) [][2]*exec.Value {
185 out := [][2]*exec.Value{}
186 in.IterateOrder(func(idx, count int, key, value *exec.Value) bool {
187 out = append(out, [2]*exec.Value{key, value})
188 return true
189 }, func() {}, reverse, true, caseSensitive)
190 return out
191 }
192
193 func sortByValue(in *exec.Value, caseSensitive, reverse bool) [][2]*exec.Value {
194 out := [][2]*exec.Value{}
195 items := in.Items()
196 var sorter func(i, j int) bool
197 switch {
198 case caseSensitive && reverse:
199 sorter = func(i, j int) bool {
200 return items[i].Value.String() > items[j].Value.String()
201 }
202 case caseSensitive && !reverse:
203 sorter = func(i, j int) bool {
204 return items[i].Value.String() < items[j].Value.String()
205 }
206 case !caseSensitive && reverse:
207 sorter = func(i, j int) bool {
208 return strings.ToLower(items[i].Value.String()) > strings.ToLower(items[j].Value.String())
209 }
210 case !caseSensitive && !reverse:
211 sorter = func(i, j int) bool {
212 return strings.ToLower(items[i].Value.String()) < strings.ToLower(items[j].Value.String())
213 }
214 }
215 sort.Slice(items, sorter)
216 for _, item := range items {
217 out = append(out, [2]*exec.Value{item.Key, item.Value})
218 }
219 return out
220 }
221
222 func filterDictSort(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
223 p := params.Expect(0, []*exec.KwArg{
224 {"case_sensitive", false},
225 {"by", "key"},
226 {"reverse", false},
227 })
228 if p.IsError() {
229 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'dictsort'"))
230 }
231
232 caseSensitive := p.KwArgs["case_sensitive"].Bool()
233 by := p.KwArgs["by"].String()
234 reverse := p.KwArgs["reverse"].Bool()
235
236 switch by {
237 case "key":
238 return exec.AsValue(sortByKey(in, caseSensitive, reverse))
239 case "value":
240 return exec.AsValue(sortByValue(in, caseSensitive, reverse))
241 default:
242 return exec.AsValue(errors.New(`by should be either 'key' or 'value`))
243 }
244 }
245
246 func filterEscape(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
247 if p := params.ExpectNothing(); p.IsError() {
248 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'escape'"))
249 }
250 if in.Safe {
251 return in
252 }
253 return exec.AsSafeValue(in.Escaped())
254 }
255
256 var (
257 bytesPrefixes = []string{"kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
258 binaryPrefixes = []string{"KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"}
259 )
260
261 func filterFileSize(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
262 p := params.Expect(0, []*exec.KwArg{{"binary", false}})
263 if p.IsError() {
264 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'filesizeformat'"))
265 }
266 bytes := in.Float()
267 binary := p.KwArgs["binary"].Bool()
268 var base float64
269 var prefixes []string
270 if binary {
271 base = 1024.0
272 prefixes = binaryPrefixes
273 } else {
274 base = 1000.0
275 prefixes = bytesPrefixes
276 }
277 if bytes == 1.0 {
278 return exec.AsValue("1 Byte")
279 } else if bytes < base {
280 return exec.AsValue(fmt.Sprintf("%.0f Bytes", bytes))
281 } else {
282 var i int
283 var unit float64
284 var prefix string
285 for i, prefix = range prefixes {
286 unit = math.Pow(base, float64(i+2))
287 if bytes < unit {
288 return exec.AsValue(fmt.Sprintf("%.1f %s", (base * bytes / unit), prefix))
289 }
290 }
291 return exec.AsValue(fmt.Sprintf("%.1f %s", (base * bytes / unit), prefix))
292 }
293 }
294
295 func filterFirst(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
296 if p := params.ExpectNothing(); p.IsError() {
297 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'first'"))
298 }
299 if in.CanSlice() && in.Len() > 0 {
300 return in.Index(0)
301 }
302 return exec.AsValue("")
303 }
304
305 func filterFloat(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
306 if p := params.ExpectNothing(); p.IsError() {
307 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'float'"))
308 }
309 return exec.AsValue(in.Float())
310 }
311
312 func filterForceEscape(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
313 if p := params.ExpectNothing(); p.IsError() {
314 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'forceescape'"))
315 }
316 return exec.AsSafeValue(in.Escaped())
317 }
318
319 func filterFormat(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
320 args := []interface{}{}
321 for _, arg := range params.Args {
322 args = append(args, arg.Interface())
323 }
324 return exec.AsValue(fmt.Sprintf(in.String(), args...))
325 }
326
327 func filterGroupBy(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
328 p := params.ExpectArgs(1)
329 if p.IsError() {
330 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'groupby"))
331 }
332 field := p.First().String()
333 groups := map[interface{}][]*exec.Value{}
334 groupers := []interface{}{}
335
336 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
337 attr, found := key.Get(field)
338 if !found {
339 return true
340 }
341 lst, exists := groups[attr.Interface()]
342 if !exists {
343 lst = []*exec.Value{}
344 groupers = append(groupers, attr.Interface())
345 }
346 lst = append(lst, key)
347 groups[attr.Interface()] = lst
348 return true
349 }, func() {})
350
351 out := []map[string]*exec.Value{}
352 for _, grouper := range groupers {
353 out = append(out, map[string]*exec.Value{
354 "grouper": exec.AsValue(grouper),
355 "list": exec.AsValue(groups[grouper]),
356 })
357 }
358 return exec.AsValue(out)
359 }
360
361 func filterIndent(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
362 p := params.Expect(0, []*exec.KwArg{
363 {"width", 4},
364 {"first", false},
365 {"blank", false},
366 })
367 if p.IsError() {
368 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'indent'"))
369 }
370 width := p.KwArgs["width"].Integer()
371 first := p.KwArgs["first"].Bool()
372 blank := p.KwArgs["blank"].Bool()
373 indent := strings.Repeat(" ", width)
374 lines := strings.Split(in.String(), "\n")
375
376
377 var out strings.Builder
378 for idx, line := range lines {
379 if line == "" && !blank {
380 out.WriteByte('\n')
381 continue
382 }
383 if idx > 0 || first {
384 out.WriteString(indent)
385 }
386 out.WriteString(line)
387 out.WriteByte('\n')
388 }
389 return exec.AsValue(out.String())
390 }
391
392 func filterInteger(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
393 if p := params.ExpectNothing(); p.IsError() {
394 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'int'"))
395 }
396 return exec.AsValue(in.Integer())
397 }
398
399 func filterJoin(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
400 p := params.Expect(0, []*exec.KwArg{
401 {"d", ""},
402 {"attribute", nil},
403 })
404 if p.IsError() {
405 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'join'"))
406 }
407 if !in.CanSlice() {
408 return in
409 }
410 sep := p.KwArgs["d"].String()
411 sl := make([]string, 0, in.Len())
412 for i := 0; i < in.Len(); i++ {
413 sl = append(sl, in.Index(i).String())
414 }
415 return exec.AsValue(strings.Join(sl, sep))
416 }
417
418 func filterLast(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
419 if p := params.ExpectNothing(); p.IsError() {
420 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'last'"))
421 }
422 if in.CanSlice() && in.Len() > 0 {
423 return in.Index(in.Len() - 1)
424 }
425 return exec.AsValue("")
426 }
427
428 func filterLength(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
429 if p := params.ExpectNothing(); p.IsError() {
430 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'length'"))
431 }
432 return exec.AsValue(in.Len())
433 }
434
435 func filterList(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
436 if p := params.ExpectNothing(); p.IsError() {
437 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'list'"))
438 }
439 if in.IsString() {
440 out := []string{}
441 for _, r := range in.String() {
442 out = append(out, string(r))
443 }
444 return exec.AsValue(out)
445 }
446 out := []*exec.Value{}
447 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
448 out = append(out, key)
449 return true
450 }, func() {})
451 return exec.AsValue(out)
452 }
453
454 func filterLower(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
455 if p := params.ExpectNothing(); p.IsError() {
456 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'lower'"))
457 }
458 return exec.AsValue(strings.ToLower(in.String()))
459 }
460
461 func filterMap(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
462 p := params.Expect(0, []*exec.KwArg{
463 {"filter", ""},
464 {"attribute", nil},
465 {"default", nil},
466 })
467 if p.IsError() {
468 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'map'"))
469 }
470 filter := p.KwArgs["filter"].String()
471 attribute := p.KwArgs["attribute"].String()
472 defaultVal := p.KwArgs["default"]
473 out := []*exec.Value{}
474 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
475 val := key
476 if len(attribute) > 0 {
477 attr, found := val.Get(attribute)
478 if found {
479 val = attr
480 } else if defaultVal != nil {
481 val = defaultVal
482 } else {
483 return true
484 }
485 }
486 if len(filter) > 0 {
487 val = e.ExecuteFilterByName(filter, val, exec.NewVarArgs())
488 }
489 out = append(out, val)
490 return true
491 }, func() {})
492 return exec.AsValue(out)
493 }
494
495 func filterMax(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
496 p := params.Expect(0, []*exec.KwArg{
497 {"case_sensitive", false},
498 {"attribute", nil},
499 })
500 if p.IsError() {
501 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'max'"))
502 }
503 caseSensitive := p.KwArgs["case_sensitive"].Bool()
504 attribute := p.KwArgs["attribute"].String()
505
506 var max *exec.Value
507 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
508 val := key
509 if len(attribute) > 0 {
510 attr, found := val.Get(attribute)
511 if found {
512 val = attr
513 } else {
514 val = nil
515 }
516 }
517 if max == nil {
518 max = val
519 return true
520 }
521 if val == nil || max == nil {
522 return true
523 }
524 switch {
525 case max.IsFloat() || max.IsInteger() && val.IsFloat() || val.IsInteger():
526 if val.Float() > max.Float() {
527 max = val
528 }
529 case max.IsString() && val.IsString():
530 if !caseSensitive && strings.ToLower(val.String()) > strings.ToLower(max.String()) {
531 max = val
532 } else if caseSensitive && val.String() > max.String() {
533 max = val
534 }
535 default:
536 max = exec.AsValue(errors.Errorf(`%s and %s are not comparable`, max.Val.Type(), val.Val.Type()))
537 }
538 return true
539 }, func() {})
540
541 if max == nil {
542 return exec.AsValue("")
543 }
544 return max
545 }
546
547 func filterMin(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
548 p := params.Expect(0, []*exec.KwArg{
549 {"case_sensitive", false},
550 {"attribute", nil},
551 })
552 if p.IsError() {
553 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'min'"))
554 }
555 caseSensitive := p.KwArgs["case_sensitive"].Bool()
556 attribute := p.KwArgs["attribute"].String()
557
558 var min *exec.Value
559 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
560 val := key
561 if len(attribute) > 0 {
562 attr, found := val.Get(attribute)
563 if found {
564 val = attr
565 } else {
566 val = nil
567 }
568 }
569 if min == nil {
570 min = val
571 return true
572 }
573 if val == nil || min == nil {
574 return true
575 }
576 switch {
577 case min.IsFloat() || min.IsInteger() && val.IsFloat() || val.IsInteger():
578 if val.Float() < min.Float() {
579 min = val
580 }
581 case min.IsString() && val.IsString():
582 if !caseSensitive && strings.ToLower(val.String()) < strings.ToLower(min.String()) {
583 min = val
584 } else if caseSensitive && val.String() < min.String() {
585 min = val
586 }
587 default:
588 min = exec.AsValue(errors.Errorf(`%s and %s are not comparable`, min.Val.Type(), val.Val.Type()))
589 }
590 return true
591 }, func() {})
592
593 if min == nil {
594 return exec.AsValue("")
595 }
596 return min
597 }
598
599 func filterPPrint(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
600 p := params.Expect(0, []*exec.KwArg{{"verbose", false}})
601 if p.IsError() {
602 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'pprint'"))
603 }
604 b, err := json.MarshalIndent(in.Interface(), "", " ")
605 if err != nil {
606 return exec.AsValue(errors.Wrapf(err, `Unable to pretty print '%s'`, in.String()))
607 }
608 return exec.AsSafeValue(string(b))
609 }
610
611 func filterRandom(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
612 if p := params.ExpectNothing(); p.IsError() {
613 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'random'"))
614 }
615 if !in.CanSlice() || in.Len() <= 0 {
616 return in
617 }
618 i := rand.Intn(in.Len())
619 return in.Index(i)
620 }
621
622 func filterReject(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
623 var test func(*exec.Value) bool
624 if len(params.Args) == 0 {
625
626 test = func(in *exec.Value) bool {
627 return in.IsTrue()
628 }
629 } else {
630 name := params.First().String()
631 testParams := &exec.VarArgs{
632 Args: params.Args[1:],
633 KwArgs: params.KwArgs,
634 }
635 test = func(in *exec.Value) bool {
636 out := e.ExecuteTestByName(name, in, testParams)
637 return out.IsTrue()
638 }
639 }
640
641 out := []*exec.Value{}
642
643 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
644 if !test(key) {
645 out = append(out, key)
646 }
647 return true
648 }, func() {})
649
650 return exec.AsValue(out)
651 }
652
653 func filterRejectAttr(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
654 var test func(*exec.Value) *exec.Value
655 if len(params.Args) < 1 {
656 return exec.AsValue(errors.New("Wrong signature for 'rejectattr', expect at least an attribute name as argument"))
657 }
658 attribute := params.First().String()
659 if len(params.Args) == 1 {
660
661 test = func(in *exec.Value) *exec.Value {
662 attr, found := in.Get(attribute)
663 if !found {
664 return exec.AsValue(errors.Errorf(`%s has no attribute '%s'`, in.String(), attribute))
665 }
666 return attr
667 }
668 } else {
669 name := params.Args[1].String()
670 testParams := &exec.VarArgs{
671 Args: params.Args[2:],
672 KwArgs: params.KwArgs,
673 }
674 test = func(in *exec.Value) *exec.Value {
675 attr, found := in.Get(attribute)
676 if !found {
677 return exec.AsValue(errors.Errorf(`%s has no attribute '%s'`, in.String(), attribute))
678 }
679 out := e.ExecuteTestByName(name, attr, testParams)
680 return out
681 }
682 }
683
684 out := []*exec.Value{}
685 var err *exec.Value
686
687 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
688 result := test(key)
689 if result.IsError() {
690 err = result
691 return false
692 }
693 if !result.IsTrue() {
694 out = append(out, key)
695 }
696 return true
697 }, func() {})
698
699 if err != nil {
700 return err
701 }
702 return exec.AsValue(out)
703 }
704
705 func filterReplace(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
706 p := params.Expect(2, []*exec.KwArg{{"count", nil}})
707 if p.IsError() {
708 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'replace'"))
709 }
710 old := p.Args[0].String()
711 new := p.Args[1].String()
712 count := p.KwArgs["count"]
713 if count.IsNil() {
714 return exec.AsValue(strings.ReplaceAll(in.String(), old, new))
715 }
716 return exec.AsValue(strings.Replace(in.String(), old, new, count.Integer()))
717 }
718
719 func filterReverse(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
720 if p := params.ExpectNothing(); p.IsError() {
721 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'safe'"))
722 }
723 if in.IsString() {
724 var out strings.Builder
725 in.IterateOrder(func(idx, count int, key, value *exec.Value) bool {
726 out.WriteString(key.String())
727 return true
728 }, func() {}, true, false, false)
729 return exec.AsValue(out.String())
730 }
731 out := []*exec.Value{}
732 in.IterateOrder(func(idx, count int, key, value *exec.Value) bool {
733 out = append(out, key)
734 return true
735 }, func() {}, true, true, false)
736 return exec.AsValue(out)
737 }
738
739 func filterRound(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
740 p := params.Expect(0, []*exec.KwArg{{"precision", 0}, {"method", "common"}})
741 if p.IsError() {
742 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'round'"))
743 }
744 method := p.KwArgs["method"].String()
745 var op func(float64) float64
746 switch method {
747 case "common":
748 op = math.Round
749 case "floor":
750 op = math.Floor
751 case "ceil":
752 op = math.Ceil
753 default:
754 return exec.AsValue(errors.Errorf(`Unknown method '%s', mush be one of 'common, 'floor', 'ceil`, method))
755 }
756 value := in.Float()
757 factor := float64(10 * p.KwArgs["precision"].Integer())
758 if factor > 0 {
759 value = value * factor
760 }
761 value = op(value)
762 if factor > 0 {
763 value = value / factor
764 }
765 return exec.AsValue(value)
766 }
767
768 func filterSafe(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
769 if p := params.ExpectNothing(); p.IsError() {
770 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'safe'"))
771 }
772 in.Safe = true
773 return in
774 }
775
776 func filterSelect(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
777 var test func(*exec.Value) bool
778 if len(params.Args) == 0 {
779
780 test = func(in *exec.Value) bool {
781 return in.IsTrue()
782 }
783 } else {
784 name := params.First().String()
785 testParams := &exec.VarArgs{
786 Args: params.Args[1:],
787 KwArgs: params.KwArgs,
788 }
789 test = func(in *exec.Value) bool {
790 out := e.ExecuteTestByName(name, in, testParams)
791 return out.IsTrue()
792 }
793 }
794
795 out := []*exec.Value{}
796
797 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
798 if test(key) {
799 out = append(out, key)
800 }
801 return true
802 }, func() {})
803
804 return exec.AsValue(out)
805 }
806
807 func filterSelectAttr(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
808 var test func(*exec.Value) *exec.Value
809 if len(params.Args) < 1 {
810 return exec.AsValue(errors.New("Wrong signature for 'selectattr', expect at least an attribute name as argument"))
811 }
812 attribute := params.First().String()
813 if len(params.Args) == 1 {
814
815 test = func(in *exec.Value) *exec.Value {
816 attr, found := in.Get(attribute)
817 if !found {
818 return exec.AsValue(errors.Errorf(`%s has no attribute '%s'`, in.String(), attribute))
819 }
820 return attr
821 }
822 } else {
823 name := params.Args[1].String()
824 testParams := &exec.VarArgs{
825 Args: params.Args[2:],
826 KwArgs: params.KwArgs,
827 }
828 test = func(in *exec.Value) *exec.Value {
829 attr, found := in.Get(attribute)
830 if !found {
831 return exec.AsValue(errors.Errorf(`%s has no attribute '%s'`, in.String(), attribute))
832 }
833 out := e.ExecuteTestByName(name, attr, testParams)
834 return out
835 }
836 }
837
838 out := []*exec.Value{}
839 var err *exec.Value
840
841 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
842 result := test(key)
843 if result.IsError() {
844 err = result
845 return false
846 }
847 if result.IsTrue() {
848 out = append(out, key)
849 }
850 return true
851 }, func() {})
852
853 if err != nil {
854 return err
855 }
856 return exec.AsValue(out)
857 }
858
859 func filterSlice(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
860 comp := strings.Split(params.Args[0].String(), ":")
861 if len(comp) != 2 {
862 return exec.AsValue(errors.New("Slice string must have the format 'from:to' [from/to can be omitted, but the ':' is required]"))
863 }
864
865 if !in.CanSlice() {
866 return in
867 }
868
869 from := exec.AsValue(comp[0]).Integer()
870 to := in.Len()
871
872 if from > to {
873 from = to
874 }
875
876 vto := exec.AsValue(comp[1]).Integer()
877 if vto >= from && vto <= in.Len() {
878 to = vto
879 }
880
881 return in.Slice(from, to)
882 }
883
884 func filterSort(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
885 p := params.Expect(0, []*exec.KwArg{{"reverse", false}, {"case_sensitive", false}})
886 if p.IsError() {
887 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'sort'"))
888 }
889 reverse := p.KwArgs["reverse"].Bool()
890 caseSensitive := p.KwArgs["case_sensitive"].Bool()
891 out := []*exec.Value{}
892 in.IterateOrder(func(idx, count int, key, value *exec.Value) bool {
893 out = append(out, key)
894 return true
895 }, func() {}, reverse, true, caseSensitive)
896 return exec.AsValue(out)
897 }
898
899 func filterString(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
900 if p := params.ExpectNothing(); p.IsError() {
901 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'string'"))
902 }
903 return exec.AsValue(in.String())
904 }
905
906 var reStriptags = regexp.MustCompile("<[^>]*?>")
907
908 func filterStriptags(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
909 if p := params.ExpectNothing(); p.IsError() {
910 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'striptags'"))
911 }
912 s := in.String()
913
914
915 s = reStriptags.ReplaceAllString(s, "")
916
917 return exec.AsValue(strings.TrimSpace(s))
918 }
919
920 func filterSum(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
921 p := params.Expect(0, []*exec.KwArg{{"attribute", nil}, {"start", 0}})
922 if p.IsError() {
923 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'sum'"))
924 }
925
926 attribute := p.KwArgs["attribute"]
927 sum := p.KwArgs["start"].Float()
928 var err error
929
930 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
931 if attribute.IsString() {
932 val := key
933 found := true
934 for _, attr := range strings.Split(attribute.String(), ".") {
935 val, found = val.Get(attr)
936 if !found {
937 err = errors.Errorf("'%s' has no attribute '%s'", key.String(), attribute.String())
938 return false
939 }
940 }
941 if found && val.IsNumber() {
942 sum += val.Float()
943 }
944 } else if attribute.IsInteger() {
945 value, found := key.Getitem(attribute.Integer())
946 if found {
947 sum += value.Float()
948 }
949 } else {
950 sum += key.Float()
951 }
952 return true
953 }, func() {})
954
955 if err != nil {
956 return exec.AsValue(err)
957 } else if sum == math.Trunc(sum) {
958 return exec.AsValue(int64(sum))
959 }
960 return exec.AsValue(sum)
961 }
962
963 func filterTitle(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
964 if p := params.ExpectNothing(); p.IsError() {
965 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'title'"))
966 }
967 if !in.IsString() {
968 return exec.AsValue("")
969 }
970 return exec.AsValue(strings.Title(strings.ToLower(in.String())))
971 }
972
973 func filterTrim(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
974 if p := params.ExpectNothing(); p.IsError() {
975 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'trim'"))
976 }
977 return exec.AsValue(strings.TrimSpace(in.String()))
978 }
979
980 func filterToJSON(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
981 p := params.Expect(0, []*exec.KwArg{{"indent", nil}})
982 if p.IsError() {
983 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'tojson'"))
984 }
985
986 indent := p.KwArgs["indent"]
987 var out string
988 if indent.IsNil() {
989 b, err := json.Marshal(in.Interface())
990 if err != nil {
991 return exec.AsValue(errors.Wrap(err, "Unable to marhsall to json"))
992 }
993 out = string(b)
994 } else if indent.IsInteger() {
995 b, err := json.MarshalIndent(in.Interface(), "", strings.Repeat(" ", indent.Integer()))
996 if err != nil {
997 return exec.AsValue(errors.Wrap(err, "Unable to marhsall to json"))
998 }
999 out = string(b)
1000 } else {
1001 return exec.AsValue(errors.Errorf("Expected an integer for 'indent', got %s", indent.String()))
1002 }
1003 return exec.AsSafeValue(out)
1004 }
1005
1006 func filterTruncate(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1007 p := params.Expect(0, []*exec.KwArg{
1008 {"length", 255},
1009 {"killwords", false},
1010 {"end", "..."},
1011 {"leeway", 0},
1012 })
1013 if p.IsError() {
1014 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'truncate'"))
1015 }
1016
1017 source := in.String()
1018 length := p.KwArgs["length"].Integer()
1019 leeway := p.KwArgs["leeway"].Integer()
1020 killwords := p.KwArgs["killwords"].Bool()
1021 end := p.KwArgs["end"].String()
1022 rEnd := []rune(end)
1023 fullLength := length + leeway
1024 runes := []rune(source)
1025
1026 if length < len(rEnd) {
1027 return exec.AsValue(errors.Errorf(`expected length >= %d, got %d`, len(rEnd), length))
1028 }
1029
1030 if len(runes) <= fullLength {
1031 return exec.AsValue(source)
1032 }
1033
1034 atLength := string(runes[:length-len(rEnd)])
1035 if !killwords {
1036 atLength = strings.TrimRightFunc(atLength, func(r rune) bool {
1037 return !unicode.IsSpace(r)
1038 })
1039 atLength = strings.TrimRight(atLength, " \n\t")
1040 }
1041 return exec.AsValue(fmt.Sprintf("%s%s", atLength, end))
1042 }
1043
1044 func filterUnique(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1045 p := params.Expect(0, []*exec.KwArg{{"case_sensitive", false}, {"attribute", nil}})
1046 if p.IsError() {
1047 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'unique'"))
1048 }
1049
1050 caseSensitive := p.KwArgs["case_sensitive"].Bool()
1051 attribute := p.KwArgs["attribute"]
1052
1053 out := exec.ValuesList{}
1054 tracker := map[interface{}]bool{}
1055 var err error
1056
1057 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
1058 val := key
1059 if attribute.IsString() {
1060 attr := attribute.String()
1061 nested, found := key.Get(attr)
1062 if !found {
1063 err = errors.Errorf(`%s has no attribute %s`, key.String(), attr)
1064 return false
1065 }
1066 val = nested
1067 }
1068 tracked := val.Interface()
1069 if !caseSensitive && val.IsString() {
1070 tracked = strings.ToLower(val.String())
1071 }
1072 if _, contains := tracker[tracked]; !contains {
1073 tracker[tracked] = true
1074 out = append(out, key)
1075 }
1076 return true
1077 }, func() {})
1078
1079 if err != nil {
1080 return exec.AsValue(err)
1081 }
1082 return exec.AsValue(out)
1083 }
1084
1085 func filterUpper(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1086 if p := params.ExpectNothing(); p.IsError() {
1087 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'upper'"))
1088 }
1089 return exec.AsValue(strings.ToUpper(in.String()))
1090 }
1091
1092 func filterUrlencode(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1093 if p := params.ExpectNothing(); p.IsError() {
1094 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'urlencode'"))
1095 }
1096 return exec.AsValue(url.QueryEscape(in.String()))
1097 }
1098
1099
1100 var filterUrlizeURLRegexp = regexp.MustCompile(`((((http|https)://)|www\.|((^|[ ])[0-9A-Za-z_\-]+(\.com|\.net|\.org|\.info|\.biz|\.de))))(?U:.*)([ ]+|$)`)
1101 var filterUrlizeEmailRegexp = regexp.MustCompile(`(\w+@\w+\.\w{2,4})`)
1102
1103 func filterUrlizeHelper(input string, trunc int, rel string, target string) (string, error) {
1104 var soutErr error
1105 sout := filterUrlizeURLRegexp.ReplaceAllStringFunc(input, func(raw_url string) string {
1106 var prefix string
1107 var suffix string
1108 if strings.HasPrefix(raw_url, " ") {
1109 prefix = " "
1110 }
1111 if strings.HasSuffix(raw_url, " ") {
1112 suffix = " "
1113 }
1114
1115 raw_url = strings.TrimSpace(raw_url)
1116
1117 url := u.IRIEncode(raw_url)
1118
1119 if !strings.HasPrefix(url, "http") {
1120 url = fmt.Sprintf("http://%s", url)
1121 }
1122
1123 title := raw_url
1124
1125 if trunc > 3 && len(title) > trunc {
1126 title = fmt.Sprintf("%s...", title[:trunc-3])
1127 }
1128
1129 title = u.Escape(title)
1130
1131 attrs := ""
1132 if len(target) > 0 {
1133 attrs = fmt.Sprintf(` target="%s"`, target)
1134 }
1135
1136 rels := []string{}
1137 cleanedRel := strings.Trim(strings.Replace(rel, "noopener", "", -1), " ")
1138 if len(cleanedRel) > 0 {
1139 rels = append(rels, cleanedRel)
1140 }
1141 rels = append(rels, "noopener")
1142 rel = strings.Join(rels, " ")
1143
1144 return fmt.Sprintf(`%s<a href="%s" rel="%s"%s>%s</a>%s`, prefix, url, rel, attrs, title, suffix)
1145 })
1146 if soutErr != nil {
1147 return "", soutErr
1148 }
1149
1150 sout = filterUrlizeEmailRegexp.ReplaceAllStringFunc(sout, func(mail string) string {
1151 title := mail
1152
1153 if trunc > 3 && len(title) > trunc {
1154 title = fmt.Sprintf("%s...", title[:trunc-3])
1155 }
1156
1157 return fmt.Sprintf(`<a href="mailto:%s">%s</a>`, mail, title)
1158 })
1159 return sout, nil
1160 }
1161
1162 func filterUrlize(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1163 p := params.Expect(0, []*exec.KwArg{
1164 {"trim_url_limit", nil},
1165 {"nofollow", false},
1166 {"target", nil},
1167 {"rel", nil},
1168 })
1169 if p.IsError() {
1170 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'urlize'"))
1171 }
1172 truncate := -1
1173 if param := p.KwArgs["trim_url_limit"]; param.IsInteger() {
1174 truncate = param.Integer()
1175 }
1176 rel := p.KwArgs["rel"]
1177 target := p.KwArgs["target"]
1178
1179 s, err := filterUrlizeHelper(in.String(), truncate, rel.String(), target.String())
1180 if err != nil {
1181 return exec.AsValue(err)
1182 }
1183
1184 return exec.AsValue(s)
1185 }
1186
1187 func filterWordcount(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1188 if p := params.ExpectNothing(); p.IsError() {
1189 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'wordcount'"))
1190 }
1191 return exec.AsValue(len(strings.Fields(in.String())))
1192 }
1193
1194 func filterWordwrap(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1195 words := strings.Fields(in.String())
1196 wordsLen := len(words)
1197 wrapAt := params.Args[0].Integer()
1198 if wrapAt <= 0 {
1199 return in
1200 }
1201
1202 linecount := wordsLen/wrapAt + wordsLen%wrapAt
1203 lines := make([]string, 0, linecount)
1204 for i := 0; i < linecount; i++ {
1205 lines = append(lines, strings.Join(words[wrapAt*i:u.Min(wrapAt*(i+1), wordsLen)], " "))
1206 }
1207 return exec.AsValue(strings.Join(lines, "\n"))
1208 }
1209
1210 func filterXMLAttr(e *exec.Evaluator, in *exec.Value, params *exec.VarArgs) *exec.Value {
1211 p := params.ExpectKwArgs([]*exec.KwArg{{"autospace", true}})
1212 if p.IsError() {
1213 return exec.AsValue(errors.Wrap(p, "Wrong signature for 'xmlattr'"))
1214 }
1215 autospace := p.KwArgs["autospace"].Bool()
1216 kvs := []string{}
1217 in.Iterate(func(idx, count int, key, value *exec.Value) bool {
1218 if !value.IsTrue() {
1219 return true
1220 }
1221 kv := fmt.Sprintf(`%s="%s"`, key.Escaped(), value.Escaped())
1222 kvs = append(kvs, kv)
1223 return true
1224 }, func() {})
1225 out := strings.Join(kvs, " ")
1226 if autospace {
1227 out = " " + out
1228 }
1229 return exec.AsValue(out)
1230 }
1231
View as plain text