"""PinMAP data validator. Validates a parsed PinMAP for structural and data integrity: 1. Pin-number uniqueness 2. Pin-number continuity (1..N with no gaps) 3. Missing PinName detection (warning, defaults to "NC") 4. Rectangular-structure sanity Usage ----- >>> from validator import validate_pinmap >>> result = validate_pinmap(pinmap) >>> if result.is_valid: ... print("All good") ... else: ... for e in result.errors: ... print(f"[ERROR] {e.message}: {e.details}") """ from collections import Counter from models import PinMAP, ValidationResult, ValidationError def validate_pinmap(pinmap: PinMAP) -> ValidationResult: """Validate a PinMAP and return a ValidationResult. Checks performed ---------------- 1. **Uniqueness** — every pin number must appear exactly once. 2. **Continuity** — pin numbers must form the sequence 1, 2, …, N with no gaps. 3. **PinName completeness** — pins with empty / whitespace-only names generate a *warning* (they will default to "NC" in the output). 4. **Structure** — width and height must each be ≥ 2. Parameters ---------- pinmap : PinMAP A pin map produced by ``pinmap_parser.parse_pinmap``. Returns ------- ValidationResult """ result = ValidationResult(is_valid=True, errors=[], warnings=[]) numbers = [p.number for p in pinmap.pins] # ── 1. Uniqueness ──────────────────────────────────────────── if len(numbers) != len(set(numbers)): counts = Counter(numbers) duplicates = sorted(n for n, c in counts.items() if c > 1) result.errors.append(ValidationError( level="error", message="Pin序号重复", details=f"重复的序号: {duplicates}", )) # ── 2. Continuity ──────────────────────────────────────────── if numbers: expected = set(range(1, max(numbers) + 1)) actual = set(numbers) missing = expected - actual if missing: result.errors.append(ValidationError( level="error", message="Pin序号不连续", details=f"缺失的序号: {sorted(missing)}", )) # ── 3. PinName completeness ────────────────────────────────── missing_names = [ p for p in pinmap.pins if not p.name or not p.name.strip() ] if missing_names: result.warnings.append(ValidationError( level="warning", message=( f"检测到 {len(missing_names)} 个引脚缺少 PinName" ), details=( f"缺失引脚序号: {[p.number for p in missing_names]}," f"将默认为 NC" ), )) # ── 4. Structure sanity ────────────────────────────────────── if pinmap.width < 2 or pinmap.height < 2: result.errors.append(ValidationError( level="error", message="方形结构不完整", details=( f"尺寸: {pinmap.width}x{pinmap.height},至少需要 2x2" ), )) # ── Final verdict ──────────────────────────────────────────── if result.errors: result.is_valid = False return result