package dara import ( "encoding/json" "errors" "fmt" "reflect" "regexp" "strconv" "strings" ) type Model interface { Validate() error ToMap() map[string]interface{} copyWithouStream() Model } func Validate(params interface{}) error { if params == nil { return nil } requestValue := reflect.ValueOf(params) if requestValue.IsNil() { return nil } err := validate(requestValue.Elem()) return err } // ValidateRequired checks if a value is set (non-nil) func ValidateRequired(value interface{}, fieldName string) error { if value == nil { return errors.New(fieldName + " should be setted") } // Check for typed nil pointers switch v := value.(type) { case *string: if v == nil { return errors.New(fieldName + " should be setted") } case *int: if v == nil { return errors.New(fieldName + " should be setted") } case *int8: if v == nil { return errors.New(fieldName + " should be setted") } case *int16: if v == nil { return errors.New(fieldName + " should be setted") } case *int32: if v == nil { return errors.New(fieldName + " should be setted") } case *int64: if v == nil { return errors.New(fieldName + " should be setted") } case *uint: if v == nil { return errors.New(fieldName + " should be setted") } case *uint8: if v == nil { return errors.New(fieldName + " should be setted") } case *uint16: if v == nil { return errors.New(fieldName + " should be setted") } case *uint32: if v == nil { return errors.New(fieldName + " should be setted") } case *uint64: if v == nil { return errors.New(fieldName + " should be setted") } case *float32: if v == nil { return errors.New(fieldName + " should be setted") } case *float64: if v == nil { return errors.New(fieldName + " should be setted") } } return nil } // ValidateMaxLength validates maximum length using type assertions func ValidateMaxLength(value interface{}, maxLength int, fieldName string) error { length := getValueLength(value) if length > maxLength { return fmt.Errorf("The length of %s is %d which is more than %d", fieldName, length, maxLength) } return nil } // ValidateMinLength validates minimum length using type assertions func ValidateMinLength(value interface{}, minLength int, fieldName string) error { length := getValueLength(value) if length < minLength { return fmt.Errorf("The length of %s is %d which is less than %d", fieldName, length, minLength) } return nil } // ValidatePattern validates string against regex pattern func ValidatePattern(value interface{}, pattern string, fieldName string) error { strValue := getStringValue(value) if strValue == "" { return nil // Empty strings are valid unless required } r, err := regexp.Compile("^" + pattern + "$") if err != nil { return err } if !r.MatchString(strValue) { return errors.New(strValue + " is not matched " + pattern) } return nil } // ValidateMaximum validates maximum value for numeric types func ValidateMaximum(value interface{}, maximum float64, fieldName string) error { numValue, ok := getNumericValue(value) if !ok { return nil // Skip validation for non-numeric types } if numValue > maximum { return fmt.Errorf("The size of %s is %f which is greater than %f", fieldName, numValue, maximum) } return nil } // ValidateMinimum validates minimum value for numeric types func ValidateMinimum(value interface{}, minimum float64, fieldName string) error { numValue, ok := getNumericValue(value) if !ok { return nil // Skip validation for non-numeric types } if numValue < minimum { return fmt.Errorf("The size of %s is %f which is less than %f", fieldName, numValue, minimum) } return nil } // ValidateArray validates array/slice elements recursively func ValidateArray(arr interface{}, validator func(interface{}) error) error { switch v := arr.(type) { case []interface{}: for _, item := range v { if err := validator(item); err != nil { return err } } case []*string: for _, item := range v { if err := validator(item); err != nil { return err } } case []*int: for _, item := range v { if err := validator(item); err != nil { return err } } // Add more specific types as needed } return nil } // ValidateMap validates map values recursively func ValidateMap(m interface{}, validator func(interface{}) error) error { switch v := m.(type) { case map[string]interface{}: for _, value := range v { if err := validator(value); err != nil { return err } } case map[string]*string: for _, value := range v { if err := validator(value); err != nil { return err } } // Add more specific types as needed } return nil } // Helper functions using pure type assertions (no reflection) // getStringValue extracts string value using type assertions func getStringValue(value interface{}) string { switch v := value.(type) { case string: return v case *string: if v != nil { return *v } return "" default: return "" } } // getNumericValue extracts numeric value as float64 using type assertions func getNumericValue(value interface{}) (float64, bool) { switch v := value.(type) { // Direct numeric types case int: return float64(v), true case int8: return float64(v), true case int16: return float64(v), true case int32: return float64(v), true case int64: return float64(v), true case uint: return float64(v), true case uint8: return float64(v), true case uint16: return float64(v), true case uint32: return float64(v), true case uint64: return float64(v), true case float32: return float64(v), true case float64: return v, true // Pointer types case *int: if v != nil { return float64(*v), true } case *int8: if v != nil { return float64(*v), true } case *int16: if v != nil { return float64(*v), true } case *int32: if v != nil { return float64(*v), true } case *int64: if v != nil { return float64(*v), true } case *uint: if v != nil { return float64(*v), true } case *uint8: if v != nil { return float64(*v), true } case *uint16: if v != nil { return float64(*v), true } case *uint32: if v != nil { return float64(*v), true } case *uint64: if v != nil { return float64(*v), true } case *float32: if v != nil { return float64(*v), true } case *float64: if v != nil { return *v, true } default: return 0, false } return 0, false } // getValueLength returns length of string, array, slice, or map using type assertions func getValueLength(value interface{}) int { switch v := value.(type) { case string: // Use the same method as original implementation for consistency return len([]rune(v)) case *string: if v != nil { return len([]rune(*v)) } return 0 case []interface{}: return len(v) case []string: return len(v) case []*string: return len(v) case []int: return len(v) case []*int: return len(v) case map[string]interface{}: return len(v) case map[string]string: return len(v) case map[string]*string: return len(v) // Add more specific types as needed default: return 0 } } // Deprecated: use new validation methods instead // Verify whether the parameters meet the requirements func validate(dataValue reflect.Value) error { if strings.HasPrefix(dataValue.Type().String(), "*") { // Determines whether the input is a structure object or a pointer object if dataValue.IsNil() { return nil } dataValue = dataValue.Elem() } dataType := dataValue.Type() for i := 0; i < dataType.NumField(); i++ { field := dataType.Field(i) valueField := dataValue.Field(i) for _, value := range validateParams { err := validateParam(field, valueField, value) if err != nil { return err } } } return nil } func validateParam(field reflect.StructField, valueField reflect.Value, tagName string) error { tag, containsTag := field.Tag.Lookup(tagName) // Take out the checked regular expression if containsTag && tagName == "require" { err := checkRequire(field, valueField) if err != nil { return err } } if strings.HasPrefix(field.Type.String(), "[]") { // Verify the parameters of the array type err := validateSlice(field, valueField, containsTag, tag, tagName) if err != nil { return err } } else if valueField.Kind() == reflect.Ptr { // Determines whether it is a pointer object err := validatePtr(field, valueField, containsTag, tag, tagName) if err != nil { return err } } return nil } func validateSlice(field reflect.StructField, valueField reflect.Value, containsregexpTag bool, tag, tagName string) error { if valueField.IsValid() && !valueField.IsNil() { // Determines whether the parameter has a value if containsregexpTag { if tagName == "maxItems" { err := checkMaxItems(field, valueField, tag) if err != nil { return err } } if tagName == "minItems" { err := checkMinItems(field, valueField, tag) if err != nil { return err } } } for m := 0; m < valueField.Len(); m++ { elementValue := valueField.Index(m) if elementValue.Type().Kind() == reflect.Ptr { // Determines whether the child elements of an array are of a basic type err := validatePtr(field, elementValue, containsregexpTag, tag, tagName) if err != nil { return err } } } } return nil } func validatePtr(field reflect.StructField, elementValue reflect.Value, containsregexpTag bool, tag, tagName string) error { if elementValue.IsNil() { return nil } if isFilterType(elementValue.Elem().Type().String(), basicTypes) { if containsregexpTag { if tagName == "pattern" { err := checkPattern(field, elementValue.Elem(), tag) if err != nil { return err } } if tagName == "maxLength" { err := checkMaxLength(field, elementValue.Elem(), tag) if err != nil { return err } } if tagName == "minLength" { err := checkMinLength(field, elementValue.Elem(), tag) if err != nil { return err } } if tagName == "maximum" { err := checkMaximum(field, elementValue.Elem(), tag) if err != nil { return err } } if tagName == "minimum" { err := checkMinimum(field, elementValue.Elem(), tag) if err != nil { return err } } } } else { err := validate(elementValue) if err != nil { return err } } return nil } func checkRequire(field reflect.StructField, valueField reflect.Value) error { name, _ := field.Tag.Lookup("json") strs := strings.Split(name, ",") name = strs[0] if !valueField.IsNil() && valueField.IsValid() { return nil } return errors.New(name + " should be setted") } func checkPattern(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() && valueField.String() != "" { value := valueField.String() r, _ := regexp.Compile("^" + tag + "$") if match := r.MatchString(value); !match { // Determines whether the parameter value satisfies the regular expression or not, and throws an error return errors.New(value + " is not matched " + tag) } } return nil } func checkMaxItems(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() && valueField.String() != "" { maxItems, err := strconv.Atoi(tag) if err != nil { return err } length := valueField.Len() if maxItems < length { errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxItems) return errors.New(errMsg) } } return nil } func checkMinItems(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() { minItems, err := strconv.Atoi(tag) if err != nil { return err } length := valueField.Len() if minItems > length { errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minItems) return errors.New(errMsg) } } return nil } func checkMaxLength(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() && valueField.String() != "" { maxLength, err := strconv.Atoi(tag) if err != nil { return err } length := valueField.Len() if valueField.Kind().String() == "string" { length = strings.Count(valueField.String(), "") - 1 } if maxLength < length { errMsg := fmt.Sprintf("The length of %s is %d which is more than %d", field.Name, length, maxLength) return errors.New(errMsg) } } return nil } func checkMinLength(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() { minLength, err := strconv.Atoi(tag) if err != nil { return err } length := valueField.Len() if valueField.Kind().String() == "string" { length = strings.Count(valueField.String(), "") - 1 } if minLength > length { errMsg := fmt.Sprintf("The length of %s is %d which is less than %d", field.Name, length, minLength) return errors.New(errMsg) } } return nil } func checkMaximum(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() && valueField.String() != "" { maximum, err := strconv.ParseFloat(tag, 64) if err != nil { return err } byt, _ := json.Marshal(valueField.Interface()) num, err := strconv.ParseFloat(string(byt), 64) if err != nil { return err } if maximum < num { errMsg := fmt.Sprintf("The size of %s is %f which is greater than %f", field.Name, num, maximum) return errors.New(errMsg) } } return nil } func checkMinimum(field reflect.StructField, valueField reflect.Value, tag string) error { if valueField.IsValid() && valueField.String() != "" { minimum, err := strconv.ParseFloat(tag, 64) if err != nil { return err } byt, _ := json.Marshal(valueField.Interface()) num, err := strconv.ParseFloat(string(byt), 64) if err != nil { return err } if minimum > num { errMsg := fmt.Sprintf("The size of %s is %f which is less than %f", field.Name, num, minimum) return errors.New(errMsg) } } return nil }