from __future__ import annotations import json from dataclasses import dataclass from datetime import datetime, timezone from typing import Dict, Any from .config import dht_config from .crypto import digest_hex def _json_dumps(data: Dict[str, Any]) -> bytes: return json.dumps(data, sort_keys=True, separators=(",", ":")).encode() @dataclass(frozen=True) class MetaKey: content_id: str schema_version: str = dht_config.schema_version def fingerprint(self) -> str: return digest_hex(self.serialize()) def serialize(self) -> bytes: return _json_dumps({"schema_version": self.schema_version, "content_id": self.content_id, "type": "meta"}) def __str__(self) -> str: return f"meta:{self.schema_version}:{self.content_id}" @dataclass(frozen=True) class MembershipKey: node_id: str schema_version: str = dht_config.schema_version def fingerprint(self) -> str: return digest_hex(self.serialize()) def serialize(self) -> bytes: return _json_dumps({"schema_version": self.schema_version, "node_id": self.node_id, "type": "membership"}) def __str__(self) -> str: return f"membership:{self.schema_version}:{self.node_id}" @dataclass(frozen=True) class MetricKey: content_id: str window_id: str schema_version: str = dht_config.schema_version @classmethod def window_for(cls, timestamp: float, window_size: int | None = None) -> str: win = int(timestamp // (window_size or dht_config.window_size)) return datetime.fromtimestamp(win * (window_size or dht_config.window_size), tz=timezone.utc).strftime("%Y%m%d%H") def fingerprint(self) -> str: return digest_hex(self.serialize()) def serialize(self) -> bytes: return _json_dumps( { "schema_version": self.schema_version, "content_id": self.content_id, "window_id": self.window_id, "type": "metric", } ) def __str__(self) -> str: return f"metric:{self.schema_version}:{self.content_id}:{self.window_id}"