from collections import defaultdict from dataclasses import dataclass, field from itertools import chain from os import environ, path from subprocess import run from typing import Iterator @dataclass class Directory: files: Iterator[str] = field(default_factory=list) subfolders: Iterator[str] = field(default_factory=set) def serialize(self): return { 'files': sorted(self.files), 'subfolders': sorted(self.subfolders) } @classmethod def deserialize(cls, d): return cls(**d) def _find_files(extensions, repository): patterns = (f'**.{ext}' for ext in extensions) zero = '\x00' return repository.git.ls_files('-z', *patterns).strip(zero).split(zero) def _fill_directories(files, top_dir): directories = defaultdict(Directory) for f in files: fdir, fname = path.split(f) directories[fdir].files.append(fname) while fdir: parent, child = path.split(fdir) directories[parent].subfolders.add(child) fdir = parent return directories def compute_directories(extensions, repository): files = _find_files(extensions, repository) top_dir = path.relpath(repository.working_dir, path.curdir) return _fill_directories(files, top_dir) def deserialize_directories(directories): return { k: Directory.deserialize(v) for k, v in directories.items() } def pandoc(page, output, template, filters, stylesheets, include_after=(), variables=None, metadata=None): cmd = ( 'pandoc', '-s', page, '-o', output, '--template', template, *chain(*(('--lua-filter', f) for f in filters)), *chain(*(('--css', s) for s in stylesheets)), *chain(*(('--include-after-body', f) for f in include_after)) ) if variables is not None: cmd += tuple(chain( *(('-V', f'{k}={v}') for k, v in variables.items()) )) if metadata is not None: cmd += tuple(chain( *(('-M', f'{k}={v}') for k, v in metadata.items()) )) environ['LUA_PATH'] = '.cache/?.lua;;' run(cmd, check=True) def generate_crumbs(target): parts = ('(top)', *target.parts) if parts[-1] == 'index': *crumbs, current = parts[:-1] else: crumbs = parts[:-1] current, _ = path.splitext(parts[-1]) crumbs_li = ( '
  • {crumb}
  • '.format( link=(path.relpath(path.join(*crumbs[1:i], 'index.html'), start=target.parent)), crumb=crumb ) for i, crumb in enumerate(crumbs, start=1) ) current_li = f'
  • {current}
  • ' return '\n'.join((*crumbs_li, current_li))