import re VIN_RE = re.compile(r"^[A-HJ-NPR-Z0-9]{17}$") def normalize_vin(value: str | None) -> str | None: if not value: return None normalized = re.sub(r"[\s-]+", "", value).upper() return normalized or None def validate_vin(value: str | None) -> str | None: normalized = normalize_vin(value) if normalized is None: return None if not VIN_RE.match(normalized): raise ValueError("VIN must contain 17 characters and cannot include I, O, or Q") return normalized def normalize_license_plate(value: str | None) -> str | None: if not value: return None normalized = re.sub(r"[\s\-_.]+", "", value).upper() return normalized or None def mask_vin(value: str | None) -> str | None: normalized = normalize_vin(value) if not normalized: return None if len(normalized) <= 6: return "*" * len(normalized) return f"{normalized[:3]}{'*' * 10}{normalized[-4:]}" def mask_license_plate(value: str | None) -> str | None: normalized = normalize_license_plate(value) if not normalized: return None if len(normalized) <= 3: return "*" * len(normalized) return f"{normalized[:2]}{'*' * max(len(normalized) - 4, 2)}{normalized[-2:]}"