import json from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] CONTENT_DIR = PROJECT_ROOT / "content" DATA_DIR = PROJECT_ROOT / "data" INLINE_LIST_MAX_LENGTH = 80 INDENT = "\t" def content_path(name: str) -> Path: return CONTENT_DIR / name def data_path(name: str) -> Path: return DATA_DIR / name def read_json(path: Path): with path.open(encoding="utf-8") as file: return json.load(file) def write_json(path: Path, data) -> None: path.parent.mkdir(parents=True, exist_ok=True) path.write_text(format_value(data, 0) + "\n", encoding="utf-8") def load(name: str): return read_json(content_path(name)) def save(name: str, data) -> None: write_json(content_path(name), data) def load_data(name: str, default): path = data_path(name) return read_json(path) if path.exists() else default def save_data(name: str, data) -> None: write_json(data_path(name), data) def is_primitive(value) -> bool: return not isinstance(value, (dict, list)) def encode(value) -> str: return json.dumps(value, ensure_ascii=False) def format_inline_dict(data: dict) -> str: if not data: return "{}" pairs = ", ".join(f"{encode(key)}: {format_value(value, 0)}" for key, value in data.items()) return "{ " + pairs + " }" def format_inline_list(items: list) -> str: return "[" + ", ".join(encode(item) for item in items) + "]" def format_value(value, depth: int) -> str: if isinstance(value, dict): return format_dict(value, depth) if isinstance(value, list): return format_list(value, depth) return encode(value) def format_dict(data: dict, depth: int) -> str: if depth > 0 and all(is_primitive(value) or is_short_primitive_list(value) for value in data.values()): return format_inline_dict(data) inner_indent = INDENT * (depth + 1) lines = [f"{inner_indent}{encode(key)}: {format_value(value, depth + 1)}" for key, value in data.items()] return "{\n" + ",\n".join(lines) + "\n" + INDENT * depth + "}" def is_short_primitive_list(value) -> bool: return isinstance(value, list) and all(is_primitive(item) for item in value) and len(format_inline_list(value)) <= INLINE_LIST_MAX_LENGTH def format_list(items: list, depth: int) -> str: if not items: return "[]" if is_short_primitive_list(items): return format_inline_list(items) inner_indent = INDENT * (depth + 1) lines = [f"{inner_indent}{format_value(item, depth + 1)}" for item in items] return "[\n" + ",\n".join(lines) + "\n" + INDENT * depth + "]"