""" Smart Template Discovery System - Grug-approved simplification Replaces the complex nested template discovery with simple, debuggable logic """ from pathlib import Path from typing import Optional, List class TemplateDiscovery: """ Simple template discovery that maintains all current functionality Following grug principles: clear priority order, easy to debug """ def __init__(self, template_root: Path): self.template_root = Path(template_root) self._last_search_candidates = [] def find_template(self, content_path: Path, file_type: str, categories: List[str], extension: str) -> Optional[Path]: """ Find the best template for the given content Uses simple priority list instead of complex nested loops """ # Build candidate template names in priority order candidates = self._build_candidate_list(content_path, file_type, categories, extension) self._last_search_candidates = candidates.copy() # Store for debugging # Search from current directory up to template root (preserve hierarchy) search_path = self.template_root / content_path.parent while search_path >= self.template_root: for candidate in candidates: template_path = search_path / candidate if template_path.exists(): return template_path search_path = search_path.parent return None def get_last_search_candidates(self) -> List[str]: """Get the candidates from the last template search - for debugging""" return self._last_search_candidates.copy() def _build_candidate_list(self, content_path: Path, file_type: str, categories: List[str], extension: str) -> List[str]: """ Build list of template candidates in priority order This replaces the complex nested loop logic with simple, clear ordering """ candidates = [] # 1. Specific file template (highest priority) candidates.append(f"{content_path.stem}.html") # 2. Type + extension templates if extension: candidates.append(f"__{file_type}.{extension}.html") # 3. Type + category templates (preserve current category system) for category in reversed(categories): # Most specific categories first candidates.append(f"__{file_type}.{category}.html") # 4. Generic type template candidates.append(f"__{file_type}.html") # 5. Default template (for ultimate fallback) candidates.append("default.html") return candidates def find_style_candidates(self, content_path: Path, file_type: str, categories: List[str], extension: str) -> List[str]: """ Find CSS style candidates - similar logic to templates but for styles Returns list of potential style paths in priority order """ candidates = [] # 1. Specific style for the content path candidates.append(f"/{content_path}.css") # 2. Type + extension styles in current and parent directories search_path = content_path.parent while True: if extension: candidates.append(f"/{search_path}/__{file_type}.{extension}.css") # 3. Type + category styles for category in reversed(categories): candidates.append(f"/{search_path}/__{file_type}.{category}.css") if search_path == Path('.'): break search_path = search_path.parent # 4. Base style (always included) candidates.append("/base.css") return candidates def debug_template_discovery(self, content_path: Path, file_type: str, categories: List[str], extension: str) -> dict: """ Debug information for template discovery - helps with troubleshooting Grug-approved: when things break, need easy way to see what's happening """ candidates = self._build_candidate_list(content_path, file_type, categories, extension) debug_info = { 'content_path': str(content_path), 'file_type': file_type, 'categories': categories, 'extension': extension, 'candidates': candidates, 'search_paths': [], 'found_templates': [], 'chosen_template': None } # Check each search path search_path = self.template_root / content_path.parent while search_path >= self.template_root: debug_info['search_paths'].append(str(search_path)) for candidate in candidates: template_path = search_path / candidate if template_path.exists(): debug_info['found_templates'].append(str(template_path)) if debug_info['chosen_template'] is None: debug_info['chosen_template'] = str(template_path) search_path = search_path.parent return debug_info class LegacyTemplateDiscovery: """ Wrapper around the original complex template discovery for comparison Helps ensure we don't break anything during migration """ def __init__(self, template_root: Path): self.template_root = Path(template_root) def find_template_legacy(self, content_path: Path, file_type: str, categories: List[str], extension: str) -> Optional[Path]: """ Original complex template discovery logic (from renderer.py) Kept for comparison and gradual migration """ templates = [] # Check for folder template if file_type == "folder": folder_template = self.template_root / content_path / "__folder.html" if folder_template.exists(): templates.append(folder_template) else: # Check for specific file template specific_template = self.template_root / f"{content_path}.html" if specific_template.exists(): templates.append(specific_template) # If no specific template found, search with complex nested logic if len(templates) == 0: search_path = self.template_root / content_path.parent while search_path >= self.template_root: # Check type + extension type_ext_template = search_path / f"__{file_type}.{extension}.html" if type_ext_template.exists(): templates.append(type_ext_template) break # Check type + categories for category in reversed(categories): type_cat_template = search_path / f"__{file_type}.{category}.html" if type_cat_template.exists(): templates.append(type_cat_template) break search_path = search_path.parent return templates[0] if templates else None def migrate_template_discovery(old_discovery: LegacyTemplateDiscovery, new_discovery: TemplateDiscovery) -> dict: """ Migration helper to compare old vs new template discovery Helps ensure we don't break anything """ test_cases = [ # Common test cases (Path("index.md"), "file", ["document"], "md"), (Path("posts/my-post.md"), "file", ["document"], "md"), (Path("images/gallery"), "folder", ["image"], None), (Path("about.md"), "file", ["document"], "md"), ] results = { 'total_tests': len(test_cases), 'matching': 0, 'differences': [], 'test_details': [] } for content_path, file_type, categories, extension in test_cases: old_result = old_discovery.find_template_legacy(content_path, file_type, categories, extension) new_result = new_discovery.find_template(content_path, file_type, categories, extension) test_detail = { 'content_path': str(content_path), 'old_result': str(old_result) if old_result else None, 'new_result': str(new_result) if new_result else None, 'matches': old_result == new_result } results['test_details'].append(test_detail) if old_result == new_result: results['matching'] += 1 else: results['differences'].append(test_detail) return results