""" Functions for finding and querying maildirs. A maildir here is a directory containing a cur, new, and tmp directory. """ from dataclasses import dataclass import os.path from .html_render import HTML from .util import find from urllib.parse import urlencode from pathlib import Path from typing import ( Union ) try: from natsort import natsorted except ModuleNotFoundError: natsorted = sorted @dataclass class MaildirEntry: """A single maildir, used by find_maildirs.""" name: str @dataclass class MaildirGroup: """A group of maildir, which isn't a maildir in itself.""" name: str children: list[Union[MaildirEntry, 'MaildirGroup']] def _build_tree(items: list[list[str]]) -> MaildirGroup: groups: dict[str, list[list[str]]] = {} direct: list[MaildirEntry] = [] for key, *rest in items: if rest: groups.setdefault(key, []).append(rest) else: direct.append(MaildirEntry(key)) node = MaildirGroup('root', []) for key, values in groups.items(): next = _build_tree(values) next.name = key node.children.append(next) node.children.extend(direct) return node def find_maildirs(basedir: str) -> MaildirGroup: """ Find all maildirs located under basedir. A maildir is defined as any directory which contains a `cur` directory. Returns a MaildirGroup of the root, whose children will be all the maildirs under basedir. Note that if a directory is both a maildir and a group of maildirs then it will have two separate entries. """ basedir = basedir.rstrip('/') files = find(Path(basedir), type='d', name='cur') # + 1 removes leading slash # - 4 removes '/cur' dirs = [entry[len(basedir) + 1:-4].decode('UTF-8').split(os.path.sep) for entry in files] return _build_tree(dirs) def serialize_maildir(maildir: MaildirGroup, path: list[str] = []) -> HTML: """Build a (recursive) list from a maildir node.""" entries: list[HTML] = [] for node in natsorted(maildir.children, key=lambda n: n.name): entry: HTML if isinstance(node, MaildirEntry): parts = '/'.join(path + [node.name]) url = 'search?' + urlencode({'q': f'maildir:"/{parts}"'}) entry = ('li', ('a', {'href': url}, node.name or ('i', 'root'))) else: entry = ('li', ('details', ('summary', node.name), serialize_maildir(node, path + [node.name]))) entries.append(entry) return ('ul', *entries)