///|
pub(all) struct ConnectOptions {
name : String
user : String?
password : String?
token : String?
jwt : String?
nkey : String?
signature : String?
echo : Bool
}
///|
pub fn ConnectOptions::new(
name~ : String = "MoonNATS",
user? : String,
password? : String,
token? : String,
jwt? : String,
nkey? : String,
signature? : String,
echo~ : Bool = true,
) -> ConnectOptions {
{ name, user, password, token, jwt, nkey, signature, echo, }
}
///|
pub fn connect_command(
options : ConnectOptions,
headers~ : Bool = true,
) -> Bytes {
let values : Map[String, Json] = {
"verbose": false,
"pedantic": true,
"tls_required": false,
"name": Json(options.name),
"lang": "moonbit",
"version": "0.3.5",
"protocol": 1,
"headers": Json(headers),
"no_responders": Json(headers),
"echo": Json(options.echo),
}
for
(key, value) in [
("user", options.user),
("pass", options.password),
("auth_token", options.token),
("jwt", options.jwt),
("nkey", options.nkey),
("sig", options.signature),
] {
if value is Some(v) {
values[key] = Json(v)
}
}
@utf8.encode("CONNECT " + Json::object(values).stringify() + "\r\n")
}
///|
pub(all) struct ServerInfo {
raw : Json
max_payload : Int32
headers : Bool
tls_required : Bool
auth_required : Bool
jetstream : Bool
nonce : String?
connect_urls : Array[String]
}
///|
pub fn ServerInfo::parse(text : String) -> ServerInfo raise ProtocolError {
let j = @json.parse(text) catch { _ => raise Invalid("invalid INFO JSON") }
guard j is Object(_) else { raise Invalid("INFO must be object") }
server_info(j)
}
///|
/// Merge an asynchronous INFO update with capabilities from the current connection.
pub fn ServerInfo::update(
self : ServerInfo,
text : String,
) -> ServerInfo raise ProtocolError {
let patch = @json.parse(text) catch {
_ => raise Invalid("invalid INFO JSON")
}
guard self.raw is Object(current) && patch is Object(fields) else {
raise Invalid("INFO must be object")
}
let merged = current.copy()
for key, value in fields {
merged[key] = value
}
server_info(Json::object(merged))
}
///|
fn server_info(j : Json) -> ServerInfo {
let max_payload = match j {
{ "max_payload": Number(n, ..), .. } if n > 0 && n <= 2147483645.0 =>
n.to_int()
_ => 1048576
}
let urls = match j {
{ "connect_urls": Array(a), .. } =>
a
.iter()
.filter_map(v => {
match v {
String(s) => Some(s)
_ => None
}
})
.to_array()
_ => []
}
{
raw: j,
max_payload,
headers: j is { "headers": True, .. },
tls_required: j is { "tls_required": True, .. },
auth_required: j is { "auth_required": True, .. },
jetstream: j is { "jetstream": True, .. },
nonce: match j {
{ "nonce": String(s), .. } => Some(s)
_ => None
},
connect_urls: urls,
}
}