///|
/// Runnable protocol-layer demo: encode client commands, feed the encoded
/// bytes plus a server frame stream through the parser, and print each
/// decoded operation. Run with `moon run cmd/main` from the project root.
fn main {
let cfg = @moonnats.ConnectConfig::default(name=Some("moonnats-demo"))
let connect = cfg.encode()
let sub = @moonnats.encode_command(
@moonnats.Sub(@moonnats.Subscribe::{
subject: "help",
sid: "1",
queue_group: Some("workers"),
}),
)
let ping = @moonnats.encode_command(@moonnats.Ping)
let pub_cmd = @moonnats.encode_command(
@moonnats.Pub(@moonnats.Publish::{
subject: "help",
reply: None,
payload: ascii_bytes("need help"),
}),
)
print_command("CONNECT", connect)
print_command("SUB", sub)
print_command("PING", ping)
print_command("PUB", pub_cmd)
println("\n--- parsing a server frame stream ---")
let server_stream = ascii_bytes(
"INFO {\"server_id\":\"NDX\",\"version\":\"2.10.11\",\"host\":\"localhost\",\"port\":4222,\"headers\":true,\"max_payload\":1048576}\r\n" +
"+OK\r\n" +
"MSG help 1 9\r\nneed help\r\n" +
"HMSG help 1 26 35\r\nNATS/1.0\r\nReply-To: me\r\n\r\nneed help\r\n" +
"PING\r\n",
)
let parser = @moonnats.Parser::new()
parser.feed(server_stream)
while true {
match parser.next_op() {
@moonnats.NeedMore => {
println("(need more bytes)")
break
}
@moonnats.Fail(reason) => {
println("parse error: \{reason}")
break
}
@moonnats.Op(op, _) =>
match op {
@moonnats.Info(json) =>
match @moonnats.parse_server_info(json) {
Ok(info) =>
println(
"INFO server=\{info.server_id} v\{info.version} max_payload=\{info.max_payload}",
)
Err(e) => println("INFO (undecodable: \{e})")
}
@moonnats.Msg(m) =>
println(
"MSG subject=\{m.subject} sid=\{m.sid} payload=\{bytes_text(m.payload)}",
)
@moonnats.HMsg(m) => {
println(
"HMSG subject=\{m.subject} payload=\{bytes_text(m.payload)}",
)
match @moonnats.parse_header_block(m.headers) {
Ok(block) =>
for h in block.headers {
println(" header \{h.name}: \{h.value}")
}
Err(e) => println(" (bad headers: \{e})")
}
}
@moonnats.Ping => println("PING")
@moonnats.Pong => println("PONG")
@moonnats.Ok => println("+OK")
@moonnats.ErrOp(desc) => println("-ERR \{desc}")
}
}
}
}
///|
fn print_command(name : String, bytes : Bytes) -> Unit {
println("\{name}: \{bytes_text(bytes)}")
}
///|
fn ascii_bytes(s : String) -> Bytes {
let out : Array[Byte] = []
for ch in s {
out.push(ch.to_int().to_byte())
}
Bytes::from_array(out)
}
///|
fn bytes_text(b : Bytes) -> String {
let sb = StringBuilder()
for byte in b {
let c = byte.to_int()
if c >= 32 && c < 127 {
sb.write_char(c.unsafe_to_char())
} else if c == 13 {
sb.write_string("\\r")
} else if c == 10 {
sb.write_string("\\n")
} else {
sb.write_string("\\x\{c.to_string(radix=16)}")
}
}
sb.to_string()
}