137 lines
3.8 KiB
Python
137 lines
3.8 KiB
Python
"""
|
|
Navigation resolution utilities.
|
|
|
|
This module resolves a ``NavSpec`` against the filesystem by expanding glob
|
|
patterns and validating that referenced documentation files exist.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from typing import Dict, Iterable, List
|
|
|
|
import glob
|
|
|
|
from docforge.nav.spec import NavSpec
|
|
|
|
|
|
class ResolvedNav:
|
|
"""
|
|
Resolved navigation structure.
|
|
|
|
A ``ResolvedNav`` represents navigation data after glob patterns have been
|
|
expanded and paths validated against the filesystem.
|
|
|
|
Attributes:
|
|
home: Relative path to the documentation home page.
|
|
groups: Mapping of navigation group titles to lists of resolved
|
|
documentation file paths.
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
home: str | None,
|
|
groups: Dict[str, List[Path]],
|
|
docs_root: Path | None = None,
|
|
) -> None:
|
|
"""
|
|
Initialize a ResolvedNav instance.
|
|
|
|
Args:
|
|
home: Relative path to the home page within the documentation root.
|
|
groups: Mapping of group titles to resolved documentation file paths.
|
|
docs_root: Root directory of the documentation source files.
|
|
"""
|
|
self.home = home
|
|
self.groups = groups
|
|
self._docs_root = docs_root
|
|
|
|
def all_files(self) -> Iterable[Path]:
|
|
"""
|
|
Iterate over all files referenced by the navigation structure.
|
|
|
|
Returns:
|
|
An iterable of ``Path`` objects representing documentation files.
|
|
|
|
Raises:
|
|
RuntimeError: If the home page is defined but the documentation
|
|
root is not available for resolution.
|
|
"""
|
|
if self.home:
|
|
if self._docs_root is None:
|
|
raise RuntimeError("docs_root is required to resolve home path")
|
|
yield self._docs_root / self.home
|
|
|
|
for paths in self.groups.values():
|
|
for p in paths:
|
|
yield p
|
|
|
|
|
|
def resolve_nav(
|
|
spec: NavSpec,
|
|
docs_root: Path,
|
|
) -> ResolvedNav:
|
|
"""
|
|
Resolve a navigation specification against the filesystem.
|
|
|
|
The function expands glob patterns defined in a ``NavSpec`` and verifies
|
|
that referenced documentation files exist within the documentation root.
|
|
|
|
Args:
|
|
spec: Navigation specification describing documentation layout.
|
|
docs_root: Root directory containing documentation Markdown files.
|
|
|
|
Returns:
|
|
A ``ResolvedNav`` instance containing validated navigation paths.
|
|
|
|
Raises:
|
|
FileNotFoundError: If the documentation root does not exist or a
|
|
navigation pattern does not match any files.
|
|
"""
|
|
if not docs_root.exists():
|
|
raise FileNotFoundError(docs_root)
|
|
|
|
def resolve_pattern(pattern: str) -> List[Path]:
|
|
"""
|
|
Resolve a glob pattern relative to the documentation root.
|
|
|
|
Args:
|
|
pattern: Glob pattern used to match documentation files.
|
|
|
|
Returns:
|
|
A sorted list of matching ``Path`` objects.
|
|
|
|
Raises:
|
|
FileNotFoundError: If the pattern does not match any files.
|
|
"""
|
|
full = docs_root / pattern
|
|
matches = sorted(
|
|
Path(p) for p in glob.glob(str(full), recursive=True)
|
|
)
|
|
|
|
if not matches:
|
|
raise FileNotFoundError(pattern)
|
|
|
|
return matches
|
|
|
|
# Resolve home page
|
|
home: str | None = None
|
|
if spec.home:
|
|
home_path = docs_root / spec.home
|
|
if not home_path.exists():
|
|
raise FileNotFoundError(spec.home)
|
|
home = spec.home
|
|
|
|
# Resolve navigation groups
|
|
resolved_groups: Dict[str, List[Path]] = {}
|
|
|
|
for group, patterns in spec.groups.items():
|
|
files: List[Path] = []
|
|
for pattern in patterns:
|
|
files.extend(resolve_pattern(pattern))
|
|
resolved_groups[group] = files
|
|
|
|
return ResolvedNav(
|
|
home=home,
|
|
groups=resolved_groups,
|
|
docs_root=docs_root,
|
|
)
|