github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/depends/kit/validator/validator__float.go (about)

     1  package validator
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strconv"
     8  
     9  	"github.com/machinefi/w3bstream/pkg/depends/kit/validator/errors"
    10  	"github.com/machinefi/w3bstream/pkg/depends/kit/validator/rules"
    11  	"github.com/machinefi/w3bstream/pkg/depends/x/ptrx"
    12  	"github.com/machinefi/w3bstream/pkg/depends/x/typesx"
    13  )
    14  
    15  type Float struct {
    16  	MaxDigits     uint
    17  	DecimalDigits *uint
    18  
    19  	Minimum          *float64
    20  	Maximum          *float64
    21  	ExclusiveMaximum bool
    22  	ExclusiveMinimum bool
    23  
    24  	MultipleOf float64
    25  
    26  	Enums map[float64]string
    27  }
    28  
    29  func init() { DefaultFactory.Register(&Float{}) }
    30  
    31  func (vf *Float) SetDefault() {
    32  	if vf != nil {
    33  		if vf.MaxDigits == 0 {
    34  			vf.MaxDigits = 7
    35  		}
    36  		if vf.DecimalDigits == nil {
    37  			vf.DecimalDigits = ptrx.Uint(2)
    38  		}
    39  	}
    40  }
    41  
    42  func (Float) Names() []string {
    43  	return []string{"float", "double", "float32", "float64"}
    44  }
    45  
    46  var (
    47  	TargetFloatValue                = "float value"
    48  	TargetDecimalDigitsOfFloatValue = "decimal digits of float value"
    49  	TargetTotalDigitsOfFloatValue   = "total digits of float value"
    50  )
    51  
    52  func (vf *Float) Validate(v interface{}) error {
    53  	rv, ok := v.(reflect.Value)
    54  	if !ok {
    55  		rv = reflect.ValueOf(v)
    56  	}
    57  
    58  	if k := rv.Type().Kind(); !typesx.IsFloatReflectKind(k) {
    59  		return errors.NewUnsupportedTypeError(rv.Type().String(), vf.String())
    60  	}
    61  
    62  	val := rv.Float()
    63  	decimal := *vf.DecimalDigits
    64  
    65  	m, d := FloatLengthOfDigit(val)
    66  	if m > vf.MaxDigits {
    67  		return &errors.OutOfRangeError{
    68  			Target:  TargetTotalDigitsOfFloatValue,
    69  			Current: m,
    70  			Maximum: vf.MaxDigits,
    71  		}
    72  	}
    73  
    74  	if d > decimal {
    75  		return &errors.OutOfRangeError{
    76  			Target:  TargetDecimalDigitsOfFloatValue,
    77  			Current: d,
    78  			Maximum: decimal,
    79  		}
    80  	}
    81  
    82  	if vf.Enums != nil {
    83  		if _, ok := vf.Enums[val]; !ok {
    84  			values := make([]interface{}, 0)
    85  			for _, v := range vf.Enums {
    86  				values = append(values, v)
    87  			}
    88  
    89  			return &errors.NotInEnumError{
    90  				Target:  TargetFloatValue,
    91  				Current: v,
    92  				Enums:   values,
    93  			}
    94  		}
    95  		return nil
    96  	}
    97  
    98  	if vf.Minimum != nil {
    99  		minimum := *vf.Minimum
   100  		if (vf.ExclusiveMinimum && val == minimum) || val < minimum {
   101  			return &errors.OutOfRangeError{
   102  				Target:           TargetFloatValue,
   103  				Current:          val,
   104  				Minimum:          minimum,
   105  				ExclusiveMinimum: vf.ExclusiveMinimum,
   106  			}
   107  		}
   108  	}
   109  
   110  	if vf.Maximum != nil {
   111  		maximum := *vf.Maximum
   112  		if (vf.ExclusiveMaximum && val == maximum) || val > maximum {
   113  			return &errors.OutOfRangeError{
   114  				Target:           TargetFloatValue,
   115  				Current:          val,
   116  				Maximum:          maximum,
   117  				ExclusiveMaximum: vf.ExclusiveMaximum,
   118  			}
   119  		}
   120  	}
   121  
   122  	if vf.MultipleOf != 0 {
   123  		if !IsFloatMultipleOf(val, vf.MultipleOf, decimal) {
   124  			return &errors.MultipleOfError{
   125  				Target:     TargetFloatValue,
   126  				Current:    val,
   127  				MultipleOf: vf.MultipleOf,
   128  			}
   129  		}
   130  	}
   131  
   132  	return nil
   133  }
   134  
   135  func (Float) New(ctx context.Context, r *Rule) (Validator, error) {
   136  	vf := &Float{}
   137  
   138  	switch r.Name {
   139  	case "float", "float32":
   140  		vf.MaxDigits = 7
   141  	case "double", "float64":
   142  		vf.MaxDigits = 15
   143  	}
   144  
   145  	// @float<max_digit,max_decimal>
   146  	// @float<5,2> 1.111(too many decimal) 12345.67 (too many digits)
   147  	if len(r.Params) > 0 {
   148  		digits, decimal, err := FloatRuleParam(r)
   149  		if err != nil {
   150  			return nil, err
   151  		}
   152  		vf.MaxDigits = uint(digits)
   153  		if decimal != nil {
   154  			vf.DecimalDigits = ptrx.Uint(uint(*decimal))
   155  		}
   156  	}
   157  
   158  	vf.SetDefault()
   159  	vf.ExclusiveMinimum = r.ExclusiveMin
   160  	vf.ExclusiveMaximum = r.ExclusiveMax
   161  
   162  	// @float[min,max]
   163  	// @float(min,max]
   164  	// @float[min,max)
   165  	// @float(min,max)
   166  	min, max, err := FloatRuleRange(r, vf.MaxDigits, vf.DecimalDigits)
   167  	if err != nil {
   168  		return nil, err
   169  	}
   170  	vf.Minimum, vf.Maximum = min, max
   171  
   172  	// @float{1.1,2.2,3.3} should be one of these
   173  	// @float{%2.2} should be multiple of 2.2
   174  	multiple, enums, err := FloatRuleValues(r, vf.MaxDigits, vf.DecimalDigits)
   175  	if err != nil {
   176  		return nil, err
   177  	}
   178  	vf.MultipleOf = multiple
   179  	vf.Enums = enums
   180  
   181  	return vf, vf.TypeCheck(r)
   182  }
   183  
   184  func (vf *Float) TypeCheck(r *Rule) error {
   185  	switch r.Type.Kind() {
   186  	case reflect.Float32:
   187  		if vf.MaxDigits > 7 {
   188  			return fmt.Errorf("max digits too large for type %s", r)
   189  		}
   190  		return nil
   191  	case reflect.Float64:
   192  		return nil
   193  	}
   194  	return errors.NewUnsupportedTypeError(r.String(), vf.String())
   195  }
   196  
   197  func (vf *Float) String() string {
   198  	vf.SetDefault()
   199  
   200  	decimal := *vf.DecimalDigits
   201  	rule := rules.NewRule(vf.Names()[0])
   202  	rule.Params = []rules.Node{
   203  		rules.NewLiteral([]byte(strconv.Itoa(int(vf.MaxDigits)))),
   204  		rules.NewLiteral([]byte(strconv.Itoa(int(decimal)))),
   205  	}
   206  
   207  	if vf.Minimum != nil || vf.Maximum != nil {
   208  		rule.Range = make([]*rules.Lit, 2)
   209  
   210  		if vf.Minimum != nil {
   211  			rule.Range[0] = rules.NewLiteral(
   212  				[]byte(fmt.Sprintf("%."+strconv.Itoa(int(decimal))+"f", *vf.Minimum)),
   213  			)
   214  		}
   215  
   216  		if vf.Maximum != nil {
   217  			rule.Range[1] = rules.NewLiteral(
   218  				[]byte(fmt.Sprintf("%."+strconv.Itoa(int(decimal))+"f", *vf.Maximum)),
   219  			)
   220  		}
   221  
   222  		rule.ExclusiveMin = vf.ExclusiveMinimum
   223  		rule.ExclusiveMax = vf.ExclusiveMaximum
   224  	}
   225  
   226  	if vf.MultipleOf != 0 {
   227  		rule.ValueMatrix = [][]*rules.Lit{{
   228  			rules.NewLiteral([]byte("%" + fmt.Sprintf("%."+strconv.Itoa(int(decimal))+"f", vf.MultipleOf))),
   229  		}}
   230  	} else if vf.Enums != nil {
   231  		values := make([]*rules.Lit, 0)
   232  		for _, str := range vf.Enums {
   233  			values = append(values, rules.NewLiteral([]byte(str)))
   234  		}
   235  		rule.ValueMatrix = [][]*rules.Lit{values}
   236  	}
   237  
   238  	return string(rule.Bytes())
   239  }