1// Copyright 2020 Jebbs. All rights reserved.
2// Use of this source code is governed by MIT
3// license that can be found in the LICENSE file.
4
5package rule
6
7// Rule is the merge rules
8type Rule struct {
9 OrderBy []Field
10 MergeBy []Field
11}
12
13// NewRule makes a new Rule
14func NewRule(fields ...Field) *Rule {
15 r := &Rule{}
16 for _, field := range fields {
17 switch field.Type {
18 case FieldTypeMerge:
19 r.MergeBy = append(r.MergeBy, field)
20 case FieldTypeOrder:
21 r.OrderBy = append(r.OrderBy, field)
22 }
23 }
24 return r
25}
26
27// Apply applies rule according to m
28func (r *Rule) Apply(m map[string]interface{}) error {
29 if r == nil || (len(r.MergeBy) == 0 && len(r.OrderBy) == 0) {
30 return nil
31 }
32 err := r.sortMergeSlices(m)
33 if err != nil {
34 return err
35 }
36 r.removeHelperFields(m)
37 return nil
38}
39
40// sortMergeSlices enumerates all slices in a map, to sort by order and merge by tag
41func (r *Rule) sortMergeSlices(target map[string]interface{}) error {
42 for key, value := range target {
43 if slice, ok := value.([]interface{}); ok {
44 sortByFields(slice, r.OrderBy)
45 s, err := mergeByFields(slice, r.MergeBy)
46 if err != nil {
47 return err
48 }
49 target[key] = s
50 for _, item := range s {
51 if m, ok := item.(map[string]interface{}); ok {
52 r.sortMergeSlices(m)
53 }
54 }
55 } else if field, ok := value.(map[string]interface{}); ok {
56 r.sortMergeSlices(field)
57 }
58 }
59 return nil
60}
61
62func (r *Rule) removeHelperFields(target map[string]interface{}) {
63 for key, value := range target {
64 if r.shouldDelete(key) {
65 delete(target, key)
66 } else if slice, ok := value.([]interface{}); ok {
67 for _, e := range slice {
68 if el, ok := e.(map[string]interface{}); ok {
69 r.removeHelperFields(el)
70 }
71 }
72 } else if field, ok := value.(map[string]interface{}); ok {
73 r.removeHelperFields(field)
74 }
75 }
76}
77
78// shouldDelete tells if the field should be deleted according to the rules
79func (r *Rule) shouldDelete(key string) bool {
80 for _, field := range r.MergeBy {
81 if key != field.Key {
82 continue
83 }
84 return field.Remove
85 }
86 for _, field := range r.OrderBy {
87 if key != field.Key {
88 continue
89 }
90 return field.Remove
91 }
92 return false
93}