Files
website/tools/content/jsonfile.py
T

93 lines
2.5 KiB
Python

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 + "]"