thingy/recovered_revisions/src/command_rs/file.381320769954

169 lines
4.5 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use std::fmt;
use std::str::{from_utf8, from_utf8_unchecked};
/// An IRC command
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
pub enum IrcCommand<'a> {
Stringy(Stringy<'a>),
Numeric(Numeric<'a>)
}
/// A stringy IRC command
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
pub struct Stringy<'a>(&'a [u8]);
/// A numeric IRC command.
#[derive(Copy,Clone,Debug,Eq,PartialEq,Ord,PartialOrd)]
pub struct Numeric<'a>(u16, &'a [u8; 3]);
/// A full IRC command line.
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> {
#[inline]
pub fn new(cmd: &'a [u8]) -> Option<Stringy<'a>> {
if cmd.len() == 3 && matches!((b"0"...b"9", b"0"...b"9", b"0"...b"9"), (cmd[0], cmd[1], cmd[2])) {
None
} else {
Some(Self::new_unchecked(cmd))
}
}
#[inline]
fn new_unchecked(cmd: &'a [u8]) -> Stringy<'a> {
Stringy(cmd)
}
#[inline]
pub fn get_bytes(&self) -> &'a [u8] {
self.0
}
}
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))
}
}
}