from collections.abc import Awaitable, Callable from mirage.commands.builtin.utils.formatting import _human_size from mirage.commands.builtin.utils.output import (format_record_text, format_records) from mirage.types import PathSpec def _format_size(size: int, human: bool) -> str: return _human_size(size) if human else str(size) def _depth(entry_path: str, base_path: str) -> int: base = base_path.rstrip("/") rel = entry_path.rstrip("/")[len(base):] if not rel: return 0 return rel.strip("/").count("/") + 1 async def _du_one( path: PathSpec, compute_total: Callable[[PathSpec], Awaitable[int]], compute_all: Callable[[PathSpec], Awaitable[tuple[list[tuple[str, int]], int]]], *, s: bool, a: bool, h: bool, max_depth: int | None, ) -> tuple[str, int]: label = path.raw_path if s: total = await compute_total(path) return _format_size(total, h) + "\t" + label, total all_entries, total = await compute_all(path) if not all_entries: total = await compute_total(path) return _format_size(total, h) + "\t" + label, total if not a: return _format_size(total, h) + "\t" + label, total entries = all_entries if max_depth is not None: entries = [(p, sz) for p, sz in entries if _depth(p, path.virtual) <= max_depth] if not entries: return _format_size(total, h) + "\t" + label, total lines = [_format_size(sz, h) + "\t" + p for p, sz in entries] display_total = sum(sz for _, sz in entries) return "\n".join(lines), display_total async def du( paths: list[PathSpec], *, compute_total: Callable[[PathSpec], Awaitable[int]], compute_all: Callable[[PathSpec], Awaitable[tuple[list[tuple[str, int]], int]]], s: bool = False, a: bool = False, h: bool = False, max_depth: int | None = None, c: bool = False, ) -> str: sub_texts: list[str] = [] totals: list[int] = [] for p in paths: text, total = await _du_one( p, compute_total, compute_all, s=s, a=a, h=h, max_depth=max_depth, ) sub_texts.append(text) totals.append(total) out = format_record_text(sub_texts) if c: grand = sum(totals) out += _format_size(grand, h) + "\ttotal\n" return out async def _du_block( p0: PathSpec, compute_total: Callable[[PathSpec], Awaitable[int]], compute_all: Callable[[PathSpec], Awaitable[list[tuple[str, int]]]] | None, *, s: bool, a: bool, h: bool, max_depth: int | None, ) -> tuple[list[str], int]: if s or compute_all is None: total = await compute_total(p0) return [_format_size(total, h) + "\t" + p0.raw_path], total all_entries = await compute_all(p0) if not all_entries: total = await compute_total(p0) return [_format_size(total, h) + "\t" + p0.raw_path], total if not a: all_entries = [(p, sz) for p, sz in all_entries if p == p0.virtual] if max_depth is not None: all_entries = [(p, sz) for p, sz in all_entries if _depth(p, p0.virtual) <= max_depth] if not all_entries: total = await compute_total(p0) return [_format_size(total, h) + "\t" + p0.raw_path], total lines = [_format_size(sz, h) + "\t" + p for p, sz in all_entries] return lines, sum(sz for _, sz in all_entries) async def du_multi( paths: list[PathSpec], *, compute_total: Callable[[PathSpec], Awaitable[int]], compute_all: Callable[[PathSpec], Awaitable[list[tuple[str, int]]]] | None = None, h: bool = False, s: bool = False, a: bool = False, max_depth: int | None = None, c: bool = False, ) -> bytes: lines: list[str] = [] totals: list[int] = [] for p0 in paths: block, total = await _du_block(p0, compute_total, compute_all, s=s, a=a, h=h, max_depth=max_depth) lines.extend(block) totals.append(total) if c: lines.append(_format_size(sum(totals), h) + "\ttotal") return format_records(lines)