package models
import (
"errors"
"fmt"
"strings"
"time"
)
const (
Version = "1.0.0"
MaxNameLength = 100
MaxEmailLength = 254
)
var (
DefaultRole = RoleUser
userCounter int
)
type UserRole int
const (
RoleGuest UserRole = iota
RoleUser
RoleAdmin
)
func (r UserRole) String() string {
switch r {
case RoleGuest:
return "guest"
case RoleUser:
return "user"
case RoleAdmin:
return "admin"
default:
return "unknown"
}
}
func (r UserRole) IsValid() bool {
return r >= RoleGuest && r <= RoleAdmin
}
type User struct {
ID uint64 `json:"id"`
name string `json:"name"` email string `json:"email"` role UserRole `json:"role"`
createdAt time.Time
lastLogin *time.Time
}
func NewUser(name, email string, role UserRole) *User {
userCounter++
return &User{
ID: uint64(userCounter),
name: strings.TrimSpace(name),
email: strings.ToLower(strings.TrimSpace(email)),
role: role,
createdAt: time.Now(),
}
}
func (u *User) Name() string {
return u.name
}
func (u *User) Email() string {
return u.email
}
func (u *User) Role() UserRole {
return u.role
}
func (u *User) CreatedAt() time.Time {
return u.createdAt
}
func (u *User) SetName(name string) error {
if strings.TrimSpace(name) == "" {
return errors.New("name cannot be empty")
}
if len(name) > MaxNameLength {
return fmt.Errorf("name too long (max %d characters)", MaxNameLength)
}
u.name = strings.TrimSpace(name)
return nil
}
func (u *User) SetEmail(email string) error {
if err := validateEmail(email); err != nil {
return err
}
u.email = strings.ToLower(strings.TrimSpace(email))
return nil
}
func (u *User) SetRole(role UserRole) error {
if !role.IsValid() {
return errors.New("invalid role")
}
u.role = role
return nil
}
func (u *User) IsAdmin() bool {
return u.role == RoleAdmin
}
func (u *User) CanModifyUser(other *User) bool {
return u.IsAdmin() || u.ID == other.ID
}
func (u *User) UpdateLastLogin() {
now := time.Now()
u.lastLogin = &now
}
func (u *User) String() string {
return fmt.Sprintf("User(id=%d, name=%s, email=%s, role=%s)",
u.ID, u.name, u.email, u.role)
}
func (u *User) Validate() error {
if strings.TrimSpace(u.name) == "" {
return NewUserError(ErrInvalidName, "name cannot be empty")
}
if err := validateEmail(u.email); err != nil {
return err
}
if !u.role.IsValid() {
return NewUserError(ErrInvalidRole, "invalid user role")
}
return nil
}
func CreateValidatedUser(name, email string, role UserRole) (*User, error) {
user := NewUser(name, email, role)
if err := user.Validate(); err != nil {
return nil, err
}
return user, nil
}
func validateEmail(email string) error {
email = strings.TrimSpace(email)
if email == "" {
return NewUserError(ErrInvalidEmail, "email cannot be empty")
}
if !strings.Contains(email, "@") {
return NewUserError(ErrInvalidEmail, "invalid email format")
}
if len(email) > MaxEmailLength {
return NewUserError(ErrInvalidEmail, fmt.Sprintf("email too long (max %d characters)", MaxEmailLength))
}
return nil
}
func CreateGuestUser(name string) *User {
email := fmt.Sprintf("%s@guest.local", strings.ToLower(strings.ReplaceAll(name, " ", ".")))
return NewUser(name, email, RoleGuest)
}
func GetUserStats() UserStats {
return UserStats{
TotalCreated: userCounter,
DefaultRole: DefaultRole.String(),
}
}
type UserStats struct {
TotalCreated int
DefaultRole string
}
type UserErrorCode int
const (
ErrInvalidName UserErrorCode = iota
ErrInvalidEmail
ErrInvalidRole
ErrUserNotFound
ErrDuplicateEmail
)
type UserError struct {
Code UserErrorCode
Message string
}
func NewUserError(code UserErrorCode, message string) *UserError {
return &UserError{
Code: code,
Message: message,
}
}
func (e *UserError) Error() string {
return fmt.Sprintf("user error [%d]: %s", int(e.Code), e.Message)
}
func (e *UserError) Is(target error) bool {
if other, ok := target.(*UserError); ok {
return e.Code == other.Code
}
return false
}
func init() {
fmt.Println("[INIT] Models package initialized")
}