from __future__ import annotations from enum import StrEnum class SyncState(StrEnum): PREPARED = "prepared" SYNCING = "syncing" ERROR = "error" RECONNECTING = "reconnecting" STOPPED = "stopped" _SYNC_STATE_TRANSITIONS: dict[SyncState, set[SyncState]] = { SyncState.PREPARED: {SyncState.SYNCING, SyncState.ERROR, SyncState.STOPPED}, SyncState.SYNCING: {SyncState.ERROR, SyncState.RECONNECTING, SyncState.STOPPED}, SyncState.ERROR: {SyncState.RECONNECTING, SyncState.STOPPED}, SyncState.RECONNECTING: {SyncState.SYNCING, SyncState.ERROR, SyncState.STOPPED}, SyncState.STOPPED: {SyncState.PREPARED}, } class SyncStateMachine: def __init__(self): self._state = SyncState.PREPARED self._last_error: str = "" self._retry_count: int = 0 self._max_retries: int = 10 @property def state(self) -> SyncState: return self._state @property def last_error(self) -> str: return self._last_error @property def retry_count(self) -> int: return self._retry_count def can_transition(self, to_state: SyncState) -> bool: allowed = _SYNC_STATE_TRANSITIONS.get(self._state, set()) return to_state in allowed def transition(self, to_state: SyncState, error: str = "") -> bool: if not self.can_transition(to_state): return False self._state = to_state self._last_error = error if to_state == SyncState.ERROR: self._retry_count += 1 elif to_state == SyncState.SYNCING: self._retry_count = 0 return True def to_healthy(self) -> bool: return self.transition(SyncState.SYNCING) def to_error(self, error: str = "") -> bool: return self.transition(SyncState.ERROR, error) def to_reconnecting(self, error: str = "") -> bool: return self.transition(SyncState.RECONNECTING, error) def to_stopped(self) -> bool: return self.transition(SyncState.STOPPED) def to_prepared(self) -> bool: return self.transition(SyncState.PREPARED) @property def is_healthy(self) -> bool: return self._state in (SyncState.PREPARED, SyncState.SYNCING) @property def is_active(self) -> bool: return self._state in (SyncState.PREPARED, SyncState.SYNCING, SyncState.RECONNECTING) @property def should_stop_retry(self) -> bool: return self._retry_count >= self._max_retries