diff options
Diffstat (limited to 'src/proto/serializer.rs')
| -rw-r--r-- | src/proto/serializer.rs | 78 |
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), + } + } +} |
