-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathspec.go
More file actions
163 lines (148 loc) · 4.08 KB
/
spec.go
File metadata and controls
163 lines (148 loc) · 4.08 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package vecna
import "fmt"
// FilterSpec represents a serializable filter specification.
// This enables programmatic filter construction from JSON or other external sources.
type FilterSpec struct {
Op string `json:"op"` // Operator: "eq", "ne", "gt", "gte", "lt", "lte", "in", "and", "or"
Field string `json:"field,omitempty"` // Field name (for field conditions)
Value any `json:"value,omitempty"` // Comparison value (for field conditions)
Children []*FilterSpec `json:"children,omitempty"` // Child filters (for and/or)
}
// FromSpec converts a FilterSpec to a validated Filter.
// The spec is validated against the schema defined by T.
// Any validation errors are accessible via Filter.Err().
func (b *Builder[T]) FromSpec(spec *FilterSpec) *Filter {
if spec == nil {
return &Filter{err: fmt.Errorf("%w: nil spec", ErrInvalidFilter)}
}
op, err := parseOp(spec.Op)
if err != nil {
return &Filter{err: err}
}
// Handle logical operators
if op == And || op == Or || op == Not {
return b.fromLogicalSpec(op, spec.Children)
}
// Handle field operators
return b.fromFieldSpec(op, spec.Field, spec.Value)
}
// fromLogicalSpec converts a logical operator spec (and/or/not) to a Filter.
func (b *Builder[T]) fromLogicalSpec(op Op, children []*FilterSpec) *Filter {
if len(children) == 0 {
return &Filter{
op: op,
err: fmt.Errorf("%w: %s requires at least one child", ErrInvalidFilter, op),
}
}
// Not requires exactly one child
if op == Not && len(children) != 1 {
return &Filter{
op: op,
err: fmt.Errorf("%w: %s requires exactly one child", ErrInvalidFilter, op),
}
}
filters := make([]*Filter, len(children))
for i, child := range children {
filters[i] = b.FromSpec(child)
}
switch op {
case And:
return b.And(filters...)
case Or:
return b.Or(filters...)
case Not:
return b.Not(filters[0])
default:
return &Filter{op: op, err: fmt.Errorf("%w: unsupported logical operator %s", ErrInvalidFilter, op)}
}
}
// fromFieldSpec converts a field operator spec to a Filter.
func (b *Builder[T]) fromFieldSpec(op Op, field string, value any) *Filter {
fb := b.Where(field)
switch op {
case Eq:
return fb.Eq(value)
case Ne:
return fb.Ne(value)
case Gt:
return fb.Gt(value)
case Gte:
return fb.Gte(value)
case Lt:
return fb.Lt(value)
case Lte:
return fb.Lte(value)
case In:
return b.fromInSpec(fb, value)
case Nin:
return b.fromNinSpec(fb, value)
case Like:
str, ok := value.(string)
if !ok {
return &Filter{op: op, field: field, value: value, err: fmt.Errorf("%w: like requires string value", ErrInvalidFilter)}
}
return fb.Like(str)
case Contains:
return fb.Contains(value)
default:
return &Filter{
op: op,
field: field,
value: value,
err: fmt.Errorf("%w: unsupported field operator %s", ErrInvalidFilter, op),
}
}
}
// fromInSpec handles the In operator which expects a slice value.
func (*Builder[T]) fromInSpec(fb *FieldBuilder[T], value any) *Filter {
// Value should be a slice when deserialized from JSON
slice, ok := value.([]any)
if !ok {
// If it's already a typed slice, pass it through
return fb.In(value)
}
return fb.In(slice...)
}
// fromNinSpec handles the Nin operator which expects a slice value.
func (*Builder[T]) fromNinSpec(fb *FieldBuilder[T], value any) *Filter {
// Value should be a slice when deserialized from JSON
slice, ok := value.([]any)
if !ok {
// If it's already a typed slice, pass it through
return fb.Nin(value)
}
return fb.Nin(slice...)
}
// parseOp converts a string operator to an Op constant.
func parseOp(s string) (Op, error) {
switch s {
case "eq":
return Eq, nil
case "ne":
return Ne, nil
case "gt":
return Gt, nil
case "gte":
return Gte, nil
case "lt":
return Lt, nil
case "lte":
return Lte, nil
case "in":
return In, nil
case "nin":
return Nin, nil
case "like":
return Like, nil
case "contains":
return Contains, nil
case "and":
return And, nil
case "or":
return Or, nil
case "not":
return Not, nil
default:
return 0, fmt.Errorf("%w: unknown operator %q", ErrInvalidFilter, s)
}
}