"""Path utility functions.""" from pathlib import Path from typing import Optional def ensure_dir(path: Path) -> Path: """Ensure a directory exists, creating it if necessary. Args: path: Path to the directory. Returns: The path to the directory. """ path.mkdir(parents=True, exist_ok=True) return path def project_root(slug: str, projects_root: Optional[Path] = None) -> Path: """Get the root path for a project. Args: slug: Project slug. projects_root: Root directory for all projects. Defaults to ./projects. Returns: Path to the project root. """ if projects_root is None: projects_root = Path("projects") return projects_root / slug def relative_to_project(slug: str, relative_path: str, projects_root: Optional[Path] = None) -> Path: """Get a path relative to a project. Args: slug: Project slug. relative_path: Relative path within the project. projects_root: Root directory for all projects. Returns: Absolute path to the file within the project. """ root = project_root(slug, projects_root) return root / relative_path def is_within_project(slug: str, file_path: Path, projects_root: Optional[Path] = None) -> bool: """Check if a file path is within a project. Args: slug: Project slug. file_path: Path to check. projects_root: Root directory for all projects. Returns: True if file_path is within the project directory. """ project_path = project_root(slug, projects_root) try: file_path.resolve().relative_to(project_path.resolve()) return True except ValueError: return False def sanitize_filename(filename: str) -> str: """Sanitize a filename by removing invalid characters. Args: filename: Original filename. Returns: Sanitized filename safe for file system use. """ # Remove or replace invalid characters invalid_chars = '<>:"/\\|?*' for char in invalid_chars: filename = filename.replace(char, "_") # Limit length if len(filename) > 255: filename = filename[:255] return filename