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  }