#!/usr/bin/env python3
"""v1.6.6 S3(承 ar 裁定 368)—— rev-pin 债可见性扫描之【谓词载体】。

令源: ar 裁定 368(答 dev 之 §五 S3 请核): 准【机械化】之向 ∧ 【扫描域须改】。
根因(ar 一手核 ∧ dev 一手复核): W2 之债(`xwcore v0.1.28` 之
`[patch."…/xwtools.git"]` `rev = e33d1c6e`)在【插件仓 Cargo.toml 之 [patch] 段】,
【不在主线 Cargo.lock】⇒ 唯扫主线 lock 之 `?rev=` 于 W2【零命中】= 恒真谓词不承证力。

本档提供二个子命令,供 `scripts/verify_release.sh` 之【检查22】调用:

  roots <metadata.json>
      由 `cargo metadata --locked --format-version 1` 之输出,解析出【所钉各仓之
      仓根 manifest】。★ 关键: cargo 所给 `manifest_path` 系【包】manifest
      (如 `…/crates/xwcore/Cargo.toml`),而 `[patch]` 段【唯存于 workspace 根】——
      故须自包 manifest 逐级上溯至含 `[workspace]` 者。不做此上溯 ⇒ 谓词恒零命中。

  scan  (manifest 路径由 stdin 逐行读入)
      于各 manifest 之 `[patch.*]` 段内,枚举 `rev = "…"` 形。
      ★★ v1.9.0 P0-2 器面专批(`ar` 裁「取甲」): 本子命令之【主线消费面】= 检查22(乙 插件面)
        **已撤** —— 该面系 `cargo metadata --locked` 依赖(非仓状态纯函数 ∧ 读数摆动 0,0,1,0)
        ⇒ 其断言之归属 = 所钉之插件仓自仓 `gate`(覆盖面记 `GAP` 显名, 见
        `release/v1.9.0/evidence/P02_GATE_PURITY.md`)。★ 本子命令**保留**(其 `selftest`
        四组夹具仍为本谓词之自核 ∧ 供后续复用); 唯主线门禁【不再调用】之。
      ★ 二处实证缺陷(本档已修,见 §夹具):
        ① 面错: 扫【包】manifest ⇒ 漏(上溯修正,见 roots)
        ② 锚错: `rev =` 于 `[patch]` 段内系【行内】形
           (`xw-tools = { git = "…", rev = "…" }`)⇒ 以 `^\\s*rev\\s*=` 锚行首者恒零命中;
           现取 `\\brev\\s*=\\s*"`(不锚行首)。

  selftest
      四组夹具对照(阳性对照【接入输出】—— 裁定 70 第五要件之精神):
        fx1 `[patch]` 段内【行内】`rev =`            ⇒ 须命中 1(★ 捕【锚行首】之缺陷)
        fx2 `rev =` 在 `[patch]` 段【之前】之他段     ⇒ 须命中 0(阴性对照)
        fx3 `rev =` 在 `[patch]` 段【之后】之他段     ⇒ 须命中 0(★ 捕【段边界泄漏】之缺陷)
        fx4 二 `[patch]` 段各有【行内】`rev =`        ⇒ 须命中 2(★ 捕【只取首段】之缺陷)
      任一不符 ⇒ 打印 `__CONTROL_FAIL__` 并 rc=1。

退出码: 0 = 正常(scan 之命中数以 `__COUNT__<n>` 行报出); 1 = selftest 对照失效。
"""
import json
import os
import re
import sys

REV_RE = re.compile(r'\brev\s*=\s*"')


def _is_patch_sec(sec):
    return bool(sec) and sec.startswith('[patch.')


def scan_paths(paths):
    """回 (hits, lines)。hits = [ (path, lineno, section, text) ]"""
    hits = []
    for path in paths:
        try:
            with open(path, encoding='utf-8', errors='replace') as fh:
                lines = fh.read().split('\n')
        except OSError:
            continue
        sec = None
        for i, line in enumerate(lines, 1):
            s = line.strip()
            if s.startswith('['):
                sec = s
            if REV_RE.search(line) and _is_patch_sec(sec):
                hits.append((path, i, sec, s[:150]))
    return hits


def cmd_roots(meta_path):
    """由 cargo metadata 输出解析所钉各仓之【仓根 manifest】。"""
    with open(meta_path, encoding='utf-8') as fh:
        meta = json.load(fh)
    roots = set()
    for pkg in meta.get('packages', []):
        src = pkg.get('source') or ''
        mp = pkg.get('manifest_path') or ''
        if 'git+' not in src or not mp or not os.path.exists(mp):
            continue
        d = os.path.dirname(mp)
        root, best, depth = None, None, 0
        while depth < 8:
            cand = os.path.join(d, 'Cargo.toml')
            if os.path.exists(cand):
                if best is None:
                    best = cand
                try:
                    with open(cand, encoding='utf-8', errors='replace') as fh:
                        text = fh.read()
                except OSError:
                    text = ''
                if '[workspace]' in text:
                    root = cand
                    break
            if os.path.exists(os.path.join(d, '.git')):
                break
            nd = os.path.dirname(d)
            if nd == d:
                break
            d = nd
            depth += 1
        roots.add(root or best or mp)
    for r in sorted(roots):
        print(r)


def _mkfix(path, text):
    with open(path, 'w', encoding='utf-8') as fh:
        fh.write(text)


def cmd_selftest():
    import tempfile
    ok = True
    with tempfile.TemporaryDirectory() as td:
        f1 = os.path.join(td, 'fx1.toml')
        _mkfix(f1, '[workspace]\nmembers = []\n'
                   '[patch."https://example.invalid/a.git"]\n'
                   'a = { git = "ssh://example.invalid/a.git", rev = "deadbeef" }\n')
        f2 = os.path.join(td, 'fx2.toml')
        _mkfix(f2, '[workspace]\nmembers = []\n'
                   '[dependencies]\n'
                   'a = { git = "ssh://example.invalid/a.git", rev = "deadbeef" }\n')
        f3 = os.path.join(td, 'fx3.toml')
        _mkfix(f3, '[workspace]\nmembers = []\n'
                   '[patch."https://example.invalid/a.git"]\n'
                   'a = { git = "ssh://example.invalid/a.git", tag = "v1" }\n'
                   '[profile.release]\n'
                   'rev = "deadbeef"\n')
        f4 = os.path.join(td, 'fx4.toml')
        _mkfix(f4, '[workspace]\nmembers = []\n'
                   '[patch."https://example.invalid/a.git"]\n'
                   'a = { git = "ssh://example.invalid/a.git", rev = "aaa" }\n'
                   '[patch."https://example.invalid/b.git"]\n'
                   'b = { git = "ssh://example.invalid/b.git", rev = "bbb" }\n')
        for label, path, want in (('fx1 [patch] 行内 rev', f1, 1),
                                  ('fx2 [patch] 前之 rev(阴性)', f2, 0),
                                  ('fx3 [patch] 后之 rev(段边界)', f3, 0),
                                  ('fx4 二 [patch] 段各一 rev', f4, 2)):
            got = len(scan_paths([path]))
            mark = 'OK' if got == want else 'FAIL'
            if got != want:
                ok = False
            print('  对照 %s: 期望 %d 实得 %d ⇒ %s' % (label, want, got, mark))
    if not ok:
        print('__CONTROL_FAIL__')
        return 1
    return 0


def main(argv):
    if len(argv) < 2:
        print(__doc__)
        return 2
    cmd = argv[1]
    if cmd == 'roots':
        if len(argv) < 3:
            sys.stderr.write('roots 须给 metadata.json 路径\n')
            return 2
        cmd_roots(argv[2])
        return 0
    if cmd == 'scan':
        paths = [l.strip() for l in sys.stdin if l.strip()]
        hits = scan_paths(paths)
        for path, i, sec, text in hits:
            print('%s:%d\t%s\t%s' % (path, i, sec, text))
        print('__COUNT__%d' % len(hits))
        return 0
    if cmd == 'selftest':
        return cmd_selftest()
    sys.stderr.write('未知子命令: %s\n' % cmd)
    return 2


if __name__ == '__main__':
    sys.exit(main(sys.argv))