Browse Source

Add random stuff

master
SoniEx2 1 year ago
commit
767331aa3d
6 changed files with 240 additions and 0 deletions
  1. +5
    -0
      .gitignore
  2. +8
    -0
      Cargo.toml
  3. +175
    -0
      src/command.rs
  4. +4
    -0
      src/commands.rs
  5. +10
    -0
      src/lib.rs
  6. +38
    -0
      src/simple.rs

+ 5
- 0
.gitignore View File

@@ -0,0 +1,5 @@

/target/
**/*.rs.bk
Cargo.lock
/recovered_revisions/

+ 8
- 0
Cargo.toml View File

@@ -0,0 +1,8 @@
[package]
name = "uirc"
version = "0.1.0"
authors = ["SoniEx2 <endermoneymod@gmail.com>"]

[dependencies]
arrayref = "0.3.4"
matches = "0.1.6"

+ 175
- 0
src/command.rs View File

@@ -0,0 +1,175 @@
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>;

/// Experimental. Do not use.
fn get_argument<'a>(&'a self, arg: usize) -> &'a [u8];
/// Experimental. Do not use.
fn get_argument_count(&self) -> usize;
/// Experimental. Do not use.
fn get_source<'a>(&'a self) -> &'a [u8];
}

impl<'a> Stringy<'a> {
#[inline]
pub fn new(cmd: &'a [u8]) -> Option<Stringy<'a>> {
if cmd.len() == 3 && matches!((cmd[0], cmd[1], cmd[2]), (b'0'...b'9', b'0'...b'9', b'0'...b'9')) {
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::new(cmd).unwrap())
}
}
}

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)
}
}

+ 4
- 0
src/commands.rs View File

@@ -0,0 +1,4 @@
// What was I gonna put here?
//
// I didn't manage to recover this file from the depths of /dev/sdb1, so I don't know what it was
// for.

+ 10
- 0
src/lib.rs View File

@@ -0,0 +1,10 @@
#[macro_use]
extern crate arrayref;
#[macro_use]
extern crate matches;

pub mod command;

pub mod commands;

pub mod simple;

+ 38
- 0
src/simple.rs View File

@@ -0,0 +1,38 @@
use command::{IrcCommand, IrcCommandLine, Numeric, Stringy};

use std::io;
use std::io::prelude::*;

use std::ops::Range;

pub struct SimpleIrcCommand {
command: Vec<u8>,
arguments: (Vec<Range<usize>>, Vec<u8>),
source: Vec<u8>,
}

impl IrcCommandLine for SimpleIrcCommand {
fn get_command<'a>(&'a self) -> IrcCommand<'a> {
IrcCommand::new(&self.command)
}

fn get_argument<'a>(&'a self, arg: usize) -> &'a [u8] {
&self.arguments.1[self.arguments.0[arg].clone()] // awful API there, rust.
}

fn get_argument_count(&self) -> usize {
self.arguments.0.len()
}

fn get_source<'a>(&'a self) -> &'a [u8] {
&self.source
}
}

pub struct NoIrcConnection;

impl NoIrcConnection {
pub fn read_command<R: Read>(reader: R) -> io::Result<SimpleIrcCommand> {
unimplemented!()
}
}

Loading…
Cancel
Save