"""Session registry -- maps nicknames to active sessions. Ported from net.i2p.sam.SAMBridge session management. """ from __future__ import annotations import asyncio from dataclasses import dataclass, field from typing import TYPE_CHECKING if TYPE_CHECKING: from i2p_sam.handler import SAMHandler @dataclass class SessionRecord: """Record for a registered SAM session.""" nickname: str style: str # STREAM, DATAGRAM, RAW, PRIMARY destination: bytes # serialized Destination (raw bytes) destination_b64: str handler: "SAMHandler" subsessions: dict[str, "SessionRecord"] = field(default_factory=dict) class SessionsDB: """Singleton registry of active SAM sessions by nickname. Thread-safe via asyncio.Lock. """ def __init__(self) -> None: self._sessions: dict[str, SessionRecord] = {} self._lock = asyncio.Lock() async def add(self, record: SessionRecord) -> bool: """Add session. Returns False if nickname already exists. Args: record: The session record to register. Returns: True if added, False if nickname is already taken. """ async with self._lock: if record.nickname in self._sessions: return False self._sessions[record.nickname] = record return True async def remove(self, nickname: str) -> SessionRecord | None: """Remove and return session. Args: nickname: The session nickname to remove. Returns: The removed SessionRecord, or None if not found. """ async with self._lock: return self._sessions.pop(nickname, None) async def get(self, nickname: str) -> SessionRecord | None: """Look up session by nickname. Args: nickname: The session nickname to look up. Returns: The SessionRecord, or None if not found. """ async with self._lock: return self._sessions.get(nickname) async def has(self, nickname: str) -> bool: """Check if a session with the given nickname exists.""" async with self._lock: return nickname in self._sessions @property def count(self) -> int: """Number of registered sessions.""" return len(self._sessions)