github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/query/models/matcher.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package models
    22  
    23  import (
    24  	"bytes"
    25  	"errors"
    26  	"fmt"
    27  	"regexp"
    28  	"strings"
    29  )
    30  
    31  func (m MatchType) String() string {
    32  	switch m {
    33  	case MatchEqual:
    34  		return "="
    35  	case MatchNotEqual:
    36  		return "!="
    37  	case MatchRegexp:
    38  		return "=~"
    39  	case MatchNotRegexp:
    40  		return "!~"
    41  	case MatchField:
    42  		return "-"
    43  	case MatchNotField:
    44  		return "!-"
    45  	case MatchAll:
    46  		return "*"
    47  	default:
    48  		return "unknown match type"
    49  	}
    50  }
    51  
    52  // NewMatcher returns a matcher object.
    53  func NewMatcher(t MatchType, n, v []byte) (Matcher, error) {
    54  	m := Matcher{
    55  		Type:  t,
    56  		Name:  n,
    57  		Value: v,
    58  	}
    59  
    60  	if len(n) == 0 && t != MatchAll {
    61  		return Matcher{}, errors.New("name must be set unless using MatchAll")
    62  	}
    63  
    64  	if t == MatchRegexp || t == MatchNotRegexp {
    65  		re, err := regexp.Compile("^(?:" + string(v) + ")$")
    66  		if err != nil {
    67  			return Matcher{}, err
    68  		}
    69  
    70  		m.re = re
    71  	}
    72  
    73  	return m, nil
    74  }
    75  
    76  func (m Matcher) String() string {
    77  	return fmt.Sprintf("%s%s%q", m.Name, m.Type, m.Value)
    78  }
    79  
    80  // ToTags converts Matchers to Tags
    81  // NB (braskin): this only works for exact matches
    82  func (m Matchers) ToTags(
    83  	tagOptions TagOptions,
    84  ) (Tags, error) {
    85  	// todo: nil is good here?
    86  	tags := NewTags(len(m), tagOptions)
    87  	for _, v := range m {
    88  		if v.Type != MatchEqual {
    89  			return EmptyTags(),
    90  				fmt.Errorf("illegal match type, got %v, but expecting: %v",
    91  					v.Type, MatchEqual)
    92  		}
    93  
    94  		tags = tags.AddTag(Tag{Name: v.Name, Value: v.Value}).Clone()
    95  	}
    96  
    97  	return tags, nil
    98  }
    99  
   100  func (m Matchers) String() string {
   101  	var buffer bytes.Buffer
   102  	for _, match := range m {
   103  		buffer.WriteString(match.String())
   104  		buffer.WriteByte(sep)
   105  	}
   106  
   107  	return buffer.String()
   108  }
   109  
   110  // TODO: make this more robust, handle types other than MatchEqual
   111  func matcherFromString(s string) (Matcher, error) {
   112  	ss := strings.Split(s, ":")
   113  	length := len(ss)
   114  	if length > 2 {
   115  		return Matcher{}, errors.New("invalid arg length for matcher")
   116  	}
   117  
   118  	if length == 0 || len(ss[0]) == 0 {
   119  		return Matcher{}, errors.New("empty matcher")
   120  	}
   121  
   122  	if length == 1 {
   123  		return Matcher{
   124  			Type:  MatchRegexp,
   125  			Name:  []byte(ss[0]),
   126  			Value: []byte{},
   127  		}, nil
   128  	}
   129  
   130  	return Matcher{
   131  		Type:  MatchRegexp,
   132  		Name:  []byte(ss[0]),
   133  		Value: []byte(ss[1]),
   134  	}, nil
   135  }
   136  
   137  // MatchersFromString parses a string into Matchers
   138  // TODO: make this more robust, handle types other than MatchEqual
   139  func MatchersFromString(s string) (Matchers, error) {
   140  	split := strings.Fields(s)
   141  	matchers := make(Matchers, len(split))
   142  	for i, ss := range split {
   143  		matcher, err := matcherFromString(ss)
   144  		if err != nil {
   145  			return nil, err
   146  		}
   147  
   148  		matchers[i] = matcher
   149  	}
   150  
   151  	return matchers, nil
   152  }