148 lines
No EOL
4 KiB
Text
148 lines
No EOL
4 KiB
Text
use std::fmt;
|
||
use std::str::{from_utf8, from_utf8_unchecked};
|
||
|
||
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
|
||
pub enum IrcCommand<'a> {
|
||
Stringy(Stringy<'a>),
|
||
Numeric(Numeric<'a>)
|
||
}
|
||
|
||
// TODO verify that the bytes don't form a numeric
|
||
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
|
||
pub struct Stringy<'a>(&'a [u8]);
|
||
|
||
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
|
||
pub struct Numeric<'a>(u16, &'a [u8; 3]);
|
||
|
||
pub trait IrcCommandLine {
|
||
fn get_command<'a>(&'a self) -> IrcCommand<'a>;
|
||
}
|
||
|
||
impl<'a> fmt::Display for Stringy<'a> {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
let mut i = 0;
|
||
let l = self.0.len();
|
||
let v = self.0;
|
||
while i < l {
|
||
let st = from_utf8(&v[i..]);
|
||
if let Ok(s) = st {
|
||
write!(f, "{}", s)?;
|
||
break;
|
||
} else {
|
||
let err = st.err().unwrap();
|
||
write!(f, "{}", unsafe { from_utf8_unchecked(&v[i..][..err.valid_up_to()])})?;
|
||
write!(f, "\u{FFFD}")?;
|
||
match err.error_len() {
|
||
None => i = l,
|
||
Some(len) => i = i + err.valid_up_to() + len
|
||
}
|
||
}
|
||
}
|
||
Ok(())
|
||
}
|
||
}
|
||
|
||
impl<'a> fmt::Display for Numeric<'a> {
|
||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||
write!(f, "{}", self.0)
|
||
}
|
||
}
|
||
|
||
impl<'a> Stringy<'a> {
|
||
}
|
||
|
||
impl<'a> Numeric<'a> {
|
||
/// Creates a new numeric from the given bytes. Returns None if the given bytes aren't a valid
|
||
/// numeric.
|
||
///
|
||
/// # Examples
|
||
/// ```
|
||
/// use uirc::command::Numeric;
|
||
///
|
||
/// match Numeric::new(b"005") {
|
||
/// Some(numeric) => println!("Numeric: {}", numeric),
|
||
/// None => println!("Not a numeric!"),
|
||
/// }
|
||
/// ```
|
||
#[inline]
|
||
pub fn new(cmd: &'a [u8; 3]) -> Option<Numeric<'a>> {
|
||
match (cmd[0], cmd[1], cmd[2]) {
|
||
(b'0'...b'9', b'0'...b'9', b'0'...b'9') => Some(Numeric::new_unchecked(cmd)),
|
||
_ => None
|
||
}
|
||
}
|
||
|
||
/// Creates a new numeric from the given bytes. Returns None if the given bytes aren't a valid
|
||
/// numeric.
|
||
///
|
||
/// # Examples
|
||
/// ```
|
||
/// use uirc::command::Numeric;
|
||
///
|
||
/// match Numeric::new_from_slice(b"005") {
|
||
/// Some(numeric) => println!("Numeric: {}", numeric),
|
||
/// None => println!("Not a numeric!"),
|
||
/// }
|
||
/// ```
|
||
#[inline]
|
||
pub fn new_from_slice(cmd: &'a [u8]) -> Option<Numeric<'a>> {
|
||
if cmd.len() == 3 {
|
||
// TODO switch to TryFrom/TryInto once those are stable. (rust-lang/rust#33417)
|
||
Self::new(array_ref![cmd, 0, 3])
|
||
} else {
|
||
None
|
||
}
|
||
}
|
||
|
||
// not unsafe, but may produce unexpected results
|
||
// keep this private (for now)
|
||
#[inline]
|
||
fn new_unchecked(cmd: &'a [u8; 3]) -> Numeric<'a> {
|
||
Numeric(cmd.iter().map(|x| (x-b'0') as u16).fold(0, |x,y| x*10+y), cmd)
|
||
}
|
||
|
||
/// Returns the numeric as a number, e.g. for processing in code.
|
||
///
|
||
/// # Examples
|
||
/// ```
|
||
/// use uirc::command::Numeric;
|
||
///
|
||
/// let numeric = Numeric::new(b"005").unwrap();
|
||
/// match numeric.get_numeric() {
|
||
/// 005 => println!("got an ISUPPORT!"),
|
||
/// _ => println!("got something else!"),
|
||
/// }
|
||
/// ```
|
||
#[inline]
|
||
pub fn get_numeric(&self) -> u16 {
|
||
self.0
|
||
}
|
||
|
||
/// Returns the numeric as bytes, e.g. for writing to a stream.
|
||
///
|
||
/// # Examples
|
||
/// ```rust
|
||
/// use uirc::command::Numeric;
|
||
/// use std::io::Write;
|
||
///
|
||
/// let mut client = Vec::new();
|
||
/// let numeric = Numeric::new(b"005").unwrap();
|
||
/// let bytes = numeric.get_bytes();
|
||
/// client.write_all(bytes).unwrap();
|
||
/// ```
|
||
#[inline]
|
||
pub fn get_bytes(&self) -> &'a [u8; 3] {
|
||
self.1
|
||
}
|
||
}
|
||
|
||
impl<'a> IrcCommand<'a> {
|
||
pub fn new(cmd: &'a [u8]) -> IrcCommand<'a> {
|
||
if let Some(numeric) = Numeric::new_from_slice(&cmd) {
|
||
IrcCommand::Numeric(numeric)
|
||
} else {
|
||
IrcCommand::Stringy(Stringy(cmd))
|
||
}
|
||
}
|
||
}
|
||
|