package utils import ( "errors" "regexp" ) var ( ErrInvalidISBN = errors.New("ISBN must be 10 or 13 digits") ) // NormalizeISBN removes hyphens and spaces from ISBN to standardize format // Handles ISBN-10 and ISBN-13 formats // Returns error if ISBN is not 10 or 13 digits after normalization // Converts ISBN-10 to ISBN-13 by prefixing with "978" and recalculating checksum func NormalizeISBN(isbn string) (string, error) { if isbn == "" { return "", nil } // Remove hyphens and spaces, return only digits and X (for ISBN-10) normalized := regexp.MustCompile(`[-\s]`).ReplaceAllString(isbn, "") // Check length: must be 10 or 13 digits length := len(normalized) if length == 10 { // Convert ISBN-10 to ISBN-13 return convertISBN10To13(normalized) } if length == 13 { return normalized, nil } return "", ErrInvalidISBN } // NormalizeISBNSafe removes hyphens and spaces from ISBN without validation // Used by scanners where metadata may be incomplete or malformed func NormalizeISBNSafe(isbn string) string { if isbn == "" { return "" } // Remove hyphens and spaces, return only digits and X (for ISBN-10) return regexp.MustCompile(`[-\s]`).ReplaceAllString(isbn, "") } // convertISBN10To13 converts ISBN-10 to ISBN-13 by prefixing "978" and recalculating checksum func convertISBN10To13(isbn10 string) (string, error) { // ISBN-10 to ISBN-13: prefix "978" and recalculate checksum // Replace last digit (X becomes 0 for calculation purposes) isbn12 := "978" + isbn10[:9] // Calculate ISBN-13 checksum sum := 0 for i := 0; i < 12; i++ { digit := int(isbn12[i] - '0') if i%2 == 0 { sum += digit * 1 } else { sum += digit * 3 } } checksum := (10 - (sum % 10)) % 10 return isbn12 + string(rune('0'+checksum)), nil }