From 4533789432d133cc399a414c97a2c0b90c14c1f0 Mon Sep 17 00:00:00 2001 From: Vladimir Date: Tue, 25 Aug 2026 01:50:45 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=BF=D0=B5=D1=80=D0=B5=D1=80=D0=B0?= =?UTF-8?q?=D0=B1=D0=BE=D1=82=D0=BA=D0=B0=20NetwrokMessage=20=D0=B8=20Even?= =?UTF-8?q?tMapper?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/mind_reader/network/message.py | 74 ++++++++++++++---------------- 1 file changed, 34 insertions(+), 40 deletions(-) diff --git a/src/mind_reader/network/message.py b/src/mind_reader/network/message.py index d332986..f87be2d 100644 --- a/src/mind_reader/network/message.py +++ b/src/mind_reader/network/message.py @@ -1,7 +1,8 @@ import json +import logging from dataclasses import asdict, dataclass, field from enum import StrEnum, auto -from typing import Any +from typing import Any, Optional from uuid_extensions import uuid7str @@ -16,6 +17,8 @@ from ..state_machines.session.events import ( SetterMoveEvent, ) +logger = logging.getLogger(__name__) + class SessionMessageKind(StrEnum): PLAYER_HELLO = auto() @@ -42,6 +45,14 @@ KIND_TO_EVENT_MAP = { } +def serializer(obj: Any) -> Any: + if isinstance(obj, StrEnum): + return obj.value + raise TypeError( + f"Object of type {type(obj).__name__} is not JSON serializable" + ) + + @dataclass(frozen=True) class NetworkMessage: kind: NetworkMessageKind @@ -50,17 +61,22 @@ class NetworkMessage: @classmethod def from_json(cls, json_str: str) -> "NetworkMessage": - data = json.loads(json_str) - return cls( - kind=NetworkMessageKind(data["kind"]), - payload=data.get("payload", {}), - id=data["id"], - ) + try: + data = json.loads(json_str) + return cls( + kind=NetworkMessageKind(data["kind"]), + payload=data.get("payload", {}), + id=data["id"], + ) + except (KeyError, ValueError, json.JSONDecodeError) as err: + logger.error( + "Failed to deserialize NetworkMessage from JSON: %s", err + ) + raise def to_json(self) -> str: - data = asdict(self) - data["kind"] = self.kind.value - return json.dumps(data, ensure_ascii=False) + data = {"kind": self.kind.value, "payload": self.payload, "id": self.id} + return json.dumps(data, ensure_ascii=False, default=serializer) class EventMapper: @@ -70,9 +86,14 @@ class EventMapper: kind = EVENT_TO_KIND_MAP.get(event_type) if not kind: - raise ValueError(f"Event {event_type} not registered") + logger.error( + "Attempted to serialize unregistered event: %s", event_type + ) + raise ValueError( + f"Event {event_type} is not registered in EVENT_TO_KIND_MAP" + ) - return NetworkMessage(kind=kind, payload=asdict(event)) + return NetworkMessage(kind=kind, payload=event.to_payload()) @staticmethod def from_message(message: NetworkMessage) -> Event | None: @@ -82,33 +103,6 @@ class EventMapper: return None try: - payload = message.payload - - player = Player( - name=payload["player"]["name"], - role=PlayerRole(payload["player"]["role"]), - ) - - match message.kind: - case NetworkMessageKind.PLAYER_HELLO: - return PlayerHelloEvent(player=player) - case NetworkMessageKind.SETTER_MOVED: - return SetterMoveEvent( - player=player, - direction=ArrowDirection(payload["direction"]), - ) - case NetworkMessageKind.GUESSER_MOVED: - return GuesserMoveEvent( - player=player, - direction=ArrowDirection(payload["direction"]), - ) - case NetworkMessageKind.SECRET_REVEALED: - return RevealSecretEvent( - player=player, - direction=ArrowDirection(payload["direction"]), - ) - case NetworkMessageKind.NEXT_STEP_REQUESTED: - return NextStepRequestedEvent(player=player) - + return event_cls.from_payload(message.payload) except (KeyError, ValueError, TypeError): return None