Skip to main content

Crate ircv3_parse

Crate ircv3_parse 

Source
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: FromMessage and ToMessage for 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_std compatible: Works in embedded and no_std environments (requires alloc)

§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:

§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 support
  • derive - Enables FromMessage and ToMessage derive macros (recommended)
  • serde - Enables Serialize implementation for Message

§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§

components
de
error
ser
validators

Structs§

Message
A parsed IRC message.
MessageBuilder

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§

FromMessagederive
ToMessagederive