package utils import ( "net/mail" "regexp" "strings" "time" "github.com/calendarapi/internal/models" "github.com/google/uuid" ) var hexColorRegex = regexp.MustCompile(`^#[0-9A-Fa-f]{6}$`) func ValidateEmail(email string) error { if email == "" { return models.NewValidationError("email is required") } if _, err := mail.ParseAddress(email); err != nil { return models.NewValidationError("invalid email format") } return nil } func ValidatePassword(password string) error { if len(password) < 10 { return models.NewValidationError("password must be at least 10 characters") } return nil } func ValidateTimezone(tz string) error { if tz == "" { return nil } if _, err := time.LoadLocation(tz); err != nil { return models.NewValidationError("invalid IANA timezone: " + tz) } return nil } func ValidateCalendarName(name string) error { if len(name) < 1 || len(name) > 80 { return models.NewValidationError("calendar name must be 1-80 characters") } return nil } func ValidateColor(color string) error { if color == "" { return nil } if !hexColorRegex.MatchString(color) { return models.NewValidationError("color must be hex format #RRGGBB") } return nil } func ValidateEventTitle(title string) error { if len(title) < 1 || len(title) > 140 { return models.NewValidationError("event title must be 1-140 characters") } return nil } func ValidateTimeRange(start, end time.Time) error { if !end.After(start) { return models.NewValidationError("end_time must be after start_time") } return nil } func ValidateReminderMinutes(minutes int32) error { if minutes < 0 || minutes > 10080 { return models.NewValidationError("minutes_before must be 0-10080") } return nil } func ValidateUUID(s string) (uuid.UUID, error) { id, err := uuid.Parse(s) if err != nil { return uuid.Nil, models.NewValidationError("invalid UUID: " + s) } return id, nil } func NormalizeEmail(email string) string { return strings.ToLower(strings.TrimSpace(email)) } func ValidateRecurrenceRangeLimit(start, end time.Time) error { if end.Sub(start) > 366*24*time.Hour { return models.NewValidationError("date range cannot exceed 1 year") } return nil } func ValidateTaskTitle(title string) error { if len(title) < 1 || len(title) > 500 { return models.NewValidationError("task title must be 1-500 characters") } return nil } func ValidateTaskStatus(status string) error { valid := map[string]bool{"todo": true, "in_progress": true, "done": true, "archived": true} if !valid[status] { return models.NewValidationError("invalid task status") } return nil } func ParseTime(s string) (time.Time, error) { t, err := time.Parse(time.RFC3339, s) if err != nil { t, err = time.Parse("2006-01-02T15:04:05Z07:00", s) } if err != nil { t, err = time.Parse("2006-01-02", s) } return t, err } func ValidateTaskPriority(priority string) error { valid := map[string]bool{"low": true, "medium": true, "high": true, "critical": true} if !valid[priority] { return models.NewValidationError("invalid task priority") } return nil }