documentation and `impl TryFrom<&[u8]> for Line`
This commit is contained in:
parent
0c38b5bb80
commit
147eeff8d1
|
@ -1,7 +1,6 @@
|
|||
use std::collections::BTreeMap;
|
||||
use criterion::{criterion_group, criterion_main, Criterion};
|
||||
use irctokens::Line;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
let line = Line {
|
||||
|
|
|
@ -2,7 +2,8 @@ use criterion::{criterion_group, criterion_main, Criterion};
|
|||
use irctokens::Line;
|
||||
|
||||
fn basic() {
|
||||
Line::tokenise(b"@tag1=tag1value;tag2=;tag3 :source COMMAND arg1 arg2 :arg3 with space").unwrap();
|
||||
Line::tokenise(b"@tag1=tag1value;tag2=;tag3 :source COMMAND arg1 arg2 :arg3 with space")
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn criterion_benchmark(c: &mut Criterion) {
|
||||
|
|
|
@ -18,6 +18,12 @@ fn tag_encode(input: &str) -> String {
|
|||
}
|
||||
|
||||
impl Line {
|
||||
#[allow(clippy::doc_markdown)]
|
||||
/// Format `self` in to a byte string by [RFC1459] and [IRCv3] protocol rules.
|
||||
///
|
||||
/// [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> {
|
||||
let mut output = Vec::new();
|
||||
|
||||
|
@ -45,9 +51,9 @@ impl Line {
|
|||
|
||||
output.extend_from_slice(self.command.as_bytes());
|
||||
|
||||
for (i, arg) in self.args.iter().enumerate() {
|
||||
for (i, arg) in self.arguments.iter().enumerate() {
|
||||
output.push(b' ');
|
||||
if i == self.args.len() - 1 {
|
||||
if i == self.arguments.len() - 1 {
|
||||
output.push(b':');
|
||||
}
|
||||
output.extend_from_slice(arg);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
mod format;
|
||||
mod obj;
|
||||
mod tokenise;
|
||||
pub mod tokenise;
|
||||
mod util;
|
||||
|
||||
pub use self::obj::{Error, Line};
|
||||
pub use self::obj::Line;
|
||||
|
|
27
src/obj.rs
27
src/obj.rs
|
@ -1,19 +1,22 @@
|
|||
use std::collections::BTreeMap;
|
||||
|
||||
/// A struct representing all the constituent pieces of an RFC1459/IRCv3 protocol line.
|
||||
///
|
||||
/// `@tagkey=tagvalue :source COMMAND arg1 arg2 :arg3 with space`
|
||||
pub struct Line {
|
||||
// tags are promised to be utf8 encoded
|
||||
/// [Message tags] of an IRC line.
|
||||
/// [`None`] if no message tags were present.
|
||||
/// keys and values are [`String`] because they are promised to be utf8 encoded.
|
||||
///
|
||||
/// [Message tags]: https://ircv3.net/specs/extensions/message-tags.html
|
||||
pub tags: Option<BTreeMap<String, Option<String>>>,
|
||||
/// The `:source` of an IRC line, or [`None`] if source is not present.
|
||||
/// This is a [`Vec<u8>`] as it may be unpredictably encoded.
|
||||
pub source: Option<Vec<u8>>,
|
||||
// commands are promised to be ascii encoded
|
||||
/// The `COMMAND` of an IRC line (e.g. `PRIVMSG`.)
|
||||
/// This is a [`String`] because commands are promised to be ascii encoded.
|
||||
pub command: String,
|
||||
pub args: Vec<Vec<u8>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
Empty,
|
||||
MissingCommand,
|
||||
CommandDecode,
|
||||
TagKeyDecode,
|
||||
TagValueDecode,
|
||||
/// The arguments of an IRC line.
|
||||
/// These are [`Vec<u8>`]s as they may be unpredictably encoded.
|
||||
pub arguments: Vec<Vec<u8>>,
|
||||
}
|
||||
|
|
|
@ -1,10 +1,24 @@
|
|||
use std::collections::{BTreeMap, VecDeque};
|
||||
|
||||
use super::util::TakeWord as _;
|
||||
use super::{Error, Line};
|
||||
use super::Line;
|
||||
|
||||
const TAG_STOP: [&[u8]; 2] = [b"", b"="];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// An empty byte array was passed to the tokeniser
|
||||
Empty,
|
||||
/// A line is invalid if it has no `COMMAND` (e.g. `PRIVMSG`)
|
||||
MissingCommand,
|
||||
/// Commands must be ascii encoded
|
||||
CommandDecode,
|
||||
/// Message tag keys must be utf8 encoded
|
||||
TagKeyDecode,
|
||||
/// Message tag values must be utf8 encoded
|
||||
TagValueDecode,
|
||||
}
|
||||
|
||||
fn tag_decode(input: &str) -> String {
|
||||
let mut escaped = false;
|
||||
let mut output = String::with_capacity(input.len());
|
||||
|
@ -21,7 +35,7 @@ fn tag_decode(input: &str) -> String {
|
|||
};
|
||||
|
||||
output.push(replace);
|
||||
} else if char == 0x5c as char {
|
||||
} else if char == '\\' {
|
||||
// backslash
|
||||
escaped = true;
|
||||
} else {
|
||||
|
@ -33,6 +47,11 @@ fn tag_decode(input: &str) -> String {
|
|||
}
|
||||
|
||||
impl Line {
|
||||
#[allow(clippy::doc_markdown)]
|
||||
/// Attempt to tokenise a byte string by [RFC1459] and [IRCv3] protocol rules.
|
||||
///
|
||||
/// [RFC1459]: https://www.rfc-editor.org/rfc/rfc1459#section-2.3
|
||||
/// [IRCv3]: https://ircv3.net/specs/extensions/message-tags.html
|
||||
pub fn tokenise(mut line: &[u8]) -> Result<Self, Error> {
|
||||
let tags = if line.first() == Some(&b'@') {
|
||||
let mut tags = &line.take_word(b' ')[1..];
|
||||
|
@ -78,7 +97,16 @@ impl Line {
|
|||
tags,
|
||||
source,
|
||||
command: String::from_utf8(command).map_err(|_| Error::CommandDecode)?,
|
||||
args: args.into(),
|
||||
arguments: args.into(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for Line {
|
||||
type Error = Error;
|
||||
|
||||
/// Utility function for [`Line::tokenise()`]
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
Self::tokenise(value)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ fn basic() {
|
|||
])),
|
||||
source: Some(b"source".to_vec()),
|
||||
command: "COMMAND".to_string(),
|
||||
args: Vec::from([
|
||||
arguments: Vec::from([
|
||||
b"arg1".to_vec(),
|
||||
b"arg2".to_vec(),
|
||||
b"arg3 with space".to_vec(),
|
||||
|
|
|
@ -9,10 +9,10 @@ fn basic() {
|
|||
assert_eq!(line.source, Some(b"source".to_vec()));
|
||||
assert_eq!(&line.command, "COMMAND");
|
||||
|
||||
assert_eq!(line.args.len(), 3);
|
||||
assert_eq!(line.args[0], b"arg1");
|
||||
assert_eq!(line.args[1], b"arg2");
|
||||
assert_eq!(line.args[2], b"arg3 with space");
|
||||
assert_eq!(line.arguments.len(), 3);
|
||||
assert_eq!(line.arguments[0], b"arg1");
|
||||
assert_eq!(line.arguments[1], b"arg2");
|
||||
assert_eq!(line.arguments[2], b"arg3 with space");
|
||||
|
||||
let tags = line.tags.unwrap();
|
||||
assert_eq!(tags.len(), 3);
|
||||
|
|
Loading…
Reference in New Issue