105 lines
2.9 KiB
Python
105 lines
2.9 KiB
Python
|
"""Workspace syncing classes"""
|
|||
|
|
|||
|
from enum import Enum, auto
|
|||
|
from typing import Any, Callable, Dict, Generic, Optional, TypeVar
|
|||
|
|
|||
|
from ..watchable import Watchable, WatchableSet
|
|||
|
|
|||
|
FnsBag = Dict[str, Callable[[Any], Any]]
|
|||
|
Thunk = Callable[[], None]
|
|||
|
T = TypeVar('T')
|
|||
|
|
|||
|
|
|||
|
class TransportStatus(Enum):
|
|||
|
OPEN = auto()
|
|||
|
CLOSED = auto()
|
|||
|
|
|||
|
|
|||
|
class ConnectionStatus(Enum):
|
|||
|
CONNECTING = auto()
|
|||
|
CLOSED = auto()
|
|||
|
|
|||
|
|
|||
|
class TransportBase:
|
|||
|
"""Base class for workspace syncers"""
|
|||
|
|
|||
|
status: Watchable[TransportStatus]
|
|||
|
is_closed: bool
|
|||
|
methods: FnsBag
|
|||
|
device_id: str
|
|||
|
connections: WatchableSet[Connection]
|
|||
|
|
|||
|
def __init__(self, device_id: str, methods: FnsBag):
|
|||
|
raise NotImplementedError()
|
|||
|
|
|||
|
def on_close(self, cb: Thunk) -> Thunk:
|
|||
|
"""Set a handler for when the connection closes"""
|
|||
|
|
|||
|
raise NotImplementedError()
|
|||
|
|
|||
|
def close(self) -> None:
|
|||
|
"""Close the syncer’s connection"""
|
|||
|
|
|||
|
raise NotImplementedError()
|
|||
|
|
|||
|
|
|||
|
class TransportLocal(TransportBase):
|
|||
|
def __init__(self, device_id: str, methods: BagType, description: str) -> None:
|
|||
|
self.device_id = device_id
|
|||
|
self.methods = methods
|
|||
|
self.description = description
|
|||
|
|
|||
|
@property
|
|||
|
def is_closed(self) -> bool:
|
|||
|
return self.status == TransportStatus.CLOSED
|
|||
|
|
|||
|
def on_close(self, func: Thunk) -> Thunk:
|
|||
|
return self.status.on_change_to(TransportStatus.CLOSED)(func)
|
|||
|
|
|||
|
def close() -> None:
|
|||
|
if self.is_closed:
|
|||
|
return
|
|||
|
|
|||
|
self.status.set(TransportStatus.CLOSED)
|
|||
|
|
|||
|
for conn in self.connections:
|
|||
|
conn.close()
|
|||
|
|
|||
|
self.connections.clear()
|
|||
|
|
|||
|
def add_connection(self, other_trans: TransportLocal[BagType]) -> Tuple[Connection, Connection]:
|
|||
|
if self.is_closed:
|
|||
|
raise Exception('Can’t use a transport after it’s closed')
|
|||
|
|
|||
|
this_conn: Connection[BagType]
|
|||
|
other_conn: Connection[BagType]
|
|||
|
|
|||
|
this_conn = Connection(
|
|||
|
description=f'conn {self.device_id} to {other_trans.device_id}',
|
|||
|
transport=self,
|
|||
|
device_id=self.device_id,
|
|||
|
methods=self.methods,
|
|||
|
send_envelope=lambda conn: ConnectionBase[BagType], env: Envelope[BagType]: other_conn.handle_incoming_envelope(env),
|
|||
|
)
|
|||
|
other_conn = Connection(
|
|||
|
description=f'conn other_trans.device_id to {this.device_id}',
|
|||
|
transport: other_trans,
|
|||
|
device_id: other_trans.device_id,
|
|||
|
methods: other_trans.methods,
|
|||
|
send_envelope: lambda conn: ConnectionBase[BagType], env: Envelope[BagType]: this_conn.handle_incoming_envelope(env),
|
|||
|
)
|
|||
|
|
|||
|
@this_conn.on_close
|
|||
|
def close_other():
|
|||
|
other_conn.close()
|
|||
|
self.connections.delete(this_conn)
|
|||
|
|
|||
|
@other_conn.on_close
|
|||
|
def close_this():
|
|||
|
this_conn.close()
|
|||
|
|
|||
|
self.connections.add(this_conn)
|
|||
|
other_trans.connections.add(other_conn)
|
|||
|
|
|||
|
return this_conn, other_conn
|