Create initial implementation
This commit is contained in:
parent
57780855b4
commit
d7e6b99c30
|
@ -6,3 +6,6 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
|
||||
[dev-dependencies]
|
||||
collection_macros = "0.2.0"
|
||||
|
|
146
src/lib.rs
Normal file
146
src/lib.rs
Normal file
|
@ -0,0 +1,146 @@
|
|||
// BSD 3-Clause License
|
||||
//
|
||||
// Copyright (c) 2022-present, Ethan Henderson
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
mod line;
|
||||
|
||||
pub use line::Line;
|
||||
use std::collections::HashMap;
|
||||
|
||||
type ParseResult<T> = Result<T, ParseError>;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParseError {
|
||||
pub details: String,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub fn new(details: &str) -> Self {
|
||||
Self {
|
||||
details: details.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.details)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_index(text: &str, char: char, start: usize) -> Option<usize> {
|
||||
for (k, _) in text.match_indices(char) {
|
||||
if k > start {
|
||||
return Some(k);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn parse(line: &str) -> ParseResult<Line> {
|
||||
if line.is_empty() {
|
||||
return Err(ParseError::new("line length cannot be 0"));
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
let mut tags: HashMap<String, String> = HashMap::new();
|
||||
let mut source: Option<String> = None;
|
||||
|
||||
// Parse tags component.
|
||||
if line.starts_with('@') {
|
||||
idx = line.find(' ').unwrap();
|
||||
|
||||
for part in Some(&line[1..idx]).unwrap().split(';') {
|
||||
let kv: Vec<&str> = part.split('=').collect();
|
||||
tags.insert(kv[0].to_string(), kv[1].to_string());
|
||||
}
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
|
||||
// Parse source component.
|
||||
if line.chars().nth(idx).unwrap() == ':' {
|
||||
let end_idx = find_index(line, ' ', idx).unwrap();
|
||||
source = Some(line[idx..end_idx].to_string());
|
||||
idx = end_idx + 1;
|
||||
}
|
||||
|
||||
// Parse command component.
|
||||
let end_idx = find_index(line, ' ', idx).unwrap();
|
||||
let command = &line[idx..end_idx];
|
||||
idx = end_idx + 1;
|
||||
|
||||
let c_idx = match find_index(line, ':', idx) {
|
||||
Some(x) => x - 1,
|
||||
None => line.len(),
|
||||
};
|
||||
|
||||
// Parse params component.
|
||||
let mut params: Vec<String> = line[idx..c_idx].split(' ').map(|x| x.to_string()).collect();
|
||||
if c_idx != line.len() {
|
||||
params.push(line[c_idx + 2..].to_string());
|
||||
}
|
||||
|
||||
Ok(Line::new(tags, source, command, params))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_lib {
|
||||
use super::parse;
|
||||
use collection_macros::hashmap;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_partial() {
|
||||
let line = parse("PRIVMSG #rickastley :Never gonna give you up!").unwrap();
|
||||
assert_eq!(line.tags, HashMap::new());
|
||||
assert_eq!(line.source, None);
|
||||
assert_eq!(line.command, "PRIVMSG");
|
||||
assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_full() {
|
||||
let line = parse("@id=123;name=rick :nick!user@host.tmi.twitch.tv PRIVMSG #rickastley :Never gonna give you up!").unwrap();
|
||||
assert_eq!(
|
||||
line.tags,
|
||||
hashmap! {
|
||||
String::from("id") => String::from("123"),
|
||||
String::from("name") => String::from("rick"),
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
line.source,
|
||||
Some(String::from(":nick!user@host.tmi.twitch.tv"))
|
||||
);
|
||||
assert_eq!(line.command, "PRIVMSG");
|
||||
assert_eq!(line.params, vec!["#rickastley", "Never gonna give you up!"]);
|
||||
}
|
||||
}
|
55
src/line.rs
Normal file
55
src/line.rs
Normal file
|
@ -0,0 +1,55 @@
|
|||
// BSD 3-Clause License
|
||||
//
|
||||
// Copyright (c) 2022-present, Ethan Henderson
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
//
|
||||
// 1. Redistributions of source code must retain the above copyright notice, this
|
||||
// list of conditions and the following disclaimer.
|
||||
//
|
||||
// 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
//
|
||||
// 3. Neither the name of the copyright holder nor the names of its
|
||||
// contributors may be used to endorse or promote products derived from
|
||||
// this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct Line {
|
||||
pub tags: HashMap<String, String>,
|
||||
pub source: Option<String>,
|
||||
pub command: String,
|
||||
pub params: Vec<String>,
|
||||
}
|
||||
|
||||
impl Line {
|
||||
pub fn new(
|
||||
tags: HashMap<String, String>,
|
||||
source: Option<String>,
|
||||
command: &str,
|
||||
params: Vec<String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
tags,
|
||||
source,
|
||||
command: command.to_string(),
|
||||
params,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue