"""Slug value object for URL-friendly identifiers.""" import re from dataclasses import dataclass from app.domain.value_objects.base import ValueObject @dataclass(frozen=True, slots=True) class Slug(ValueObject[str]): """URL slug value object.""" MAX_LENGTH: int = 200 SLUG_PATTERN: str = r"^[a-z0-9]+(?:-[a-z0-9]+)*$" def _validate(self) -> None: if not isinstance(self.value, str): raise ValueError("Slug must be a string") if len(self.value) > self.MAX_LENGTH: raise ValueError(f"Slug must be at most {self.MAX_LENGTH} characters") if not re.match(self.SLUG_PATTERN, self.value): raise ValueError( "Slug must contain only lowercase letters, numbers, and hyphens" ) @classmethod def from_title(cls, title: str) -> "Slug": """Generate slug from title.""" # Convert to lowercase, replace spaces with hyphens slug = title.lower().strip() # Keep only alphanumeric, spaces, and hyphens slug = re.sub(r"[^a-z0-9\s-]", "", slug) # Replace spaces and multiple hyphens with single hyphen slug = re.sub(r"[-\s]+", "-", slug) # Limit length and strip hyphens max_len = 200 # Same as MAX_LENGTH slug = slug[:max_len].strip("-") # Ensure we have at least one character if not slug: slug = "post" return cls(value=slug)