first code commit
This commit is contained in:
parent
e6b659ef83
commit
b484a0834e
|
@ -0,0 +1,2 @@
|
||||||
|
/target
|
||||||
|
/Cargo.lock
|
|
@ -0,0 +1,8 @@
|
||||||
|
[package]
|
||||||
|
name = "irctokens"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
|
@ -0,0 +1,88 @@
|
||||||
|
use std::collections::{HashMap, VecDeque};
|
||||||
|
|
||||||
|
pub struct Line {
|
||||||
|
// tags are promised to be utf8 encoded
|
||||||
|
pub tags: Option<HashMap<String, Option<String>>>,
|
||||||
|
pub source: Option<Vec<u8>>,
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
trait TakeWord<'a> {
|
||||||
|
fn take_word(&mut self, sep: u8) -> &'a [u8];
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> TakeWord<'a> for &'a [u8] {
|
||||||
|
fn take_word(&mut self, sep: u8) -> &'a [u8] {
|
||||||
|
if let Some(i) = self.iter().position(|c| c == &sep) {
|
||||||
|
let word = &self[..i];
|
||||||
|
*self = &self[i + 1..];
|
||||||
|
word
|
||||||
|
} else {
|
||||||
|
let word = &self[..];
|
||||||
|
*self = &self[self.len()..];
|
||||||
|
word
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn tokenise(mut line: &[u8]) -> Result<Line, Error> {
|
||||||
|
let tags = match line.first() {
|
||||||
|
Some(b'@') => {
|
||||||
|
let mut tags = &line.take_word(b' ')[1..];
|
||||||
|
let mut tags_map = HashMap::new();
|
||||||
|
|
||||||
|
while !tags.is_empty() {
|
||||||
|
let mut keyvalue = tags.take_word(b';');
|
||||||
|
let tag = keyvalue.take_word(b'=');
|
||||||
|
tags_map.insert(
|
||||||
|
String::from_utf8(tag.to_vec()).map_err(|_| Error::TagKeyDecode)?,
|
||||||
|
match keyvalue {
|
||||||
|
b"" | b"=" => None,
|
||||||
|
_ => Some(
|
||||||
|
String::from_utf8(keyvalue.to_vec())
|
||||||
|
.map_err(|_| Error::TagValueDecode)?,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(tags_map)
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let source = match line.first() {
|
||||||
|
Some(b':') => Some(line.take_word(b' ')[1..].to_vec()),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut args = VecDeque::<Vec<u8>>::new();
|
||||||
|
while !line.is_empty() {
|
||||||
|
if line[0] == b':' {
|
||||||
|
args.push_back(line[1..].to_vec());
|
||||||
|
line = &[];
|
||||||
|
} else {
|
||||||
|
args.push_back(line.take_word(b' ').to_vec());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let command = args.pop_front().ok_or(Error::MissingCommand)?;
|
||||||
|
|
||||||
|
Ok(Line {
|
||||||
|
tags,
|
||||||
|
source,
|
||||||
|
command: String::from_utf8(command).map_err(|_| Error::CommandDecode)?,
|
||||||
|
args: args.into(),
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
use irctokens::tokenise;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic() {
|
||||||
|
let line =
|
||||||
|
tokenise(b"@tag1=tag1value;tag2=;tag3 :source COMMAND arg1 arg2 :arg3 with space").unwrap();
|
||||||
|
|
||||||
|
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");
|
||||||
|
|
||||||
|
let tags = line.tags.unwrap();
|
||||||
|
assert_eq!(tags.len(), 3);
|
||||||
|
assert_eq!(tags["tag1"], Some("tag1value".to_string()));
|
||||||
|
assert_eq!(tags["tag2"], None);
|
||||||
|
assert_eq!(tags["tag3"], None);
|
||||||
|
}
|
Loading…
Reference in New Issue