Expand description
Zero-copy IRC message parser with IRCv3 support.
§Key Features
- Zero-copy parsing: Message components are slices into the original input string
- IRCv3 support: Full support for message tags, source, and all IRCv3 features
- Derive macros:
FromMessageandToMessagefor easy message extraction and generation - Manual implementations: Full control over parsing and serialization when needed
- Builder pattern: Flexible, order-independent message construction with
MessageBuilder no_stdcompatible: Works in embedded andno_stdenvironments (requiresalloc)
§Quick Start
§Parsing Messages with FromMessage
use ircv3_parse::FromMessage;
#[derive(FromMessage)]
#[irc(command = "PRIVMSG")]
struct PrivMsg<'a> {
#[irc(source = "name")]
nick: &'a str,
#[irc(trailing)]
message: &'a str
}
let input = ":nick!user@example.com PRIVMSG #channel :Hello everyone!";
let msg: PrivMsg = ircv3_parse::from_str(input)?;
println!("From: {}", msg.nick);
println!("Message: {}", msg.message);§Building Messages with ToMessage
use ircv3_parse::ToMessage;
#[derive(ToMessage)]
#[irc(command = "PRIVMSG", crlf)]
struct PrivMsg<'a> {
#[irc(tag)]
msgid: &'a str,
#[irc(param)]
channel: &'a str,
#[irc(trailing)]
message: &'a str,
}
let msg = PrivMsg {
msgid: "123",
channel: "#channel",
message: "hi",
};
let output = ircv3_parse::to_message(&msg)?;
assert_eq!(b"@msgid=123 PRIVMSG #channel :hi\r\n", output.as_ref());§Derive Attributes
See the Derive Macro Reference for the full attribute reference.
§Manual FromMessage Implementation
For more complex parsing logic, implement the FromMessage trait manually.
§Understanding Message Structure
The Message struct provides access to IRC message components:
Message::tags()- ReturnsTagsMessage::source()- ReturnsSourceMessage::command()- ReturnsCommandsMessage::params()- ReturnsParams
§Example Implementation
use ircv3_parse::{de::FromMessage, DeError, Message};
struct PrivMsg<'a> {
color: Option<&'a str>,
channel: &'a str,
nick: &'a str,
message: &'a str,
}
impl<'a> FromMessage<'a> for PrivMsg<'a> {
fn from_message(msg: &Message<'a>) -> Result<Self, DeError> {
// Validate command
let command = msg.command();
if !command.is_privmsg() {
return Err(DeError::command_mismatch("PRIVMSG", command.as_str()));
}
// Extract tags (optional)
let color = msg.tags()
.and_then(|tags| tags.get("color"))
.map(|v| v.as_str());
// Extract source (required)
let source = msg.source()
.ok_or_else(|| DeError::source_component_not_found())?;
let nick = source.name;
// Extract parameters
let params = msg.params();
let channel = params.middles.first()
.ok_or_else(|| DeError::not_found_param(0))?;
let message = params.trailing.as_str();
Ok(Self {
color,
channel,
nick,
message,
})
}
}§Manual ToMessage Implementation
§Example Implementation
use ircv3_parse::ser::ToMessage;
struct PrivMsg<'a> {
msgid: &'a str,
subscriber: bool,
channel: &'a str,
message: String,
}
impl ToMessage for PrivMsg<'_> {
fn to_message<S: ircv3_parse::ser::MessageSerializer>(
&self,
serialize: &mut S,
) -> Result<(), ircv3_parse::SerError> {
use ircv3_parse::Commands;
let tags = serialize.tags();
tags.insert_tag("msgid", Some(self.msgid))?;
if self.subscriber {
tags.insert_flag("subscriber")?;
}
Commands::PRIVMSG.to_message(serialize)?;
let params = serialize.params();
params.push(self.channel)?;
serialize.set_trailing(self.message.as_ref())?;
serialize.end()?;
Ok(())
}
}
let msg = PrivMsg {
msgid: "1",
subscriber: false,
channel: "#channel",
message: "hi".to_string(),
};
let msg = ircv3_parse::to_message(&msg)?;
assert_eq!("@msgid=1 PRIVMSG #channel :hi\r\n", msg);
§Using MessageBuilder
The builder pattern allows order-independent message construction.
use ircv3_parse::{Commands, MessageBuilder};
let mut msg = MessageBuilder::new();
msg.set_command(Commands::PRIVMSG);
msg.add_tag("tag1", Some("value1"))?
.add_tag("tag2", None)?
.add_tag_flag("flag")?;
msg.set_source_name("nick")?;
msg.set_source_user("user")?;
msg.set_source_host("example.com")?;
msg.set_trailing("hi")?;
let actual = msg.build().unwrap();
assert_eq!(
b"@tag1=value1;tag2=;flag :nick!user@example.com PRIVMSG :hi\r\n",
actual.as_ref()
);
§Error Handling
§FromMessage
Returns DeError for deserialization failures.
use ircv3_parse::FromMessage;
#[derive(FromMessage)]
#[irc(command = "PRIVMSG")]
struct PrivMsg {
#[irc(trailing)]
message: String
}
let input = "NOTICE all :hi";
let result = ircv3_parse::from_str::<PrivMsg>(input);
if let Err(e) = result {
if e.is_parse_error() {
println!("Invalid IRC message format: {e}");
}
if e.is_command_mismatch() {
println!("Expected PRIVMSG, got {input}");
}
if e.is_tags_component_not_found() {
println!("Message has no tags component");
}
if e.is_source_component_not_found() {
println!("Message has no source component");
}
if e.is_param_component_not_found() {
println!("Message has no parameters");
}
if e.is_not_found_tag() {
println!("Specific tag not found");
}
}§ToMessage
Returns SerError for serialization failures.
§Feature Flags
std(enabled by default) - Enables standard library supportderive- EnablesFromMessageandToMessagederive macros (recommended)serde- EnablesSerializeimplementation forMessage
§Using in no_std Environments
This crate supports no_std environments with the alloc crate:
[dependencies]
ircv3_parse = { version = "4", default-features = false, features = ["derive"] }Re-exports§
pub use components::Commands;pub use error::DeError;pub use error::IRCError;pub use error::SerError;
Modules§
Structs§
- Message
- A parsed IRC message.
- Message
Builder
Functions§
- from_
str - Parse an IRC message into a type implementing
de::FromMessage. - parse
- Parse an IRC message from a string.
- to_
message - Serialize a custom data structure as Bytes.
- unescape
- Unescapes an IRCv3 tag value according to the specification.
Derive Macros§
- From
Message derive - ToMessage
derive