feat: pack the files a directory entry names

This commit is contained in:
Abdessamad Derraz committed 2026-10-04 17:58:10 +02:00
1 parent 9059234810
commit 2efc37a738
12 files changed
+466 -5

No files matched your search

+95
View File
@@ -1116,6 +1116,101 @@ def group_identical_platforms(
return result
_directory_members_cache: dict[int, tuple[dict, dict[str, dict[str, str]]]] = {}
def directory_members(db: dict, directory: str) -> dict[str, str]:
"""Files the collection holds under a directory an emulator reads whole.
Returns {destination: sha1}, each destination starting with the directory
as the profile writes it. The collection and the destination meet on a
tail, so the members are the indexed tails that begin with it.
A generic directory name can sit in two unrelated trees (``voice/``,
``data/``). Members that do not share one parent, or a destination two
contents claim, would merge them into one emulator's directory: nothing
is returned and the entry stays a reported gap.
"""
tail = directory.strip("/")
if not tail:
return {}
by_path_suffix = db.get("indexes", {}).get("by_path_suffix", {})
cached = _directory_members_cache.get(id(by_path_suffix))
if cached is None or cached[0] is not by_path_suffix:
cached = (by_path_suffix, {})
_directory_members_cache[id(by_path_suffix)] = cached
if tail in cached[1]:
return cached[1][tail]
prefix = f"{tail}/"
infix = f"/{prefix}"
held: dict[str, list[str]] = {}
parent: dict[str, str] = {}
for key, sha1s in by_path_suffix.items():
if key.startswith(prefix):
held[key] = sha1s
continue
at = key.find(infix)
if at > 0:
member = key[at + 1:]
if len(key[:at]) > len(parent.get(member, "")):
parent[member] = key[:at]
held = {
dest: sha1s for dest, sha1s in held.items() if "/.variants/" not in dest
}
one_tree = len({parent.get(dest, "") for dest in held}) <= 1
one_content = all(len(sha1s) == 1 for sha1s in held.values())
members = (
{dest: held[dest][0] for dest in sorted(held)}
if one_tree and one_content
else {}
)
cached[1][tail] = members
return members
def expand_directory_entries(
files: list[dict], db: dict, standalone: bool = False
) -> list[dict]:
"""Replace each ``type: directory`` entry by the files held under it.
A profile declares the directory its code walks; what a pack can carry
is the files. Each member keeps the entry's fields and is pinned to the
content the collection holds at that destination. A directory nothing
answers is kept as declared, one gap, and a member the profile also
names as a file is left to that entry.
"""
def destination(entry: dict) -> str:
chosen = entry.get("standalone_path") if standalone else None
return chosen or entry.get("path") or entry.get("name", "")
if not any(entry.get("type") == "directory" for entry in files):
return files
named = {
destination(entry) for entry in files if entry.get("type") != "directory"
}
expanded: list[dict] = []
for entry in files:
if entry.get("type") != "directory":
expanded.append(entry)
continue
members = directory_members(db, destination(entry))
if not members:
expanded.append(entry)
continue
inherited = {
key: value
for key, value in entry.items()
if key not in ("type", "standalone_path")
}
expanded.extend(
{**inherited, "name": dest, "path": dest, "sha1": sha1}
for dest, sha1 in members.items()
if dest not in named
)
return expanded
def runs_standalone(
emu_name: str, profile: dict, standalone_cores: set[str]
) -> bool: