<commit_before>//! Aim to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being

use super::libc;
pub extern crate time;
use self::time::{Tm, Timespec};

use ::std::io::Result as IOResult;
use ::std::io::Error as IOError;
use ::std::ptr;
use ::std::borrow::Cow;
use ::std::ffi::CStr;
use ::std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

macro_rules! bytes2cow {
    ($name:expr) => (
        unsafe {
            CStr::from_ptr($name.as_ref().as_ptr()).to_string_lossy()
        }
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

/// Login records
///
/// Examples:
/// ---------
///
/// ```
/// for ut in Utmpx::iter_all_records() {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
///
/// Specifying the path to login record:
///
/// ```
/// for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> Cow<str> {
        bytes2cow!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> Cow<str> {
        bytes2cow!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> Cow<str> {
        bytes2cow!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> Cow<str> {
        bytes2cow!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
                               self.inner.ut_tv.tv_usec as i32))
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host.as_ref()).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(c_host.as_ptr(),
                              ptr::null(),
                              &hints as *const _,
                              &mut res as *mut _)
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host.as_ref()))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx {
                    inner: ptr::read(res as *const _)
                })
            } else {
                endutxent();
                None
            }
        }
    }
}
<commit_msg>uucore::utmpx: change API and fix error<commit_after>//! Aim to provide platform-independent methods to obtain login records
//!
//! **ONLY** support linux, macos and freebsd for the time being

// This file is part of the uutils coreutils package.
//
// (c) Jian Zeng <anonymousknight96@gmail.com>
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
//

use super::libc;
pub extern crate time;
use self::time::{Tm, Timespec};

use ::std::io::Result as IOResult;
use ::std::io::Error as IOError;
use ::std::ptr;
use ::std::ffi::CString;

pub use self::ut::*;
use libc::utmpx;
// pub use libc::getutxid;
// pub use libc::getutxline;
// pub use libc::pututxline;
pub use libc::getutxent;
pub use libc::setutxent;
pub use libc::endutxent;
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub use libc::utmpxname;
#[cfg(target_os = "freebsd")]
pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
    0
}

// In case the c_char array doesn' t end with NULL
macro_rules! chars2string {
    ($arr:expr) => (
        $arr.iter().take_while(|i| **i > 0).map(|&i| i as u8 as char).collect::<String>()
    )
}

#[cfg(target_os = "linux")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmp";

    pub use libc::__UT_LINESIZE as UT_LINESIZE;
    pub use libc::__UT_NAMESIZE as UT_NAMESIZE;
    pub use libc::__UT_HOSTSIZE as UT_HOSTSIZE;
    pub const UT_IDSIZE: usize = 4;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
}

#[cfg(target_os = "macos")]
mod ut {
    pub static DEFAULT_FILE: &'static str = "/var/run/utmpx";

    pub use libc::_UTX_LINESIZE as UT_LINESIZE;
    pub use libc::_UTX_USERSIZE as UT_NAMESIZE;
    pub use libc::_UTX_HOSTSIZE as UT_HOSTSIZE;
    pub use libc::_UTX_IDSIZE as UT_IDSIZE;

    pub use libc::EMPTY;
    pub use libc::RUN_LVL;
    pub use libc::BOOT_TIME;
    pub use libc::NEW_TIME;
    pub use libc::OLD_TIME;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::USER_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::ACCOUNTING;
    pub use libc::SIGNATURE;
    pub use libc::SHUTDOWN_TIME;
}

#[cfg(target_os = "freebsd")]
mod ut {
    use super::libc;

    pub static DEFAULT_FILE: &'static str = "";

    pub const UT_LINESIZE: usize = 16;
    pub const UT_NAMESIZE: usize = 32;
    pub const UT_IDSIZE: usize = 8;
    pub const UT_HOSTSIZE: usize = 128;

    pub use libc::EMPTY;
    pub use libc::BOOT_TIME;
    pub use libc::OLD_TIME;
    pub use libc::NEW_TIME;
    pub use libc::USER_PROCESS;
    pub use libc::INIT_PROCESS;
    pub use libc::LOGIN_PROCESS;
    pub use libc::DEAD_PROCESS;
    pub use libc::SHUTDOWN_TIME;
}

/// Login records
///
/// Examples:
/// ---------
///
/// ```
/// for ut in Utmpx::iter_all_records() {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
///
/// Specifying the path to login record:
///
/// ```
/// for ut in Utmpx::iter_all_records().read_from("/some/where/else") {
///     if ut.is_user_process() {
///         println!("{}: {}", ut.host(), ut.user())
///     }
/// }
/// ```
pub struct Utmpx {
    inner: utmpx,
}

impl Utmpx {
    /// A.K.A. ut.ut_type
    pub fn record_type(&self) -> i16 {
        self.inner.ut_type as i16
    }
    /// A.K.A. ut.ut_pid
    pub fn pid(&self) -> i32 {
        self.inner.ut_pid as i32
    }
    /// A.K.A. ut.ut_id
    pub fn terminal_suffix(&self) -> String {
        chars2string!(self.inner.ut_id)
    }
    /// A.K.A. ut.ut_user
    pub fn user(&self) -> String {
        chars2string!(self.inner.ut_user)
    }
    /// A.K.A. ut.ut_host
    pub fn host(&self) -> String {
        chars2string!(self.inner.ut_host)
    }
    /// A.K.A. ut.ut_line
    pub fn tty_device(&self) -> String {
        chars2string!(self.inner.ut_line)
    }
    /// A.K.A. ut.ut_tv
    pub fn login_time(&self) -> Tm {
        time::at(Timespec::new(self.inner.ut_tv.tv_sec as i64,
                               self.inner.ut_tv.tv_usec as i32))
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (e_termination, e_exit)
    #[cfg(any(target_os = "linux", target_os = "android"))]
    pub fn exit_status(&self) -> (i16, i16) {
        (self.inner.ut_exit.e_termination, self.inner.ut_exit.e_exit)
    }
    /// A.K.A. ut.ut_exit
    ///
    /// Return (0, 0) on Non-Linux platform
    #[cfg(not(any(target_os = "linux", target_os = "android")))]
    pub fn exit_status(&self) -> (i16, i16) {
        (0, 0)
    }
    /// Consumes the `Utmpx`, returning the underlying C struct utmpx
    pub fn into_inner(self) -> utmpx {
        self.inner
    }
    pub fn is_user_process(&self) -> bool {
        !self.user().is_empty() && self.record_type() == USER_PROCESS
    }

    /// Canonicalize host name using DNS
    pub fn canon_host(&self) -> IOResult<String> {
        const AI_CANONNAME: libc::c_int = 0x2;
        let host = self.host();
        let host = host.split(':').nth(0).unwrap();
        let hints = libc::addrinfo {
            ai_flags: AI_CANONNAME,
            ai_family: 0,
            ai_socktype: 0,
            ai_protocol: 0,
            ai_addrlen: 0,
            ai_addr: ptr::null_mut(),
            ai_canonname: ptr::null_mut(),
            ai_next: ptr::null_mut(),
        };
        let c_host = CString::new(host).unwrap();
        let mut res = ptr::null_mut();
        let status = unsafe {
            libc::getaddrinfo(c_host.as_ptr(),
                              ptr::null(),
                              &hints as *const _,
                              &mut res as *mut _)
        };
        if status == 0 {
            let info: libc::addrinfo = unsafe { ptr::read(res as *const _) };
            // http://lists.gnu.org/archive/html/bug-coreutils/2006-09/msg00300.html
            // says Darwin 7.9.0 getaddrinfo returns 0 but sets
            // res->ai_canonname to NULL.
            let ret = if info.ai_canonname.is_null() {
                Ok(String::from(host))
            } else {
                Ok(unsafe { CString::from_raw(info.ai_canonname).into_string().unwrap() })
            };
            unsafe {
                libc::freeaddrinfo(res);
            }
            ret
        } else {
            Err(IOError::last_os_error())
        }
    }
    pub fn iter_all_records() -> UtmpxIter {
        UtmpxIter
    }
}

/// Iterator of login records
pub struct UtmpxIter;

impl UtmpxIter {
    /// Sets the name of the utmpx-format file for the other utmpx functions to access.
    ///
    /// If not set, default record file will be used(file path depends on the target OS)
    pub fn read_from(self, f: &str) -> Self {
        let res = unsafe { utmpxname(CString::new(f).unwrap().as_ptr()) };
        if res != 0 {
            println!("Warning: {}", IOError::last_os_error());
        }
        unsafe {
            setutxent();
        }
        self
    }
}

impl Iterator for UtmpxIter {
    type Item = Utmpx;
    fn next(&mut self) -> Option<Self::Item> {
        unsafe {
            let res = getutxent();
            if !res.is_null() {
                Some(Utmpx { inner: ptr::read(res as *const _) })
            } else {
                endutxent();
                None
            }
        }
    }
}
<|endoftext|>


<commit_before>//! This module contains paths to types and functions Clippy needs to know
//! about.

pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
pub const ARC: [&str; 3] = ["alloc", "sync", "Arc"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
pub const CSTRING_NEW: [&str; 5] = ["std", "ffi", "c_str", "CString", "new"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 4] = ["core", "iter", "traits", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 4] = ["core", "iter", "iterator", "Iterator"];
pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
<commit_msg>Update changed iterator paths<commit_after>//! This module contains paths to types and functions Clippy needs to know
//! about.

pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
pub const ARC: [&str; 3] = ["alloc", "sync", "Arc"];
pub const ASMUT_TRAIT: [&str; 3] = ["core", "convert", "AsMut"];
pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
pub const BEGIN_PANIC: [&str; 3] = ["std", "panicking", "begin_panic"];
pub const BEGIN_PANIC_FMT: [&str; 3] = ["std", "panicking", "begin_panic_fmt"];
pub const BINARY_HEAP: [&str; 4] = ["alloc", "collections", "binary_heap", "BinaryHeap"];
pub const BORROW_TRAIT: [&str; 3] = ["core", "borrow", "Borrow"];
pub const BTREEMAP: [&str; 5] = ["alloc", "collections", "btree", "map", "BTreeMap"];
pub const BTREEMAP_ENTRY: [&str; 5] = ["alloc", "collections", "btree", "map", "Entry"];
pub const BTREESET: [&str; 5] = ["alloc", "collections", "btree", "set", "BTreeSet"];
pub const CLONE_TRAIT: [&str; 3] = ["core", "clone", "Clone"];
pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
pub const CMP_MAX: [&str; 3] = ["core", "cmp", "max"];
pub const CMP_MIN: [&str; 3] = ["core", "cmp", "min"];
pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
pub const CSTRING_NEW: [&str; 5] = ["std", "ffi", "c_str", "CString", "new"];
pub const DEFAULT_TRAIT: [&str; 3] = ["core", "default", "Default"];
pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
pub const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
pub const DISPLAY_FMT_METHOD: [&str; 4] = ["core", "fmt", "Display", "fmt"];
pub const DOUBLE_ENDED_ITERATOR: [&str; 4] = ["core", "iter", "traits", "DoubleEndedIterator"];
pub const DROP: [&str; 3] = ["core", "mem", "drop"];
pub const DURATION: [&str; 3] = ["core", "time", "Duration"];
pub const EARLY_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "EarlyContext"];
pub const FMT_ARGUMENTS_NEWV1: [&str; 4] = ["core", "fmt", "Arguments", "new_v1"];
pub const FMT_ARGUMENTS_NEWV1FORMATTED: [&str; 4] = ["core", "fmt", "Arguments", "new_v1_formatted"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
pub const HASHSET: [&str; 5] = ["std", "collections", "hash", "set", "HashSet"];
pub const INDEX: [&str; 3] = ["core", "ops", "Index"];
pub const INDEX_MUT: [&str; 3] = ["core", "ops", "IndexMut"];
pub const INIT: [&str; 4] = ["core", "intrinsics", "", "init"];
pub const INTO: [&str; 3] = ["core", "convert", "Into"];
pub const INTO_ITERATOR: [&str; 4] = ["core", "iter", "traits", "IntoIterator"];
pub const IO_READ: [&str; 3] = ["std", "io", "Read"];
pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
pub const ITERATOR: [&str; 5] = ["core", "iter", "traits", "iterator", "Iterator"];
pub const LATE_CONTEXT: [&str; 4] = ["rustc", "lint", "context", "LateContext"];
pub const LINKED_LIST: [&str; 4] = ["alloc", "collections", "linked_list", "LinkedList"];
pub const LINT: [&str; 3] = ["rustc", "lint", "Lint"];
pub const LINT_PASS: [&str; 3] = ["rustc", "lint", "LintPass"];
pub const MEM_DISCRIMINANT: [&str; 3] = ["core", "mem", "discriminant"];
pub const MEM_FORGET: [&str; 3] = ["core", "mem", "forget"];
pub const MEM_REPLACE: [&str; 3] = ["core", "mem", "replace"];
pub const MEM_UNINIT: [&str; 3] = ["core", "mem", "uninitialized"];
pub const MEM_ZEROED: [&str; 3] = ["core", "mem", "zeroed"];
pub const MUTEX: [&str; 4] = ["std", "sync", "mutex", "Mutex"];
pub const OPEN_OPTIONS: [&str; 3] = ["std", "fs", "OpenOptions"];
pub const OPS_MODULE: [&str; 2] = ["core", "ops"];
pub const OPTION: [&str; 3] = ["core", "option", "Option"];
pub const OPTION_NONE: [&str; 4] = ["core", "option", "Option", "None"];
pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
pub const OS_STRING: [&str; 4] = ["std", "ffi", "os_str", "OsString"];
pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
pub const PARTIAL_ORD: [&str; 3] = ["core", "cmp", "PartialOrd"];
pub const PATH_BUF: [&str; 3] = ["std", "path", "PathBuf"];
pub const PATH_TO_PATH_BUF: [&str; 4] = ["std", "path", "Path", "to_path_buf"];
pub const PTR_NULL: [&str; 2] = ["ptr", "null"];
pub const PTR_NULL_MUT: [&str; 2] = ["ptr", "null_mut"];
pub const RANGE: [&str; 3] = ["core", "ops", "Range"];
pub const RANGE_ARGUMENT_TRAIT: [&str; 3] = ["core", "ops", "RangeBounds"];
pub const RANGE_FROM: [&str; 3] = ["core", "ops", "RangeFrom"];
pub const RANGE_FROM_STD: [&str; 3] = ["std", "ops", "RangeFrom"];
pub const RANGE_FULL: [&str; 3] = ["core", "ops", "RangeFull"];
pub const RANGE_FULL_STD: [&str; 3] = ["std", "ops", "RangeFull"];
pub const RANGE_INCLUSIVE_NEW: [&str; 4] = ["core", "ops", "RangeInclusive", "new"];
pub const RANGE_INCLUSIVE_STD_NEW: [&str; 4] = ["std", "ops", "RangeInclusive", "new"];
pub const RANGE_STD: [&str; 3] = ["std", "ops", "Range"];
pub const RANGE_TO: [&str; 3] = ["core", "ops", "RangeTo"];
pub const RANGE_TO_INCLUSIVE: [&str; 3] = ["core", "ops", "RangeToInclusive"];
pub const RANGE_TO_INCLUSIVE_STD: [&str; 3] = ["std", "ops", "RangeToInclusive"];
pub const RANGE_TO_STD: [&str; 3] = ["std", "ops", "RangeTo"];
pub const RC: [&str; 3] = ["alloc", "rc", "Rc"];
pub const REGEX: [&str; 3] = ["regex", "re_unicode", "Regex"];
pub const REGEX_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "unicode", "RegexBuilder", "new"];
pub const REGEX_BYTES_BUILDER_NEW: [&str; 5] = ["regex", "re_builder", "bytes", "RegexBuilder", "new"];
pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "re_bytes", "Regex", "new"];
pub const REGEX_BYTES_SET_NEW: [&str; 5] = ["regex", "re_set", "bytes", "RegexSet", "new"];
pub const REGEX_NEW: [&str; 4] = ["regex", "re_unicode", "Regex", "new"];
pub const REGEX_SET_NEW: [&str; 5] = ["regex", "re_set", "unicode", "RegexSet", "new"];
pub const REPEAT: [&str; 3] = ["core", "iter", "repeat"];
pub const RESULT: [&str; 3] = ["core", "result", "Result"];
pub const RESULT_ERR: [&str; 4] = ["core", "result", "Result", "Err"];
pub const RESULT_OK: [&str; 4] = ["core", "result", "Result", "Ok"];
pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
pub const SLICE_ITER: [&str; 3] = ["core", "slice", "Iter"];
pub const STRING: [&str; 3] = ["alloc", "string", "String"];
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];
pub const TO_STRING_METHOD: [&str; 4] = ["alloc", "string", "ToString", "to_string"];
pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
pub const TRY_INTO_RESULT: [&str; 4] = ["std", "ops", "Try", "into_result"];
pub const UNINIT: [&str; 4] = ["core", "intrinsics", "", "uninit"];
pub const VEC: [&str; 3] = ["alloc", "vec", "Vec"];
pub const VEC_DEQUE: [&str; 4] = ["alloc", "collections", "vec_deque", "VecDeque"];
pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
pub const WEAK_ARC: [&str; 3] = ["alloc", "sync", "Weak"];
pub const WEAK_RC: [&str; 3] = ["alloc", "rc", "Weak"];
<|endoftext|>


<commit_before>pub mod space;
pub mod seq;
pub mod seq_of;
pub mod set;
pub mod set_of;
pub mod choice;
pub mod int;
pub mod spec;

use nom::{is_alphanumeric, alpha, digit};
use parse::space::skip_other;
use parse::seq::asn1_seq;
use parse::seq_of::asn1_seq_of;
use parse::set::asn1_set;
use parse::set_of::asn1_set_of;
use parse::choice::asn1_choice;
use parse::int::asn1_integer;
use data::{Asn1Type, Asn1Class, Asn1Tag, Asn1Def, Asn1Field, Asn1Optional};

named!(pub asn1_class_tag <Asn1Tag>, do_parse!(
  class: opt!(alpha) >>
  opt!(skip_other) >>
  tag_num: digit >>
  ( {
      let class = class.and_then(|i|
        String::from_utf8(Vec::from(i)).ok()
      );
      Asn1Class::parse(class).expect("Couldn't parse tag class")
    },
    String::from_utf8(Vec::from(tag_num)).unwrap().parse().expect("Couldn't parse tag number")
  )
));

named!(pub asn1_tag <Asn1Tag>, do_parse!(
  tag: delimited!(
    tag!("["),
    asn1_class_tag,
    tuple!(opt!(skip_other), tag!("]"))
  ) >>
  (tag)
));

named!(pub asn1_type_def <Asn1Def>, do_parse!(
  name: asn1_type_name >>
  opt!(skip_other) >>
  tag!("::=") >>
  opt!(skip_other) >>
  tag: opt!(asn1_tag) >>
  opt!(skip_other) >>
  asn1_type: asn1_type >>
  (Asn1Def {
    name: name,
    tag: tag,
    assign: asn1_type,
  })
));

named!(pub asn1_type <Asn1Type>, alt!(
  asn1_seq |
  asn1_seq_of |
  asn1_set |
  asn1_set_of |
  asn1_choice |
  asn1_integer |
  asn1_assignment
));

named!(pub asn1_type_name <String>, do_parse!(
  s: alt!(
    tag!("BIT STRING") |
    tag!("OCTET STRING") |
    tag!("OBJECT IDENTIFIER") |
    tag!("INSTANCE OF") |
    tag!("EMBEDDED PDV") |
    tag!("CHARACTER STRING") |
    take_while!(is_alphanumeric)
  ) >>
  (String::from_utf8(Vec::from(s)).unwrap())
));

named!(pub asn1_assignment <Asn1Type>, do_parse!(
  t: asn1_type_name >>
  (Asn1Type::Type(t))
));

named!(pub asn1_field <Asn1Field>, do_parse!(
  name: asn1_type_name >>
  opt!(skip_other) >>
  tag: opt!(asn1_tag) >>
  opt!(skip_other) >>
  asn1_type: asn1_type >>
  opt!(skip_other) >>
  optional: opt!(alt!(asn1_field_optional | asn1_field_default)) >>
  (Asn1Field {
    name: name,
    tag: tag,
    asn1_type: asn1_type,
    optional: optional,
  })
));

named!(pub asn1_field_optional <Asn1Optional>, do_parse!(
  tag!("OPTIONAL") >>
  (Asn1Optional::Optional)
));

named!(pub asn1_field_default <Asn1Optional>, do_parse!(
  tag!("DEFAULT") >>
  opt!(skip_other) >>
  default: take_while!(is_alphanumeric) >>
  (Asn1Optional::Default(
    String::from_utf8(Vec::from(default)).unwrap()
  ))
));

#[test]
fn test_asn1_type() {
  assert_eq!(asn1_type_name("Foo Bar".as_bytes()).unwrap().1, "Foo");
  assert_eq!(asn1_type_name("OCTET STRING".as_bytes()).unwrap().1, "OCTET STRING");
}

#[test]
fn test_asn1_tag() {
  let tag: Asn1Tag = (Asn1Class::ContextSpecific, 32);
  assert_eq!(asn1_tag("[32]".as_bytes()).unwrap().1, tag);
  assert_eq!(asn1_tag("[ 32 ]".as_bytes()).unwrap().1, tag);
  assert_eq!(
    asn1_tag("[APPLICATION 12]".as_bytes()).unwrap().1,
    (Asn1Class::Application, 12)
  );
  assert_eq!(
    asn1_tag("[PRIVATE 24]".as_bytes()).unwrap().1,
    (Asn1Class::Private, 24)
  );
}

#[test]
fn test_asn1_field() {
  let field1 = ::Asn1Field {
    name: "foo".into(),
    tag: None,
    asn1_type: ::Asn1Type::Type("Bar".into()),
    optional: None,
  };
  let field2 = ::Asn1Field {
    name: "asdf".into(),
    tag: Some((Asn1Class::Application, 9)),
    asn1_type: ::Asn1Type::Integer,
    optional: Some(Asn1Optional::Optional),
  };
  let field3 = ::Asn1Field {
    name: "sample".into(),
    tag: None,
    asn1_type: ::Asn1Type::Integer,
    optional: Some(Asn1Optional::Default("TRUE".into())),
  };
  assert_eq!(
    field1,
    asn1_field("foo Bar,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field2,
    asn1_field("asdf [APPLICATION 9] INTEGER OPTIONAL,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field1,
    asn1_field("foo--test\n Bar,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field3,
    asn1_field("sample INTEGER DEFAULT TRUE,".as_bytes()).unwrap().1
  );
}
<commit_msg>Force asn1_type to only parse non-empty strings<commit_after>pub mod space;
pub mod seq;
pub mod seq_of;
pub mod set;
pub mod set_of;
pub mod choice;
pub mod int;
pub mod spec;

use nom::{is_alphanumeric, alpha, digit};

use parse::space::skip_other;
use parse::seq::asn1_seq;
use parse::seq_of::asn1_seq_of;
use parse::set::asn1_set;
use parse::set_of::asn1_set_of;
use parse::choice::asn1_choice;
use parse::int::asn1_integer;
use data::{Asn1Type, Asn1Class, Asn1Tag, Asn1Def, Asn1Field, Asn1Optional};

named!(pub asn1_class_tag <Asn1Tag>, do_parse!(
  class: opt!(alpha) >>
  opt!(skip_other) >>
  tag_num: digit >>
  ( {
      let class = class.and_then(|i|
        String::from_utf8(Vec::from(i)).ok()
      );
      Asn1Class::parse(class).expect("Couldn't parse tag class")
    },
    String::from_utf8(Vec::from(tag_num)).unwrap().parse().expect("Couldn't parse tag number")
  )
));

named!(pub asn1_tag <Asn1Tag>, do_parse!(
  tag: delimited!(
    tag!("["),
    asn1_class_tag,
    tuple!(opt!(skip_other), tag!("]"))
  ) >>
  (tag)
));

named!(pub asn1_type_def <Asn1Def>, do_parse!(
  name: asn1_type_name >>
  opt!(skip_other) >>
  tag!("::=") >>
  opt!(skip_other) >>
  tag: opt!(asn1_tag) >>
  opt!(skip_other) >>
  asn1_type: asn1_type >>
  (Asn1Def {
    name: name,
    tag: tag,
    assign: asn1_type,
  })
));

named!(pub asn1_type <Asn1Type>, alt!(
  asn1_seq |
  asn1_seq_of |
  asn1_set |
  asn1_set_of |
  asn1_choice |
  asn1_integer |
  asn1_assignment
));

named!(pub asn1_type_name <String>, do_parse!(
  s: alt!(
    tag!("BIT STRING") |
    tag!("OCTET STRING") |
    tag!("OBJECT IDENTIFIER") |
    tag!("INSTANCE OF") |
    tag!("EMBEDDED PDV") |
    tag!("CHARACTER STRING") |
    take_while1!(is_alphanumeric) |
    tag!("ERROR")
  ) >>
  (String::from_utf8(Vec::from(s)).unwrap())
));

named!(pub asn1_assignment <Asn1Type>, do_parse!(
  t: asn1_type_name >>
  (Asn1Type::Type(t))
));

named!(pub asn1_field <Asn1Field>, do_parse!(
  name: asn1_type_name >>
  opt!(skip_other) >>
  tag: opt!(asn1_tag) >>
  opt!(skip_other) >>
  asn1_type: asn1_type >>
  opt!(skip_other) >>
  optional: opt!(alt!(asn1_field_optional | asn1_field_default)) >>
  (Asn1Field {
    name: name,
    tag: tag,
    asn1_type: asn1_type,
    optional: optional,
  })
));

named!(pub asn1_field_optional <Asn1Optional>, do_parse!(
  tag!("OPTIONAL") >>
  (Asn1Optional::Optional)
));

named!(pub asn1_field_default <Asn1Optional>, do_parse!(
  tag!("DEFAULT") >>
  opt!(skip_other) >>
  default: take_while!(is_alphanumeric) >>
  (Asn1Optional::Default(
    String::from_utf8(Vec::from(default)).unwrap()
  ))
));

#[test]
fn test_asn1_type() {
  assert_eq!(asn1_type_name("Foo Bar".as_bytes()).unwrap().1, "Foo");
  assert_eq!(asn1_type_name("OCTET STRING".as_bytes()).unwrap().1, "OCTET STRING");
}

#[test]
fn test_asn1_tag() {
  let tag: Asn1Tag = (Asn1Class::ContextSpecific, 32);
  assert_eq!(asn1_tag("[32]".as_bytes()).unwrap().1, tag);
  assert_eq!(asn1_tag("[ 32 ]".as_bytes()).unwrap().1, tag);
  assert_eq!(
    asn1_tag("[APPLICATION 12]".as_bytes()).unwrap().1,
    (Asn1Class::Application, 12)
  );
  assert_eq!(
    asn1_tag("[PRIVATE 24]".as_bytes()).unwrap().1,
    (Asn1Class::Private, 24)
  );
}

#[test]
fn test_asn1_field() {
  let field1 = ::Asn1Field {
    name: "foo".into(),
    tag: None,
    asn1_type: ::Asn1Type::Type("Bar".into()),
    optional: None,
  };
  let field2 = ::Asn1Field {
    name: "asdf".into(),
    tag: Some((Asn1Class::Application, 9)),
    asn1_type: ::Asn1Type::Integer,
    optional: Some(Asn1Optional::Optional),
  };
  let field3 = ::Asn1Field {
    name: "sample".into(),
    tag: None,
    asn1_type: ::Asn1Type::Integer,
    optional: Some(Asn1Optional::Default("TRUE".into())),
  };
  assert_eq!(
    field1,
    asn1_field("foo Bar,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field2,
    asn1_field("asdf [APPLICATION 9] INTEGER OPTIONAL,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field1,
    asn1_field("foo--test\n Bar,".as_bytes()).unwrap().1
  );
  assert_eq!(
    field3,
    asn1_field("sample INTEGER DEFAULT TRUE,".as_bytes()).unwrap().1
  );
}
<|endoftext|>


<commit_before>use std::borrow;
use std::cmp::Ordering;
use std::fmt;

use chrono;

pub type Date = chrono::DateTime<chrono::FixedOffset>;

/// A Liquid scalar value
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Scalar(ScalarEnum);

/// An enum to represent different value types
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
enum ScalarEnum {
    Integer(i32),
    Float(f64),
    Bool(bool),
    #[cfg_attr(feature = "serde", serde(with = "friendly_date"))]
    Date(Date),
    Str(String),
}

impl Scalar {
    pub fn new<T: Into<Self>>(value: T) -> Self {
        value.into()
    }

    pub fn to_str(&self) -> borrow::Cow<str> {
        match self.0 {
            ScalarEnum::Integer(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Float(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Bool(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Date(ref x) => borrow::Cow::Owned(x.format(DATE_FORMAT).to_string()),
            ScalarEnum::Str(ref x) => borrow::Cow::Borrowed(x.as_str()),
        }
    }

    pub fn into_string(self) -> String {
        match self.0 {
            ScalarEnum::Integer(x) => x.to_string(),
            ScalarEnum::Float(x) => x.to_string(),
            ScalarEnum::Bool(x) => x.to_string(),
            ScalarEnum::Date(x) => x.to_string(),
            ScalarEnum::Str(x) => x,
        }
    }

    /// Interpret as an integer, if possible
    pub fn to_integer(&self) -> Option<i32> {
        match self.0 {
            ScalarEnum::Integer(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => x.parse::<i32>().ok(),
            _ => None,
        }
    }

    /// Interpret as an float, if possible
    pub fn to_float(&self) -> Option<f64> {
        match self.0 {
            ScalarEnum::Integer(ref x) => Some(f64::from(*x)),
            ScalarEnum::Float(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => x.parse::<f64>().ok(),
            _ => None,
        }
    }

    /// Interpret as an bool, if possible
    pub fn to_bool(&self) -> Option<bool> {
        match self.0 {
            ScalarEnum::Bool(ref x) => Some(*x),
            _ => None,
        }
    }

    /// Interpret as an bool, if possible
    pub fn to_date(&self) -> Option<Date> {
        match self.0 {
            ScalarEnum::Date(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => parse_date(x.as_str()),
            _ => None,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_truthy(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match self.0 {
            ScalarEnum::Bool(ref x) => *x,
            _ => true,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_default(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match self.0 {
            ScalarEnum::Bool(ref x) => !*x,
            ScalarEnum::Str(ref x) => x.is_empty(),
            _ => false,
        }
    }

    pub fn type_name(&self) -> &'static str {
        match self.0 {
            ScalarEnum::Integer(_) => "whole number",
            ScalarEnum::Float(_) => "fractional number",
            ScalarEnum::Bool(_) => "boolean",
            ScalarEnum::Date(_) => "date",
            ScalarEnum::Str(_) => "string",
        }
    }
}

impl From<i32> for Scalar {
    fn from(s: i32) -> Self {
        Scalar {
            0: ScalarEnum::Integer(s),
        }
    }
}

impl From<f64> for Scalar {
    fn from(s: f64) -> Self {
        Scalar {
            0: ScalarEnum::Float(s),
        }
    }
}

impl From<bool> for Scalar {
    fn from(s: bool) -> Self {
        Scalar {
            0: ScalarEnum::Bool(s),
        }
    }
}

impl From<Date> for Scalar {
    fn from(s: Date) -> Self {
        Scalar {
            0: ScalarEnum::Date(s),
        }
    }
}

impl From<String> for Scalar {
    fn from(s: String) -> Self {
        Scalar {
            0: ScalarEnum::Str(s),
        }
    }
}

impl<'a> From<&'a String> for Scalar {
    fn from(s: &String) -> Self {
        Scalar {
            0: ScalarEnum::Str(s.to_owned()),
        }
    }
}

impl<'a> From<&'a str> for Scalar {
    fn from(s: &str) -> Self {
        Scalar {
            0: ScalarEnum::Str(s.to_owned()),
        }
    }
}

impl PartialEq<Scalar> for Scalar {
    fn eq(&self, other: &Self) -> bool {
        match (&self.0, &other.0) {
            (&ScalarEnum::Integer(x), &ScalarEnum::Integer(y)) => x == y,
            (&ScalarEnum::Integer(x), &ScalarEnum::Float(y)) => (f64::from(x)) == y,
            (&ScalarEnum::Float(x), &ScalarEnum::Integer(y)) => x == (f64::from(y)),
            (&ScalarEnum::Float(x), &ScalarEnum::Float(y)) => x == y,
            (&ScalarEnum::Bool(x), &ScalarEnum::Bool(y)) => x == y,
            (&ScalarEnum::Date(x), &ScalarEnum::Date(y)) => x == y,
            (&ScalarEnum::Str(ref x), &ScalarEnum::Str(ref y)) => x == y,
            // encode Ruby truthiness: all values except false and nil are true
            (_, &ScalarEnum::Bool(b)) | (&ScalarEnum::Bool(b), _) => b,
            _ => false,
        }
    }
}

impl Eq for Scalar {}

impl PartialOrd<Scalar> for Scalar {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (&self.0, &other.0) {
            (&ScalarEnum::Integer(x), &ScalarEnum::Integer(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Integer(x), &ScalarEnum::Float(y)) => (f64::from(x)).partial_cmp(&y),
            (&ScalarEnum::Float(x), &ScalarEnum::Integer(y)) => x.partial_cmp(&(f64::from(y))),
            (&ScalarEnum::Float(x), &ScalarEnum::Float(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Bool(x), &ScalarEnum::Bool(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Date(x), &ScalarEnum::Date(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Str(ref x), &ScalarEnum::Str(ref y)) => x.partial_cmp(y),
            _ => None,
        }
    }
}

impl fmt::Display for Scalar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let data = self.to_str();
        write!(f, "{}", data)
    }
}

const DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z";

#[cfg(feature = "serde")]
mod friendly_date {
    use super::*;
    use serde::{self, Deserialize, Deserializer, Serializer};

    pub fn serialize<S>(date: &Date, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = date.format(DATE_FORMAT).to_string();
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Date::parse_from_str(&s, DATE_FORMAT).map_err(serde::de::Error::custom)
    }
}

fn parse_date(s: &str) -> Option<Date> {
    let formats = ["%d %B %Y %H:%M:%S %z", "%Y-%m-%d %H:%M:%S %z"];
    formats
        .iter()
        .filter_map(|f| Date::parse_from_str(s, f).ok())
        .next()
}

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

    static TRUE: Scalar = Scalar(ScalarEnum::Bool(true));
    static FALSE: Scalar = Scalar(ScalarEnum::Bool(false));

    #[test]
    fn test_to_str_bool() {
        assert_eq!(TRUE.to_str(), "true");
    }

    #[test]
    fn test_to_str_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_str(), "42");
    }

    #[test]
    fn test_to_str_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_str(), "42");

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_str(), "42.34");
    }

    #[test]
    fn test_to_str_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_str(), "foobar");
    }

    #[test]
    fn test_to_integer_bool() {
        assert_eq!(TRUE.to_integer(), None);
    }

    #[test]
    fn test_to_integer_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_integer(), Some(42i32));
    }

    #[test]
    fn test_to_integer_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_integer(), None);
    }

    #[test]
    fn test_to_integer_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = "42.34".into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = "42".into();
        assert_eq!(val.to_integer(), Some(42));
    }

    #[test]
    fn test_to_float_bool() {
        assert_eq!(TRUE.to_float(), None);
    }

    #[test]
    fn test_to_float_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_float(), Some(42f64));
    }

    #[test]
    fn test_to_float_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_float(), Some(42f64));

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_float(), Some(42.34));
    }

    #[test]
    fn test_to_float_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_float(), None);

        let val: Scalar = "42.34".into();
        assert_eq!(val.to_float(), Some(42.34));

        let val: Scalar = "42".into();
        assert_eq!(val.to_float(), Some(42f64));
    }

    #[test]
    fn test_to_bool_bool() {
        assert_eq!(TRUE.to_bool(), Some(true));
    }

    #[test]
    fn test_to_bool_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn test_to_bool_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn test_to_bool_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = "true".into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = "false".into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn integer_equality() {
        let val: Scalar = 42i32.into();
        let zero: Scalar = 0i32.into();
        assert_eq!(val, val);
        assert_eq!(zero, zero);
        assert!(val != zero);
        assert!(zero != val);
    }

    #[test]
    fn integers_have_ruby_truthiness() {
        let val: Scalar = 42i32.into();
        let zero: Scalar = 0i32.into();
        assert_eq!(TRUE, val);
        assert_eq!(val, TRUE);
        assert!(val.is_truthy());

        assert_eq!(TRUE, zero);
        assert_eq!(zero, TRUE);
        assert!(zero.is_truthy());
    }

    #[test]
    fn float_equality() {
        let val: Scalar = 42f64.into();
        let zero: Scalar = 0f64.into();
        assert_eq!(val, val);
        assert_eq!(zero, zero);
        assert!(val != zero);
        assert!(zero != val);
    }

    #[test]
    fn floats_have_ruby_truthiness() {
        let val: Scalar = 42f64.into();
        let zero: Scalar = 0f64.into();
        assert_eq!(TRUE, val);
        assert_eq!(val, TRUE);
        assert!(val.is_truthy());

        assert_eq!(TRUE, zero);
        assert_eq!(zero, TRUE);
        assert!(zero.is_truthy());
    }

    #[test]
    fn boolean_equality() {
        assert_eq!(TRUE, TRUE);
        assert_eq!(FALSE, FALSE);
        assert!(FALSE != TRUE);
        assert!(TRUE != FALSE);
    }

    #[test]
    fn booleans_have_ruby_truthiness() {
        assert!(TRUE.is_truthy());
        assert!(!FALSE.is_truthy());
    }

    #[test]
    fn string_equality() {
        let alpha: Scalar = "alpha".into();
        let beta: Scalar = "beta".into();
        let empty: Scalar = "".into();
        assert_eq!(alpha, alpha);
        assert_eq!(empty, empty);
        assert!(alpha != beta);
        assert!(beta != alpha);
    }

    #[test]
    fn strings_have_ruby_truthiness() {
        // all strings in ruby are true
        let alpha: Scalar = "alpha".into();
        let empty: Scalar = "".into();
        assert_eq!(TRUE, alpha);
        assert_eq!(alpha, TRUE);
        assert!(alpha.is_truthy());

        assert_eq!(TRUE, empty);
        assert_eq!(empty, TRUE);
        assert!(empty.is_truthy());
    }
}
<commit_msg>feat(date): Support today/now<commit_after>use std::borrow;
use std::cmp::Ordering;
use std::fmt;

use chrono;

pub type Date = chrono::DateTime<chrono::FixedOffset>;

/// A Liquid scalar value
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Scalar(ScalarEnum);

/// An enum to represent different value types
#[derive(Clone, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(untagged))]
enum ScalarEnum {
    Integer(i32),
    Float(f64),
    Bool(bool),
    #[cfg_attr(feature = "serde", serde(with = "friendly_date"))]
    Date(Date),
    Str(String),
}

impl Scalar {
    pub fn new<T: Into<Self>>(value: T) -> Self {
        value.into()
    }

    pub fn to_str(&self) -> borrow::Cow<str> {
        match self.0 {
            ScalarEnum::Integer(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Float(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Bool(ref x) => borrow::Cow::Owned(x.to_string()),
            ScalarEnum::Date(ref x) => borrow::Cow::Owned(x.format(DATE_FORMAT).to_string()),
            ScalarEnum::Str(ref x) => borrow::Cow::Borrowed(x.as_str()),
        }
    }

    pub fn into_string(self) -> String {
        match self.0 {
            ScalarEnum::Integer(x) => x.to_string(),
            ScalarEnum::Float(x) => x.to_string(),
            ScalarEnum::Bool(x) => x.to_string(),
            ScalarEnum::Date(x) => x.to_string(),
            ScalarEnum::Str(x) => x,
        }
    }

    /// Interpret as an integer, if possible
    pub fn to_integer(&self) -> Option<i32> {
        match self.0 {
            ScalarEnum::Integer(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => x.parse::<i32>().ok(),
            _ => None,
        }
    }

    /// Interpret as an float, if possible
    pub fn to_float(&self) -> Option<f64> {
        match self.0 {
            ScalarEnum::Integer(ref x) => Some(f64::from(*x)),
            ScalarEnum::Float(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => x.parse::<f64>().ok(),
            _ => None,
        }
    }

    /// Interpret as an bool, if possible
    pub fn to_bool(&self) -> Option<bool> {
        match self.0 {
            ScalarEnum::Bool(ref x) => Some(*x),
            _ => None,
        }
    }

    /// Interpret as an bool, if possible
    pub fn to_date(&self) -> Option<Date> {
        match self.0 {
            ScalarEnum::Date(ref x) => Some(*x),
            ScalarEnum::Str(ref x) => parse_date(x.as_str()),
            _ => None,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_truthy(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match self.0 {
            ScalarEnum::Bool(ref x) => *x,
            _ => true,
        }
    }

    /// Evaluate using Liquid "truthiness"
    pub fn is_default(&self) -> bool {
        // encode Ruby truthiness: all values except false and nil are true
        match self.0 {
            ScalarEnum::Bool(ref x) => !*x,
            ScalarEnum::Str(ref x) => x.is_empty(),
            _ => false,
        }
    }

    pub fn type_name(&self) -> &'static str {
        match self.0 {
            ScalarEnum::Integer(_) => "whole number",
            ScalarEnum::Float(_) => "fractional number",
            ScalarEnum::Bool(_) => "boolean",
            ScalarEnum::Date(_) => "date",
            ScalarEnum::Str(_) => "string",
        }
    }
}

impl From<i32> for Scalar {
    fn from(s: i32) -> Self {
        Scalar {
            0: ScalarEnum::Integer(s),
        }
    }
}

impl From<f64> for Scalar {
    fn from(s: f64) -> Self {
        Scalar {
            0: ScalarEnum::Float(s),
        }
    }
}

impl From<bool> for Scalar {
    fn from(s: bool) -> Self {
        Scalar {
            0: ScalarEnum::Bool(s),
        }
    }
}

impl From<Date> for Scalar {
    fn from(s: Date) -> Self {
        Scalar {
            0: ScalarEnum::Date(s),
        }
    }
}

impl From<String> for Scalar {
    fn from(s: String) -> Self {
        Scalar {
            0: ScalarEnum::Str(s),
        }
    }
}

impl<'a> From<&'a String> for Scalar {
    fn from(s: &String) -> Self {
        Scalar {
            0: ScalarEnum::Str(s.to_owned()),
        }
    }
}

impl<'a> From<&'a str> for Scalar {
    fn from(s: &str) -> Self {
        Scalar {
            0: ScalarEnum::Str(s.to_owned()),
        }
    }
}

impl PartialEq<Scalar> for Scalar {
    fn eq(&self, other: &Self) -> bool {
        match (&self.0, &other.0) {
            (&ScalarEnum::Integer(x), &ScalarEnum::Integer(y)) => x == y,
            (&ScalarEnum::Integer(x), &ScalarEnum::Float(y)) => (f64::from(x)) == y,
            (&ScalarEnum::Float(x), &ScalarEnum::Integer(y)) => x == (f64::from(y)),
            (&ScalarEnum::Float(x), &ScalarEnum::Float(y)) => x == y,
            (&ScalarEnum::Bool(x), &ScalarEnum::Bool(y)) => x == y,
            (&ScalarEnum::Date(x), &ScalarEnum::Date(y)) => x == y,
            (&ScalarEnum::Str(ref x), &ScalarEnum::Str(ref y)) => x == y,
            // encode Ruby truthiness: all values except false and nil are true
            (_, &ScalarEnum::Bool(b)) | (&ScalarEnum::Bool(b), _) => b,
            _ => false,
        }
    }
}

impl Eq for Scalar {}

impl PartialOrd<Scalar> for Scalar {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        match (&self.0, &other.0) {
            (&ScalarEnum::Integer(x), &ScalarEnum::Integer(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Integer(x), &ScalarEnum::Float(y)) => (f64::from(x)).partial_cmp(&y),
            (&ScalarEnum::Float(x), &ScalarEnum::Integer(y)) => x.partial_cmp(&(f64::from(y))),
            (&ScalarEnum::Float(x), &ScalarEnum::Float(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Bool(x), &ScalarEnum::Bool(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Date(x), &ScalarEnum::Date(y)) => x.partial_cmp(&y),
            (&ScalarEnum::Str(ref x), &ScalarEnum::Str(ref y)) => x.partial_cmp(y),
            _ => None,
        }
    }
}

impl fmt::Display for Scalar {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let data = self.to_str();
        write!(f, "{}", data)
    }
}

const DATE_FORMAT: &str = "%Y-%m-%d %H:%M:%S %z";

#[cfg(feature = "serde")]
mod friendly_date {
    use super::*;
    use serde::{self, Deserialize, Deserializer, Serializer};

    pub fn serialize<S>(date: &Date, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = date.format(DATE_FORMAT).to_string();
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Date, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        Date::parse_from_str(&s, DATE_FORMAT).map_err(serde::de::Error::custom)
    }
}

fn parse_date(s: &str) -> Option<Date> {
    match s {
        "now" | "today" => {
            let now = chrono::offset::Utc::now();
            let now = now.naive_utc();
            let now = chrono::DateTime::from_utc(now, chrono::offset::FixedOffset::east(0));
            Some(now)
        }
        _ => {
            let formats = ["%d %B %Y %H:%M:%S %z", "%Y-%m-%d %H:%M:%S %z"];
            formats
                .iter()
                .filter_map(|f| Date::parse_from_str(s, f).ok())
                .next()
        }
    }
}

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

    static TRUE: Scalar = Scalar(ScalarEnum::Bool(true));
    static FALSE: Scalar = Scalar(ScalarEnum::Bool(false));

    #[test]
    fn test_to_str_bool() {
        assert_eq!(TRUE.to_str(), "true");
    }

    #[test]
    fn test_to_str_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_str(), "42");
    }

    #[test]
    fn test_to_str_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_str(), "42");

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_str(), "42.34");
    }

    #[test]
    fn test_to_str_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_str(), "foobar");
    }

    #[test]
    fn test_to_integer_bool() {
        assert_eq!(TRUE.to_integer(), None);
    }

    #[test]
    fn test_to_integer_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_integer(), Some(42i32));
    }

    #[test]
    fn test_to_integer_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_integer(), None);
    }

    #[test]
    fn test_to_integer_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = "42.34".into();
        assert_eq!(val.to_integer(), None);

        let val: Scalar = "42".into();
        assert_eq!(val.to_integer(), Some(42));
    }

    #[test]
    fn test_to_float_bool() {
        assert_eq!(TRUE.to_float(), None);
    }

    #[test]
    fn test_to_float_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_float(), Some(42f64));
    }

    #[test]
    fn test_to_float_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_float(), Some(42f64));

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_float(), Some(42.34));
    }

    #[test]
    fn test_to_float_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_float(), None);

        let val: Scalar = "42.34".into();
        assert_eq!(val.to_float(), Some(42.34));

        let val: Scalar = "42".into();
        assert_eq!(val.to_float(), Some(42f64));
    }

    #[test]
    fn test_to_bool_bool() {
        assert_eq!(TRUE.to_bool(), Some(true));
    }

    #[test]
    fn test_to_bool_integer() {
        let val: Scalar = 42i32.into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn test_to_bool_float() {
        let val: Scalar = 42f64.into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = 42.34.into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn test_to_bool_str() {
        let val: Scalar = "foobar".into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = "true".into();
        assert_eq!(val.to_bool(), None);

        let val: Scalar = "false".into();
        assert_eq!(val.to_bool(), None);
    }

    #[test]
    fn integer_equality() {
        let val: Scalar = 42i32.into();
        let zero: Scalar = 0i32.into();
        assert_eq!(val, val);
        assert_eq!(zero, zero);
        assert!(val != zero);
        assert!(zero != val);
    }

    #[test]
    fn integers_have_ruby_truthiness() {
        let val: Scalar = 42i32.into();
        let zero: Scalar = 0i32.into();
        assert_eq!(TRUE, val);
        assert_eq!(val, TRUE);
        assert!(val.is_truthy());

        assert_eq!(TRUE, zero);
        assert_eq!(zero, TRUE);
        assert!(zero.is_truthy());
    }

    #[test]
    fn float_equality() {
        let val: Scalar = 42f64.into();
        let zero: Scalar = 0f64.into();
        assert_eq!(val, val);
        assert_eq!(zero, zero);
        assert!(val != zero);
        assert!(zero != val);
    }

    #[test]
    fn floats_have_ruby_truthiness() {
        let val: Scalar = 42f64.into();
        let zero: Scalar = 0f64.into();
        assert_eq!(TRUE, val);
        assert_eq!(val, TRUE);
        assert!(val.is_truthy());

        assert_eq!(TRUE, zero);
        assert_eq!(zero, TRUE);
        assert!(zero.is_truthy());
    }

    #[test]
    fn boolean_equality() {
        assert_eq!(TRUE, TRUE);
        assert_eq!(FALSE, FALSE);
        assert!(FALSE != TRUE);
        assert!(TRUE != FALSE);
    }

    #[test]
    fn booleans_have_ruby_truthiness() {
        assert!(TRUE.is_truthy());
        assert!(!FALSE.is_truthy());
    }

    #[test]
    fn string_equality() {
        let alpha: Scalar = "alpha".into();
        let beta: Scalar = "beta".into();
        let empty: Scalar = "".into();
        assert_eq!(alpha, alpha);
        assert_eq!(empty, empty);
        assert!(alpha != beta);
        assert!(beta != alpha);
    }

    #[test]
    fn strings_have_ruby_truthiness() {
        // all strings in ruby are true
        let alpha: Scalar = "alpha".into();
        let empty: Scalar = "".into();
        assert_eq!(TRUE, alpha);
        assert_eq!(alpha, TRUE);
        assert!(alpha.is_truthy());

        assert_eq!(TRUE, empty);
        assert_eq!(empty, TRUE);
        assert!(empty.is_truthy());
    }

    #[test]
    fn parse_date_empty_is_bad() {
        assert!(parse_date("").is_none());
    }

    #[test]
    fn parse_date_bad() {
        assert!(parse_date("aaaaa").is_none());
    }

    #[test]
    fn parse_date_now() {
        assert!(parse_date("now").is_some());
    }

    #[test]
    fn parse_date_today() {
        assert!(parse_date("today").is_some());
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
use std::sync::mpsc::Receiver;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            // Vector of absolute perfect matches
            let mut vones = Vec::new();

            // Look through each word in the message
            for word in &message.borrow().instances {
                let borrow = word.borrow();
                // Check each instance in that words instances
                for instance in &borrow.word.borrow().instances {
                    // Get the message for each instance
                    let omessage = instance.borrow().message.clone();
                    // Find what kind of matches exist between the messages
                    if let Mismatch::One(best) =
                        Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                        vones.push(best);
                    }
                }
            }

            // Now that we have perfect matches, merge them into the same Category
            for ms in vones {
                // We only want to combine if they aren't already in the same category
                if ms.0.borrow().category != ms.1.borrow().category {
                    let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                    Category::merge(cats);
                }
            }
        }
    }

    pub fn show_categories(&self) {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        for cat in set {
            println!("Category:");
            let catr = unsafe{&*cat};
            for instance in &catr.instances {
                let ib = instance.borrow();
                println!("\t{}", ib.word.borrow().name);
            }
        }
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<commit_msg>only print unique categories<commit_after>extern crate rand;
extern crate itertools;
use self::itertools::Itertools;

use std::collections::BTreeMap;
use std::collections::btree_map::Entry;
use std::rc::Rc;
use std::cell::RefCell;
use std::cell::Ref;
use std::sync::mpsc::Receiver;

type WordCell = Rc<RefCell<Word>>;
pub type AuthorCell = Rc<RefCell<Author>>;
pub type SourceCell = Rc<RefCell<Source>>;
type MessageCell = Rc<RefCell<Message>>;
type CategoryCell = Rc<RefCell<Category>>;
type ConversationCell = Rc<RefCell<Conversation>>;
type InstanceCell = Rc<RefCell<WordInstance>>;

enum Mismatch<T> {
    // Incompatible for matching or no mismatch (exactly the same)
    None,
    // Exactly one mismatch
    One(T),
    // Multiple mismatches
    Multiple,
}

fn wrap<T>(t: T) -> Rc<RefCell<T>> {
    Rc::new(RefCell::new(t))
}

// const RATIO_CONTAINED_BEFORE_COMBINATION: f64 = 0.8;

#[derive(Default)]
pub struct Lexicon<R: rand::Rng> {
    rng: R,
    words: BTreeMap<String, WordCell>,
    sources: BTreeMap<String, SourceCell>,
    conversations: Vec<ConversationCell>,
    messages: Vec<MessageCell>,

    active_conversations: BTreeMap<*const Source, ConversationCell>,
}

impl<R: rand::Rng> Lexicon<R> {
    pub fn new(rng: R) -> Lexicon<R> {
        Lexicon{
            rng: rng,
            words: Default::default(),
            sources: Default::default(),
            conversations: Default::default(),
            messages: Default::default(),
            active_conversations: Default::default(),
        }
    }

    pub fn source(&mut self, name: String) -> SourceCell {
        match self.sources.entry(name.clone()) {
            Entry::Vacant(v) => v.insert(wrap(Source{
                name: name,
                messages: 0,
                authors: BTreeMap::default(),
            })).clone(),
            Entry::Occupied(o) => o.get().clone(),
        }
    }

    pub fn author(&mut self, source: SourceCell, name: String) -> AuthorCell {
        match source.borrow_mut().authors.entry(name.clone()) {
            Entry::Vacant(v) => {
                let a = wrap(Author{
                    source: source.clone(),
                    name: name.clone(),
                });
                v.insert(a.clone());
                a
            },
            Entry::Occupied(o) => o.get().clone(),
        }
    }
    pub fn tell(&mut self, source: SourceCell, author: AuthorCell, content: String) -> Option<String> {
        let conversation = match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                let c = wrap(Conversation{
                    source: source.clone(),
                    messages: Vec::new(),
                });
                v.insert(c.clone());
                self.conversations.push(c.clone());
                c
            },
            Entry::Occupied(o) => {
                o.get().clone()
            },
        };

        let message = wrap(Message{
            author: author.clone(),
            conversation: conversation.clone(),
            index: conversation.borrow().messages.len(),
            instances: Vec::new(),
        });

        self.messages.push(message.clone());

        for word in content.split(' ').map(|s| {
            let string = s.to_string();
            match self.words.entry(string.clone()) {
                Entry::Vacant(v) => {
                    let w = wrap(Word{
                        name: string,
                        instances: Vec::new(),
                    });
                    v.insert(w.clone());
                    w
                },
                Entry::Occupied(o) => {
                    o.get().clone()
                },
            }
        }) {
            // Create empty category for the word
            let category = wrap(Category::default());
            // Create instance of the word
            let instance = wrap(WordInstance{
                word: word.clone(),
                category: category.clone(),
                message: message.clone(),
                index: message.borrow().instances.len(),
            });
            // Insert word instance into the word, category, and message for future reference
            message.borrow_mut().instances.push(instance.clone());
            category.borrow_mut().instances.push(instance.clone());
            word.borrow_mut().instances.push(instance);
        }

        // Increment the messages by 1 for the source
        source.borrow_mut().messages += 1;

        // TODO: Determine what to say back
        None
    }

    pub fn initiate(&mut self, source: SourceCell) -> Option<String> {
        let conversation = wrap(Conversation{
            source: source.clone(),
            messages: Vec::new(),
        });

        match self.active_conversations.entry(&*source.borrow() as *const Source) {
            Entry::Vacant(v) => {
                v.insert(conversation.clone());
            },
            Entry::Occupied(mut o) => {
                o.insert(conversation.clone());
            },
        };

        self.conversations.push(conversation.clone());

        let base = match self.rng.choose(&self.messages[..]) {
            Some(m) => m.clone(),
            None => return None,
        };

        let bborrow = base.borrow();

        Some(format!("{} ~ {}",
            bborrow.instances.iter()
                .map(|instance| instance.borrow().category.clone())
                // TODO: Allow random choosing from co-category instances as well
                .map(|category| self.rng.choose(&category.borrow().instances[..]).unwrap().clone())
                .map(|instance| {
                    let ins = instance.borrow();
                    let word = ins.word.borrow();
                    word.name.clone()
                })
                .join(" "),
            bborrow.instances.iter()
                .map(|i| {
                    let b = i.borrow();
                    let b = b.word.borrow();
                    b.name.clone()
                })
                .join(" "))
        )
    }

    pub fn think(&mut self, end: Receiver<()>) {
        use std::sync::mpsc::TryRecvError::Empty;
        while end.try_recv() == Err(Empty) {
            // Choose a random message or break if there are none
            let message = match self.rng.choose(&self.messages[..]) {
                Some(m) => m.clone(),
                None => break,
            };

            // Vector of absolute perfect matches
            let mut vones = Vec::new();

            // Look through each word in the message
            for word in &message.borrow().instances {
                let borrow = word.borrow();
                // Check each instance in that words instances
                for instance in &borrow.word.borrow().instances {
                    // Get the message for each instance
                    let omessage = instance.borrow().message.clone();
                    // Find what kind of matches exist between the messages
                    if let Mismatch::One(best) =
                        Message::category_and_word_mismatch((message.clone(), omessage.clone())) {
                        vones.push(best);
                    }
                }
            }

            // Now that we have perfect matches, merge them into the same Category
            for ms in vones {
                // We only want to combine if they aren't already in the same category
                if ms.0.borrow().category != ms.1.borrow().category {
                    let cats = (ms.0.borrow().category.clone(), ms.1.borrow().category.clone());
                    Category::merge(cats);
                }
            }
        }
    }

    /// Print all multiple categories and return the amount of categories total
    pub fn show_categories(&self) -> usize {
        use std::collections::BTreeSet;
        let mut set = BTreeSet::new();
        for message in &self.messages {
            for instance in &message.borrow().instances {
                let ib = instance.borrow();
                set.insert(&*ib.category.borrow() as *const Category);
            }
        }

        let len = set.len();

        for cat in set {
            let catr = unsafe{&*cat};
            if catr.instances.len() != 1 {
                println!("Category:");
                for instance in &catr.instances {
                    let ib = instance.borrow();
                    println!("\t{}", ib.word.borrow().name);
                }
            }
        }
        len
    }
}

macro_rules! pointer_eq {
    ($s:ident) => {
        impl PartialEq for $s {
            fn eq(&self, other: &Self) -> bool {
                self as *const Self == other as *const Self
            }

            fn ne(&self, other: &Self) -> bool {
                self as *const Self != other as *const Self
            }
        }
    };
}

struct Conversation {
    source: SourceCell,
    messages: Vec<MessageCell>,
}

pointer_eq!(Conversation);

pub struct Author {
    source: SourceCell,
    name: String,
}

pointer_eq!(Author);

pub struct Source {
    name: String,
    messages: u64,
    authors: BTreeMap<String, AuthorCell>,
}

pointer_eq!(Source);

struct WordInstance {
    word: WordCell,
    category: CategoryCell,
    message: MessageCell,
    index: usize,
}

pointer_eq!(WordInstance);

struct Message {
    author: AuthorCell,
    conversation: ConversationCell,
    index: usize,
    instances: Vec<InstanceCell>,
}

impl Message {
    fn mismatch<F>(messages: (MessageCell, MessageCell), diff: F) -> Mismatch<(InstanceCell, InstanceCell)>
        where F: Fn((&WordInstance, &WordInstance)) -> bool
    {
        if messages.0 == messages.1 ||
            messages.0.borrow().instances.len() != messages.1.borrow().instances.len() {
            return Mismatch::None;
        }
        messages.0.borrow().instances.iter()
            .zip(messages.1.borrow().instances.iter())
            .fold(Mismatch::None, |acc, ws| {
                let bs = (ws.0.borrow(), ws.1.borrow());
                match acc {
                    Mismatch::None => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::One((ws.0.clone(), ws.1.clone()))
                        } else {
                            Mismatch::None
                        }
                    },
                    Mismatch::One(best) => {
                        if diff((&*bs.0, &*bs.1)) {
                            Mismatch::Multiple
                        } else {
                            Mismatch::One(best)
                        }
                    },
                    Mismatch::Multiple => Mismatch::Multiple,
                }
            })
    }
    /*fn category_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.category != ins.1.category)
    }

    fn word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word)
    }*/

    fn category_and_word_mismatch(messages: (MessageCell, MessageCell)) ->
        Mismatch<(InstanceCell, InstanceCell)> {
        Self::mismatch(messages, |ins| ins.0.word != ins.1.word && ins.0.category != ins.1.category)
    }
}

pointer_eq!(Message);

#[derive(Default)]
struct Category {
    instances: Vec<InstanceCell>,
    cocategories: Vec<CategoryCell>,
}

impl Category {
    fn merge(cs: (CategoryCell, CategoryCell)) -> CategoryCell {
        let cc = wrap(Self::default());
        {
            let clos = |cat: &CategoryCell| {
                for instance in cat.borrow().instances.iter().cloned() {
                    instance.borrow_mut().category = cc.clone();
                }
            };
            clos(&cs.0);
            clos(&cs.1);
            let mut ccm = cc.borrow_mut();
            ccm.instances.append(&mut cs.0.borrow_mut().instances);
            ccm.instances.append(&mut cs.1.borrow_mut().instances);
        }
        cc
    }
}

pointer_eq!(Category);

struct Word {
    name: String,
    instances: Vec<InstanceCell>,
}

pointer_eq!(Word);
<|endoftext|>


<commit_before>extern crate clock_ticks;
extern crate rand;
extern crate sdl2;
extern crate sdl2_gfx;
extern crate sdl2_image;
extern crate sdl2_mixer;
extern crate sdl2_ttf;

use pongo::ui::{Drawable,Ui};

use rand::distributions::{IndependentSample, Range};

use sdl2::{AudioSubsystem, Sdl};
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::{Renderer, Texture};

use sdl2_gfx::primitives::DrawRenderer;
use sdl2_image::{LoadTexture, INIT_PNG};
use sdl2_mixer::{AUDIO_S16LSB, DEFAULT_FREQUENCY, Music};
use sdl2_ttf::{Font, Hinting, Sdl2TtfContext};

use std::cell::RefCell;
use std::f32;
use std::path::Path;
use std::rc::Rc;
use std::thread;
use std::vec::Vec;

use super::Resettable;

pub struct Net {
    pub color: Color,
    pub x: f32,            // x pixel coordinate of top left corner
    pub dot_width: f32,
    pub dot_height: f32,
    pub num_dots: i32
}

impl Net {

    pub fn new(color: Color, x: f32, dot_width: f32, dot_height: f32, num_dots: i32) -> Net {
        return Net {
            color: color,
            x: x,
            dot_width: dot_width,
            dot_height: dot_height,
            num_dots: num_dots
        };
    }

}

impl Drawable for Net {

    fn draw(&self, ui: &mut Ui) {
        let dot_x = self.x;
        let num_gaps = self.num_dots - 1;
        for i in 0..self.num_dots + num_gaps + 1 {
            if i % 2 == 0 {
                let dot_y = i as f32 * self.dot_height;
                ui.renderer.set_draw_color(self.color);
                let dot_rect = Rect::new_unwrap(dot_x as i32, dot_y as i32,
                                                self.dot_width as u32,
                                                self.dot_height as u32);
                ui.renderer.fill_rect(dot_rect);
            }
        }
    }
}
<commit_msg>fixed warnings in net.rs<commit_after>extern crate clock_ticks;
extern crate rand;
extern crate sdl2;
extern crate sdl2_gfx;
extern crate sdl2_image;
extern crate sdl2_mixer;
extern crate sdl2_ttf;

use pongo::ui::{Drawable,Ui};

use sdl2::pixels::Color;
use sdl2::rect::Rect;

pub struct Net {
    pub color: Color,
    pub x: f32,            // x pixel coordinate of top left corner
    pub dot_width: f32,
    pub dot_height: f32,
    pub num_dots: i32
}

impl Net {

    pub fn new(color: Color, x: f32, dot_width: f32, dot_height: f32, num_dots: i32) -> Net {
        return Net {
            color: color,
            x: x,
            dot_width: dot_width,
            dot_height: dot_height,
            num_dots: num_dots
        };
    }

}

impl Drawable for Net {

    fn draw(&self, ui: &mut Ui) {
        let dot_x = self.x;
        let num_gaps = self.num_dots - 1;
        for i in 0..self.num_dots + num_gaps + 1 {
            if i % 2 == 0 {
                let dot_y = i as f32 * self.dot_height;
                ui.renderer.set_draw_color(self.color);
                let dot_rect = Rect::new_unwrap(dot_x as i32, dot_y as i32,
                                                self.dot_width as u32,
                                                self.dot_height as u32);
                ui.renderer.fill_rect(dot_rect);
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! High performance Trivial File Transfer Protocol (TFTP) protocol implementation.
//!
//! RFCs implemented:
//!
//! - RFC 1350 - TFTP Protocol (revision 2) (http://tools.ietf.org/html/rfc1350)

#![crate_id = "tftp"]
#![license = "MIT/ASL2"]
#![crate_type = "lib"]

pub mod packet;
pub mod netascii;

pub mod client;
<commit_msg>Changed crate_id to crate_name<commit_after>// Copyright 2014 Arjan Topolovec
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//

//! High performance Trivial File Transfer Protocol (TFTP) protocol implementation.
//!
//! RFCs implemented:
//!
//! - RFC 1350 - TFTP Protocol (revision 2) (http://tools.ietf.org/html/rfc1350)

#![crate_name = "tftp"]
#![license = "MIT/ASL2"]
#![crate_type = "lib"]

pub mod packet;
pub mod netascii;

pub mod client;
<|endoftext|>


<commit_before>pub fn tokenize(text: &str) -> Vec<&str> {
  let vec_with_empty: Vec<&str> = text.split(|c: char| char_is_token(c)).collect();
  let mut ret_vec = Vec::new();
  for s in vec_with_empty.into_iter() {
    if s.len() > 0 {
      ret_vec.push(s);
    }
  }
  ret_vec
}

fn char_is_token(a: char) -> bool {
  match a {
    ' ' => true,
    ',' => true,
    '.' => true,
    '!' => true,
    '?' => true,
    ';' => true,
    '\'' => true,
    '"' => true,
    ':' => true,
    '\t' => true,
    '\n' => true,
    '(' => true,
    ')' => true,
    '-' => true,
    _ => false
  }
}<commit_msg>Optimize tokenize<commit_after>use std::str::CharEq;

pub fn tokenize(text: &str) -> Vec<&str> {
  text.split(Splitter).filter(|s| s.len() > 0).collect()
}

struct Splitter;

impl CharEq for Splitter {
    fn matches(&mut self, c: char) -> bool {
        match c {
            ' ' | ',' | '.' | '!' | '?' | ';' | '\'' |  '"'
            | ':' | '\t' | '\n' | '(' | ')' | '-' => true,
            _ => false
        }
    }

    // We're only matching ASCII chars, so we can use the faster impl.
    fn only_ascii(&self) -> bool { true }
}
<|endoftext|>