Files
alfred/domain/shared/value_objects.py
Francwa 2c8cdd3ab1 New archi: domain driven development
Working but need to check out code
2025-12-01 07:10:03 +01:00

129 lines
3.5 KiB
Python

"""Shared value objects used across multiple domains."""
from dataclasses import dataclass
from pathlib import Path
from typing import Union
import re
from .exceptions import ValidationError
@dataclass(frozen=True)
class ImdbId:
"""
Value object representing an IMDb ID.
IMDb IDs follow the format: tt followed by 7-8 digits (e.g., tt1375666)
"""
value: str
def __post_init__(self):
"""Validate IMDb ID format."""
if not self.value:
raise ValidationError("IMDb ID cannot be empty")
if not isinstance(self.value, str):
raise ValidationError(f"IMDb ID must be a string, got {type(self.value)}")
# IMDb ID format: tt + 7-8 digits
pattern = r'^tt\d{7,8}$'
if not re.match(pattern, self.value):
raise ValidationError(
f"Invalid IMDb ID format: {self.value}. "
"Expected format: tt followed by 7-8 digits (e.g., tt1375666)"
)
def __str__(self) -> str:
return self.value
def __repr__(self) -> str:
return f"ImdbId('{self.value}')"
@dataclass(frozen=True)
class FilePath:
"""
Value object representing a file path with validation.
Ensures the path is valid and optionally checks existence.
"""
value: Path
def __init__(self, path: Union[str, Path]):
"""
Initialize FilePath.
Args:
path: String or Path object representing the file path
"""
if isinstance(path, str):
path_obj = Path(path)
elif isinstance(path, Path):
path_obj = path
else:
raise ValidationError(f"Path must be str or Path, got {type(path)}")
# Use object.__setattr__ because dataclass is frozen
object.__setattr__(self, 'value', path_obj)
def exists(self) -> bool:
"""Check if the path exists."""
return self.value.exists()
def is_file(self) -> bool:
"""Check if the path is a file."""
return self.value.is_file()
def is_dir(self) -> bool:
"""Check if the path is a directory."""
return self.value.is_dir()
def __str__(self) -> str:
return str(self.value)
def __repr__(self) -> str:
return f"FilePath('{self.value}')"
@dataclass(frozen=True)
class FileSize:
"""
Value object representing a file size in bytes.
Provides human-readable formatting.
"""
bytes: int
def __post_init__(self):
"""Validate file size."""
if not isinstance(self.bytes, int):
raise ValidationError(f"File size must be an integer, got {type(self.bytes)}")
if self.bytes < 0:
raise ValidationError(f"File size cannot be negative: {self.bytes}")
def to_human_readable(self) -> str:
"""
Convert bytes to human-readable format.
Returns:
String like "1.5 GB", "500 MB", etc.
"""
units = ['B', 'KB', 'MB', 'GB', 'TB']
size = float(self.bytes)
unit_index = 0
while size >= 1024 and unit_index < len(units) - 1:
size /= 1024
unit_index += 1
if unit_index == 0:
return f"{int(size)} {units[unit_index]}"
else:
return f"{size:.2f} {units[unit_index]}"
def __str__(self) -> str:
return self.to_human_readable()
def __repr__(self) -> str:
return f"FileSize({self.bytes})"