uagent 0.1.0

Browser user agents with matching navigation headers and deterministic profile selection.
Documentation
#!/usr/bin/env python3
"""Generate Rust header data from checked-in captures, without network access."""

import argparse
import json
import pathlib
import subprocess

ROOT = pathlib.Path(__file__).resolve().parents[1]
EXCLUDED = {"host", "connection", "proxy-connection", "accept-encoding", "cookie"}
BROWSERS = ["Chrome", "Edge", "Firefox", "Safari"]


def generate():
    captures = [json.loads(path.read_text()) for path in sorted((ROOT / "tests/captures").glob("*.json"))]
    latest = {}
    for browser in BROWSERS:
        options = [capture for capture in captures if capture["browser"] == browser]
        # Prefer macOS for equal majors so adding Linux captures does not change
        # default language and order incidentally.
        latest[browser] = max(options, key=lambda c: (c["version"], c["platform"] == "MacOs"))
    lines = [
        "// Generated by tools/update.py from tests/captures; do not edit by hand.",
        "use crate::Header;", "",
    ]
    for browser, capture in latest.items():
        lines.append(f"pub(crate) const {browser.upper()}_LATEST: u32 = {capture['version']};")
    lines += [""]
    for browser, capture in latest.items():
        lines.append(f"pub(crate) static {browser.upper()}_HEADERS: &[Header] = &[")
        for name, value in capture["headers"]:
            if name not in EXCLUDED:
                lines.append(f"    Header::from_static({json.dumps(name)}, {json.dumps(value)}),")
        lines += ["];", ""]
    return "\n".join(lines)


def main():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--check", action="store_true")
    args = parser.parse_args()
    target = ROOT / "src/generated.rs"
    generated = generate()
    # Use the repository formatter, including its nightly import settings.
    formatted = subprocess.run(
        ["rustfmt", "+nightly", "--edition", "2024", "--emit", "stdout"],
        input=generated, text=True, check=True, capture_output=True,
    ).stdout
    if args.check:
        if target.read_text() != formatted:
            raise SystemExit("src/generated.rs is stale: run python3 tools/update.py")
    else:
        target.write_text(formatted)


if __name__ == "__main__":
    main()