diff --git a/wizard-work/spark/json-validator.py b/wizard-work/spark/json-validator.py index 1f14a67..249750c 100644 --- a/wizard-work/spark/json-validator.py +++ b/wizard-work/spark/json-validator.py @@ -1,8 +1,14 @@ #!/usr/bin/env python3 """JSON Validator - validates JSON files with proper exit codes. -Exit 0: Valid JSON -Exit 1: Invalid JSON or file not found + +Exit 0: All valid +Exit 1: One or more invalid Exit 2: Usage error + +Features: +- Validate individual files: json-validator.py ... +- Validate directory: json-validator.py --dir +- Returns count of invalid files as exit code for --dir mode """ import sys @@ -10,43 +16,98 @@ import json from pathlib import Path -def validate_json_file(filepath: str) -> bool: - """Validate a JSON file. Returns True if valid, False otherwise.""" +def validate_json_file(filepath: str) -> tuple[bool, str]: + """Validate a JSON file. + + Returns: + (True, "") if valid + (False, error_message) if invalid + """ try: with open(filepath, 'r') as f: json.load(f) print(f"VALID: {filepath}") - return True + return True, "" except json.JSONDecodeError as e: - print(f"INVALID: {filepath} - line {e.lineno}, column {e.colno}: {e.msg}", file=sys.stderr) - return False + return False, f"Invalid JSON at line {e.lineno}, column {e.colno}: {e.msg}" except FileNotFoundError: - print(f"NOT FOUND: {filepath}", file=sys.stderr) - return False + return False, "File not found" + except Exception as e: + return False, f"Error: {e}" + + +def validate_json_directory(directory: str) -> dict: + """Validate all .json files in a directory. + + Returns: + Dict with totals and counts + """ + dir_path = Path(directory) + if not dir_path.exists(): + return {"error": f"Directory not found: {directory}", "total": 0, "valid": 0, "invalid": 0} + if not dir_path.is_dir(): + return {"error": f"Not a directory: {directory}", "total": 0, "valid": 0, "invalid": 0} + + json_files = list(dir_path.glob("*.json")) + valid = 0 + invalid = 0 + invalid_files = [] + + for jf in json_files: + is_valid, error = validate_json_file(str(jf)) + if is_valid: + valid += 1 + else: + invalid += 1 + invalid_files.append((jf.name, error)) + print(f"INVALID: {jf.name} - {error}") + + return {"total": len(json_files), "valid": valid, "invalid": invalid, "invalid_files": invalid_files[:5]} def main(): if len(sys.argv) < 2: print("Usage: json-validator.py ", file=sys.stderr) + print(" json-validator.py --dir ", file=sys.stderr) sys.exit(2) - paths = sys.argv[1:] - all_valid = True - - for path in paths: - p = Path(path) - if p.is_dir(): - for json_file in p.rglob("*.json"): - if not validate_json_file(str(json_file)): + # Check for --dir flag + if "--dir" in sys.argv: + try: + dir_idx = sys.argv.index("--dir") + directory = sys.argv[dir_idx + 1] + result = validate_json_directory(directory) + if "error" in result: + print(f"Error: {result['error']}", file=sys.stderr) + sys.exit(2) + print(f"\nDirectory: {directory}") + print(f"Total: {result['total']}") + print(f"Valid: {result['valid']}") + print(f"Invalid: {result['invalid']}") + sys.exit(result['invalid']) + except IndexError: + print("Error: --dir requires a directory argument", file=sys.stderr) + sys.exit(2) + else: + paths = sys.argv[1:] + all_valid = True + + for path in paths: + p = Path(path) + if p.is_dir(): + for json_file in p.rglob("*.json"): + is_valid, error = validate_json_file(str(json_file)) + if not is_valid: + all_valid = False + elif p.is_file(): + is_valid, error = validate_json_file(str(p)) + if not is_valid: all_valid = False - elif p.is_file(): - if not validate_json_file(str(p)): + else: + print(f"NOT FOUND: {path}", file=sys.stderr) all_valid = False - else: - print(f"NOT FOUND: {path}", file=sys.stderr) - all_valid = False - - sys.exit(0 if all_valid else 1) + + sys.exit(0 if all_valid else 1) if __name__ == "__main__":