github.com/tobgu/qframe@v0.4.0/filter.go (about) 1 package qframe 2 3 import ( 4 "fmt" 5 "strings" 6 7 "github.com/tobgu/qframe/filter" 8 "github.com/tobgu/qframe/internal/index" 9 "github.com/tobgu/qframe/internal/math/integer" 10 "github.com/tobgu/qframe/qerrors" 11 ) 12 13 // FilterClause is an internal interface representing a filter of some kind that can be applied on a QFrame. 14 type FilterClause interface { 15 fmt.Stringer 16 filter(qf QFrame) QFrame 17 Err() error 18 } 19 20 // Filter is the lowest level in a filter clause. 21 // See the docs for filter.Filter for an in depth description of the fields. 22 type Filter filter.Filter 23 24 type comboClause struct { 25 err error //nolint:structcheck 26 subClauses []FilterClause //nolint:structcheck 27 } 28 29 // AndClause represents the logical conjunction of multiple clauses. 30 type AndClause comboClause 31 32 // OrClause represents the logical disjunction of multiple clauses. 33 type OrClause comboClause 34 35 // NotClause represents the logical inverse of of a filter clause. 36 type NotClause struct { 37 subClause FilterClause 38 } 39 40 // NullClause is a convenience type to simplify clients when no filtering is to be done. 41 type NullClause struct{} 42 43 func anyFilterErr(clauses []FilterClause) error { 44 for _, c := range clauses { 45 if c.Err() != nil { 46 return c.Err() 47 } 48 } 49 return nil 50 } 51 52 // And returns a new AndClause that represents the conjunction of the passed filter clauses. 53 func And(clauses ...FilterClause) AndClause { 54 if len(clauses) == 0 { 55 return AndClause{err: qerrors.New("new AND clause", "zero subclauses not allowed")} 56 } 57 58 return AndClause{subClauses: clauses, err: anyFilterErr(clauses)} 59 } 60 61 func clauseString(clauses []FilterClause) string { 62 reps := make([]string, 0, len(clauses)) 63 for _, c := range clauses { 64 reps = append(reps, c.String()) 65 } 66 67 return strings.Join(reps, ", ") 68 } 69 70 // String returns a textual description of the filter. 71 func (c AndClause) String() string { 72 if c.Err() != nil { 73 return c.Err().Error() 74 } 75 return fmt.Sprintf(`["and", %s]`, clauseString(c.subClauses)) 76 } 77 78 func (c AndClause) filter(qf QFrame) QFrame { 79 if qf.Err != nil { 80 return qf 81 } 82 83 if c.Err() != nil { 84 return qf.withErr(c.Err()) 85 } 86 87 filteredQf := &qf 88 for _, c := range c.subClauses { 89 newQf := c.filter(*filteredQf) 90 filteredQf = &newQf 91 } 92 93 return *filteredQf 94 } 95 96 // Err returns any error that may have occurred during creation of the filter 97 func (c AndClause) Err() error { 98 return c.err 99 } 100 101 // Or returns a new OrClause that represents the disjunction of the passed filter clauses. 102 func Or(clauses ...FilterClause) OrClause { 103 if len(clauses) == 0 { 104 return OrClause{err: qerrors.New("new OR clause", "zero subclauses not allowed")} 105 } 106 107 return OrClause{subClauses: clauses, err: anyFilterErr(clauses)} 108 } 109 110 // String returns a textual description of the filter. 111 func (c OrClause) String() string { 112 if c.Err() != nil { 113 return c.Err().Error() 114 } 115 116 return fmt.Sprintf(`["or", %s]`, clauseString(c.subClauses)) 117 } 118 119 func orFrames(original, lhs, rhs *QFrame) *QFrame { 120 if lhs == nil { 121 return rhs 122 } 123 124 if lhs.Err != nil { 125 return lhs 126 } 127 128 if rhs.Err != nil { 129 return rhs 130 } 131 132 resultIx := make(index.Int, 0, integer.Max(len(lhs.index), len(rhs.index))) 133 lhsI, rhsI := 0, 0 134 for _, ix := range original.index { 135 found := false 136 if lhsI < len(lhs.index) && lhs.index[lhsI] == ix { 137 found = true 138 lhsI++ 139 } 140 141 if rhsI < len(rhs.index) && rhs.index[rhsI] == ix { 142 found = true 143 rhsI++ 144 } 145 146 if found { 147 resultIx = append(resultIx, ix) 148 } 149 150 // Perhaps optimized special cases here for when one or both of 151 // the sides are exhausted? 152 } 153 154 newFrame := original.withIndex(resultIx) 155 return &newFrame 156 } 157 158 func (c OrClause) filter(qf QFrame) QFrame { 159 if qf.Err != nil { 160 return qf 161 } 162 163 if c.Err() != nil { 164 return qf.withErr(c.Err()) 165 } 166 167 filters := make([]filter.Filter, 0) 168 var filteredQf *QFrame 169 170 for _, c := range c.subClauses { 171 if f, ok := c.(Filter); ok { 172 filters = append(filters, filter.Filter(f)) 173 } else { 174 if len(filters) > 0 { 175 newQf := qf.filter(filters...) 176 filteredQf = orFrames(&qf, filteredQf, &newQf) 177 filters = filters[:0] 178 } 179 180 newQf := c.filter(qf) 181 filteredQf = orFrames(&qf, filteredQf, &newQf) 182 } 183 } 184 185 if len(filters) > 0 { 186 newQf := qf.filter(filters...) 187 filteredQf = orFrames(&qf, filteredQf, &newQf) 188 } 189 190 return *filteredQf 191 } 192 193 // Err returns any error that may have occurred during creation of the filter 194 func (c OrClause) Err() error { 195 return c.err 196 } 197 198 // String returns a textual description of the filter. 199 func (c Filter) String() string { 200 if c.Err() != nil { 201 return c.Err().Error() 202 } 203 204 return filter.Filter(c).String() 205 } 206 207 func (c Filter) filter(qf QFrame) QFrame { 208 return qf.filter(filter.Filter(c)) 209 } 210 211 // Err returns any error that may have occurred during creation of the filter 212 func (c Filter) Err() error { 213 return nil 214 } 215 216 // Not creates a new NotClause that represents the inverse of the passed filter clause. 217 func Not(c FilterClause) NotClause { 218 return NotClause{subClause: c} 219 } 220 221 // String returns a textual description of the filter clause. 222 func (c NotClause) String() string { 223 if c.Err() != nil { 224 return c.Err().Error() 225 } 226 227 return fmt.Sprintf(`["!", %s]`, c.subClause.String()) 228 } 229 230 func (c NotClause) filter(qf QFrame) QFrame { 231 if qf.Err != nil { 232 return qf 233 } 234 235 if c.Err() != nil { 236 return qf.withErr(c.Err()) 237 } 238 239 if fc, ok := c.subClause.(Filter); ok { 240 f := filter.Filter(fc) 241 f.Inverse = !f.Inverse 242 return qf.filter(f) 243 } 244 245 newQf := c.subClause.filter(qf) 246 if newQf.Err != nil { 247 return newQf 248 } 249 250 newIx := make(index.Int, 0, qf.index.Len()-newQf.index.Len()) 251 newQfI := 0 252 for _, ix := range qf.index { 253 if newQfI < newQf.index.Len() && newQf.index[newQfI] == ix { 254 newQfI++ 255 } else { 256 newIx = append(newIx, ix) 257 } 258 } 259 260 return qf.withIndex(newIx) 261 } 262 263 // Err returns any error that may have occurred during creation of the filter 264 func (c NotClause) Err() error { 265 return c.subClause.Err() 266 } 267 268 // Null returns a new NullClause 269 func Null() NullClause { 270 return NullClause{} 271 } 272 273 // Err for NullClause always returns an empty string. 274 func (c NullClause) String() string { 275 return "" 276 } 277 278 func (c NullClause) filter(qf QFrame) QFrame { 279 return qf 280 } 281 282 // Err for NullClause always returns nil. 283 func (c NullClause) Err() error { 284 return nil 285 }