package inquiry import ( "fmt" "regexp" "strings" "github.com/asaskevich/govalidator" ) var ( lengthRulePattern = regexp.MustCompile(`length\((\d+)\|(\d+)\)`) inRulePattern = regexp.MustCompile(`in\(([^)]+)\)`) ) // FormatValidationErrors converts govalidator errors into user-facing messages. func FormatValidationErrors(err error) string { if err == nil { return "" } errs, ok := err.(govalidator.Errors) if !ok { return humanizeValidationError(err.Error()) } messages := make([]string, 0, len(errs)) for _, item := range errs { messages = append(messages, humanizeValidationError(item.Error())) } return strings.Join(messages, "; ") } func humanizeValidationError(raw string) string { field, rule, ok := parseGovalidatorError(raw) if !ok { return raw } label := inquiryFieldLabel(field) lowerRule := strings.ToLower(rule) switch { case strings.Contains(lowerRule, "required"): return label + " is required" case strings.Contains(lowerRule, "email"): return label + " must be a valid email address" case strings.Contains(lowerRule, "length("): if min, max, ok := parseLengthRule(rule); ok { if min == max { return fmt.Sprintf("%s must be exactly %d characters", label, min) } return fmt.Sprintf("%s must be between %d and %d characters", label, min, max) } return label + " has an invalid length" case strings.Contains(lowerRule, "in("): if values, ok := parseInRule(rule); ok { return fmt.Sprintf("%s must be one of: %s", label, strings.Join(values, ", ")) } return label + " has an invalid value" case strings.Contains(lowerRule, "range("): return label + " is out of the allowed range" default: return label + " is invalid" } } func parseGovalidatorError(raw string) (field, rule string, ok bool) { parts := strings.SplitN(raw, ": ", 2) if len(parts) != 2 { return "", "", false } return parts[0], parts[1], true } func parseLengthRule(rule string) (min, max int, ok bool) { matches := lengthRulePattern.FindStringSubmatch(rule) if len(matches) != 3 { return 0, 0, false } _, err := fmt.Sscanf(matches[1]+" "+matches[2], "%d %d", &min, &max) return min, max, err == nil } func parseInRule(rule string) (values []string, ok bool) { matches := inRulePattern.FindStringSubmatch(rule) if len(matches) != 2 { return nil, false } for _, value := range strings.Split(matches[1], "|") { values = append(values, value) } return values, len(values) > 0 } func inquiryFieldLabel(field string) string { field = strings.TrimPrefix(field, "CreateInquiryForm.") field = strings.TrimPrefix(field, "UpdateInquiryStatusForm.") field = strings.TrimPrefix(field, "SearchInquiriesForm.") labels := map[string]string{ "FullName": "Full name", "CompanyName": "Company name", "WorkEmail": "Work email", "PhoneNumber": "Phone number", "HCaptchaToken": "hCaptcha token", "Status": "Status", "Reason": "Reason", "Description": "Description", "Search": "Search query", "Limit": "Limit", "Offset": "Offset", "SortBy": "Sort field", "SortOrder": "Sort order", } if label, exists := labels[field]; exists { return label } return field }