///|
/// Transport abstraction: how JSON-RPC messages enter and leave a server/client.
///
/// The trait only requires [send]. [start] and [close] are provided as
/// default no-ops so transports that do not need them (e.g. in-memory) incur
/// no boilerplate.
pub trait Transport {
fn send(Self, String) -> Unit
}
///|
/// The callback invoked when a complete JSON-RPC message line arrives.
pub type MessageHandler = (String) -> Unit
///|
/// An in-memory transport pair for tests and same-process use.
///
/// Create a linked pair with [InMemoryTransport::pair]; messages sent on one
/// end are delivered to the handler of the other.
pub(all) struct InMemoryTransport {
mut handler : MessageHandler?
mut closed : Bool
mut peer : InMemoryTransport?
}
///|
/// Create a linked pair of in-memory transports. Messages sent on one are
/// delivered to the other's handler.
pub fn InMemoryTransport::pair() -> (InMemoryTransport, InMemoryTransport) {
let a : InMemoryTransport = { handler: None, closed: false, peer: None }
let b : InMemoryTransport = { handler: None, closed: false, peer: None }
a.peer = Some(b)
b.peer = Some(a)
(a, b)
}
///|
/// Set the message handler for this transport.
pub fn InMemoryTransport::on_message(
self : InMemoryTransport,
h : MessageHandler,
) -> Unit {
self.handler = Some(h)
}
///|
/// Close the transport (subsequent sends are dropped).
pub fn InMemoryTransport::close(self : InMemoryTransport) -> Unit {
self.closed = true
}
///|
impl Transport for InMemoryTransport with fn send(self, msg : String) -> Unit {
if self.closed {
return
}
match self.peer {
Some(p) =>
match p.handler {
Some(h) => h(msg)
None => ()
}
None => ()
}
}
///|
/// A buffered transport that collects sent messages in an array (useful for testing).
pub(all) struct BufferedTransport {
mut sent : Array[String]
mut closed : Bool
}
///|
pub fn BufferedTransport::new() -> BufferedTransport {
{ sent: [], closed: false }
}
///|
/// Messages collected so far (in send order).
pub fn BufferedTransport::messages(self : BufferedTransport) -> Array[String] {
self.sent
}
///|
pub fn BufferedTransport::close(self : BufferedTransport) -> Unit {
self.closed = true
}
///|
impl Transport for BufferedTransport with fn send(self, msg : String) -> Unit {
if !self.closed {
self.sent.push(msg)
}
}
///|
/// Send a message through any transport (generic helper for trait dispatch).
pub fn[T : Transport] send_message(t : T, msg : String) -> Unit {
T::send(t, msg)
}
///|
/// Send a message through any transport and return the transport (for chaining).
pub fn[T : Transport] send_and_return(t : T, msg : String) -> T {
T::send(t, msg)
t
}
///|
test "in-memory transport send via trait" {
let (a, b) = InMemoryTransport::pair()
let mut received = ""
b.on_message(fn(s) { received = s })
send_message(a, "hello")
assert_true(received == "hello")
}
///|
test "buffered transport send via trait" {
let t = BufferedTransport::new()
send_message(t, "msg1")
send_message(t, "msg2")
assert_true(t.messages().length() == 2)
}