restructure project, add claudemd
This commit is contained in:
211
go/validation/validation.go
Normal file
211
go/validation/validation.go
Normal file
@@ -0,0 +1,211 @@
|
||||
package validation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func SanitizeEmail(input string) string {
|
||||
input = strings.ToLower(strings.TrimSpace(input))
|
||||
|
||||
re := regexp.MustCompile(`[a-z0-9!#$%&'*+/=?^_` + "`" + `{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_` + "`" + `{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?`)
|
||||
match := re.FindString(input)
|
||||
|
||||
return match
|
||||
}
|
||||
|
||||
// Checks an email address against a first and last name and returns true if it matches one of the following patterns:
|
||||
// firstlast@domain.tld, flast@domain.tld, firstl@domain.tld, first.last@domain.tld, f.last@domain.tld,
|
||||
// first.l@domain.tld, first-last@domain.tld, f-last@domain.tld, first-l@domain.tld. Otherwise, false.
|
||||
// Also handles hyphenated and space-separated last names (e.g., Meyer-Ogren or Van Der Berg)
|
||||
func DoesNameMatchEmail(email string, firstName string, lastName string) bool {
|
||||
// Validate inputs
|
||||
if email == "" || firstName == "" || lastName == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
email = strings.ToLower(email)
|
||||
firstName = strings.ToLower(firstName)
|
||||
lastName = strings.ToLower(lastName)
|
||||
|
||||
emailIdentifier := strings.Split(email, "@")[0]
|
||||
firstInitial := string(firstName[0])
|
||||
|
||||
// Split hyphenated and space-separated last names
|
||||
lastNameParts := []string{lastName}
|
||||
|
||||
// Also add concatenated version (removing hyphens/spaces)
|
||||
lastNameNormalized := strings.ReplaceAll(lastName, "-", "")
|
||||
lastNameNormalized = strings.ReplaceAll(lastNameNormalized, " ", "")
|
||||
if lastNameNormalized != lastName {
|
||||
lastNameParts = append(lastNameParts, lastNameNormalized)
|
||||
}
|
||||
|
||||
// Add individual parts
|
||||
if strings.Contains(lastName, "-") {
|
||||
lastNameParts = append(lastNameParts, strings.Split(lastName, "-")...)
|
||||
}
|
||||
if strings.Contains(lastName, " ") {
|
||||
lastNameParts = append(lastNameParts, strings.Split(lastName, " ")...)
|
||||
}
|
||||
|
||||
separators := []string{"", ".", "-"}
|
||||
|
||||
// Check standalone first or last names
|
||||
if emailIdentifier == firstName {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check each part of the last name
|
||||
for _, lastPart := range lastNameParts {
|
||||
if lastPart == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if emailIdentifier == lastPart {
|
||||
return true
|
||||
}
|
||||
|
||||
lastPartInitial := string(lastPart[0])
|
||||
|
||||
// Check all separator combinations with each last name part
|
||||
for _, separator := range separators {
|
||||
switch emailIdentifier {
|
||||
case strings.Join([]string{firstName, lastPart}, separator),
|
||||
strings.Join([]string{firstInitial, lastPart}, separator),
|
||||
strings.Join([]string{firstName, lastPartInitial}, separator):
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func SanitizePhone(input string) string {
|
||||
// Regular expression to match non-digit characters
|
||||
re := regexp.MustCompile(`[^0-9]`)
|
||||
|
||||
// Replace non-digit characters with empty string
|
||||
sanitized := re.ReplaceAllString(input, "")
|
||||
|
||||
return sanitized
|
||||
}
|
||||
|
||||
func ValidatePhoneNumber(phoneNumber string) error {
|
||||
isNumber, _ := regexp.MatchString(`^\d+$`, phoneNumber)
|
||||
if !isNumber {
|
||||
return errors.New("Phone number must only contain numbers.")
|
||||
}
|
||||
|
||||
if len(phoneNumber) != 10 {
|
||||
return errors.New("Invalid phone number length.")
|
||||
}
|
||||
|
||||
areaCode, _ := strconv.Atoi(phoneNumber[:3])
|
||||
|
||||
// Lowest area code is 200, so less than (200) 000-0000
|
||||
if areaCode < 200 {
|
||||
return errors.New("Phone number has invalid area code.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes invalid UTF-8 sequences and replaces special characters
|
||||
func SanitizeString(s string) string {
|
||||
// Replace non-breaking spaces (0xa0) with regular spaces
|
||||
s = strings.ReplaceAll(s, "\u00a0", " ")
|
||||
|
||||
// Remove other common problematic characters
|
||||
s = strings.ReplaceAll(s, "\r", "")
|
||||
|
||||
// Remove replacement character (<28>) that appears when invalid UTF-8 is encountered
|
||||
s = strings.ReplaceAll(s, "\ufffd", "")
|
||||
|
||||
// Validate and fix UTF-8 encoding
|
||||
if !utf8.ValidString(s) {
|
||||
// Convert to valid UTF-8 by removing invalid bytes
|
||||
v := make([]rune, 0, len(s))
|
||||
for _, r := range s {
|
||||
if r != utf8.RuneError {
|
||||
v = append(v, r)
|
||||
}
|
||||
}
|
||||
s = string(v)
|
||||
}
|
||||
|
||||
// Trim whitespace
|
||||
return strings.TrimSpace(s)
|
||||
}
|
||||
|
||||
var stateCodes []string = []string{
|
||||
"AL", "AK", "AZ", "AR", "AS", "CA", "CO", "CT", "DE", "DC", "FL", "GA",
|
||||
"GU", "HI", "ID", "IL", "IN", "IA", "KS", "KY", "LA", "ME", "MD", "MA",
|
||||
"MI", "MN", "MS", "MO", "MT", "NE", "NV", "NH", "NJ", "NM", "NY", "NC",
|
||||
"ND", "MP", "OH", "OK", "OR", "PA", "PR", "RI", "SC", "SD", "TN", "TX",
|
||||
"TT", "UT", "VT", "VA", "VI", "WA", "WV", "WI", "WY",
|
||||
}
|
||||
|
||||
// Checks if the input string is a proper two-letter state code
|
||||
func ValidateStateCode(s string) error {
|
||||
if slices.Contains(stateCodes, s) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("Invalid state code.")
|
||||
}
|
||||
|
||||
func ValidateTaxId(id string) error {
|
||||
isNumber, _ := regexp.MatchString(`^\d+$`, id)
|
||||
if !isNumber {
|
||||
return errors.New("Tax ID must only contain numbers.")
|
||||
}
|
||||
|
||||
if len(id) != 9 {
|
||||
return errors.New("Tax ID must be 9 digits long.")
|
||||
}
|
||||
|
||||
idNum, _ := strconv.Atoi(id)
|
||||
if idNum == 0 {
|
||||
return errors.New("Tax ID cannot be 00-0000000.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateZipCode(zip string) error {
|
||||
isNumber, _ := regexp.MatchString(`^\d+$`, zip)
|
||||
if !isNumber {
|
||||
return errors.New("ZIP code must only contain numbers.")
|
||||
}
|
||||
|
||||
if (len(zip) != 5) && (len(zip) != 9) {
|
||||
return errors.New("Invalid length for ZIP code.")
|
||||
}
|
||||
|
||||
zipCode, _ := strconv.Atoi(zip[:5])
|
||||
|
||||
// Lowest 5 digit ZIP code is 00501
|
||||
if zipCode < 501 {
|
||||
return errors.New("Invalid ZIP code.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func ValidateUrl(url string) error {
|
||||
err := errors.New("Invalid URL.")
|
||||
|
||||
valid, _ := regexp.MatchString(`[(http(s)?):\/\/(www\.)?a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b([-a-zA-Z0-9@:%_\+.~#?&//=]*)`, url)
|
||||
|
||||
if !valid {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user