93 lines
2.5 KiB
Python
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 + "]"
|