github.com/m3db/m3@v1.5.0/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  // UnmarshalYAML unmarshals text-encoded data into an transformation type.
   192  func (t *Type) UnmarshalYAML(unmarshal func(interface{}) error) error {
   193  	var str string
   194  	if err := unmarshal(&str); err != nil {
   195  		return err
   196  	}
   197  	value, err := ParseType(str)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	*t = value
   202  	return nil
   203  }
   204  
   205  // MarshalText serializes this type to its textual representation.
   206  func (t Type) MarshalText() (text []byte, err error) {
   207  	if !t.IsValid() {
   208  		return nil, fmt.Errorf("invalid aggregation type %s", t.String())
   209  	}
   210  	return []byte(t.String()), nil
   211  }
   212  
   213  // ParseType parses a transformation type.
   214  func ParseType(str string) (Type, error) {
   215  	t, ok := typeStringMap[str]
   216  	if !ok {
   217  		return UnknownType, fmt.Errorf("invalid transformation type: %s", str)
   218  	}
   219  	return t, nil
   220  }
   221  
   222  // Op represents a transform operation.
   223  type Op struct {
   224  	unary      UnaryTransform
   225  	binary     BinaryTransform
   226  	unaryMulti UnaryMultiOutputTransform
   227  	// opType determines which one of the above transformations are applied
   228  	opType Type
   229  }
   230  
   231  // Type returns the op type.
   232  func (o Op) Type() Type {
   233  	return o.opType
   234  }
   235  
   236  // UnaryTransform returns the active unary transform if op is unary transform.
   237  func (o Op) UnaryTransform() (UnaryTransform, bool) {
   238  	if !o.Type().IsUnaryTransform() {
   239  		return nil, false
   240  	}
   241  	return o.unary, true
   242  }
   243  
   244  // BinaryTransform returns the active binary transform if op is binary transform.
   245  func (o Op) BinaryTransform() (BinaryTransform, bool) {
   246  	if !o.Type().IsBinaryTransform() {
   247  		return nil, false
   248  	}
   249  	return o.binary, true
   250  }
   251  
   252  // UnaryMultiOutputTransform returns the active unary multi transform if op is unary multi transform.
   253  func (o Op) UnaryMultiOutputTransform() (UnaryMultiOutputTransform, bool) {
   254  	if !o.Type().IsUnaryMultiOutputTransform() {
   255  		return nil, false
   256  	}
   257  	return o.unaryMulti, true
   258  }
   259  
   260  var (
   261  	unaryTransforms = map[Type]func() UnaryTransform{
   262  		Absolute: transformAbsolute,
   263  		Add:      transformAdd,
   264  	}
   265  	binaryTransforms = map[Type]func() BinaryTransform{
   266  		PerSecond: transformPerSecond,
   267  		Increase:  transformIncrease,
   268  	}
   269  	unaryMultiOutputTransforms = map[Type]func() UnaryMultiOutputTransform{
   270  		Reset: transformReset,
   271  	}
   272  	typeStringMap map[string]Type
   273  )
   274  
   275  func init() {
   276  	typeStringMap = make(map[string]Type)
   277  	for t := range unaryTransforms {
   278  		typeStringMap[t.String()] = t
   279  	}
   280  	for t := range binaryTransforms {
   281  		typeStringMap[t.String()] = t
   282  	}
   283  	for t := range unaryMultiOutputTransforms {
   284  		typeStringMap[t.String()] = t
   285  	}
   286  }