import posixpath from collections.abc import Mapping from dataclasses import dataclass from mirage.commands.builtin.utils.wrap import to_pathspec from mirage.commands.config import CommandOpts from mirage.commands.spec import SPECS from mirage.commands.spec.types import FlagValue, FlagView from mirage.io.types import ByteSource, IOResult from mirage.types import PathSpec, StatFn from mirage.utils.key_prefix import mount_prefix_of async def _exists(stat_fn: StatFn, path: PathSpec) -> bool: try: await stat_fn(path) return True except (FileNotFoundError, ValueError): return False async def realpath( paths: list[PathSpec], *, stat_fn: StatFn, e: bool = False, m: bool = False, ) -> tuple[ByteSource | None, IOResult]: lines: list[str] = [] for p in paths: resolved_display = posixpath.normpath(p.virtual) if e: prefix = mount_prefix_of(p.virtual, p.resource_path) resolved = to_pathspec(resolved_display, prefix) if not await _exists(stat_fn, resolved): # Fully GNU-formatted (quoted path), emitted verbatim by # format_fs_error. Must NOT be a FileNotFoundError: the fs # branch would re-prefix and re-suffix it (doubling the # message); a plain error matches the TS realpath throw. raise ValueError( f"realpath: '{p.virtual}': No such file or directory") lines.append(resolved_display) return ("\n".join(lines) + "\n").encode(), IOResult() __all__ = ["realpath"] @dataclass(frozen=True, slots=True) class RealpathFlags: must_exist: bool = False allow_missing: bool = False def parse_flags(flags: Mapping[str, FlagValue]) -> RealpathFlags: fl = FlagView(flags, spec=SPECS["realpath"]) return RealpathFlags(must_exist=fl.as_bool("e"), allow_missing=fl.as_bool("m")) async def realpath_generic( paths: list[PathSpec], texts: list[str], opts: CommandOpts, stat_fn: StatFn, ) -> tuple[ByteSource | None, IOResult]: parsed = parse_flags(opts.flags) return await realpath(paths, stat_fn=stat_fn, e=parsed.must_exist, m=parsed.allow_missing)