""" utils.py Common helper functions """ from __future__ import annotations import hashlib from pathlib import Path from typing import Iterable from config import ( CHARS_PER_TOKEN, LANGUAGE_MAP, ) def estimate_tokens(text: str) -> int: """ Rough token estimation. """ return max(1, int(len(text) / CHARS_PER_TOKEN)) def short_sha256(text: str) -> str: """ First 12 chars of sha256. """ return hashlib.sha256( text.encode("utf-8", errors="ignore") ).hexdigest()[:12] def xml_safe(text: str) -> str: """ Prevent CDATA termination. """ return text.replace("]]>", "]]]]>") def language_from_extension(ext: str) -> str: return LANGUAGE_MAP.get( ext.lower(), ext.lower() if ext else "Unknown", ) def human_size(num_bytes: int) -> str: value = float(num_bytes) for unit in ("B", "KB", "MB", "GB", "TB"): if value < 1024: return f"{value:.2f} {unit}" value /= 1024 return f"{value:.2f} PB" def count_lines(text: str) -> int: if not text: return 0 return text.count("\n") + 1 def unique_languages(entries: Iterable[dict]) -> list[str]: langs = { language_from_extension( e.get("ext", "") ) for e in entries } return sorted(langs) def safe_relative(path: Path, root: Path) -> str: return path.relative_to(root).as_posix() def average_file_size(total_size: int, count: int) -> int: if count == 0: return 0 return total_size // count def project_name(root: Path) -> str: return root.resolve().name