2023-04-08 18:32:32 +00:00
|
|
|
use std::io::Write;
|
2023-03-22 22:14:17 +00:00
|
|
|
|
2023-04-08 18:32:32 +00:00
|
|
|
use super::Line;
|
2023-03-22 22:14:17 +00:00
|
|
|
|
2023-04-08 18:32:32 +00:00
|
|
|
fn tag_encode(input: &str, output: &mut (impl Write + ?Sized)) -> std::io::Result<()> {
|
2023-03-22 22:14:17 +00:00
|
|
|
for char in input.chars() {
|
2023-04-08 18:32:32 +00:00
|
|
|
match char {
|
|
|
|
';' => write!(output, "\\:")?,
|
|
|
|
' ' => write!(output, "\\s")?,
|
|
|
|
'\\' => write!(output, "\\")?,
|
|
|
|
'\r' => write!(output, "\\r")?,
|
|
|
|
'\n' => write!(output, "\\n")?,
|
|
|
|
_ => write!(output, "{char}")?,
|
|
|
|
}
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
2023-04-08 18:32:32 +00:00
|
|
|
Ok(())
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Line {
|
2023-03-23 18:12:04 +00:00
|
|
|
#[allow(clippy::doc_markdown)]
|
2023-04-08 18:32:32 +00:00
|
|
|
/// Write `self` to `output` as a formatted byte string by [RFC1459] and [IRCV3] protocol rules.
|
2023-03-23 21:20:51 +00:00
|
|
|
///
|
2023-04-08 18:32:32 +00:00
|
|
|
/// Does NOT write a CRLF nor flush the stream.
|
|
|
|
/// This function makes a large number of small writes;
|
|
|
|
/// it is advised to use a buffered [`Write`] implementation here.
|
2023-03-23 18:12:04 +00:00
|
|
|
///
|
|
|
|
/// [RFC1459]: https://www.rfc-editor.org/rfc/rfc1459#section-2.3
|
|
|
|
/// [IRCv3]: https://ircv3.net/specs/extensions/message-tags.html
|
2023-04-08 18:32:32 +00:00
|
|
|
pub fn write_to(&self, output: &mut (impl Write + ?Sized)) -> std::io::Result<()> {
|
2023-03-22 22:14:17 +00:00
|
|
|
if let Some(tags) = &self.tags {
|
2023-04-08 18:32:32 +00:00
|
|
|
let mut not_at_start = false;
|
|
|
|
for (key, value) in tags {
|
|
|
|
if not_at_start {
|
|
|
|
write!(output, ";{key}")?;
|
|
|
|
} else {
|
|
|
|
not_at_start = true;
|
|
|
|
write!(output, "@{key}")?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
|
|
|
if let Some(value) = value {
|
2023-04-08 18:32:32 +00:00
|
|
|
output.write_all(b"=")?;
|
|
|
|
tag_encode(value, output)?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
|
|
|
}
|
2023-04-08 18:32:32 +00:00
|
|
|
output.write_all(b" ")?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(source) = &self.source {
|
2023-04-08 18:32:32 +00:00
|
|
|
output.write_all(b":")?;
|
|
|
|
output.write_all(source)?;
|
|
|
|
output.write_all(b" ")?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
|
|
|
|
2023-04-08 18:32:32 +00:00
|
|
|
output.write_all(self.command.as_bytes())?;
|
2023-03-22 22:14:17 +00:00
|
|
|
|
2023-04-08 18:32:32 +00:00
|
|
|
if let Some((last, args)) = self.arguments.split_last() {
|
|
|
|
for arg in args {
|
|
|
|
output.write_all(b" ")?;
|
|
|
|
output.write_all(arg)?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
2023-04-08 18:32:32 +00:00
|
|
|
output.write_all(b" :")?;
|
|
|
|
output.write_all(last)?;
|
2023-03-22 22:14:17 +00:00
|
|
|
}
|
2023-04-08 18:32:32 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
#[allow(clippy::doc_markdown)]
|
|
|
|
/// Format `self` into a byte string by [RFC1459] and [IRCv3] protocol rules.
|
|
|
|
///
|
|
|
|
/// The returned byte string is NOT suffixed with a CRLF.
|
|
|
|
///
|
|
|
|
/// [RFC1459]: https://www.rfc-editor.org/rfc/rfc1459#section-2.3
|
|
|
|
/// [IRCv3]: https://ircv3.net/specs/extensions/message-tags.html
|
|
|
|
#[must_use]
|
|
|
|
pub fn format(&self) -> Vec<u8> {
|
|
|
|
// Minimum size of a message is its command's length plus 2 bytes per argument.
|
|
|
|
// In practice reallocation is basically guaranteed, but this provides a starting point.
|
|
|
|
let mut output = Vec::with_capacity(self.command.len() + self.arguments.len() * 2);
|
|
|
|
std::mem::drop(self.write_to(&mut output));
|
2023-03-22 22:14:17 +00:00
|
|
|
output
|
|
|
|
}
|
|
|
|
}
|