Files
wonder 4b56297498 refactor(todo): 去除 MySQL 依赖,改为 JSON 本地存储
- 数据存储: ~/.local/share/todo/tasks.json 纯字符串数组
- 操作简化: 仅保留 add/list/done,无状态/优先级/归档
- 新增 scripts/validate.py 校验脚本 (格式/结构/去重)
- 新增 schema/tasks.schema.json
- 删除 references/setup.md (MySQL 配置指南)

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-03 21:06:21 +08:00

143 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""校验 ~/.local/share/todo/tasks.json 的格式、结构和完整性。
退出码:
0 - 校验通过
1 - 校验失败(错误信息输出到 stderr)
用法:
python3 validate.py [文件路径]
默认路径: ~/.local/share/todo/tasks.json
"""
import json
import os
import sys
from pathlib import Path
DEFAULT_PATH = Path.home() / ".local" / "share" / "todo" / "tasks.json"
SCHEMA_PATH = Path(__file__).parent.parent / "schema" / "tasks.schema.json"
def load_json(path: Path) -> tuple[dict | None, str | None]:
"""加载并解析 JSON 文件。"""
if not path.exists():
return None, f"文件不存在: {path}"
try:
with open(path, "r", encoding="utf-8") as f:
data = json.load(f)
return data, None
except json.JSONDecodeError as e:
return None, f"JSON 格式错误: {e}"
def load_schema() -> tuple[dict | None, str | None]:
"""加载 JSON Schema。"""
if not SCHEMA_PATH.exists():
return None, f"Schema 文件不存在: {SCHEMA_PATH}"
try:
with open(SCHEMA_PATH, "r", encoding="utf-8") as f:
return json.load(f), None
except json.JSONDecodeError as e:
return None, f"Schema 格式错误: {e}"
def validate_structure(data: dict) -> list[str]:
"""校验数据结构。"""
errors = []
if not isinstance(data, dict):
errors.append("顶层必须是对象")
return errors
if "tasks" not in data:
errors.append("缺少必需字段 'tasks'")
return errors
tasks = data["tasks"]
if not isinstance(tasks, list):
errors.append("'tasks' 必须是数组")
return errors
for i, item in enumerate(tasks):
if not isinstance(item, str):
errors.append(f"tasks[{i}] 必须是字符串,实际类型: {type(item).__name__}")
elif len(item.strip()) == 0:
errors.append(f"tasks[{i}] 不能为空字符串")
return errors
def validate_duplicates(tasks: list[str]) -> list[str]:
"""校验任务是否有重复(大小写不敏感)。"""
errors = []
seen = {}
for i, task in enumerate(tasks):
key = task.strip().lower()
if key in seen:
errors.append(f"tasks[{i}] '{task}' 与 tasks[{seen[key]}] 重复")
else:
seen[key] = i
return errors
def validate_schema(data: dict) -> list[str]:
"""使用 JSON Schema 校验(如果 jsonschema 可用)。"""
try:
import jsonschema
except ImportError:
# jsonschema 未安装,跳过 schema 校验
return []
schema, err = load_schema()
if err:
return [f"Schema 校验跳过: {err}"]
errors = []
try:
jsonschema.validate(instance=data, schema=schema)
except jsonschema.ValidationError as e:
errors.append(f"Schema 校验失败: {e.message}")
except jsonschema.SchemaError as e:
errors.append(f"Schema 定义错误: {e.message}")
return errors
def main() -> int:
path = Path(sys.argv[1]) if len(sys.argv) > 1 else DEFAULT_PATH
# 1. JSON 格式校验
data, err = load_json(path)
if err:
print(err, file=sys.stderr)
return 1
# 2. 结构校验
errors = validate_structure(data)
if errors:
for e in errors:
print(f"结构错误: {e}", file=sys.stderr)
return 1
# 3. JSON Schema 校验
schema_errors = validate_schema(data)
if schema_errors:
for e in schema_errors:
print(f"Schema 错误: {e}", file=sys.stderr)
return 1
# 4. 去重校验
dup_errors = validate_duplicates(data["tasks"])
if dup_errors:
for e in dup_errors:
print(f"重复错误: {e}", file=sys.stderr)
return 1
print(f"校验通过: {path} ({len(data['tasks'])} 个任务)")
return 0
if __name__ == "__main__":
sys.exit(main())