Files
Jason Wen 922ad64d13 sync
2026-03-12 21:46:34 -04:00

89 lines
2.6 KiB
Python

"""Parse zensical.toml nav structure into a flat list of doc entries.
Reads the nav tree from zensical.toml and produces a flat list of
{title, path, breadcrumb} dicts suitable for the sync orchestrator.
"""
from __future__ import annotations
import tomllib
from dataclasses import dataclass, field
from pathlib import Path
SKIP_FILES = frozenset({"index.md", "README.md"})
@dataclass(frozen=True)
class NavEntry:
"""A single navigable documentation page."""
title: str
path: str
breadcrumb: tuple[str, ...] = field(default_factory=tuple)
def parse(config_path: str | Path) -> list[NavEntry]:
"""Parse zensical.toml and return all nav entries.
Args:
config_path: Path to zensical.toml.
Returns:
Flat list of NavEntry for every page in the nav tree,
excluding index.md, README.md, and external links.
"""
config_path = Path(config_path)
with config_path.open("rb") as f:
config = tomllib.load(f)
nav = config.get("project", {}).get("nav", [])
entries = _flatten_nav(nav)
return [e for e in entries if Path(e.path).name not in SKIP_FILES]
def parse_all(config_path: str | Path) -> list[NavEntry]:
"""Like parse(), but includes index.md and README.md entries."""
config_path = Path(config_path)
with config_path.open("rb") as f:
config = tomllib.load(f)
nav = config.get("project", {}).get("nav", [])
return _flatten_nav(nav)
def _flatten_nav(
items: list[dict[str, str | list] | str],
breadcrumb: tuple[str, ...] = (),
) -> list[NavEntry]:
"""Recursively flatten the nav tree into NavEntry objects."""
result: list[NavEntry] = []
for item in items:
if isinstance(item, dict):
for title, value in item.items():
if isinstance(value, str):
# Skip external links
if value.startswith("http"):
continue
result.append(NavEntry(
title=title,
path=value,
breadcrumb=breadcrumb + (title,),
))
elif isinstance(value, list):
result.extend(
_flatten_nav(value, breadcrumb + (title,))
)
elif isinstance(item, str):
# Bare path without title
if item.startswith("http"):
continue
name = Path(item).stem.replace("-", " ").capitalize()
result.append(NavEntry(
title=name,
path=item,
breadcrumb=breadcrumb + (name,),
))
return result