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
|