aboutsummaryrefslogtreecommitdiffstats
path: root/src/proto/serializer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/proto/serializer.rs')
-rw-r--r--src/proto/serializer.rs78
1 files changed, 78 insertions, 0 deletions
diff --git a/src/proto/serializer.rs b/src/proto/serializer.rs
new file mode 100644
index 0000000..603cc0b
--- /dev/null
+++ b/src/proto/serializer.rs
@@ -0,0 +1,78 @@
+use crate::proto::message::{IrcMessage, Prefix};
+use std::fmt::Write;
+
+pub fn serialize(msg: &IrcMessage) -> String {
+ let mut out = String::with_capacity(512);
+
+ // tags
+ if !msg.tags.is_empty() {
+ out.push('@');
+ let mut first = true;
+ for (key, value) in &msg.tags {
+ if !first {
+ out.push(';');
+ }
+ first = false;
+ out.push_str(key);
+ if let Some(v) = value {
+ out.push('=');
+ escape_tag_value(&mut out, v);
+ }
+ }
+ out.push(' ');
+ }
+
+ // prefix
+ if let Some(prefix) = &msg.prefix {
+ out.push(':');
+ match prefix {
+ Prefix::Server(s) => out.push_str(s),
+ Prefix::User { nick, user, host } => {
+ out.push_str(nick);
+ if let Some(u) = user {
+ out.push('!');
+ out.push_str(u);
+ }
+ if let Some(h) = host {
+ out.push('@');
+ out.push_str(h);
+ }
+ }
+ }
+ out.push(' ');
+ }
+
+ // command
+ let _ = write!(out, "{}", msg.command);
+
+ // params
+ let last_idx = msg.params.len().saturating_sub(1);
+ for (i, param) in msg.params.iter().enumerate() {
+ out.push(' ');
+ // The last param must be trailing if it contains spaces or starts with ':'
+ let needs_trailing = i == last_idx
+ && (param.contains(' ')
+ || param.starts_with(':')
+ || param.is_empty()
+ || msg.params.len() > 1);
+ if needs_trailing {
+ out.push(':');
+ }
+ out.push_str(param);
+ }
+
+ out
+}
+
+fn escape_tag_value(out: &mut String, value: &str) {
+ for ch in value.chars() {
+ match ch {
+ ';' => out.push_str(r"\:"),
+ ' ' => out.push_str(r"\s"),
+ '\\' => out.push_str(r"\\"),
+ '\r' => out.push_str(r"\r"),
+ '\n' => out.push_str(r"\n"),
+ c => out.push(c),
+ }
+ }
+}