60 lines
1.8 KiB
Python
60 lines
1.8 KiB
Python
"""
|
|
Small JSON-file helpers shared by the per-document stores.
|
|
|
|
BookmarkManager and HighlightManager both keep a JSON file per document under a
|
|
directory, and both had their own copy of "make the directory, try to read it,
|
|
swallow and print on failure". The duplication is the point of this module; the
|
|
file formats themselves stay owned by each store.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Any
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def ensure_dir(path: str | Path) -> Path:
|
|
"""Return `path` as a Path, creating it and any missing parents."""
|
|
directory = Path(path)
|
|
directory.mkdir(parents=True, exist_ok=True)
|
|
return directory
|
|
|
|
|
|
def read_json(path: Path, default: Any) -> Any:
|
|
"""
|
|
Read JSON from `path`, returning `default` if it is missing or unreadable.
|
|
|
|
A corrupt store must not stop a book from opening, so failures are logged
|
|
and swallowed. `default` is returned as given, so pass a fresh mutable if
|
|
the caller intends to mutate it.
|
|
"""
|
|
if not path.exists():
|
|
return default
|
|
|
|
try:
|
|
with open(path, 'r', encoding='utf-8') as handle:
|
|
return json.load(handle)
|
|
except (OSError, ValueError):
|
|
logger.warning("Could not read %s; ignoring its contents", path, exc_info=True)
|
|
return default
|
|
|
|
|
|
def write_json(path: Path, data: Any) -> bool:
|
|
"""
|
|
Write `data` to `path` as JSON.
|
|
|
|
Returns True on success. Failures are logged rather than raised: losing a
|
|
bookmark is not a reason to take down the reader.
|
|
"""
|
|
try:
|
|
with open(path, 'w', encoding='utf-8') as handle:
|
|
json.dump(data, handle, indent=2)
|
|
return True
|
|
except (OSError, TypeError, ValueError):
|
|
logger.error("Could not write %s", path, exc_info=True)
|
|
return False
|