<commit_before>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::collections::HashMap;
use std::io::{BufferedReader, RefReader, IoResult, IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    match c.spawn() {
        Ok(mut p) => {
            p.set_timeout(timeout);
            Ok(p)
        },
        Err(e) => Err(e)
    }
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
pub fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    // would like to use `and_then` here, but we get problems with capturing
    // on_success, seemingly because the compiler cannot enforce that
    // the closure passed to `and_then` only calls it once
    match spawn_with_timeout(c, timeout) {
        Ok(mut p) => {
            match p.wait() {
                Ok(res) => res.if_ok(on_success),
                Err(e) => Err(e)
            }
        },
        Err(e) => Err(e)
    }
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

pub trait EnvSetup {
    fn env_timeout(&self) -> Option<u64>;

    fn env_command(&self) -> Command;

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<()> {
        run_command(&self.env_command(), self.env_timeout(), ())
    }
}

pub trait BuildSetup {
    fn build_timeout(&self) -> Option<u64>;

    fn build_command(&self) -> Command;

    fn do_build(&self) -> IoResult<()> {
        run_command(&self.build_command(), self.build_timeout(), ())
    }
}

#[deriving(Show, PartialEq)]
pub enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command, timeout: Option<u64>) -> IoResult<ProcessReader> {
        spawn_with_timeout(cmd, timeout).map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            Reader::by_ref(
                self.p.stdout.as_mut().unwrap()))
    }
}

fn parse_test_result(line: &str) -> IoResult<TestResult> {
    match line {
        "PASS" => Ok(Pass),
        "FAIL" => Ok(Fail),
        _ => Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test result",
                detail: Some(line.to_string())
            })
    }
}

fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
    let results: Vec<&str> = line.split_str(":").collect();
    if results.len() == 2 {
        parse_test_result(results[1]).map(|res| {
            (results[0].to_string(), res)
        })
    } else {
        Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test string",
                detail: Some(line.to_string())
            })
    }
}

pub trait Tester {
    fn test_timeout(&self) -> Option<u64>;

    fn test_command(&self) -> Command;

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        // TODO: without do syntax this becomes nightmarish in
        // functional style, and I had issues with closures capturing
        // too much
        match ProcessReader::new(&self.test_command(), self.test_timeout()) {
            Ok(mut reader) => {
                let mut map = HashMap::new();
                for op_line in reader.output_reader().lines() {
                    match op_line {
                        Ok(line) => {
                            match parse_line(line.as_slice()) {
                                Ok((k, v)) => {
                                    map.insert(k, v);
                                }
                                Err(s) => { return Err(s); }
                            }
                        }
                        Err(s) => { return Err(s); }
                    }
                }
                Ok(map)

            }
            Err(s) => { return Err(s); }
        }
    }
}

#[cfg(test)]
mod tests {
    use std::io::process::Command;
    use super::{run_command, ProcessReader, parse_test_result,
                Pass, Fail, parse_line};

    #[test]
    fn echo_ok() {
        assert!(run_command(&*Command::new("echo").arg("foobar"), None, ()).is_ok());
    }

    #[test]
    fn false_ok() {
        assert!(run_command(&Command::new("false"), None, ()).is_err());
    }

    fn output_from_command(cmd: &Command) -> Vec<String> {
        let pr = ProcessReader::new(cmd, None);
        assert!(pr.is_ok());
        pr.unwrap().output_reader().lines()
            .map(|line| {
                assert!(line.is_ok());
                line.unwrap().as_slice().trim().to_string()
            }).collect()
    }

    #[test]
    fn output_single_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foobar"));
        assert_eq!(lines.len(), 1);
        assert_eq!(lines[0].as_slice(), "foobar");
    }

    #[test]
    fn output_multi_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foo; echo bar"));
        assert_eq!(lines.len(), 2);
        assert_eq!(lines[0].as_slice(), "foo");
        assert_eq!(lines[1].as_slice(), "bar");
    }

    #[test]
    fn parse_test_pass() {
        let res = parse_test_result("PASS");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Pass);
    }

    #[test]
    fn parse_test_fail() {
        let res = parse_test_result("FAIL");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Fail);
    }

    #[test]
    fn parse_test_bad_test() {
        let res = parse_test_result("foobar");
        assert!(res.is_err());
    }

    #[test]
    fn parse_valid_test_line() {
        let res = parse_line("my test:PASS");
        assert!(res.is_ok());
        let (key, result) = res.unwrap();
        assert_eq!(key.as_slice(), "my test");
        assert_eq!(result, Pass);
    }

    #[test]
    fn parse_invalid_test_line() {
        assert!(parse_line("this:is:PASS").is_err());
    }
}
<commit_msg>Reorganized some unit tests.<commit_after>// Portion that builds and runs tests.  Assumes that this can be
// done via a call to `make build` and `make test`.  Compiler failure
// is assumed to be communicated by return value.  Tests are assumed
// to have the following output format:
//
// some test name: <PASS|FAIL>
//
// ...where each test result is on its own line.  If multiple tests
// have the same name, then only the last test is recorded.

use std::collections::HashMap;
use std::io::{BufferedReader, RefReader, IoResult, IoError, OtherIoError};
use std::io::pipe::PipeStream;
use std::io::process::{Command, Process, ExitStatus, ExitSignal,
                       ProcessExit};

fn spawn_with_timeout(c: &Command, timeout: Option<u64>) -> IoResult<Process> {
    match c.spawn() {
        Ok(mut p) => {
            p.set_timeout(timeout);
            Ok(p)
        },
        Err(e) => Err(e)
    }
}

/// Runs the given command with the given timeout, ignoring the output.
/// If it returns non-zero, then it's a failure, as with a signal.
/// Takes what it should return on success.
pub fn run_command<A>(c: &Command, timeout: Option<u64>, on_success: A) -> IoResult<A> {
    // would like to use `and_then` here, but we get problems with capturing
    // on_success, seemingly because the compiler cannot enforce that
    // the closure passed to `and_then` only calls it once
    match spawn_with_timeout(c, timeout) {
        Ok(mut p) => {
            match p.wait() {
                Ok(res) => res.if_ok(on_success),
                Err(e) => Err(e)
            }
        },
        Err(e) => Err(e)
    }
}

trait ErrorSimplifier {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A>;
}

impl ErrorSimplifier for ProcessExit {
    fn if_ok<A>(&self, ret_this: A) -> IoResult<A> {
        match *self {
            ExitStatus(0) => Ok(ret_this),
            ExitStatus(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Non-zero exit code",
                    detail: Some(x.to_string())
                }),
            ExitSignal(x) => Err(
                IoError {
                    kind: OtherIoError,
                    desc: "Exit signal",
                    detail: Some(x.to_string())
                })
        }
    }
}

pub trait EnvSetup {
    fn env_timeout(&self) -> Option<u64>;

    fn env_command(&self) -> Command;

    /// Gets everything in order for testing to be performed.
    /// After calling this, it is assumed that we are ready
    /// to call make
    fn setup_env(&self) -> IoResult<()> {
        run_command(&self.env_command(), self.env_timeout(), ())
    }
}

pub trait BuildSetup {
    fn build_timeout(&self) -> Option<u64>;

    fn build_command(&self) -> Command;

    fn do_build(&self) -> IoResult<()> {
        run_command(&self.build_command(), self.build_timeout(), ())
    }
}

#[deriving(Show, PartialEq)]
pub enum TestResult {
    Pass,
    Fail
}

struct ProcessReader {
    p: Process
}

impl ProcessReader {
    fn new(cmd: &Command, timeout: Option<u64>) -> IoResult<ProcessReader> {
        spawn_with_timeout(cmd, timeout).map(|p| ProcessReader { p: p })
    }

    fn output_reader<'a>(&'a mut self) -> BufferedReader<RefReader<'a, PipeStream>> {
        // unwrap should be ok - the documentation says `Some` is the default,
        // and we are not messing with any of the defaults
        BufferedReader::new(
            Reader::by_ref(
                self.p.stdout.as_mut().unwrap()))
    }
}

fn parse_test_result(line: &str) -> IoResult<TestResult> {
    match line {
        "PASS" => Ok(Pass),
        "FAIL" => Ok(Fail),
        _ => Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test result",
                detail: Some(line.to_string())
            })
    }
}

fn parse_line(line: &str) -> IoResult<(String, TestResult)> {
    let results: Vec<&str> = line.split_str(":").collect();
    if results.len() == 2 {
        parse_test_result(results[1]).map(|res| {
            (results[0].to_string(), res)
        })
    } else {
        Err(
            IoError {
                kind: OtherIoError,
                desc: "Malformed test string",
                detail: Some(line.to_string())
            })
    }
}

pub trait Tester {
    fn test_timeout(&self) -> Option<u64>;

    fn test_command(&self) -> Command;

    fn do_testing(&self) -> IoResult<HashMap<String, TestResult>> {
        // TODO: without do syntax this becomes nightmarish in
        // functional style, and I had issues with closures capturing
        // too much
        match ProcessReader::new(&self.test_command(), self.test_timeout()) {
            Ok(mut reader) => {
                let mut map = HashMap::new();
                for op_line in reader.output_reader().lines() {
                    match op_line {
                        Ok(line) => {
                            match parse_line(line.as_slice()) {
                                Ok((k, v)) => {
                                    map.insert(k, v);
                                }
                                Err(s) => { return Err(s); }
                            }
                        }
                        Err(s) => { return Err(s); }
                    }
                }
                Ok(map)

            }
            Err(s) => { return Err(s); }
        }
    }
}

#[cfg(test)]
mod process_tests {
    use std::io::process::Command;
    use super::{run_command, ProcessReader};

    #[test]
    fn echo_ok() {
        assert!(run_command(&*Command::new("echo").arg("foobar"), None, ()).is_ok());
    }

    #[test]
    fn false_ok() {
        assert!(run_command(&Command::new("false"), None, ()).is_err());
    }

    fn output_from_command(cmd: &Command) -> Vec<String> {
        let pr = ProcessReader::new(cmd, None);
        assert!(pr.is_ok());
        pr.unwrap().output_reader().lines()
            .map(|line| {
                assert!(line.is_ok());
                line.unwrap().as_slice().trim().to_string()
            }).collect()
    }

    #[test]
    fn output_single_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foobar"));
        assert_eq!(lines.len(), 1);
        assert_eq!(lines[0].as_slice(), "foobar");
    }

    #[test]
    fn output_multi_line_read() {
        let lines = output_from_command(
            &*Command::new("sh").arg("-c").arg("echo foo; echo bar"));
        assert_eq!(lines.len(), 2);
        assert_eq!(lines[0].as_slice(), "foo");
        assert_eq!(lines[1].as_slice(), "bar");
    }
}

#[cfg(test)]
mod parse_tests {
    use super::{parse_test_result, Pass, Fail, parse_line};

    #[test]
    fn parse_test_pass() {
        let res = parse_test_result("PASS");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Pass);
    }

    #[test]
    fn parse_test_fail() {
        let res = parse_test_result("FAIL");
        assert!(res.is_ok());
        assert_eq!(res.unwrap(), Fail);
    }

    #[test]
    fn parse_test_bad_test() {
        let res = parse_test_result("foobar");
        assert!(res.is_err());
    }

    #[test]
    fn parse_valid_test_line() {
        let res = parse_line("my test:PASS");
        assert!(res.is_ok());
        let (key, result) = res.unwrap();
        assert_eq!(key.as_slice(), "my test");
        assert_eq!(result, Pass);
    }

    #[test]
    fn parse_invalid_test_line() {
        assert!(parse_line("this:is:PASS").is_err());
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use chrono::prelude::*;
use regex::Regex;

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

pub fn parse_command(command_line : String) -> Command {
    debug!("parse incoming text: {}", command_line);
    let reg_main    = Regex::new(r"(?P<spec>[\d\w]*)(?P<divider> )(?P<main_text>.*)").unwrap();

    let reg_day     = r"(?P<days>[\d]*)[D|d]";
    let reg_hour    = r"(?P<hours>[\d]*)[H|h]";
    let reg_min     = r"(?P<minuts>[\d]*)[M|m]";
    let reg_sec     = r"(?P<seconds>[\d]*)[S|s]";

    let caps = reg_main.captures(&command_line);
    if caps.is_none() {
        warn!("parse_command: incoming text doesn't match main regex");
        return Command::BadCommand;
    }
    let caps = caps.unwrap();
    let spec = caps.name("spec").unwrap().as_str();
    let text = caps.name("main_text").unwrap().as_str();

    let days    = get_first_regex_group_as_u32(reg_day,  spec);
    let hours   = get_first_regex_group_as_u32(reg_hour, spec);
    let minutes = get_first_regex_group_as_u32(reg_min,  spec);
    let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

    if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
        warn!("parse_command: incoming text doesn't match main regex");
        return Command::BadCommand;
    }

    // TODO: should we a better way to do this
    let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
                                    + (hours as i64) * (60*60)
                                    + (minutes as i64) * 60
                                    + (seconds as i64)
                                    );

    let event_time = Utc::now() + dt;

    Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : event_time }
        )
}

fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
    let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
    let number = match reg.captures(text){
        None => "0",
        Some(d) => d.get(1)
            .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
            .as_str(),
    };
    number.parse().unwrap_or(0)
}
<commit_msg>Russian letter in command line<commit_after>extern crate chrono;

use chrono::prelude::*;
use regex::Regex;

#[derive(Debug)]
pub enum Command {
    BadCommand,
    OneTimeEvent(OneTimeEventImpl),
}

#[derive(Debug)]
pub struct OneTimeEventImpl{
    pub event_time : DateTime<Utc>,
    pub event_text : String,
}

pub fn parse_command(command_line : String) -> Command {
    debug!("parse incoming text: {}", command_line);
    let reg_main    = Regex::new(r"(?P<spec>[\d\w]*)(?P<divider> )(?P<main_text>.*)").unwrap();

    let reg_day     = r"(?P<days>[\d]*)[D|d|Д|д]";
    let reg_hour    = r"(?P<hours>[\d]*)[H|h|Ч|ч]";
    let reg_min     = r"(?P<minuts>[\d]*)[M|m|М|м]";
    let reg_sec     = r"(?P<seconds>[\d]*)[S|s|С|с]";

    let caps = reg_main.captures(&command_line);
    if caps.is_none() {
        warn!("parse_command: incoming text doesn't match main regex");
        return Command::BadCommand;
    }
    let caps = caps.unwrap();
    let spec = caps.name("spec").unwrap().as_str();
    let text = caps.name("main_text").unwrap().as_str();

    let days    = get_first_regex_group_as_u32(reg_day,  spec);
    let hours   = get_first_regex_group_as_u32(reg_hour, spec);
    let minutes = get_first_regex_group_as_u32(reg_min,  spec);
    let seconds = get_first_regex_group_as_u32(reg_sec,  spec);

    if days == 0 && hours == 0 && minutes == 0 && seconds == 0 {
        warn!("parse_command: incoming text doesn't match main regex");
        return Command::BadCommand;
    }

    // TODO: should we a better way to do this
    let dt = chrono::Duration::seconds((days as i64) * (60*60*24)
                                    + (hours as i64) * (60*60)
                                    + (minutes as i64) * 60
                                    + (seconds as i64)
                                    );

    let event_time = Utc::now() + dt;

    Command::OneTimeEvent(OneTimeEventImpl
            { event_text : String::from(text)
            , event_time : event_time }
        )
}

fn get_first_regex_group_as_u32(reg : &str, text : &str) -> u32{
    let reg = Regex::new(reg).expect("get_first_regex_group_as_u32: wrong regex string");
    let number = match reg.captures(text){
        None => "0",
        Some(d) => d.get(1)
            .expect("get_first_regex_group_as_u32: expect regex string with at least 1 group")
            .as_str(),
    };
    number.parse().unwrap_or(0)
}
<|endoftext|>


<commit_before>use libc;

use crypto::digest::Digest;
use crypto::md5::Md5;

use byteorder::{ByteOrder, BigEndian, LittleEndian};
use rand::{Rng, OsRng};
use rustc_serialize::hex::{self, FromHex};
use time;

use std::{fmt, io, error, result};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

const TIMESTAMP_SIZE: usize = 4;
const MACHINE_ID_SIZE: usize = 3;
const PROCESS_ID_SIZE: usize = 2;
const COUNTER_SIZE: usize = 3;

const TIMESTAMP_OFFSET: usize = 0;
const MACHINE_ID_OFFSET: usize = TIMESTAMP_OFFSET + TIMESTAMP_SIZE;
const PROCESS_ID_OFFSET: usize = MACHINE_ID_OFFSET + MACHINE_ID_SIZE;
const COUNTER_OFFSET: usize = PROCESS_ID_OFFSET + PROCESS_ID_SIZE;

const MAX_U24: usize = 0xFFFFFF;

static OID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
static mut MACHINE_BYTES: [u8; 3] = [0; 3];

extern {
    fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}

/// Errors that can occur during OID construction and generation.
#[derive(Debug)]
pub enum Error {
    ArgumentError(String),
    FromHexError(hex::FromHexError),
    IoError(io::Error),
    HostnameError,
}

impl From<hex::FromHexError> for Error {
    fn from(err: hex::FromHexError) -> Error {
        Error::FromHexError(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

/// Alias for Result<T, oid::Error>.
pub type Result<T> = result::Result<T, Error>;

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::ArgumentError(ref inner) => inner.fmt(fmt),
            &Error::FromHexError(ref inner) => inner.fmt(fmt),
            &Error::IoError(ref inner) => inner.fmt(fmt),
            &Error::HostnameError => write!(fmt, "Failed to retrieve hostname for OID generation."),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::ArgumentError(ref inner) => &inner,
            &Error::FromHexError(ref inner) => inner.description(),
            &Error::IoError(ref inner) => inner.description(),
            &Error::HostnameError => "Failed to retrieve hostname for OID generation.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error::ArgumentError(_) => None,
            &Error::FromHexError(ref inner) => Some(inner),
            &Error::IoError(ref inner) => Some(inner),
            &Error::HostnameError => None,
        }
    }
}

/// A wrapper around raw 12-byte ObjectId representations.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct ObjectId {
    id: [u8; 12],
}

impl ObjectId {
    /// Generates a new ObjectID, represented in bytes.
    /// See the [docs](http://docs.mongodb.org/manual/reference/object-id/)
    /// for more information.
    pub fn new() -> Result<ObjectId> {
        let timestamp = ObjectId::gen_timestamp();
        let machine_id = try!(ObjectId::gen_machine_id());
        let process_id = ObjectId::gen_process_id();
        let counter = try!(ObjectId::gen_count());

        let mut buf: [u8; 12] = [0; 12];
        for i in 0..TIMESTAMP_SIZE { buf[TIMESTAMP_OFFSET + i] = timestamp[i]; }
        for i in 0..MACHINE_ID_SIZE { buf[MACHINE_ID_OFFSET + i] = machine_id[i]; }
        for i in 0..PROCESS_ID_SIZE { buf[PROCESS_ID_OFFSET + i] = process_id[i]; }
        for i in 0..COUNTER_SIZE { buf[COUNTER_OFFSET +i] = counter[i]; }

        Ok(ObjectId::with_bytes(buf))
    }

    /// Constructs a new ObjectId wrapper around the raw byte representation.
    pub fn with_bytes(bytes: [u8; 12]) -> ObjectId {
        ObjectId {
            id: bytes,
        }
    }

    /// Creates an ObjectID using a 12-byte (24-char) hexadecimal string.
    pub fn with_string(s: &str) -> Result<ObjectId> {
        let bytes = try!(s.from_hex());
        if bytes.len() != 12 {
            Err(Error::ArgumentError("Provided string must be a 12-byte hexadecimal string.".to_owned()))
        } else {
            let mut byte_array: [u8; 12] = [0; 12];
            for i in 0..12 {
                byte_array[i] = bytes[i];
            }
            Ok(ObjectId::with_bytes(byte_array))
        }
    }

    /// Creates a dummy ObjectId with a specific generation time.
    /// This method should only be used to do range queries on a field
    /// containing ObjectId instances.
    pub fn with_timestamp(time: u32) -> ObjectId {
        let mut buf: [u8; 12] = [0; 12];
        BigEndian::write_u32(&mut buf, time);
        ObjectId::with_bytes(buf)
    }

    /// Returns the raw byte representation of an ObjectId.
    pub fn bytes(&self) -> [u8; 12] {
        self.id
    }

    /// Retrieves the timestamp (seconds since epoch) from an ObjectId.
    pub fn timestamp(&self) -> u32 {
        BigEndian::read_u32(&self.id)
    }

    /// Retrieves the machine id associated with an ObjectId.
    pub fn machine_id(&self) -> u32 {
        let mut buf: [u8; 4] = [0; 4];
        for i in 0..MACHINE_ID_SIZE {
            buf[i] = self.id[MACHINE_ID_OFFSET+i];
        }
        LittleEndian::read_u32(&buf)
    }

    /// Retrieves the process id associated with an ObjectId.
    pub fn process_id(&self) -> u16 {
        LittleEndian::read_u16(&self.id[PROCESS_ID_OFFSET..])
    }

    /// Retrieves the increment counter from an ObjectId.
    pub fn counter(&self) -> u32 {
        let mut buf: [u8; 4] = [0; 4];
        for i in 0..COUNTER_SIZE {
            buf[i+1] = self.id[COUNTER_OFFSET+i];
        }
        BigEndian::read_u32(&buf)
    }

    // Generates a new timestamp representing the current seconds since epoch.
    // Represented in Big Endian.
    fn gen_timestamp() -> [u8; 4] {
        let timespec = time::get_time();
        let timestamp = timespec.sec as u32;

        let mut buf: [u8; 4] = [0; 4];
        BigEndian::write_u32(&mut buf,timestamp);
        buf
    }

    // Generates a new machine id represented as an MD5-hashed 3-byte-encoded hostname string.
    // Represented in Little Endian.
    fn gen_machine_id() -> Result<[u8; 3]> {
        // Short-circuit if machine id has already been calculated.
        // Since the generated machine id is not variable, arising race conditions
        // will have the same MACHINE_BYTES result.
        unsafe {
            if MACHINE_BYTES[0] != 0 || MACHINE_BYTES[1] != 0 || MACHINE_BYTES[2] != 0 {
                return Ok(MACHINE_BYTES);
            }
        }

        // Retrieve hostname through libc
        let len = 255;
        let mut buf = Vec::<u8>::with_capacity(len);
        let ptr = buf.as_mut_ptr();
        let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) } as i32;

        if err != 0 {
            return Err(Error::HostnameError);
        }

        // Convert bytes into string
        let s = String::from_utf8_lossy(&buf);

        // Hash hostname string
        let mut md5 = Md5::new();
        md5.input_str(&s.into_owned()[..]);
        let hash = md5.result_str();

        // Re-convert string to bytes and grab first three
        let mut bytes = hash.bytes();
        let mut vec: [u8; 3] = [0; 3];
        for i in 0..MACHINE_ID_SIZE {
            match bytes.next() {
                Some(b) => vec[i] = b,
                None => break,
            }
        }

        unsafe { MACHINE_BYTES = vec };
        Ok(vec)
    }

    // Gets the process ID and returns it as a 2-byte array.
    // Represented in Little Endian.
    fn gen_process_id() -> [u8; 2] {
        let pid = unsafe { libc::getpid() as u16 };
        let mut buf: [u8; 2] = [0; 2];
        LittleEndian::write_u16(&mut buf, pid);
        buf
    }

    // Gets an incremental 3-byte count.
    // Represented in Big Endian.
    fn gen_count() -> Result<[u8; 3]> {
        // Init oid counter
        if OID_COUNTER.load(Ordering::SeqCst) == 0 {
            let mut rng = try!(OsRng::new());
            let start = rng.gen_range(0, MAX_U24 + 1);
            OID_COUNTER.store(start, Ordering::SeqCst);
        }

        let u_counter = OID_COUNTER.fetch_add(1, Ordering::SeqCst);

        // Mod result instead of OID_COUNTER to prevent threading issues.
        // Static mutexes are currently unstable; once they have been
        // stabilized, one should be used to access OID_COUNTER and
        // perform multiple operations atomically.
        let u = u_counter % MAX_U24;

        // Convert usize to writable u64, then extract the first three bytes.
        let u_int = u as u64;

        let mut buf: [u8; 8] = [0; 8];
        BigEndian::write_u64(&mut buf, u_int);
        let buf_u24: [u8; 3] = [buf[5], buf[6], buf[7]];
        Ok(buf_u24)
    }
}

#[test]
fn pid_generation() {
    let pid = unsafe { libc::getpid() as u16 };
    let generated = ObjectId::gen_process_id();
    assert_eq!(pid, LittleEndian::read_u16(&generated));
}

#[test]
fn count_generation() {
    let start = 52222;
    OID_COUNTER.store(start, Ordering::SeqCst);
    let count_res = ObjectId::gen_count();
    assert!(count_res.is_ok());
    let count_bytes = count_res.unwrap();

    let mut buf: [u8; 4] = [0; 4];
    for i in 0..COUNTER_SIZE {
        buf[i+1] = count_bytes[i];
    }

    let count = BigEndian::read_u32(&buf);
    assert_eq!(start as u32, count);
}

#[test]
fn count_is_big_endian() {
    let start = 1122867;
    OID_COUNTER.store(start, Ordering::SeqCst);
    let oid_res = ObjectId::new();
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();

    assert_eq!(0x11u8, oid.bytes()[COUNTER_OFFSET]);
    assert_eq!(0x22u8, oid.bytes()[COUNTER_OFFSET + 1]);
    assert_eq!(0x33u8, oid.bytes()[COUNTER_OFFSET + 2]);
}
<commit_msg>Use option for mutable static<commit_after>use libc;

use crypto::digest::Digest;
use crypto::md5::Md5;

use byteorder::{ByteOrder, BigEndian, LittleEndian};
use rand::{Rng, OsRng};
use rustc_serialize::hex::{self, FromHex};
use time;

use std::{fmt, io, error, result};
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

const TIMESTAMP_SIZE: usize = 4;
const MACHINE_ID_SIZE: usize = 3;
const PROCESS_ID_SIZE: usize = 2;
const COUNTER_SIZE: usize = 3;

const TIMESTAMP_OFFSET: usize = 0;
const MACHINE_ID_OFFSET: usize = TIMESTAMP_OFFSET + TIMESTAMP_SIZE;
const PROCESS_ID_OFFSET: usize = MACHINE_ID_OFFSET + MACHINE_ID_SIZE;
const COUNTER_OFFSET: usize = PROCESS_ID_OFFSET + PROCESS_ID_SIZE;

const MAX_U24: usize = 0xFFFFFF;

static OID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT;
static mut MACHINE_BYTES: Option<[u8; 3]> = None;

extern {
    fn gethostname(name: *mut libc::c_char, size: libc::size_t) -> libc::c_int;
}

/// Errors that can occur during OID construction and generation.
#[derive(Debug)]
pub enum Error {
    ArgumentError(String),
    FromHexError(hex::FromHexError),
    IoError(io::Error),
    HostnameError,
}

impl From<hex::FromHexError> for Error {
    fn from(err: hex::FromHexError) -> Error {
        Error::FromHexError(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::IoError(err)
    }
}

/// Alias for Result<T, oid::Error>.
pub type Result<T> = result::Result<T, Error>;

impl fmt::Display for Error {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::ArgumentError(ref inner) => inner.fmt(fmt),
            &Error::FromHexError(ref inner) => inner.fmt(fmt),
            &Error::IoError(ref inner) => inner.fmt(fmt),
            &Error::HostnameError => write!(fmt, "Failed to retrieve hostname for OID generation."),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match self {
            &Error::ArgumentError(ref inner) => &inner,
            &Error::FromHexError(ref inner) => inner.description(),
            &Error::IoError(ref inner) => inner.description(),
            &Error::HostnameError => "Failed to retrieve hostname for OID generation.",
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match self {
            &Error::ArgumentError(_) => None,
            &Error::FromHexError(ref inner) => Some(inner),
            &Error::IoError(ref inner) => Some(inner),
            &Error::HostnameError => None,
        }
    }
}

/// A wrapper around raw 12-byte ObjectId representations.
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)]
pub struct ObjectId {
    id: [u8; 12],
}

impl ObjectId {
    /// Generates a new ObjectID, represented in bytes.
    /// See the [docs](http://docs.mongodb.org/manual/reference/object-id/)
    /// for more information.
    pub fn new() -> Result<ObjectId> {
        let timestamp = ObjectId::gen_timestamp();
        let machine_id = try!(ObjectId::gen_machine_id());
        let process_id = ObjectId::gen_process_id();
        let counter = try!(ObjectId::gen_count());

        let mut buf: [u8; 12] = [0; 12];
        for i in 0..TIMESTAMP_SIZE { buf[TIMESTAMP_OFFSET + i] = timestamp[i]; }
        for i in 0..MACHINE_ID_SIZE { buf[MACHINE_ID_OFFSET + i] = machine_id[i]; }
        for i in 0..PROCESS_ID_SIZE { buf[PROCESS_ID_OFFSET + i] = process_id[i]; }
        for i in 0..COUNTER_SIZE { buf[COUNTER_OFFSET +i] = counter[i]; }

        Ok(ObjectId::with_bytes(buf))
    }

    /// Constructs a new ObjectId wrapper around the raw byte representation.
    pub fn with_bytes(bytes: [u8; 12]) -> ObjectId {
        ObjectId {
            id: bytes,
        }
    }

    /// Creates an ObjectID using a 12-byte (24-char) hexadecimal string.
    pub fn with_string(s: &str) -> Result<ObjectId> {
        let bytes = try!(s.from_hex());
        if bytes.len() != 12 {
            Err(Error::ArgumentError("Provided string must be a 12-byte hexadecimal string.".to_owned()))
        } else {
            let mut byte_array: [u8; 12] = [0; 12];
            for i in 0..12 {
                byte_array[i] = bytes[i];
            }
            Ok(ObjectId::with_bytes(byte_array))
        }
    }

    /// Creates a dummy ObjectId with a specific generation time.
    /// This method should only be used to do range queries on a field
    /// containing ObjectId instances.
    pub fn with_timestamp(time: u32) -> ObjectId {
        let mut buf: [u8; 12] = [0; 12];
        BigEndian::write_u32(&mut buf, time);
        ObjectId::with_bytes(buf)
    }

    /// Returns the raw byte representation of an ObjectId.
    pub fn bytes(&self) -> [u8; 12] {
        self.id
    }

    /// Retrieves the timestamp (seconds since epoch) from an ObjectId.
    pub fn timestamp(&self) -> u32 {
        BigEndian::read_u32(&self.id)
    }

    /// Retrieves the machine id associated with an ObjectId.
    pub fn machine_id(&self) -> u32 {
        let mut buf: [u8; 4] = [0; 4];
        for i in 0..MACHINE_ID_SIZE {
            buf[i] = self.id[MACHINE_ID_OFFSET+i];
        }
        LittleEndian::read_u32(&buf)
    }

    /// Retrieves the process id associated with an ObjectId.
    pub fn process_id(&self) -> u16 {
        LittleEndian::read_u16(&self.id[PROCESS_ID_OFFSET..])
    }

    /// Retrieves the increment counter from an ObjectId.
    pub fn counter(&self) -> u32 {
        let mut buf: [u8; 4] = [0; 4];
        for i in 0..COUNTER_SIZE {
            buf[i+1] = self.id[COUNTER_OFFSET+i];
        }
        BigEndian::read_u32(&buf)
    }

    // Generates a new timestamp representing the current seconds since epoch.
    // Represented in Big Endian.
    fn gen_timestamp() -> [u8; 4] {
        let timespec = time::get_time();
        let timestamp = timespec.sec as u32;

        let mut buf: [u8; 4] = [0; 4];
        BigEndian::write_u32(&mut buf,timestamp);
        buf
    }

    // Generates a new machine id represented as an MD5-hashed 3-byte-encoded hostname string.
    // Represented in Little Endian.
    fn gen_machine_id() -> Result<[u8; 3]> {
        // Short-circuit if machine id has already been calculated.
        // Since the generated machine id is not variable, arising race conditions
        // will have the same MACHINE_BYTES result.
        unsafe {
            if let Some(bytes) = MACHINE_BYTES.as_ref() {
                return Ok(bytes.clone());
            }
        }

        // Retrieve hostname through libc
        let len = 255;
        let mut buf = Vec::<u8>::with_capacity(len);
        let ptr = buf.as_mut_ptr();
        let err = unsafe { gethostname(ptr as *mut libc::c_char, len as libc::size_t) } as i32;

        if err != 0 {
            return Err(Error::HostnameError);
        }

        // Convert bytes into string
        let s = String::from_utf8_lossy(&buf);

        // Hash hostname string
        let mut md5 = Md5::new();
        md5.input_str(&s.into_owned()[..]);
        let hash = md5.result_str();

        // Re-convert string to bytes and grab first three
        let mut bytes = hash.bytes();
        let mut vec: [u8; 3] = [0; 3];
        for i in 0..MACHINE_ID_SIZE {
            match bytes.next() {
                Some(b) => vec[i] = b,
                None => break,
            }
        }

        unsafe { MACHINE_BYTES = Some(vec) };
        Ok(vec)
    }

    // Gets the process ID and returns it as a 2-byte array.
    // Represented in Little Endian.
    fn gen_process_id() -> [u8; 2] {
        let pid = unsafe { libc::getpid() as u16 };
        let mut buf: [u8; 2] = [0; 2];
        LittleEndian::write_u16(&mut buf, pid);
        buf
    }

    // Gets an incremental 3-byte count.
    // Represented in Big Endian.
    fn gen_count() -> Result<[u8; 3]> {
        // Init oid counter
        if OID_COUNTER.load(Ordering::SeqCst) == 0 {
            let mut rng = try!(OsRng::new());
            let start = rng.gen_range(0, MAX_U24 + 1);
            OID_COUNTER.store(start, Ordering::SeqCst);
        }

        let u_counter = OID_COUNTER.fetch_add(1, Ordering::SeqCst);

        // Mod result instead of OID_COUNTER to prevent threading issues.
        // Static mutexes are currently unstable; once they have been
        // stabilized, one should be used to access OID_COUNTER and
        // perform multiple operations atomically.
        let u = u_counter % MAX_U24;

        // Convert usize to writable u64, then extract the first three bytes.
        let u_int = u as u64;

        let mut buf: [u8; 8] = [0; 8];
        BigEndian::write_u64(&mut buf, u_int);
        let buf_u24: [u8; 3] = [buf[5], buf[6], buf[7]];
        Ok(buf_u24)
    }
}

#[test]
fn pid_generation() {
    let pid = unsafe { libc::getpid() as u16 };
    let generated = ObjectId::gen_process_id();
    assert_eq!(pid, LittleEndian::read_u16(&generated));
}

#[test]
fn count_generation() {
    let start = 52222;
    OID_COUNTER.store(start, Ordering::SeqCst);
    let count_res = ObjectId::gen_count();
    assert!(count_res.is_ok());
    let count_bytes = count_res.unwrap();

    let mut buf: [u8; 4] = [0; 4];
    for i in 0..COUNTER_SIZE {
        buf[i+1] = count_bytes[i];
    }

    let count = BigEndian::read_u32(&buf);
    assert_eq!(start as u32, count);
}

#[test]
fn count_is_big_endian() {
    let start = 1122867;
    OID_COUNTER.store(start, Ordering::SeqCst);
    let oid_res = ObjectId::new();
    assert!(oid_res.is_ok());
    let oid = oid_res.unwrap();

    assert_eq!(0x11u8, oid.bytes()[COUNTER_OFFSET]);
    assert_eq!(0x22u8, oid.bytes()[COUNTER_OFFSET + 1]);
    assert_eq!(0x33u8, oid.bytes()[COUNTER_OFFSET + 2]);
}
<|endoftext|>


<commit_before>#![allow(visible_private_types)]

use std::cmp;
use std::iter;
use std::slice::Vector;
use std::str::{MaybeOwned, Owned};
use super::parse;
use super::parse::{
    Flags, FLAG_EMPTY,
    Nothing, Literal, Dot, Class, Begin, End, WordBoundary, Capture, Cat, Alt,
    Rep,
    ZeroOne, ZeroMore, OneMore,
};

type InstIdx = uint;

#[deriving(Show, Clone)]
pub enum MaybeStatic<T> {
    Dynamic(Vec<T>),
    Static(&'static [T]),
}

impl<T> Vector<T> for MaybeStatic<T> {
    fn as_slice<'a>(&'a self) -> &'a [T] {
        match *self {
            Dynamic(ref xs) => xs.as_slice(),
            Static(xs) => xs,
        }
    }
}

impl<T> Container for MaybeStatic<T> {
    fn len(&self) -> uint { self.as_slice().len() }
}

#[deriving(Show, Clone)]
pub enum Inst {
    // When a Match instruction is executed, the current thread is successful.
    Match,

    // The OneChar instruction matches a literal character.
    // The flags indicate whether to do a case insensitive match.
    OneChar(char, Flags),

    // The CharClass instruction tries to match one input character against
    // the range of characters given.
    // The flags indicate whether to do a case insentivie match and whether
    // the character class is negated or not.
    CharClass(MaybeStatic<(char, char)>, Flags),

    // Matches any character except new lines.
    // The flags indicate whether to include the '\n' character.
    Any(Flags),

    // Matches the beginning of the string, consumes no characters.
    // The flags indicate whether it matches if the preceding character
    // is a new line.
    EmptyBegin(Flags),

    // Matches the end of the string, consumes no characters.
    // The flags indicate whether it matches if the proceding character
    // is a new line.
    EmptyEnd(Flags),

    // Matches a word boundary (\w on one side and \W \A or \z on the other),
    // and consumes no character.
    // The flags indicate whether this matches a word boundary or something
    // that isn't a word boundary.
    EmptyWordBoundary(Flags),

    // Saves the current position in the input string to the Nth save slot.
    Save(uint),

    // Jumps to the instruction at the index given.
    Jump(InstIdx),

    // Jumps to the instruction at the first index given. If that leads to
    // a failing state, then the instruction at the second index given is
    // tried.
    Split(InstIdx, InstIdx),
}

/// Program represents a compiled regular expression. Once an expression is
/// compiled, its representation is immutable and will never change.
///
/// All of the data in a compiled expression is wrapped in "MaybeStatic" or
/// "MaybeOwned" types so that a `Program` can be represented as static data.
/// (This makes it convenient and efficient for use with the `re!` macro.)
pub struct Program {
    pub regex: MaybeOwned<'static>,
    pub insts: MaybeStatic<Inst>,
    pub names: MaybeStatic<Option<MaybeOwned<'static>>>,
    pub prefix: MaybeStatic<char>,
}

impl Program {
    pub fn new(regex: &str, ast: ~parse::Ast) -> Program {
        let mut c = Compiler {
            insts: Vec::with_capacity(100),
            names: Vec::with_capacity(10),
        };

        c.insts.push(Save(0));
        c.compile(ast);
        c.insts.push(Save(1));
        c.insts.push(Match);

        // Try to discover a literal string prefix.
        // This is a bit hacky since we have to skip over the initial
        // 'Save' instruction.
        let mut pre = Vec::with_capacity(5);
        for i in iter::range(1, c.insts.len()) {
            match *c.insts.get(i) {
                OneChar(c, FLAG_EMPTY) => pre.push(c),
                _ => break
            }
        }

        let names = c.names.clone();
        Program {
            regex: Owned(regex.to_owned()),
            insts: Dynamic(c.insts),
            names: Dynamic(names),
            prefix: Dynamic(pre),
        }
    }

    /// Returns the total number of capture groups in the regular expression.
    /// This includes the zeroth capture.
    pub fn num_captures(&self) -> uint {
        let mut n = 0;
        for inst in self.insts.as_slice().iter() {
            match *inst {
                Save(c) => n = cmp::max(n, c+1),
                _ => {}
            }
        }
        // There's exactly 2 Save slots for every capture.
        n / 2
    }
}

struct Compiler<'r> {
    insts: Vec<Inst>,
    names: Vec<Option<MaybeOwned<'r>>>,
}

// The compiler implemented here is extremely simple. Most of the complexity
// in this crate is in the parser or the VM.
// The only tricky thing here is patching jump/split instructions to point to
// the right instruction.
impl<'r> Compiler<'r> {
    fn compile(&mut self, ast: ~parse::Ast) {
        match ast {
            ~Nothing => {},
            ~Literal(c, flags) => self.push(OneChar(c, flags)),
            ~Dot(nl) => self.push(Any(nl)),
            ~Class(ranges, flags) =>
                self.push(CharClass(Dynamic(ranges), flags)),
            ~Begin(flags) => self.push(EmptyBegin(flags)),
            ~End(flags) => self.push(EmptyEnd(flags)),
            ~WordBoundary(flags) => self.push(EmptyWordBoundary(flags)),
            ~Capture(cap, name, x) => {
                let len = self.names.len();
                if cap >= len {
                    self.names.grow(10 + cap - len, &None)
                }
                *self.names.get_mut(cap) = name.map(Owned);

                self.push(Save(2 * cap));
                self.compile(x);
                self.push(Save(2 * cap + 1));
            }
            ~Cat(xs) => {
                for x in xs.move_iter() {
                    self.compile(x)
                }
            }
            ~Alt(x, y) => {
                let split = self.empty_split(); // push: split 0, 0
                let j1 = self.insts.len();
                self.compile(x);                // push: insts for x
                let jmp = self.empty_jump();    // push: jmp 0
                let j2 = self.insts.len();
                self.compile(y);                // push: insts for y
                let j3 = self.insts.len();

                self.set_split(split, j1, j2);  // split 0, 0 -> split j1, j2
                self.set_jump(jmp, j3);         // jmp 0      -> jmp j3
            }
            ~Rep(x, ZeroOne, g) => {
                let split = self.empty_split();
                let j1 = self.insts.len();
                self.compile(x);
                let j2 = self.insts.len();

                if g.is_greedy() {
                    self.set_split(split, j1, j2);
                } else {
                    self.set_split(split, j2, j1);
                }
            }
            ~Rep(x, ZeroMore, g) => {
                let j1 = self.insts.len();
                let split = self.empty_split();
                let j2 = self.insts.len();
                self.compile(x);
                let jmp = self.empty_jump();
                let j3 = self.insts.len();

                self.set_jump(jmp, j1);
                if g.is_greedy() {
                    self.set_split(split, j2, j3);
                } else {
                    self.set_split(split, j3, j2);
                }
            }
            ~Rep(x, OneMore, g) => {
                let j1 = self.insts.len();
                self.compile(x);
                let split = self.empty_split();
                let j2 = self.insts.len();

                if g.is_greedy() {
                    self.set_split(split, j1, j2);
                } else {
                    self.set_split(split, j2, j1);
                }
            }
        }
    }

    /// Appends the given instruction to the program.
    #[inline(always)]
    fn push(&mut self, x: Inst) {
        self.insts.push(x)
    }

    /// Appends an *empty* `Split` instruction to the program and returns
    /// the index of that instruction. (The index can then be used to "patch"
    /// the actual locations of the split in later.)
    #[inline(always)]
    fn empty_split(&mut self) -> InstIdx {
        self.insts.push(Split(0, 0));
        self.insts.len() - 1
    }

    /// Sets the left and right locations of a `Split` instruction at index
    /// `i` to `pc1` and `pc2`, respectively.
    /// If the instruction at index `i` isn't a `Split` instruction, then
    /// `fail!` is called.
    #[inline(always)]
    fn set_split(&mut self, i: InstIdx, pc1: InstIdx, pc2: InstIdx) {
        let split = self.insts.get_mut(i);
        match *split {
            Split(_, _) => *split = Split(pc1, pc2),
            _ => fail!("BUG: Invalid split index."),
        }
    }

    /// Appends an *empty* `Jump` instruction to the program and returns the
    /// index of that instruction.
    #[inline(always)]
    fn empty_jump(&mut self) -> InstIdx {
        self.insts.push(Jump(0));
        self.insts.len() - 1
    }

    /// Sets the location of a `Jump` instruction at index `i` to `pc`.
    /// If the instruction at index `i` isn't a `Jump` instruction, then
    /// `fail!` is called.
    #[inline(always)]
    fn set_jump(&mut self, i: InstIdx, pc: InstIdx) {
        let jmp = self.insts.get_mut(i);
        match *jmp {
            Jump(_) => *jmp = Jump(pc),
            _ => fail!("BUG: Invalid jump index."),
        }
    }
}
<commit_msg>Document representation of compiled expression.<commit_after>#![allow(visible_private_types)]

use std::cmp;
use std::iter;
use std::slice::Vector;
use std::str::{MaybeOwned, Owned};
use super::parse;
use super::parse::{
    Flags, FLAG_EMPTY,
    Nothing, Literal, Dot, Class, Begin, End, WordBoundary, Capture, Cat, Alt,
    Rep,
    ZeroOne, ZeroMore, OneMore,
};

type InstIdx = uint;

#[deriving(Show, Clone)]
pub enum MaybeStatic<T> {
    Dynamic(Vec<T>),
    Static(&'static [T]),
}

impl<T> Vector<T> for MaybeStatic<T> {
    fn as_slice<'a>(&'a self) -> &'a [T] {
        match *self {
            Dynamic(ref xs) => xs.as_slice(),
            Static(xs) => xs,
        }
    }
}

impl<T> Container for MaybeStatic<T> {
    fn len(&self) -> uint { self.as_slice().len() }
}

#[deriving(Show, Clone)]
pub enum Inst {
    // When a Match instruction is executed, the current thread is successful.
    Match,

    // The OneChar instruction matches a literal character.
    // The flags indicate whether to do a case insensitive match.
    OneChar(char, Flags),

    // The CharClass instruction tries to match one input character against
    // the range of characters given.
    // The flags indicate whether to do a case insentivie match and whether
    // the character class is negated or not.
    CharClass(MaybeStatic<(char, char)>, Flags),

    // Matches any character except new lines.
    // The flags indicate whether to include the '\n' character.
    Any(Flags),

    // Matches the beginning of the string, consumes no characters.
    // The flags indicate whether it matches if the preceding character
    // is a new line.
    EmptyBegin(Flags),

    // Matches the end of the string, consumes no characters.
    // The flags indicate whether it matches if the proceding character
    // is a new line.
    EmptyEnd(Flags),

    // Matches a word boundary (\w on one side and \W \A or \z on the other),
    // and consumes no character.
    // The flags indicate whether this matches a word boundary or something
    // that isn't a word boundary.
    EmptyWordBoundary(Flags),

    // Saves the current position in the input string to the Nth save slot.
    Save(uint),

    // Jumps to the instruction at the index given.
    Jump(InstIdx),

    // Jumps to the instruction at the first index given. If that leads to
    // a failing state, then the instruction at the second index given is
    // tried.
    Split(InstIdx, InstIdx),
}

/// Program represents a compiled regular expression. Once an expression is
/// compiled, its representation is immutable and will never change.
///
/// All of the data in a compiled expression is wrapped in "MaybeStatic" or
/// "MaybeOwned" types so that a `Program` can be represented as static data.
/// (This makes it convenient and efficient for use with the `re!` macro.)
pub struct Program {
    // A copy of the original regular expression.
    // It's not currently used.
    pub regex: MaybeOwned<'static>,
    // A sequence of instructions.
    pub insts: MaybeStatic<Inst>,
    // A sequence of names in correspondence with the number of capture groups
    // in an expression. If a capture group doesn't have a name, then the
    // corresponding position in `names` is None.
    pub names: MaybeStatic<Option<MaybeOwned<'static>>>,
    // If the regular expression requires a literal prefix in order to have a
    // match, that prefix is stored here. (It's used in the VM to implement
    // an optimization.)
    pub prefix: MaybeStatic<char>,
}

impl Program {
    pub fn new(regex: &str, ast: ~parse::Ast) -> Program {
        let mut c = Compiler {
            insts: Vec::with_capacity(100),
            names: Vec::with_capacity(10),
        };

        c.insts.push(Save(0));
        c.compile(ast);
        c.insts.push(Save(1));
        c.insts.push(Match);

        // Try to discover a literal string prefix.
        // This is a bit hacky since we have to skip over the initial
        // 'Save' instruction.
        let mut pre = Vec::with_capacity(5);
        for i in iter::range(1, c.insts.len()) {
            match *c.insts.get(i) {
                OneChar(c, FLAG_EMPTY) => pre.push(c),
                _ => break
            }
        }

        let names = c.names.clone();
        Program {
            regex: Owned(regex.to_owned()),
            insts: Dynamic(c.insts),
            names: Dynamic(names),
            prefix: Dynamic(pre),
        }
    }

    /// Returns the total number of capture groups in the regular expression.
    /// This includes the zeroth capture.
    pub fn num_captures(&self) -> uint {
        let mut n = 0;
        for inst in self.insts.as_slice().iter() {
            match *inst {
                Save(c) => n = cmp::max(n, c+1),
                _ => {}
            }
        }
        // There's exactly 2 Save slots for every capture.
        n / 2
    }
}

struct Compiler<'r> {
    insts: Vec<Inst>,
    names: Vec<Option<MaybeOwned<'r>>>,
}

// The compiler implemented here is extremely simple. Most of the complexity
// in this crate is in the parser or the VM.
// The only tricky thing here is patching jump/split instructions to point to
// the right instruction.
impl<'r> Compiler<'r> {
    fn compile(&mut self, ast: ~parse::Ast) {
        match ast {
            ~Nothing => {},
            ~Literal(c, flags) => self.push(OneChar(c, flags)),
            ~Dot(nl) => self.push(Any(nl)),
            ~Class(ranges, flags) =>
                self.push(CharClass(Dynamic(ranges), flags)),
            ~Begin(flags) => self.push(EmptyBegin(flags)),
            ~End(flags) => self.push(EmptyEnd(flags)),
            ~WordBoundary(flags) => self.push(EmptyWordBoundary(flags)),
            ~Capture(cap, name, x) => {
                let len = self.names.len();
                if cap >= len {
                    self.names.grow(10 + cap - len, &None)
                }
                *self.names.get_mut(cap) = name.map(Owned);

                self.push(Save(2 * cap));
                self.compile(x);
                self.push(Save(2 * cap + 1));
            }
            ~Cat(xs) => {
                for x in xs.move_iter() {
                    self.compile(x)
                }
            }
            ~Alt(x, y) => {
                let split = self.empty_split(); // push: split 0, 0
                let j1 = self.insts.len();
                self.compile(x);                // push: insts for x
                let jmp = self.empty_jump();    // push: jmp 0
                let j2 = self.insts.len();
                self.compile(y);                // push: insts for y
                let j3 = self.insts.len();

                self.set_split(split, j1, j2);  // split 0, 0 -> split j1, j2
                self.set_jump(jmp, j3);         // jmp 0      -> jmp j3
            }
            ~Rep(x, ZeroOne, g) => {
                let split = self.empty_split();
                let j1 = self.insts.len();
                self.compile(x);
                let j2 = self.insts.len();

                if g.is_greedy() {
                    self.set_split(split, j1, j2);
                } else {
                    self.set_split(split, j2, j1);
                }
            }
            ~Rep(x, ZeroMore, g) => {
                let j1 = self.insts.len();
                let split = self.empty_split();
                let j2 = self.insts.len();
                self.compile(x);
                let jmp = self.empty_jump();
                let j3 = self.insts.len();

                self.set_jump(jmp, j1);
                if g.is_greedy() {
                    self.set_split(split, j2, j3);
                } else {
                    self.set_split(split, j3, j2);
                }
            }
            ~Rep(x, OneMore, g) => {
                let j1 = self.insts.len();
                self.compile(x);
                let split = self.empty_split();
                let j2 = self.insts.len();

                if g.is_greedy() {
                    self.set_split(split, j1, j2);
                } else {
                    self.set_split(split, j2, j1);
                }
            }
        }
    }

    /// Appends the given instruction to the program.
    #[inline(always)]
    fn push(&mut self, x: Inst) {
        self.insts.push(x)
    }

    /// Appends an *empty* `Split` instruction to the program and returns
    /// the index of that instruction. (The index can then be used to "patch"
    /// the actual locations of the split in later.)
    #[inline(always)]
    fn empty_split(&mut self) -> InstIdx {
        self.insts.push(Split(0, 0));
        self.insts.len() - 1
    }

    /// Sets the left and right locations of a `Split` instruction at index
    /// `i` to `pc1` and `pc2`, respectively.
    /// If the instruction at index `i` isn't a `Split` instruction, then
    /// `fail!` is called.
    #[inline(always)]
    fn set_split(&mut self, i: InstIdx, pc1: InstIdx, pc2: InstIdx) {
        let split = self.insts.get_mut(i);
        match *split {
            Split(_, _) => *split = Split(pc1, pc2),
            _ => fail!("BUG: Invalid split index."),
        }
    }

    /// Appends an *empty* `Jump` instruction to the program and returns the
    /// index of that instruction.
    #[inline(always)]
    fn empty_jump(&mut self) -> InstIdx {
        self.insts.push(Jump(0));
        self.insts.len() - 1
    }

    /// Sets the location of a `Jump` instruction at index `i` to `pc`.
    /// If the instruction at index `i` isn't a `Jump` instruction, then
    /// `fail!` is called.
    #[inline(always)]
    fn set_jump(&mut self, i: InstIdx, pc: InstIdx) {
        let jmp = self.insts.get_mut(i);
        match *jmp {
            Jump(_) => *jmp = Jump(pc),
            _ => fail!("BUG: Invalid jump index."),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

use glutin::VirtualKeyCode as Key;
use std::cmp::Ordering;
use std::collections::VecDeque;
use std::collections::HashMap;
use std::iter::FromIterator;

/// Stores console commands.
pub struct CmdRegistry<'a> {
    cmds: HashMap<String, Box<Fn(Vec<&str>) + 'a>>,
}

impl<'a> CmdRegistry<'a> {
    pub fn new() -> CmdRegistry<'a> {
        CmdRegistry {
            cmds: HashMap::new(),
        }
    }

    /// Registers a new command.
    ///
    /// Returns an error if a command with the specified name already exists.
    pub fn add_cmd<S>(&mut self, name: S, cmd: Box<Fn(Vec<&str>) + 'a>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(_) => {
                error!("Command \"{}\" already registered.", name);
                return Err(());
            }
            None => {
                self.cmds.insert(name, cmd);
            }
        }

        Ok(())
    }

    /// Executes a command.
    ///
    /// Returns an error if no command with the specified name exists.
    pub fn exec_cmd<S>(&mut self, name: S, args: Vec<&str>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(cmd) => cmd(args),
            None => return Err(()),
        }

        Ok(())
    }
}

/// A configuration variable.
///
/// Cvars are the primary method of configuring the game.
struct Cvar {
    // Value of this variable
    val: String,

    // If true, this variable should be archived in vars.rc
    archive: bool,

    // If true, updating this variable must also update serverinfo/userinfo
    info: bool,

    // The default value of this variable
    default: String,
}

pub struct CvarRegistry {
    cvars: HashMap<String, Cvar>
}

impl CvarRegistry {
    /// Construct a new empty `CvarRegistry`.
    pub fn new() -> CvarRegistry {
        CvarRegistry {
            cvars: HashMap::new(),
        }
    }

    /// Register a new `Cvar` with the given name.
    pub fn register<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new archived `Cvar` with the given name.
    ///
    /// The value of this `Cvar` should be written to `vars.rc` whenever the game is closed or
    /// `host_writeconfig` is issued.
    pub fn register_archive<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new info `Cvar` with the given name.
    ///
    /// When this `Cvar` is set, the serverinfo or userinfo string should be update to reflect its
    /// new value.
    pub fn register_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    pub fn register_archive_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }
}

/// The line of text currently being edited in the console.
pub struct ConsoleInput {
    text: Vec<char>,
    curs: usize,
}

impl ConsoleInput {
    /// Constructs a new `ConsoleInput`.
    ///
    /// Initializes the text content to be empty and places the cursor at position 0.
    pub fn new() -> ConsoleInput {
        ConsoleInput {
            text: Vec::new(),
            curs: 0,
        }
    }

    /// Returns the current content of the `ConsoleInput`.
    pub fn get_text(&self) -> Vec<char> {
        self.text.to_owned()
    }

    /// Sets the content of the `ConsoleInput` to `Text`.
    ///
    /// This also moves the cursor to the end of the line.
    pub fn set_text(&mut self, text: &Vec<char>) {
        self.text = text.clone();
        self.curs = self.text.len();
    }

    /// Inserts the specified character at the position of the cursor.
    ///
    /// The cursor is moved one character to the right.
    pub fn insert(&mut self, c: char) {
        self.text.insert(self.curs, c);
        self.cursor_right();
    }

    /// Moves the cursor to the right.
    ///
    /// If the cursor is at the end of the current text, no change is made.
    pub fn cursor_right(&mut self) {
        if self.curs < self.text.len() {
            self.curs += 1;
        }
    }

    /// Moves the cursor to the left.
    ///
    /// If the cursor is at the beginning of the current text, no change is made.
    pub fn cursor_left(&mut self) {
        if self.curs > 0 {
            self.curs -= 1;
        }
    }

    /// Deletes the character to the right of the cursor.
    ///
    /// If the cursor is at the end of the current text, no character is deleted.
    pub fn delete(&mut self) {
        if self.curs < self.text.len() {
            self.text.remove(self.curs);
        }
    }

    /// Deletes the character to the left of the cursor.
    ///
    /// If the cursor is at the beginning of the current text, no character is deleted.
    pub fn backspace(&mut self) {
        if self.curs > 0 {
            self.text.remove(self.curs - 1);
            self.curs -= 1;
        }
    }

    /// Clears the contents of the `ConsoleInput`.
    ///
    /// Also moves the cursor to position 0.
    pub fn clear(&mut self) {
        self.text.clear();
        self.curs = 0;
    }

    pub fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.text[..self.curs].to_owned().into_iter()),
            String::from_iter(self.text[self.curs..].to_owned().into_iter())
        )
    }
}

pub struct History {
    lines: VecDeque<Vec<char>>,
    curs: usize,
}

impl History {
    pub fn new() -> History {
        History {
            lines: VecDeque::new(),
            curs: 0,
        }
    }

    pub fn add_line(&mut self, line: Vec<char>) {
        self.lines.push_front(line);
        self.curs = 0;
    }

    // TODO: handle case where history is empty
    pub fn line_up(&mut self) -> Option<Vec<char>> {
        if self.lines.len() == 0 || self.curs >= self.lines.len() {
            None
        } else {
            self.curs += 1;
            Some(self.lines[self.curs - 1].clone())
        }
    }

    pub fn line_down(&mut self) -> Option<Vec<char>> {
        if self.curs > 0 {
            self.curs -= 1;
        }

        if self.curs > 0 {
            Some(self.lines[self.curs - 1].clone())
        } else {
            Some(Vec::new().clone())
        }
    }
}

pub struct ConsoleOutput {
    lines: Vec<Vec<char>>,
}

impl ConsoleOutput {
    pub fn println<S>(&mut self, msg: S)
    where
        S: AsRef<str>,
    {
        println!("{}", msg.as_ref());
    }
}

pub struct Console {
    input: ConsoleInput,
    hist: History,
}

impl Console {
    pub fn new() -> Console {
        Console {
            input: ConsoleInput::new(),
            hist: History::new(),
        }
    }

    pub fn send_char(&mut self, c: char) -> Result<(), ()> {
        match c {
            '\r' => {
                let entered = self.get_string();
                let mut parts = entered.split_whitespace();

                let cmd_name = match parts.next() {
                    Some(c) => c,
                    None => return Ok(()),
                };

                let args: Vec<&str> = parts.collect();

                self.hist.add_line(self.input.get_text());
                self.input.clear();
            }

            // backspace
            '\x08' => self.input.backspace(),

            // delete
            '\x7f' => self.input.delete(),

            '\t' => (), // TODO: tab completion

            c => self.input.insert(c),
        }

        println!("{}", self.debug_string());

        Ok(())
    }

    pub fn send_key(&mut self, key: Key) {
        match key {
            Key::Up => if let Some(line) = self.hist.line_up() {
                self.input.set_text(&line);
            }

            Key::Down => if let Some(line) = self.hist.line_down() {
                self.input.set_text(&line);
            }

            Key::Right => self.input.cursor_right(),
            Key::Left => self.input.cursor_left(),
            _ => return,
        }

        println!("{}", self.debug_string());
    }

    fn get_string(&self) -> String {
        String::from_iter(self.input.text.clone().into_iter())
    }

    fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.input.text[..self.input.curs].to_owned().into_iter()),
            String::from_iter(self.input.text[self.input.curs..].to_owned().into_iter())
        )
    }
}
<commit_msg>Add Tokenizer type<commit_after>// Copyright © 2017 Cormac O'Brien
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

// TODO: implement proper Error types

use std::collections::VecDeque;
use std::collections::HashMap;
use std::iter::FromIterator;

use glutin::VirtualKeyCode as Key;

/// Stores console commands.
pub struct CmdRegistry<'a> {
    cmds: HashMap<String, Box<Fn(Vec<&str>) + 'a>>,
}

impl<'a> CmdRegistry<'a> {
    pub fn new() -> CmdRegistry<'a> {
        CmdRegistry {
            cmds: HashMap::new(),
        }
    }

    /// Registers a new command.
    ///
    /// Returns an error if a command with the specified name already exists.
    pub fn add_cmd<S>(&mut self, name: S, cmd: Box<Fn(Vec<&str>) + 'a>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(_) => {
                error!("Command \"{}\" already registered.", name);
                return Err(());
            }
            None => {
                self.cmds.insert(name, cmd);
            }
        }

        Ok(())
    }

    /// Executes a command.
    ///
    /// Returns an error if no command with the specified name exists.
    pub fn exec_cmd<S>(&mut self, name: S, args: Vec<&str>) -> Result<(), ()>
    where
        S: AsRef<str>,
    {
        let name = name.as_ref().to_owned();

        match self.cmds.get(&name) {
            Some(cmd) => cmd(args),
            None => return Err(()),
        }

        Ok(())
    }
}

/// A configuration variable.
///
/// Cvars are the primary method of configuring the game.
struct Cvar {
    // Value of this variable
    val: String,

    // If true, this variable should be archived in vars.rc
    archive: bool,

    // If true, updating this variable must also update serverinfo/userinfo
    info: bool,

    // The default value of this variable
    default: String,
}

pub struct CvarRegistry {
    cvars: HashMap<String, Cvar>
}

impl CvarRegistry {
    /// Construct a new empty `CvarRegistry`.
    pub fn new() -> CvarRegistry {
        CvarRegistry {
            cvars: HashMap::new(),
        }
    }

    /// Register a new `Cvar` with the given name.
    pub fn register<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new archived `Cvar` with the given name.
    ///
    /// The value of this `Cvar` should be written to `vars.rc` whenever the game is closed or
    /// `host_writeconfig` is issued.
    pub fn register_archive<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: false,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    /// Register a new info `Cvar` with the given name.
    ///
    /// When this `Cvar` is set, the serverinfo or userinfo string should be update to reflect its
    /// new value.
    pub fn register_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: false,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }

    pub fn register_archive_updateinfo<S>(&mut self, name: S, default: S) -> Result<(), ()> where S: AsRef<str> {
        let name = name.as_ref();
        let default = default.as_ref();

        match self.cvars.get(name) {
            Some(_) => return Err(()),
            None => {
                self.cvars.insert(name.to_owned(), Cvar {
                    val: default.to_owned(),
                    archive: true,
                    info: true,
                    default: default.to_owned(),
                });
            }
        }

        Ok(())
    }
}

/// The line of text currently being edited in the console.
pub struct ConsoleInput {
    text: Vec<char>,
    curs: usize,
}

impl ConsoleInput {
    /// Constructs a new `ConsoleInput`.
    ///
    /// Initializes the text content to be empty and places the cursor at position 0.
    pub fn new() -> ConsoleInput {
        ConsoleInput {
            text: Vec::new(),
            curs: 0,
        }
    }

    /// Returns the current content of the `ConsoleInput`.
    pub fn get_text(&self) -> Vec<char> {
        self.text.to_owned()
    }

    /// Sets the content of the `ConsoleInput` to `Text`.
    ///
    /// This also moves the cursor to the end of the line.
    pub fn set_text(&mut self, text: &Vec<char>) {
        self.text = text.clone();
        self.curs = self.text.len();
    }

    /// Inserts the specified character at the position of the cursor.
    ///
    /// The cursor is moved one character to the right.
    pub fn insert(&mut self, c: char) {
        self.text.insert(self.curs, c);
        self.cursor_right();
    }

    /// Moves the cursor to the right.
    ///
    /// If the cursor is at the end of the current text, no change is made.
    pub fn cursor_right(&mut self) {
        if self.curs < self.text.len() {
            self.curs += 1;
        }
    }

    /// Moves the cursor to the left.
    ///
    /// If the cursor is at the beginning of the current text, no change is made.
    pub fn cursor_left(&mut self) {
        if self.curs > 0 {
            self.curs -= 1;
        }
    }

    /// Deletes the character to the right of the cursor.
    ///
    /// If the cursor is at the end of the current text, no character is deleted.
    pub fn delete(&mut self) {
        if self.curs < self.text.len() {
            self.text.remove(self.curs);
        }
    }

    /// Deletes the character to the left of the cursor.
    ///
    /// If the cursor is at the beginning of the current text, no character is deleted.
    pub fn backspace(&mut self) {
        if self.curs > 0 {
            self.text.remove(self.curs - 1);
            self.curs -= 1;
        }
    }

    /// Clears the contents of the `ConsoleInput`.
    ///
    /// Also moves the cursor to position 0.
    pub fn clear(&mut self) {
        self.text.clear();
        self.curs = 0;
    }

    pub fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.text[..self.curs].to_owned().into_iter()),
            String::from_iter(self.text[self.curs..].to_owned().into_iter())
        )
    }
}

pub struct History {
    lines: VecDeque<Vec<char>>,
    curs: usize,
}

impl History {
    pub fn new() -> History {
        History {
            lines: VecDeque::new(),
            curs: 0,
        }
    }

    pub fn add_line(&mut self, line: Vec<char>) {
        self.lines.push_front(line);
        self.curs = 0;
    }

    // TODO: handle case where history is empty
    pub fn line_up(&mut self) -> Option<Vec<char>> {
        if self.lines.len() == 0 || self.curs >= self.lines.len() {
            None
        } else {
            self.curs += 1;
            Some(self.lines[self.curs - 1].clone())
        }
    }

    pub fn line_down(&mut self) -> Option<Vec<char>> {
        if self.curs > 0 {
            self.curs -= 1;
        }

        if self.curs > 0 {
            Some(self.lines[self.curs - 1].clone())
        } else {
            Some(Vec::new().clone())
        }
    }
}

pub struct ConsoleOutput {
    lines: Vec<Vec<char>>,
}

impl ConsoleOutput {
    pub fn println<S>(&mut self, msg: S)
    where
        S: AsRef<str>,
    {
        println!("{}", msg.as_ref());
    }
}

pub struct Console {
    input: ConsoleInput,
    hist: History,
}

impl Console {
    pub fn new() -> Console {
        Console {
            input: ConsoleInput::new(),
            hist: History::new(),
        }
    }

    pub fn send_char(&mut self, c: char) -> Result<(), ()> {
        match c {
            '\r' => {
                let entered = self.get_string();
                let mut parts = entered.split_whitespace();

                let cmd_name = match parts.next() {
                    Some(c) => c,
                    None => return Ok(()),
                };

                let args: Vec<&str> = parts.collect();

                self.hist.add_line(self.input.get_text());
                self.input.clear();
            }

            // backspace
            '\x08' => self.input.backspace(),

            // delete
            '\x7f' => self.input.delete(),

            '\t' => (), // TODO: tab completion

            c => self.input.insert(c),
        }

        println!("{}", self.debug_string());

        Ok(())
    }

    pub fn send_key(&mut self, key: Key) {
        match key {
            Key::Up => if let Some(line) = self.hist.line_up() {
                self.input.set_text(&line);
            }

            Key::Down => if let Some(line) = self.hist.line_down() {
                self.input.set_text(&line);
            }

            Key::Right => self.input.cursor_right(),
            Key::Left => self.input.cursor_left(),
            _ => return,
        }

        println!("{}", self.debug_string());
    }

    fn get_string(&self) -> String {
        String::from_iter(self.input.text.clone().into_iter())
    }

    fn debug_string(&self) -> String {
        format!(
            "{}_{}",
            String::from_iter(self.input.text[..self.input.curs].to_owned().into_iter()),
            String::from_iter(self.input.text[self.input.curs..].to_owned().into_iter())
        )
    }
}

struct Tokenizer<'a> {
    input: &'a str,
    byte_offset: usize,
}

impl<'a> Tokenizer<'a> {
    fn new(input: &'a str) -> Tokenizer<'a> {
        Tokenizer {
            input: input,
            byte_offset: 0,
        }
    }

    fn get_remaining_input(&self) -> &'a str {
        &self.input[self.byte_offset..]
    }

    fn skip_spaces(&mut self) {
        let iter = self.get_remaining_input().char_indices();
        match iter.skip_while(|&(_, c)| c.is_whitespace()).next() {
            Some((i, _)) => self.byte_offset += i,
            None => self.byte_offset = self.input.len(),
        }
    }

    fn try_skip_line_comment(&mut self) -> bool {
        if self.get_remaining_input().starts_with("//") {
            match self.get_remaining_input()
                .char_indices()
                .skip_while(|&(_, c)| c != '\n')
                .next()
            {
                Some((i, _)) => self.byte_offset += i,
                None => self.byte_offset = self.input.len(),
            }

            return true;
        }

        false
    }

    fn next(&mut self) -> Option<&'a str> {
        loop {
            self.skip_spaces();

            if !self.try_skip_line_comment() {
                break;
            }
        }

        let mut char_indices = self.get_remaining_input().char_indices();
        match char_indices.next() {
            Some((start_i, '"')) => {
                let offset = self.byte_offset + start_i;
                match char_indices.skip_while(|&(_, c)| c != '"').next() {
                    Some((end_i, '"')) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }
                    Some(_) => panic!("wtf hapen"),
                    None => None
                }
            }
            Some((start_i, _)) => {
                let offset = self.byte_offset + start_i;

                match char_indices.take_while(|&(_, c)| !c.is_whitespace()).last() {
                    Some((end_i, _)) => {
                        let len = end_i + 1 - start_i;
                        self.byte_offset += len;
                        Some(&self.input[offset..offset + len])
                    }
                    None => None,
                }
            }
            None => None,
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_tokenizer_empty() {
        let mut tokenizer = Tokenizer::new("");
        assert_eq!(tokenizer.next(), None);
    }
}
<|endoftext|>


<commit_before>//!
//! The matching module defines how a request is matched
//! against a list of potential interactions.
//!

use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;

use itertools::Itertools;
use serde_json::json;

use pact_matching::Mismatch;
use pact_matching::models::{Interaction, Request, RequestResponseInteraction, Response};
use pact_matching::s;
use pact_models::PactSpecification;

/// Enum to define a match result
#[derive(Debug, Clone, PartialEq)]
pub enum MatchResult {
  /// Match result where the request was successfully matched
  RequestMatch(Request, Response),
  /// Match result where there were a number of mismatches
  RequestMismatch(Request, Vec<Mismatch>),
  /// Match result where the request was not expected
  RequestNotFound(Request),
  /// Match result where an expected request was not received
  MissingRequest(Request)
}

impl MatchResult {
    /// Returns the match key for this mismatch
    pub fn match_key(&self) -> String {
        match self {
            &MatchResult::RequestMatch(_, _) => "Request-Matched",
            &MatchResult::RequestMismatch(_, _) => "Request-Mismatch",
            &MatchResult::RequestNotFound(_) => "Unexpected-Request",
            &MatchResult::MissingRequest(_) => "Missing-Request"
        }.to_string()
    }

    /// Returns true if this match result is a `RequestMatch`
    pub fn matched(&self) -> bool {
        match self {
            &MatchResult::RequestMatch(_, _) => true,
            _ => false
        }
    }

    /// Returns true if this is an unexpected OPTIONS request
    pub fn cors_preflight(&self) -> bool {
      match self {
        MatchResult::RequestNotFound(req) => req.method == "OPTIONS",
        _ => false
      }
    }

    /// Converts this match result to a `Value` struct
    pub fn to_json(&self) -> serde_json::Value {
        match self {
            &MatchResult::RequestMatch(_, _) => json!({ "type" : "request-match"}),
            &MatchResult::RequestMismatch(ref request, ref mismatches) => mismatches_to_json(request, mismatches),
            &MatchResult::RequestNotFound(ref req) => json!({
                "type": "request-not-found",
                "method": req.method,
                "path": req.path,
                "request": req.to_json(&PactSpecification::V3)
            }),
            &MatchResult::MissingRequest(ref request) => json!({
                "type": "missing-request",
                "method": request.method,
                "path": request.path,
                "request": request.to_json(&PactSpecification::V3)
            })
        }
    }
}

impl Display for MatchResult {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    match self {
      MatchResult::RequestMatch(request, _) => {
        write!(f, "Request matched OK - {}", request)
      },
      MatchResult::RequestMismatch(request, mismatches) => {
        write!(f, "Request did not match - {}", request)?;
        for (i, mismatch) in mismatches.iter().enumerate() {
          write!(f, "    {}) {}", i, mismatch)?;
        }
        Ok(())
      },
      MatchResult::RequestNotFound(request) => {
        write!(f, "Request was not expected - {}", request)
      },
      MatchResult::MissingRequest(request) => {
        write!(f, "Request was not received - {}", request)
      }
    }
  }
}

fn mismatches_to_json(request: &Request, mismatches: &Vec<Mismatch>) -> serde_json::Value {
    json!({
        "type" : "request-mismatch",
        "method" : request.method,
        "path" : request.path,
        "mismatches" : mismatches.iter().map(|m| m.to_json()).collect::<serde_json::Value>()
    })
}

///
/// Matches a request against a list of interactions
///
pub fn match_request(req: &Request, interactions: Vec<&dyn Interaction>) -> MatchResult {
  let mut match_results = interactions
    .into_iter()
    .filter(|i| i.is_request_response())
    .map(|i| {
      let interaction = i.as_request_response().unwrap();
      (i.clone(), pact_matching::match_request(interaction.request.clone(), req.clone()))
    })
    .sorted_by(|(_, i1), (_, i2)| {
      Ord::cmp(&i2.score(), &i1.score())
    });
  match match_results.next() {
    Some((interaction, result)) => {
      let request_response_interaction = interaction.as_request_response().unwrap();
      if result.all_matched() {
        MatchResult::RequestMatch(request_response_interaction.request, request_response_interaction.response)
      } else if result.method_or_path_mismatch() {
        MatchResult::RequestNotFound(req.clone())
      } else {
        MatchResult::RequestMismatch(request_response_interaction.request, result.mismatches())
      }
    },
    None => MatchResult::RequestNotFound(req.clone())
  }
}
<commit_msg>fix: clippy violation: using `clone` on a double-reference<commit_after>//!
//! The matching module defines how a request is matched
//! against a list of potential interactions.
//!

use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;

use itertools::Itertools;
use serde_json::json;

use pact_matching::Mismatch;
use pact_matching::models::{Interaction, Request, RequestResponseInteraction, Response};
use pact_matching::s;
use pact_models::PactSpecification;

/// Enum to define a match result
#[derive(Debug, Clone, PartialEq)]
pub enum MatchResult {
  /// Match result where the request was successfully matched
  RequestMatch(Request, Response),
  /// Match result where there were a number of mismatches
  RequestMismatch(Request, Vec<Mismatch>),
  /// Match result where the request was not expected
  RequestNotFound(Request),
  /// Match result where an expected request was not received
  MissingRequest(Request)
}

impl MatchResult {
    /// Returns the match key for this mismatch
    pub fn match_key(&self) -> String {
        match self {
            &MatchResult::RequestMatch(_, _) => "Request-Matched",
            &MatchResult::RequestMismatch(_, _) => "Request-Mismatch",
            &MatchResult::RequestNotFound(_) => "Unexpected-Request",
            &MatchResult::MissingRequest(_) => "Missing-Request"
        }.to_string()
    }

    /// Returns true if this match result is a `RequestMatch`
    pub fn matched(&self) -> bool {
        match self {
            &MatchResult::RequestMatch(_, _) => true,
            _ => false
        }
    }

    /// Returns true if this is an unexpected OPTIONS request
    pub fn cors_preflight(&self) -> bool {
      match self {
        MatchResult::RequestNotFound(req) => req.method == "OPTIONS",
        _ => false
      }
    }

    /// Converts this match result to a `Value` struct
    pub fn to_json(&self) -> serde_json::Value {
        match self {
            &MatchResult::RequestMatch(_, _) => json!({ "type" : "request-match"}),
            &MatchResult::RequestMismatch(ref request, ref mismatches) => mismatches_to_json(request, mismatches),
            &MatchResult::RequestNotFound(ref req) => json!({
                "type": "request-not-found",
                "method": req.method,
                "path": req.path,
                "request": req.to_json(&PactSpecification::V3)
            }),
            &MatchResult::MissingRequest(ref request) => json!({
                "type": "missing-request",
                "method": request.method,
                "path": request.path,
                "request": request.to_json(&PactSpecification::V3)
            })
        }
    }
}

impl Display for MatchResult {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    match self {
      MatchResult::RequestMatch(request, _) => {
        write!(f, "Request matched OK - {}", request)
      },
      MatchResult::RequestMismatch(request, mismatches) => {
        write!(f, "Request did not match - {}", request)?;
        for (i, mismatch) in mismatches.iter().enumerate() {
          write!(f, "    {}) {}", i, mismatch)?;
        }
        Ok(())
      },
      MatchResult::RequestNotFound(request) => {
        write!(f, "Request was not expected - {}", request)
      },
      MatchResult::MissingRequest(request) => {
        write!(f, "Request was not received - {}", request)
      }
    }
  }
}

fn mismatches_to_json(request: &Request, mismatches: &Vec<Mismatch>) -> serde_json::Value {
    json!({
        "type" : "request-mismatch",
        "method" : request.method,
        "path" : request.path,
        "mismatches" : mismatches.iter().map(|m| m.to_json()).collect::<serde_json::Value>()
    })
}

///
/// Matches a request against a list of interactions
///
pub fn match_request(req: &Request, interactions: Vec<&dyn Interaction>) -> MatchResult {
  let mut match_results = interactions
    .into_iter()
    .filter(|i| i.is_request_response())
    .map(|i| {
      let interaction = i.as_request_response().unwrap();
      (interaction.clone(), pact_matching::match_request(interaction.request.clone(), req.clone()))
    })
    .sorted_by(|(_, i1), (_, i2)| {
      Ord::cmp(&i2.score(), &i1.score())
    });
  match match_results.next() {
    Some((interaction, result)) => {
      let request_response_interaction = interaction.as_request_response().unwrap();
      if result.all_matched() {
        MatchResult::RequestMatch(request_response_interaction.request, request_response_interaction.response)
      } else if result.method_or_path_mismatch() {
        MatchResult::RequestNotFound(req.clone())
      } else {
        MatchResult::RequestMismatch(request_response_interaction.request, result.mismatches())
      }
    },
    None => MatchResult::RequestNotFound(req.clone())
  }
}
<|endoftext|>