github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/metrics/transformation/type.go (about)

     1  // go:generate stringer -type=Type
     2  // Copyright (c) 2017 Uber Technologies, Inc.
     3  //
     4  // Permission is hereby granted, free of charge, to any person obtaining a copy
     5  // of this software and associated documentation files (the "Software"), to deal
     6  // in the Software without restriction, including without limitation the rights
     7  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     8  // copies of the Software, and to permit persons to whom the Software is
     9  // furnished to do so, subject to the following conditions:
    10  //
    11  // The above copyright notice and this permission notice shall be included in
    12  // all copies or substantial portions of the Software.
    13  //
    14  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    15  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    16  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    17  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    18  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    19  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    20  // THE SOFTWARE.
    21  
    22  package transformation
    23  
    24  import (
    25  	"errors"
    26  	"fmt"
    27  
    28  	"github.com/m3db/m3/src/metrics/generated/proto/transformationpb"
    29  )
    30  
    31  // Type defines a transformation function.
    32  type Type int32
    33  
    34  var errUnknownTransformationType = errors.New("unknown transformation type")
    35  
    36  // Supported transformation types.
    37  const (
    38  	UnknownType Type = iota
    39  	Absolute
    40  	PerSecond
    41  	Increase
    42  	Add
    43  	Reset
    44  )
    45  
    46  const (
    47  	_minValidTransformationType = Absolute
    48  	_maxValidTransformationType = Reset
    49  )
    50  
    51  // IsValid checks if the transformation type is valid.
    52  func (t Type) IsValid() bool {
    53  	return t.IsUnaryTransform() || t.IsBinaryTransform() || t.IsUnaryMultiOutputTransform()
    54  }
    55  
    56  // IsUnaryTransform returns whether this is a unary transformation.
    57  func (t Type) IsUnaryTransform() bool {
    58  	_, exists := unaryTransforms[t]
    59  	return exists
    60  }
    61  
    62  // IsBinaryTransform returns whether this is a binary transformation.
    63  func (t Type) IsBinaryTransform() bool {
    64  	_, exists := binaryTransforms[t]
    65  	return exists
    66  }
    67  
    68  func (t Type) IsUnaryMultiOutputTransform() bool {
    69  	_, exists := unaryMultiOutputTransforms[t]
    70  	return exists
    71  }
    72  
    73  // NewOp returns a constructed operation that is allocated once and can be
    74  // reused.
    75  func (t Type) NewOp() (Op, error) {
    76  	var (
    77  		err        error
    78  		unary      UnaryTransform
    79  		binary     BinaryTransform
    80  		unaryMulti UnaryMultiOutputTransform
    81  	)
    82  	switch {
    83  	case t.IsUnaryTransform():
    84  		unary, err = t.UnaryTransform()
    85  	case t.IsBinaryTransform():
    86  		binary, err = t.BinaryTransform()
    87  	case t.IsUnaryMultiOutputTransform():
    88  		unaryMulti, err = t.UnaryMultiOutputTransform()
    89  	default:
    90  		err = errUnknownTransformationType
    91  	}
    92  	if err != nil {
    93  		return Op{}, err
    94  	}
    95  	return Op{
    96  		opType:     t,
    97  		unary:      unary,
    98  		binary:     binary,
    99  		unaryMulti: unaryMulti,
   100  	}, nil
   101  }
   102  
   103  // UnaryTransform returns the unary transformation function associated with
   104  // the transformation type if applicable, or an error otherwise.
   105  func (t Type) UnaryTransform() (UnaryTransform, error) {
   106  	tf, exists := unaryTransforms[t]
   107  	if !exists {
   108  		return nil, fmt.Errorf("%v is not a unary transfomration", t)
   109  	}
   110  	return tf(), nil
   111  }
   112  
   113  // MustUnaryTransform returns the unary transformation function associated with
   114  // the transformation type if applicable, or panics otherwise.
   115  func (t Type) MustUnaryTransform() UnaryTransform {
   116  	tf, err := t.UnaryTransform()
   117  	if err != nil {
   118  		panic(err)
   119  	}
   120  	return tf
   121  }
   122  
   123  // BinaryTransform returns the binary transformation function associated with
   124  // the transformation type if applicable, or an error otherwise.
   125  func (t Type) BinaryTransform() (BinaryTransform, error) {
   126  	tf, exists := binaryTransforms[t]
   127  	if !exists {
   128  		return nil, fmt.Errorf("%v is not a binary transfomration", t)
   129  	}
   130  	return tf(), nil
   131  }
   132  
   133  // MustBinaryTransform returns the binary transformation function associated with
   134  // the transformation type if applicable, or an error otherwise.
   135  func (t Type) MustBinaryTransform() BinaryTransform {
   136  	tf, err := t.BinaryTransform()
   137  	if err != nil {
   138  		panic(err)
   139  	}
   140  	return tf
   141  }
   142  
   143  // UnaryMultiOutputTransform returns the unary transformation function associated with
   144  // the transformation type if applicable, or an error otherwise.
   145  func (t Type) UnaryMultiOutputTransform() (UnaryMultiOutputTransform, error) {
   146  	tf, exists := unaryMultiOutputTransforms[t]
   147  	if !exists {
   148  		return nil, fmt.Errorf("%v is not a unary transfomration", t)
   149  	}
   150  	return tf(), nil
   151  }
   152  
   153  // MustUnaryMultiOutputTransform returns the unary transformation function associated with
   154  // the transformation type if applicable, or panics otherwise.
   155  func (t Type) MustUnaryMultiOutputTransform() UnaryMultiOutputTransform {
   156  	tf, err := t.UnaryMultiOutputTransform()
   157  	if err != nil {
   158  		panic(err)
   159  	}
   160  	return tf
   161  }
   162  
   163  // ToProto converts the transformation type to a protobuf message in place.
   164  func (t Type) ToProto(pb *transformationpb.TransformationType) error {
   165  	if t < _minValidTransformationType || t > _maxValidTransformationType {
   166  		return errUnknownTransformationType
   167  	}
   168  	*pb = transformationpb.TransformationType(t)
   169  	return nil
   170  }
   171  
   172  // FromProto converts the protobuf message to a transformation type in place.
   173  func (t *Type) FromProto(pb transformationpb.TransformationType) error {
   174  	*t = Type(pb)
   175  	if *t < _minValidTransformationType || *t > _maxValidTransformationType {
   176  		return errUnknownTransformationType
   177  	}
   178  	return nil
   179  }
   180  
   181  // UnmarshalText extracts this type from the textual representation
   182  func (t *Type) UnmarshalText(text []byte) error {
   183  	parsed, err := ParseType(string(text))
   184  	if err != nil {
   185  		return err
   186  	}
   187  	*t = parsed
   188  	return nil
   189  }
   190  
   191  // MarshalYAML marshals a Type.
   192  func (t Type) MarshalYAML() (interface{}, error) {
   193  	return t.String(), nil
   194  }
   195  
   196  // UnmarshalYAML unmarshals text-encoded data into an transformation type.
   197  func (t *Type) UnmarshalYAML(unmarshal func(interface{}) error) error {
   198  	var str string
   199  	if err := unmarshal(&str); err != nil {
   200  		return err
   201  	}
   202  	value, err := ParseType(str)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	*t = value
   207  	return nil
   208  }
   209  
   210  // MarshalText serializes this type to its textual representation.
   211  func (t Type) MarshalText() (text []byte, err error) {
   212  	if !t.IsValid() {
   213  		return nil, fmt.Errorf("invalid aggregation type %s", t.String())
   214  	}
   215  	return []byte(t.String()), nil
   216  }
   217  
   218  // ParseType parses a transformation type.
   219  func ParseType(str string) (Type, error) {
   220  	t, ok := typeStringMap[str]
   221  	if !ok {
   222  		return UnknownType, fmt.Errorf("invalid transformation type: %s", str)
   223  	}
   224  	return t, nil
   225  }
   226  
   227  // Op represents a transform operation.
   228  type Op struct {
   229  	unary      UnaryTransform
   230  	binary     BinaryTransform
   231  	unaryMulti UnaryMultiOutputTransform
   232  	// opType determines which one of the above transformations are applied
   233  	opType Type
   234  }
   235  
   236  // Type returns the op type.
   237  func (o Op) Type() Type {
   238  	return o.opType
   239  }
   240  
   241  // UnaryTransform returns the active unary transform if op is unary transform.
   242  func (o Op) UnaryTransform() (UnaryTransform, bool) {
   243  	if !o.Type().IsUnaryTransform() {
   244  		return nil, false
   245  	}
   246  	return o.unary, true
   247  }
   248  
   249  // BinaryTransform returns the active binary transform if op is binary transform.
   250  func (o Op) BinaryTransform() (BinaryTransform, bool) {
   251  	if !o.Type().IsBinaryTransform() {
   252  		return nil, false
   253  	}
   254  	return o.binary, true
   255  }
   256  
   257  // UnaryMultiOutputTransform returns the active unary multi transform if op is unary multi transform.
   258  func (o Op) UnaryMultiOutputTransform() (UnaryMultiOutputTransform, bool) {
   259  	if !o.Type().IsUnaryMultiOutputTransform() {
   260  		return nil, false
   261  	}
   262  	return o.unaryMulti, true
   263  }
   264  
   265  var (
   266  	unaryTransforms = map[Type]func() UnaryTransform{
   267  		Absolute: transformAbsolute,
   268  		Add:      transformAdd,
   269  	}
   270  	binaryTransforms = map[Type]func() BinaryTransform{
   271  		PerSecond: transformPerSecond,
   272  		Increase:  transformIncrease,
   273  	}
   274  	unaryMultiOutputTransforms = map[Type]func() UnaryMultiOutputTransform{
   275  		Reset: transformReset,
   276  	}
   277  	typeStringMap map[string]Type
   278  )
   279  
   280  func init() {
   281  	typeStringMap = make(map[string]Type)
   282  	for t := range unaryTransforms {
   283  		typeStringMap[t.String()] = t
   284  	}
   285  	for t := range binaryTransforms {
   286  		typeStringMap[t.String()] = t
   287  	}
   288  	for t := range unaryMultiOutputTransforms {
   289  		typeStringMap[t.String()] = t
   290  	}
   291  }