# SPDX-License-Identifier: MIT """Learning project: bounded local task-status reporting, not a general JSON validator.""" import argparse import json import sys from pathlib import Path MAX_BYTES = 65536 MAX_RECORDS = 1000 def reject_constant(value): raise ValueError("non-standard JSON constant") def unique_object(pairs): result = {} for key, value in pairs: if key in result: raise ValueError("duplicate JSON key") result[key] = value return result def summarise(records): if not isinstance(records, list) or len(records) > MAX_RECORDS: raise ValueError("expected at most 1000 records") counts = {"closed": 0, "open": 0} for record in records: if not isinstance(record, dict) or set(record) != {"title", "status"}: raise ValueError("expected title and status only") title, status = record["title"], record["status"] if not isinstance(title, str) or not title.strip() or len(title) > 100: raise ValueError("expected a nonblank title of at most 100 characters") if not isinstance(status, str) or status not in counts: raise ValueError("expected open or closed status") counts[status] += 1 return {"records": len(records), "by_status": counts} def read_records(path): with Path(path).open("rb") as handle: raw = handle.read(MAX_BYTES + 1) if len(raw) > MAX_BYTES: raise ValueError("input exceeds 65536 bytes") return json.loads(raw.decode("utf-8"), parse_constant=reject_constant, object_pairs_hook=unique_object) def main(argv=None): parser = argparse.ArgumentParser(description="Summarise a small local task list.") parser.add_argument("input", help="UTF-8 JSON file, at most 65536 bytes") args = parser.parse_args(argv) try: result = summarise(read_records(args.input)) except (OSError, UnicodeError, ValueError, RecursionError): print("error: invalid or unreadable task file", file=sys.stderr) return 2 print(json.dumps(result, sort_keys=True, allow_nan=False)) return 0 if __name__ == "__main__": raise SystemExit(main())