Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:20:28 +08:00
commit b727790a9e
65 changed files with 16412 additions and 0 deletions

View File

@@ -0,0 +1,263 @@
#!/usr/bin/env python3
# ============================================================================
# JSON Validator Script
# ============================================================================
# Purpose: Multi-backend JSON syntax validation with detailed error reporting
# Version: 1.0.0
# Usage: ./json-validator.py --file <path> [--verbose]
# Returns: 0=valid, 1=invalid, 2=error
# Backends: jq (preferred), python3 json module (fallback)
# ============================================================================
import json
import sys
import argparse
import subprocess
import shutil
from pathlib import Path
# ====================
# Color Support
# ====================
class Colors:
"""ANSI color codes for terminal output"""
RED = '\033[0;31m'
GREEN = '\033[0;32m'
YELLOW = '\033[1;33m'
BLUE = '\033[0;34m'
CYAN = '\033[0;36m'
BOLD = '\033[1m'
NC = '\033[0m'
@classmethod
def disable(cls):
"""Disable colors for non-TTY output"""
cls.RED = cls.GREEN = cls.YELLOW = cls.BLUE = cls.CYAN = cls.BOLD = cls.NC = ''
if not sys.stdout.isatty():
Colors.disable()
# ====================
# Backend Detection
# ====================
def detect_backend():
"""Detect available JSON validation backend"""
if shutil.which('jq'):
return 'jq'
elif sys.version_info >= (3, 0):
return 'python3'
else:
return 'none'
def print_backend_info():
"""Print detected backend information"""
backend = detect_backend()
if backend == 'jq':
print(f"{Colors.GREEN}✅ Backend: jq (preferred){Colors.NC}")
elif backend == 'python3':
print(f"{Colors.YELLOW}⚠️ Backend: python3 (fallback){Colors.NC}")
else:
print(f"{Colors.RED}❌ No JSON validator available{Colors.NC}")
print(f"{Colors.BLUE} Install jq for better error messages: apt-get install jq{Colors.NC}")
return backend
# ====================
# JQ Backend
# ====================
def validate_with_jq(file_path, verbose=False):
"""Validate JSON using jq (provides better error messages)"""
try:
result = subprocess.run(
['jq', 'empty', file_path],
capture_output=True,
text=True,
check=False
)
if result.returncode == 0:
print(f"{Colors.GREEN}✅ Valid JSON: {file_path}{Colors.NC}")
print(f"Backend: jq")
return 0
else:
# Parse jq error message
error_msg = result.stderr.strip()
print(f"{Colors.RED}❌ Invalid JSON: {file_path}{Colors.NC}")
if verbose:
print(f"{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.NC}")
print(f"{Colors.RED}Error Details:{Colors.NC}")
print(f" {error_msg}")
print()
print(f"{Colors.YELLOW}Remediation:{Colors.NC}")
print(" - Check for missing commas between object properties")
print(" - Verify bracket matching: [ ] { }")
print(" - Ensure proper string quoting")
print(" - Use a JSON formatter/linter in your editor")
else:
# Extract line number if available
if "parse error" in error_msg.lower():
print(f"Error: {error_msg}")
return 1
except FileNotFoundError:
print(f"{Colors.RED}❌ File not found: {file_path}{Colors.NC}", file=sys.stderr)
return 2
except Exception as e:
print(f"{Colors.RED}❌ Error running jq: {e}{Colors.NC}", file=sys.stderr)
return 2
# ====================
# Python3 Backend
# ====================
def validate_with_python(file_path, verbose=False):
"""Validate JSON using Python's json module (universal fallback)"""
try:
with open(file_path, 'r', encoding='utf-8') as f:
content = f.read()
# Attempt to parse JSON
json.loads(content)
print(f"{Colors.GREEN}✅ Valid JSON: {file_path}{Colors.NC}")
print(f"Backend: python3")
return 0
except FileNotFoundError:
print(f"{Colors.RED}❌ File not found: {file_path}{Colors.NC}", file=sys.stderr)
return 2
except json.JSONDecodeError as e:
print(f"{Colors.RED}❌ Invalid JSON: {file_path}{Colors.NC}")
if verbose:
print(f"{Colors.BOLD}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━{Colors.NC}")
print(f"{Colors.RED}Error Details:{Colors.NC}")
print(f" Line: {e.lineno}")
print(f" Column: {e.colno}")
print(f" Issue: {e.msg}")
print()
# Show problematic section
try:
lines = content.split('\n')
start = max(0, e.lineno - 3)
end = min(len(lines), e.lineno + 2)
print(f"Problematic Section (lines {start+1}-{end}):")
for i in range(start, end):
line_num = i + 1
marker = "" if line_num == e.lineno else " "
print(f" {marker} {line_num:3d} | {lines[i]}")
print()
except:
pass
print(f"{Colors.YELLOW}Remediation:{Colors.NC}")
print(" - Check for missing commas between array elements or object properties")
print(" - Verify bracket matching: [ ] { }")
print(" - Ensure all strings are properly quoted")
print(" - Use a JSON formatter/linter in your editor")
else:
print(f"Error: {e.msg} at line {e.lineno}, column {e.colno}")
return 1
except Exception as e:
print(f"{Colors.RED}❌ Error reading file: {e}{Colors.NC}", file=sys.stderr)
return 2
# ====================
# Main Validation
# ====================
def validate_json(file_path, verbose=False):
"""Main validation function with backend selection"""
backend = detect_backend()
if backend == 'none':
print(f"{Colors.RED}❌ No JSON validation backend available{Colors.NC}", file=sys.stderr)
print(f"{Colors.BLUE} Install jq or ensure python3 is available{Colors.NC}", file=sys.stderr)
return 2
if backend == 'jq':
return validate_with_jq(file_path, verbose)
else:
return validate_with_python(file_path, verbose)
# ====================
# CLI Interface
# ====================
def main():
"""CLI entry point"""
parser = argparse.ArgumentParser(
description='Validate JSON file syntax with multi-backend support',
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog='''
Examples:
./json-validator.py --file plugin.json
./json-validator.py --file marketplace.json --verbose
./json-validator.py --detect
Backends:
- jq (preferred): Fast, excellent error messages
- python3 (fallback): Universal availability
Exit codes:
0: Valid JSON
1: Invalid JSON
2: File error or backend unavailable
'''
)
parser.add_argument(
'--file',
type=str,
help='Path to JSON file to validate'
)
parser.add_argument(
'--verbose',
action='store_true',
help='Show detailed error information and remediation'
)
parser.add_argument(
'--detect',
action='store_true',
help='Detect and display available backend'
)
args = parser.parse_args()
# Handle backend detection
if args.detect:
print_backend_info()
return 0
# Validate required arguments
if not args.file:
parser.print_help()
return 2
# Perform validation
return validate_json(args.file, args.verbose)
if __name__ == '__main__':
sys.exit(main())