pub const WIRE_NAME: usize = 24;
pub type WireName = [u8; WIRE_NAME];
pub fn wire_name(name: &str) -> WireName {
let mut out = [0u8; WIRE_NAME];
let mut len = 0;
for ch in name.chars() {
let next = len + ch.len_utf8();
if next > WIRE_NAME {
break;
}
ch.encode_utf8(&mut out[len..next]);
len = next;
}
out
}
pub const WIRE_CHAT: usize = 96;
pub type WireChat = [u8; WIRE_CHAT];
pub const CHAT_CHARS: usize = 48;
pub fn wire_chat(line: &str) -> WireChat {
let mut out = [0u8; WIRE_CHAT];
let mut len = 0;
for ch in line.chars().take(CHAT_CHARS) {
let next = len + ch.len_utf8();
if next > WIRE_CHAT {
break;
}
ch.encode_utf8(&mut out[len..next]);
len = next;
}
out
}
pub fn chat_from_wire(bytes: &WireChat) -> String {
let end = bytes.iter().position(|&b| b == 0).unwrap_or(WIRE_CHAT);
let Ok(text) = std::str::from_utf8(&bytes[..end]) else {
return String::new();
};
text.chars()
.filter(|ch| !ch.is_control())
.take(CHAT_CHARS)
.collect::<String>()
.trim()
.to_string()
}
const NAME_CHARS: usize = 12;
pub fn name_from_wire(bytes: &WireName) -> String {
let end = bytes.iter().position(|&b| b == 0).unwrap_or(WIRE_NAME);
let Ok(text) = std::str::from_utf8(&bytes[..end]) else {
return String::new();
};
text.chars()
.filter(|&c| !c.is_control() && c != '|' && c != ':')
.take(NAME_CHARS)
.collect::<String>()
.trim()
.to_string()
}
#[cfg(test)]
mod chat_tests {
use super::*;
#[test]
fn a_line_of_chat_survives_the_trip_and_is_tidied_on_the_way() {
for line in ["ready?", "wait for me!", "Anna: 3 > 2 | ok", "héllo"] {
assert_eq!(chat_from_wire(&wire_chat(line)), line, "{line}");
}
assert_eq!(chat_from_wire(&wire_chat("one\ntwo\u{1b}[0m")), "onetwo[0m");
assert_eq!(chat_from_wire(&wire_chat(" ")), "");
assert_eq!(chat_from_wire(&wire_chat(" hi ")), "hi");
let long = "é".repeat(400);
let short = chat_from_wire(&wire_chat(&long));
assert!(short.chars().count() <= CHAT_CHARS, "{}", short.len());
let mut junk = [0xFFu8; WIRE_CHAT];
junk[WIRE_CHAT - 1] = 0;
assert_eq!(chat_from_wire(&junk), "");
}
}