<commit_before>//! Schannel credentials.
use winapi;
use secur32;
use std::io;
use std::mem;
use std::ptr;

use Inner;
use cert_context::CertContext;

lazy_static! {
    static ref UNISP_NAME: Vec<u8> = winapi::UNISP_NAME.bytes().chain(Some(0)).collect();
}

/// The communication direction that an `SchannelCred` will support.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum Direction {
    /// Server-side, inbound connections.
    Inbound,
    /// Client-side, outbound connections.
    Outbound,
}

/// Algorithms supported by Schannel.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum Algorithm {
    /// Advanced Encryption Standard (AES).
    Aes = winapi::CALG_AES,
    /// 128 bit AES.
    Aes128 = winapi::CALG_AES_128,
    /// 192 bit AES.
    Aes192 = winapi::CALG_AES_192,
    /// 256 bit AES.
    Aes256 = winapi::CALG_AES_256,
    /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
    AgreedkeyAny = winapi::CALG_AGREEDKEY_ANY,
    /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
    /// its key length 64 bits.
    CylinkMek = winapi::CALG_CYLINK_MEK,
    /// DES encryption algorithm.
    Des = winapi::CALG_DES,
    /// DESX encryption algorithm.
    Desx = winapi::CALG_DESX,
    /// Diffie-Hellman ephemeral key exchange algorithm.
    DhEphem = winapi::CALG_DH_EPHEM,
    /// Diffie-Hellman store and forward key exchange algorithm.
    DhSf = winapi::CALG_DH_SF,
    /// DSA public key signature algorithm.
    DssSign = winapi::CALG_DSS_SIGN,
    /// Elliptic curve Diffie-Hellman key exchange algorithm.
    Ecdh = winapi::CALG_ECDH,
    // https://github.com/retep998/winapi-rs/issues/287
    // /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
    // EcdhEphem = winapi::CALG_ECDH_EPHEM,
    /// Elliptic curve digital signature algorithm.
    Ecdsa = winapi::CALG_ECDSA,
    /// One way function hashing algorithm.
    HashReplaceOwf = winapi::CALG_HASH_REPLACE_OWF,
    /// Hughes MD5 hashing algorithm.
    HughesMd5 = winapi::CALG_HUGHES_MD5,
    /// HMAC keyed hash algorithm.
    Hmac = winapi::CALG_HMAC,
    /// MAC keyed hash algorithm.
    Mac = winapi::CALG_MAC,
    /// MD2 hashing algorithm.
    Md2 = winapi::CALG_MD2,
    /// MD4 hashing algorithm.
    Md4 = winapi::CALG_MD4,
    /// MD5 hashing algorithm.
    Md5 = winapi::CALG_MD5,
    /// No signature algorithm..
    NoSign = winapi::CALG_NO_SIGN,
    /// RC2 block encryption algorithm.
    Rc2 = winapi::CALG_RC2,
    /// RC4 stream encryption algorithm.
    Rc4 = winapi::CALG_RC4,
    /// RC5 block encryption algorithm.
    Rc5 = winapi::CALG_RC5,
    /// RSA public key exchange algorithm.
    RsaKeyx = winapi::CALG_RSA_KEYX,
    /// RSA public key signature algorithm.
    RsaSign = winapi::CALG_RSA_SIGN,
    /// SHA hashing algorithm.
    Sha1 = winapi::CALG_SHA1,
    /// 256 bit SHA hashing algorithm.
    Sha256 = winapi::CALG_SHA_256,
    /// 384 bit SHA hashing algorithm.
    Sha384 = winapi::CALG_SHA_384,
    /// 512 bit SHA hashing algorithm.
    Sha512 = winapi::CALG_SHA_512,
    /// Triple DES encryption algorithm.
    TripleDes = winapi::CALG_3DES,
    /// Two-key triple DES encryption with effective key length equal to 112 bits.
    TripleDes112 = winapi::CALG_3DES_112,
    #[doc(hidden)]
    __ForExtensibility,
}

/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
pub enum Protocol {
    /// Secure Sockets Layer 3.0
    Ssl3,
    /// Transport Layer Security 1.0
    Tls10,
    /// Transport Layer Security 1.1
    Tls11,
    /// Transport Layer Security 1.2
    Tls12,
    #[doc(hidden)]
    __ForExtensibility,
}

impl Protocol {
    fn dword(self, direction: Direction) -> winapi::DWORD {
        match (self, direction) {
            (Protocol::Ssl3, Direction::Inbound) => winapi::SP_PROT_SSL3_SERVER,
            (Protocol::Tls10, Direction::Inbound) => winapi::SP_PROT_TLS1_0_SERVER,
            (Protocol::Tls11, Direction::Inbound) => winapi::SP_PROT_TLS1_1_SERVER,
            (Protocol::Tls12, Direction::Inbound) => winapi::SP_PROT_TLS1_2_SERVER,
            (Protocol::Ssl3, Direction::Outbound) => winapi::SP_PROT_SSL3_CLIENT,
            (Protocol::Tls10, Direction::Outbound) => winapi::SP_PROT_TLS1_0_CLIENT,
            (Protocol::Tls11, Direction::Outbound) => winapi::SP_PROT_TLS1_1_CLIENT,
            (Protocol::Tls12, Direction::Outbound) => winapi::SP_PROT_TLS1_2_CLIENT,
            (Protocol::__ForExtensibility, _) => unreachable!(),
        }
    }
}

/// A builder type for `SchannelCred`s.
#[derive(Default, Debug)]
pub struct Builder {
    supported_algorithms: Option<Vec<Algorithm>>,
    enabled_protocols: Option<Vec<Protocol>>,
    certs: Vec<CertContext>,
}

impl Builder {
    /// Returns a new `Builder`.
    pub fn new() -> Builder {
        Builder::default()
    }

    /// Sets the algorithms supported for credentials created from this builder.
    pub fn supported_algorithms(&mut self,
                                supported_algorithms: &[Algorithm])
                                -> &mut Builder {
        assert!(supported_algorithms.iter()
            .all(|a| {
                match *a {
                    Algorithm::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.supported_algorithms = Some(supported_algorithms.to_owned());
        self
    }

    /// Sets the protocols enabled for credentials created from this builder.
    pub fn enabled_protocols(&mut self,
                             enabled_protocols: &[Protocol])
                             -> &mut Builder {
        assert!(enabled_protocols.iter()
            .all(|a| {
                match *a {
                    Protocol::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.enabled_protocols = Some(enabled_protocols.to_owned());
        self
    }

    /// Add a certificate to get passed down when the credentials are acquired.
    ///
    /// Certificates passed here may specify a certificate that contains a
    /// private key to be used in authenticating the application. Typically,
    /// this is called once for each key exchange method supported by
    /// servers.
    ///
    /// Clients often do not call this function and either depend on Schannel to
    /// find an appropriate certificate or create a certificate later if needed.
    pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
        self.certs.push(cx);
        self
    }

    /// Creates a new `SchannelCred`.
    pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
        unsafe {
            let mut handle = mem::zeroed();
            let mut cred_data: winapi::SCHANNEL_CRED = mem::zeroed();
            cred_data.dwVersion = winapi::SCHANNEL_CRED_VERSION;
            cred_data.dwFlags = winapi::SCH_USE_STRONG_CRYPTO;
            if let Some(ref supported_algorithms) = self.supported_algorithms {
                cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD;
                cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
            }
            if let Some(ref enabled_protocols) = self.enabled_protocols {
                cred_data.grbitEnabledProtocols = enabled_protocols.iter()
                    .map(|p| p.dword(direction))
                    .fold(0, |acc, p| acc | p);
            }
            let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
            cred_data.cCreds = certs.len() as winapi::DWORD;
            cred_data.paCred = certs.as_mut_ptr();

            let direction = match direction {
                Direction::Inbound => winapi::SECPKG_CRED_INBOUND,
                Direction::Outbound => winapi::SECPKG_CRED_OUTBOUND,
            };

            match secur32::AcquireCredentialsHandleA(ptr::null_mut(),
                                                     UNISP_NAME.as_ptr() as *const _ as *mut _,
                                                     direction,
                                                     ptr::null_mut(),
                                                     &mut cred_data as *mut _ as *mut _,
                                                     None,
                                                     ptr::null_mut(),
                                                     &mut handle,
                                                     ptr::null_mut()) {
                winapi::SEC_E_OK => Ok(SchannelCred(handle)),
                err => Err(io::Error::from_raw_os_error(err as i32)),
            }
        }
    }
}

/// An SChannel credential.
pub struct SchannelCred(winapi::CredHandle);

impl Drop for SchannelCred {
    fn drop(&mut self) {
        unsafe {
            secur32::FreeCredentialsHandle(&mut self.0);
        }
    }
}

impl Inner<winapi::CredHandle> for SchannelCred {
    unsafe fn from_inner(inner: winapi::CredHandle) -> SchannelCred {
        SchannelCred(inner)
    }

    fn as_inner(&self) -> winapi::CredHandle {
        self.0
    }

    fn get_mut(&mut self) -> &mut winapi::CredHandle {
        &mut self.0
    }
}

impl SchannelCred {
    /// Returns a builder.
    pub fn builder() -> Builder {
        Builder::new()
    }
}
<commit_msg>do not automatically attempt to get a child-certificate chain, fix #37<commit_after>//! Schannel credentials.
use winapi;
use secur32;
use std::io;
use std::mem;
use std::ptr;

use Inner;
use cert_context::CertContext;

lazy_static! {
    static ref UNISP_NAME: Vec<u8> = winapi::UNISP_NAME.bytes().chain(Some(0)).collect();
}

/// The communication direction that an `SchannelCred` will support.
#[derive(Copy, Debug, Clone, PartialEq, Eq)]
pub enum Direction {
    /// Server-side, inbound connections.
    Inbound,
    /// Client-side, outbound connections.
    Outbound,
}

/// Algorithms supported by Schannel.
// https://msdn.microsoft.com/en-us/library/windows/desktop/aa375549(v=vs.85).aspx
#[derive(Debug, Copy, Clone)]
#[repr(u32)]
pub enum Algorithm {
    /// Advanced Encryption Standard (AES).
    Aes = winapi::CALG_AES,
    /// 128 bit AES.
    Aes128 = winapi::CALG_AES_128,
    /// 192 bit AES.
    Aes192 = winapi::CALG_AES_192,
    /// 256 bit AES.
    Aes256 = winapi::CALG_AES_256,
    /// Temporary algorithm identifier for handles of Diffie-Hellman–agreed keys.
    AgreedkeyAny = winapi::CALG_AGREEDKEY_ANY,
    /// An algorithm to create a 40-bit DES key that has parity bits and zeroed key bits to make
    /// its key length 64 bits.
    CylinkMek = winapi::CALG_CYLINK_MEK,
    /// DES encryption algorithm.
    Des = winapi::CALG_DES,
    /// DESX encryption algorithm.
    Desx = winapi::CALG_DESX,
    /// Diffie-Hellman ephemeral key exchange algorithm.
    DhEphem = winapi::CALG_DH_EPHEM,
    /// Diffie-Hellman store and forward key exchange algorithm.
    DhSf = winapi::CALG_DH_SF,
    /// DSA public key signature algorithm.
    DssSign = winapi::CALG_DSS_SIGN,
    /// Elliptic curve Diffie-Hellman key exchange algorithm.
    Ecdh = winapi::CALG_ECDH,
    // https://github.com/retep998/winapi-rs/issues/287
    // /// Ephemeral elliptic curve Diffie-Hellman key exchange algorithm.
    // EcdhEphem = winapi::CALG_ECDH_EPHEM,
    /// Elliptic curve digital signature algorithm.
    Ecdsa = winapi::CALG_ECDSA,
    /// One way function hashing algorithm.
    HashReplaceOwf = winapi::CALG_HASH_REPLACE_OWF,
    /// Hughes MD5 hashing algorithm.
    HughesMd5 = winapi::CALG_HUGHES_MD5,
    /// HMAC keyed hash algorithm.
    Hmac = winapi::CALG_HMAC,
    /// MAC keyed hash algorithm.
    Mac = winapi::CALG_MAC,
    /// MD2 hashing algorithm.
    Md2 = winapi::CALG_MD2,
    /// MD4 hashing algorithm.
    Md4 = winapi::CALG_MD4,
    /// MD5 hashing algorithm.
    Md5 = winapi::CALG_MD5,
    /// No signature algorithm..
    NoSign = winapi::CALG_NO_SIGN,
    /// RC2 block encryption algorithm.
    Rc2 = winapi::CALG_RC2,
    /// RC4 stream encryption algorithm.
    Rc4 = winapi::CALG_RC4,
    /// RC5 block encryption algorithm.
    Rc5 = winapi::CALG_RC5,
    /// RSA public key exchange algorithm.
    RsaKeyx = winapi::CALG_RSA_KEYX,
    /// RSA public key signature algorithm.
    RsaSign = winapi::CALG_RSA_SIGN,
    /// SHA hashing algorithm.
    Sha1 = winapi::CALG_SHA1,
    /// 256 bit SHA hashing algorithm.
    Sha256 = winapi::CALG_SHA_256,
    /// 384 bit SHA hashing algorithm.
    Sha384 = winapi::CALG_SHA_384,
    /// 512 bit SHA hashing algorithm.
    Sha512 = winapi::CALG_SHA_512,
    /// Triple DES encryption algorithm.
    TripleDes = winapi::CALG_3DES,
    /// Two-key triple DES encryption with effective key length equal to 112 bits.
    TripleDes112 = winapi::CALG_3DES_112,
    #[doc(hidden)]
    __ForExtensibility,
}

/// Protocols supported by Schannel.
#[derive(Debug, Copy, Clone)]
pub enum Protocol {
    /// Secure Sockets Layer 3.0
    Ssl3,
    /// Transport Layer Security 1.0
    Tls10,
    /// Transport Layer Security 1.1
    Tls11,
    /// Transport Layer Security 1.2
    Tls12,
    #[doc(hidden)]
    __ForExtensibility,
}

impl Protocol {
    fn dword(self, direction: Direction) -> winapi::DWORD {
        match (self, direction) {
            (Protocol::Ssl3, Direction::Inbound) => winapi::SP_PROT_SSL3_SERVER,
            (Protocol::Tls10, Direction::Inbound) => winapi::SP_PROT_TLS1_0_SERVER,
            (Protocol::Tls11, Direction::Inbound) => winapi::SP_PROT_TLS1_1_SERVER,
            (Protocol::Tls12, Direction::Inbound) => winapi::SP_PROT_TLS1_2_SERVER,
            (Protocol::Ssl3, Direction::Outbound) => winapi::SP_PROT_SSL3_CLIENT,
            (Protocol::Tls10, Direction::Outbound) => winapi::SP_PROT_TLS1_0_CLIENT,
            (Protocol::Tls11, Direction::Outbound) => winapi::SP_PROT_TLS1_1_CLIENT,
            (Protocol::Tls12, Direction::Outbound) => winapi::SP_PROT_TLS1_2_CLIENT,
            (Protocol::__ForExtensibility, _) => unreachable!(),
        }
    }
}

/// A builder type for `SchannelCred`s.
#[derive(Default, Debug)]
pub struct Builder {
    supported_algorithms: Option<Vec<Algorithm>>,
    enabled_protocols: Option<Vec<Protocol>>,
    certs: Vec<CertContext>,
}

impl Builder {
    /// Returns a new `Builder`.
    pub fn new() -> Builder {
        Builder::default()
    }

    /// Sets the algorithms supported for credentials created from this builder.
    pub fn supported_algorithms(&mut self,
                                supported_algorithms: &[Algorithm])
                                -> &mut Builder {
        assert!(supported_algorithms.iter()
            .all(|a| {
                match *a {
                    Algorithm::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.supported_algorithms = Some(supported_algorithms.to_owned());
        self
    }

    /// Sets the protocols enabled for credentials created from this builder.
    pub fn enabled_protocols(&mut self,
                             enabled_protocols: &[Protocol])
                             -> &mut Builder {
        assert!(enabled_protocols.iter()
            .all(|a| {
                match *a {
                    Protocol::__ForExtensibility => false,
                    _ => true,
                }
            }));
        self.enabled_protocols = Some(enabled_protocols.to_owned());
        self
    }

    /// Add a certificate to get passed down when the credentials are acquired.
    ///
    /// Certificates passed here may specify a certificate that contains a
    /// private key to be used in authenticating the application. Typically,
    /// this is called once for each key exchange method supported by
    /// servers.
    ///
    /// Clients often do not call this function and either depend on Schannel to
    /// find an appropriate certificate or create a certificate later if needed.
    pub fn cert(&mut self, cx: CertContext) -> &mut Builder {
        self.certs.push(cx);
        self
    }

    /// Creates a new `SchannelCred`.
    pub fn acquire(&self, direction: Direction) -> io::Result<SchannelCred> {
        unsafe {
            let mut handle = mem::zeroed();
            let mut cred_data: winapi::SCHANNEL_CRED = mem::zeroed();
            cred_data.dwVersion = winapi::SCHANNEL_CRED_VERSION;
            cred_data.dwFlags = winapi::SCH_USE_STRONG_CRYPTO | winapi::SCH_CRED_NO_DEFAULT_CREDS;
            if let Some(ref supported_algorithms) = self.supported_algorithms {
                cred_data.cSupportedAlgs = supported_algorithms.len() as winapi::DWORD;
                cred_data.palgSupportedAlgs = supported_algorithms.as_ptr() as *mut _;
            }
            if let Some(ref enabled_protocols) = self.enabled_protocols {
                cred_data.grbitEnabledProtocols = enabled_protocols.iter()
                    .map(|p| p.dword(direction))
                    .fold(0, |acc, p| acc | p);
            }
            let mut certs = self.certs.iter().map(|c| c.as_inner()).collect::<Vec<_>>();
            cred_data.cCreds = certs.len() as winapi::DWORD;
            cred_data.paCred = certs.as_mut_ptr();

            let direction = match direction {
                Direction::Inbound => winapi::SECPKG_CRED_INBOUND,
                Direction::Outbound => winapi::SECPKG_CRED_OUTBOUND,
            };

            match secur32::AcquireCredentialsHandleA(ptr::null_mut(),
                                                     UNISP_NAME.as_ptr() as *const _ as *mut _,
                                                     direction,
                                                     ptr::null_mut(),
                                                     &mut cred_data as *mut _ as *mut _,
                                                     None,
                                                     ptr::null_mut(),
                                                     &mut handle,
                                                     ptr::null_mut()) {
                winapi::SEC_E_OK => Ok(SchannelCred(handle)),
                err => Err(io::Error::from_raw_os_error(err as i32)),
            }
        }
    }
}

/// An SChannel credential.
pub struct SchannelCred(winapi::CredHandle);

impl Drop for SchannelCred {
    fn drop(&mut self) {
        unsafe {
            secur32::FreeCredentialsHandle(&mut self.0);
        }
    }
}

impl Inner<winapi::CredHandle> for SchannelCred {
    unsafe fn from_inner(inner: winapi::CredHandle) -> SchannelCred {
        SchannelCred(inner)
    }

    fn as_inner(&self) -> winapi::CredHandle {
        self.0
    }

    fn get_mut(&mut self) -> &mut winapi::CredHandle {
        &mut self.0
    }
}

impl SchannelCred {
    /// Returns a builder.
    pub fn builder() -> Builder {
        Builder::new()
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, All Contributors (see CONTRIBUTORS file)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

macro_rules! write_size_into_slice {
    ($size:expr, $slice: expr) => {
     match $size {
        0...120 => {
            $slice[0] = $size as u8;
            1
        }
        121...255 => {
            $slice[0] = 121u8;
            $slice[1] = $size as u8;
            2
        }
        256...65535 => {
            $slice[0] = 122u8;
            $slice[1] = ($size >> 8) as u8;
            $slice[2] = $size as u8;
            3
        }
        65536...4294967296 => {
            $slice[0] = 123u8;
            $slice[1] = ($size >> 24) as u8;
            $slice[2] = ($size >> 16) as u8;
            $slice[3] = ($size >> 8) as u8;
            $slice[4] = $size as u8;
            5
        }
        _ => unreachable!(),
    }
    };
}

macro_rules! handle_words {
    ($env: expr, $program: expr, $word: expr, $res: ident,
     $pid: ident, { $($me: expr => $name: ident),* }, $block: expr) => {
    {
      let mut env = $env;
      if $word != TRY_END && !env.aborting_try.is_empty() {
        let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
        $block
      } else {
          $(
           env =
            match $me.$name(env, $word, $pid) {
              Err((env, Error::Reschedule)) => return Ok((env, Some($program.clone()))),
              Err((env, Error::UnknownWord)) => env,
              Err((mut env, err @ Error::ProgramError(_))) => {
                if env.tracking_errors > 0 {
                   env.aborting_try.push(err);
                   let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
                   $block
                } else {
                   return Err((env, err))
                }
              },
              Err((env, err)) => return Err((env, err)),
              Ok($res) => $block
            };
          )*
          if env.tracking_errors > 0 {
             env.aborting_try.push(error_unknown_word!($word));
             let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
             $block
          } else {
             return Err((env, error_unknown_word!($word)))
          }
      }
    }
    };
}

macro_rules! validate_lockout {
    ($env: expr, $name: expr, $pid: expr) => {
        if let Some((pid_, _)) = $name {
            if pid_ != $pid {
                return Err(($env, Error::Reschedule))
            }
        }
    };
}

macro_rules! read_or_write_transaction {
    ($me: expr, $env: expr) => {
        if let Some((_, ref txn)) = $me.db_write_txn {
            txn.deref()
        } else if let Some((_, ref txn)) = $me.db_read_txn {
            txn.deref()
        } else {
            return Err(($env, error_no_transaction!()));
        };
    };
}

macro_rules! stack_pop {
    ($env: expr) => {
        match $env.pop() {
            None => {
                return Err(($env, error_empty_stack!()))
            }
            Some(e) => {
                e
            }
        }
    }
}

macro_rules! word_is {
    ($env: expr, $word: expr, $exp: expr) => {
        if $word != $exp {
            return Err(($env, Error::UnknownWord))
        }
    };
}

macro_rules! error_program {
    ($desc: expr, $details: expr, $code: expr) => {{
        let mut error = Vec::new();

        write_size_header!($desc, error);
        error.extend_from_slice($desc);

        write_size_header!($details, error);
        error.extend_from_slice($details);

        error.extend_from_slice($code);

        Error::ProgramError(error)
    }}
}

macro_rules! error_database {
    ($err: expr) => {{
        let vec = Vec::new();

        error_program!(
            $err.description().as_bytes(),
            &vec,
            ERROR_DATABASE
        )
    }}
}

macro_rules! error_no_transaction {
    () => {{
        let vec = Vec::new();
        error_program!(
            "No transaction".as_bytes(),
            &vec,
            ERROR_NO_TX
        )
    }}
}

macro_rules! error_unknown_key {
    ($key: expr) => {{
        error_program!(
            "Unknown key".as_bytes(),
            $key,
            ERROR_UNKNOWN_KEY
        )
    }}
}

macro_rules! error_duplicate_key {
    ($key: expr) => {{
        error_program!(
            "Duplicate key".as_bytes(),
            $key,
            ERROR_DUPLICATE_KEY
        )
    }}
}

macro_rules! error_decoding {
    () => {{
        let vec = Vec::new();
        error_program!(
            "Decoding error".as_bytes(),
            &vec,
            ERROR_DECODING
        )
    }}
}

macro_rules! error_empty_stack {
    () => {{
        let vec = Vec::new();
        error_program!(
            "Empty stack".as_bytes(),
            &vec,
            ERROR_EMPTY_STACK
        )
    }}
}

macro_rules! error_invalid_value_word {
    ($value: expr) => {{
        error_program!(
            "Invalid value".as_bytes(),
            $value,
            ERROR_INVALID_VALUE
        )
    }}
}

macro_rules! error_invalid_value {
    ($value: expr) => {{
        let mut details = Vec::new();
        write_size_header!($value, details);
        details.extend_from_slice($value);

        error_program!(
            "Invalid value".as_bytes(),
            &details,
            ERROR_INVALID_VALUE
        )
    }}
}

macro_rules! error_unknown_word {
    ($word: expr) => { {
        let (_, w) = binparser::word($word).unwrap();

        let word = match str::from_utf8(&w[1..]) {
            Ok(word) => word,
            Err(_) => "Error parsing word"
        };

        let desc = format!("Unknown word: {}", word);
        let desc_bytes = desc.as_bytes();

        error_program!(
            desc_bytes,
            $word,
            ERROR_UNKNOWN_WORD
        )
    } }
}

macro_rules! write_size_header {
    ($bytes: expr, $vec: expr) => {{
        let mut header = vec![0;offset_by_size($bytes.len())];
        write_size_into_slice!($bytes.len(), header.as_mut_slice());
        $vec.append(&mut header);
    }};
}

#[cfg(test)]
macro_rules! eval {
        ($script: expr, $env: ident, $expr: expr) => {
           eval!($script, $env, _result, $expr);
        };
        ($script: expr, $env: ident, $result: ident, $expr: expr) => {
           eval!($script, $env, $result, publisher_accessor, {}, $expr);
        };
        ($script: expr, $env: ident, $result: ident, $publisher_accessor: ident, {$($init: tt)*}, $expr: expr) => {
          {
            let dir = TempDir::new("pumpkindb").unwrap();
            let path = dir.path().to_str().unwrap();
            fs::create_dir_all(path).expect("can't create directory");
            let env = unsafe {
                lmdb::EnvBuilder::new()
                    .expect("can't create env builder")
                    .open(path, lmdb::open::Flags::empty(), 0o600)
                    .expect("can't open env")
            };

            let db = lmdb::Database::open(&env,
                                 None,
                                 &lmdb::DatabaseOptions::new(lmdb::db::CREATE))
                                 .expect("can't open database");
            crossbeam::scope(|scope| {
                let mut publisher = pubsub::Publisher::new();
                let $publisher_accessor = publisher.accessor();
                let publisher_thread = scope.spawn(move || publisher.run());
                $($init)*
                let mut vm = VM::new(&env, &db, $publisher_accessor.clone());
                let sender = vm.sender();
                let handle = scope.spawn(move || {
                    vm.run();
                });
                let script = parse($script).unwrap();
                let (callback, receiver) = mpsc::channel::<ResponseMessage>();
                let _ = sender.send(RequestMessage::ScheduleEnv(EnvId::new(),
                                    script.clone(), callback));
                match receiver.recv() {
                   Ok(ResponseMessage::EnvTerminated(_, stack, stack_size)) => {
                      let _ = sender.send(RequestMessage::Shutdown);
                      let $result = Ok::<(), Error>(());
                      let mut $env = Env::new_with_stack(stack, stack_size).unwrap();
                      $expr;
                   }
                   Ok(ResponseMessage::EnvFailed(_, err, stack, stack_size)) => {
                      let _ = sender.send(RequestMessage::Shutdown);
                      let $result = Err::<(), Error>(err);
                      let mut $env = Env::new_with_stack(stack.unwrap(), stack_size.unwrap()).unwrap();
                      $expr;
                   }
                   Err(err) => {
                      panic!("recv error: {:?}", err);
                   }
                }
                let _ = handle.join();
                $publisher_accessor.shutdown();
                let _ = publisher_thread.join();
          });
        };
      }
}

#[cfg(test)]
macro_rules! data {
    ($ptr:expr) => {
        {
          let (_, size) = binparser::data_size($ptr).unwrap();
          &$ptr[offset_by_size(size)..$ptr.len()]
        }
    };
}

#[cfg(test)]
macro_rules! parsed_data {
        ($s: expr) => {
           data!(parse($s).unwrap().as_slice())
        };
}

#[cfg(test)]
macro_rules! assert_error {
    ($result: expr, $expected: expr) => {{
        assert!($result.is_err());
        let error = $result.err().unwrap();
        assert!(matches!(error, Error::ProgramError(_)));
        if let Error::ProgramError(inner) = error {
            assert_eq!(inner, parsed_data!($expected));
        } else {
        }
    }};
}<commit_msg>Problem: failing tests hang `cargo tests`<commit_after>// Copyright (c) 2017, All Contributors (see CONTRIBUTORS file)
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

macro_rules! write_size_into_slice {
    ($size:expr, $slice: expr) => {
     match $size {
        0...120 => {
            $slice[0] = $size as u8;
            1
        }
        121...255 => {
            $slice[0] = 121u8;
            $slice[1] = $size as u8;
            2
        }
        256...65535 => {
            $slice[0] = 122u8;
            $slice[1] = ($size >> 8) as u8;
            $slice[2] = $size as u8;
            3
        }
        65536...4294967296 => {
            $slice[0] = 123u8;
            $slice[1] = ($size >> 24) as u8;
            $slice[2] = ($size >> 16) as u8;
            $slice[3] = ($size >> 8) as u8;
            $slice[4] = $size as u8;
            5
        }
        _ => unreachable!(),
    }
    };
}

macro_rules! handle_words {
    ($env: expr, $program: expr, $word: expr, $res: ident,
     $pid: ident, { $($me: expr => $name: ident),* }, $block: expr) => {
    {
      let mut env = $env;
      if $word != TRY_END && !env.aborting_try.is_empty() {
        let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
        $block
      } else {
          $(
           env =
            match $me.$name(env, $word, $pid) {
              Err((env, Error::Reschedule)) => return Ok((env, Some($program.clone()))),
              Err((env, Error::UnknownWord)) => env,
              Err((mut env, err @ Error::ProgramError(_))) => {
                if env.tracking_errors > 0 {
                   env.aborting_try.push(err);
                   let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
                   $block
                } else {
                   return Err((env, err))
                }
              },
              Err((env, err)) => return Err((env, err)),
              Ok($res) => $block
            };
          )*
          if env.tracking_errors > 0 {
             env.aborting_try.push(error_unknown_word!($word));
             let $res : (Env<'a>, Option<Vec<u8>>) = (env, None);
             $block
          } else {
             return Err((env, error_unknown_word!($word)))
          }
      }
    }
    };
}

macro_rules! validate_lockout {
    ($env: expr, $name: expr, $pid: expr) => {
        if let Some((pid_, _)) = $name {
            if pid_ != $pid {
                return Err(($env, Error::Reschedule))
            }
        }
    };
}

macro_rules! read_or_write_transaction {
    ($me: expr, $env: expr) => {
        if let Some((_, ref txn)) = $me.db_write_txn {
            txn.deref()
        } else if let Some((_, ref txn)) = $me.db_read_txn {
            txn.deref()
        } else {
            return Err(($env, error_no_transaction!()));
        };
    };
}

macro_rules! stack_pop {
    ($env: expr) => {
        match $env.pop() {
            None => {
                return Err(($env, error_empty_stack!()))
            }
            Some(e) => {
                e
            }
        }
    }
}

macro_rules! word_is {
    ($env: expr, $word: expr, $exp: expr) => {
        if $word != $exp {
            return Err(($env, Error::UnknownWord))
        }
    };
}

macro_rules! error_program {
    ($desc: expr, $details: expr, $code: expr) => {{
        let mut error = Vec::new();

        write_size_header!($desc, error);
        error.extend_from_slice($desc);

        write_size_header!($details, error);
        error.extend_from_slice($details);

        error.extend_from_slice($code);

        Error::ProgramError(error)
    }}
}

macro_rules! error_database {
    ($err: expr) => {{
        let vec = Vec::new();

        error_program!(
            $err.description().as_bytes(),
            &vec,
            ERROR_DATABASE
        )
    }}
}

macro_rules! error_no_transaction {
    () => {{
        let vec = Vec::new();
        error_program!(
            "No transaction".as_bytes(),
            &vec,
            ERROR_NO_TX
        )
    }}
}

macro_rules! error_unknown_key {
    ($key: expr) => {{
        error_program!(
            "Unknown key".as_bytes(),
            $key,
            ERROR_UNKNOWN_KEY
        )
    }}
}

macro_rules! error_duplicate_key {
    ($key: expr) => {{
        error_program!(
            "Duplicate key".as_bytes(),
            $key,
            ERROR_DUPLICATE_KEY
        )
    }}
}

macro_rules! error_decoding {
    () => {{
        let vec = Vec::new();
        error_program!(
            "Decoding error".as_bytes(),
            &vec,
            ERROR_DECODING
        )
    }}
}

macro_rules! error_empty_stack {
    () => {{
        let vec = Vec::new();
        error_program!(
            "Empty stack".as_bytes(),
            &vec,
            ERROR_EMPTY_STACK
        )
    }}
}

macro_rules! error_invalid_value_word {
    ($value: expr) => {{
        error_program!(
            "Invalid value".as_bytes(),
            $value,
            ERROR_INVALID_VALUE
        )
    }}
}

macro_rules! error_invalid_value {
    ($value: expr) => {{
        let mut details = Vec::new();
        write_size_header!($value, details);
        details.extend_from_slice($value);

        error_program!(
            "Invalid value".as_bytes(),
            &details,
            ERROR_INVALID_VALUE
        )
    }}
}

macro_rules! error_unknown_word {
    ($word: expr) => { {
        let (_, w) = binparser::word($word).unwrap();

        let word = match str::from_utf8(&w[1..]) {
            Ok(word) => word,
            Err(_) => "Error parsing word"
        };

        let desc = format!("Unknown word: {}", word);
        let desc_bytes = desc.as_bytes();

        error_program!(
            desc_bytes,
            $word,
            ERROR_UNKNOWN_WORD
        )
    } }
}

macro_rules! write_size_header {
    ($bytes: expr, $vec: expr) => {{
        let mut header = vec![0;offset_by_size($bytes.len())];
        write_size_into_slice!($bytes.len(), header.as_mut_slice());
        $vec.append(&mut header);
    }};
}

#[cfg(test)]
macro_rules! eval {
        ($script: expr, $env: ident, $expr: expr) => {
           eval!($script, $env, _result, $expr);
        };
        ($script: expr, $env: ident, $result: ident, $expr: expr) => {
           eval!($script, $env, $result, publisher_accessor, {}, $expr);
        };
        ($script: expr, $env: ident, $result: ident, $publisher_accessor: ident, {$($init: tt)*}, $expr: expr) => {
          {
            let dir = TempDir::new("pumpkindb").unwrap();
            let path = dir.path().to_str().unwrap();
            fs::create_dir_all(path).expect("can't create directory");
            let env = unsafe {
                lmdb::EnvBuilder::new()
                    .expect("can't create env builder")
                    .open(path, lmdb::open::Flags::empty(), 0o600)
                    .expect("can't open env")
            };

            let db = lmdb::Database::open(&env,
                                 None,
                                 &lmdb::DatabaseOptions::new(lmdb::db::CREATE))
                                 .expect("can't open database");
            crossbeam::scope(|scope| {
                let mut publisher = pubsub::Publisher::new();
                let $publisher_accessor = publisher.accessor();
                let publisher_thread = scope.spawn(move || publisher.run());
                $($init)*
                let mut vm = VM::new(&env, &db, $publisher_accessor.clone());
                let sender = vm.sender();
                let handle = scope.spawn(move || {
                    vm.run();
                });
                let script = parse($script).unwrap();
                let (callback, receiver) = mpsc::channel::<ResponseMessage>();
                let _ = sender.send(RequestMessage::ScheduleEnv(EnvId::new(),
                                    script.clone(), callback));
                match receiver.recv() {
                   Ok(ResponseMessage::EnvTerminated(_, stack, stack_size)) => {
                      let _ = sender.send(RequestMessage::Shutdown);
                      $publisher_accessor.shutdown();
                      let $result = Ok::<(), Error>(());
                      let mut $env = Env::new_with_stack(stack, stack_size).unwrap();
                      $expr;
                   }
                   Ok(ResponseMessage::EnvFailed(_, err, stack, stack_size)) => {
                      let _ = sender.send(RequestMessage::Shutdown);
                      $publisher_accessor.shutdown();
                      let $result = Err::<(), Error>(err);
                      let mut $env = Env::new_with_stack(stack.unwrap(), stack_size.unwrap()).unwrap();
                      $expr;
                   }
                   Err(err) => {
                      let _ = sender.send(RequestMessage::Shutdown);
                      $publisher_accessor.shutdown();
                      panic!("recv error: {:?}", err);
                   }
                }
                let _ = handle.join();
                let _ = publisher_thread.join();
          });
        };
      }
}

#[cfg(test)]
macro_rules! data {
    ($ptr:expr) => {
        {
          let (_, size) = binparser::data_size($ptr).unwrap();
          &$ptr[offset_by_size(size)..$ptr.len()]
        }
    };
}

#[cfg(test)]
macro_rules! parsed_data {
        ($s: expr) => {
           data!(parse($s).unwrap().as_slice())
        };
}

#[cfg(test)]
macro_rules! assert_error {
    ($result: expr, $expected: expr) => {{
        assert!($result.is_err());
        let error = $result.err().unwrap();
        assert!(matches!(error, Error::ProgramError(_)));
        if let Error::ProgramError(inner) = error {
            assert_eq!(inner, parsed_data!($expected));
        } else {
        }
    }};
}<|endoftext|>


<commit_before>//! Server-side integration with [Nickel](http://nickel.rs/) via the `nickel_` feature
//! (optional, enables `hyper` feature).
//!
//! Not shown here: [`impl HttpRequest for &mut nickel::Request`](../trait.HttpRequest.html#implementors).
//!
//! **Note**: in-crate integration for Nickel is deprecated and will be removed in 0.11.0;
//! integration will be provided in the
//! [`multipart-nickel`](https://crates.io/crates/multipart-nickel)
//! crate for the foreseeable future.
#![deprecated(since = "0.10.2", note = "Nickel integration has moved to the `multipart-nickel`
                                        crate; in-crate integration will be removed in 0.11.0")]
use nickel::Request as NickelRequest;

use hyper::server::Request as HyperRequest;

use super::HttpRequest;

#[deprecated(since = "0.10.2", note = "Nickel integration has moved to the `multipart-nickel`
                                       crate; in-crate integration will be removed in 0.11.0")]
impl<'r, 'mw, 'server, D: 'mw> HttpRequest for &'r mut NickelRequest<'mw, 'server, D> {
    type Body = &'r mut HyperRequest<'mw, 'server>;

    fn multipart_boundary(&self) -> Option<&str> {
        self.origin.multipart_boundary()
    }

    fn body(self) -> Self::Body {
        warn!("In-crate Nickel integration is deprecated and will be removed in 0.11.0; \
               please consider switching to the `multipart-nickel` crate to avoid breakage.");

        &mut self.origin
    }
}
<commit_msg>Lower deprecation log message to `info`<commit_after>//! Server-side integration with [Nickel](http://nickel.rs/) via the `nickel_` feature
//! (optional, enables `hyper` feature).
//!
//! Not shown here: [`impl HttpRequest for &mut nickel::Request`](../trait.HttpRequest.html#implementors).
//!
//! **Note**: in-crate integration for Nickel is deprecated and will be removed in 0.11.0;
//! integration will be provided in the
//! [`multipart-nickel`](https://crates.io/crates/multipart-nickel)
//! crate for the foreseeable future.
#![deprecated(since = "0.10.2", note = "Nickel integration has moved to the `multipart-nickel`
                                        crate; in-crate integration will be removed in 0.11.0")]
use nickel::Request as NickelRequest;

use hyper::server::Request as HyperRequest;

use super::HttpRequest;

#[deprecated(since = "0.10.2", note = "Nickel integration has moved to the `multipart-nickel`
                                       crate; in-crate integration will be removed in 0.11.0")]
impl<'r, 'mw, 'server, D: 'mw> HttpRequest for &'r mut NickelRequest<'mw, 'server, D> {
    type Body = &'r mut HyperRequest<'mw, 'server>;

    fn multipart_boundary(&self) -> Option<&str> {
        self.origin.multipart_boundary()
    }

    fn body(self) -> Self::Body {
        info!("In-crate Nickel integration is deprecated and will be removed in 0.11.0; \
               please consider switching to the `multipart-nickel` crate to avoid breakage.");

        &mut self.origin
    }
}
<|endoftext|>


<commit_before>extern crate hyper;

use self::hyper::client::Client;

use std::thread;
use std::time::Duration;

use super::{Image,process_downloads,req_and_parse};

use std::sync::mpsc::Receiver;

pub fn main(rc: &Receiver<()>) {
    let client = Client::new();
    let mut url_string = "https://konachan.com/post.json?limit=500".to_string(); // Konachan вроде как вообще не ограничивает
    let mut page = 1;

    loop {
        let res = match req_and_parse(&client, &url_string) {
            Ok(x) => x,
            Err(_) => {
                thread::sleep(Duration::new(3,0));
                continue
            }
        };

        let images = res.as_array().unwrap();
        if images.is_empty() { break }

        let images = images.iter().fold(Vec::new(), |mut acc, x| {
            let image = x.as_object().unwrap();
            let tags = image["tags"].as_string().unwrap().split_whitespace().map(String::from).collect::<Vec<_>>();
            let rating = image["rating"].as_string().unwrap().chars().nth(0).unwrap();

            let url = image["file_url"].as_string().unwrap().to_string();

            let ext = url.clone();
            let ext = ext.split('.').collect::<Vec<_>>();
            let ext = ext.last().unwrap();

            let id = image["id"].as_i64().unwrap();
            let score = image["score"].as_i64().unwrap();

            let name = format!("konachan_{}.{}", id, ext);

            acc.push(Image{
                name: name.to_string(),
                got_from: "konachan".to_string(),
                url: url,
                tags: tags,
                rating: rating,
                post_url: format!("http://konachan.com/post/show/{}", id),
                score: score as i32
            });
            acc
        });

        if process_downloads(&client, &images, rc).is_err() { break }

        page += 1;

        url_string = format!("https://konachan.com/post.json?page={}&limit=100", page);
    }
}
<commit_msg>Или нет. Пусть останется 100<commit_after>extern crate hyper;

use self::hyper::client::Client;

use std::thread;
use std::time::Duration;

use super::{Image,process_downloads,req_and_parse};

use std::sync::mpsc::Receiver;

pub fn main(rc: &Receiver<()>) {
    let client = Client::new();
    let mut url_string = "https://konachan.com/post.json?limit=100".to_string();
    let mut page = 1;

    loop {
        let res = match req_and_parse(&client, &url_string) {
            Ok(x) => x,
            Err(_) => {
                thread::sleep(Duration::new(3,0));
                continue
            }
        };

        let images = res.as_array().unwrap();
        if images.is_empty() { break }

        let images = images.iter().fold(Vec::new(), |mut acc, x| {
            let image = x.as_object().unwrap();
            let tags = image["tags"].as_string().unwrap().split_whitespace().map(String::from).collect::<Vec<_>>();
            let rating = image["rating"].as_string().unwrap().chars().nth(0).unwrap();

            let url = image["file_url"].as_string().unwrap().to_string();

            let ext = url.clone();
            let ext = ext.split('.').collect::<Vec<_>>();
            let ext = ext.last().unwrap();

            let id = image["id"].as_i64().unwrap();
            let score = image["score"].as_i64().unwrap();

            let name = format!("konachan_{}.{}", id, ext);

            acc.push(Image{
                name: name.to_string(),
                got_from: "konachan".to_string(),
                url: url,
                tags: tags,
                rating: rating,
                post_url: format!("http://konachan.com/post/show/{}", id),
                score: score as i32
            });
            acc
        });

        if process_downloads(&client, &images, rc).is_err() { break }

        page += 1;

        url_string = format!("https://konachan.com/post.json?page={}&limit=100", page);
    }
}
<|endoftext|>


<commit_before>use syntax::lexer::Tokenizer;
use syntax::core::tokens::Token;
use syntax::core::tokens::Token::*;
use syntax::core::keywords::Keywords;
use syntax::core::punctuation::Punctuations;
use syntax::ast::expr::*;

#[allow(dead_code)]
enum OldExpr {
    Integer(IntegerAST),
    UInteger(UIntegerAST),
    Float(FloatAST),
}

#[allow(dead_code)]
struct IntegerAST {
    val: i64,
}

#[allow(dead_code)]
struct UIntegerAST {
    val: u64,
}

#[allow(dead_code)]
struct FloatAST {
    val: f64,
}

pub struct Parser<TokType: Tokenizer> {
    lexer: TokType,
    indentation: usize,
    run_codegen: bool,
    ast_root: ExprWrapper
}

impl<TokType: Tokenizer> Parser<TokType> {
    pub fn new(tokenizer: TokType) -> Parser<TokType> {
        let expr = Box::new(Expr::BlockExpr(Vec::new()));
        let wrapper = ExprWrapper::new(expr, 0, 0, 0, 0);

        Parser {
            lexer: tokenizer,
            indentation: 0,
            run_codegen: true,
            ast_root: wrapper
        }
    }

    // Get the next token from the lexer
    fn next_token(&mut self) -> Token {
        self.lexer.get_tok()
    }

    // Create an error from the current lexer's
    // state, and a message
    fn write_error(&mut self, msg: &str) {
        let (start_line, start_column, _, _) = self.lexer.get_error_pos();

        self.run_codegen = false;

        println!("Error on line: {} col: {}, {}", start_line, start_column, msg);

        // Skip to the end of the line (at Indent token) and allow parsing to continue
        loop {
            // Note: This consumes the Indent token, which probably isnt ideal.
            match self.next_token() {
                Indent(_) => break,
                EOF => break,
                _ => ()
            }
        }
    }

    fn expect_error(&mut self, reason: &str, expect: &str, got: &str) {
        self.write_error(&format!("{}. Expected {}, but got {}", reason, expect, got));
    }

    // Perform any necessary on-start actions
    fn start(&self) {
        // Start code gen startup

        // Implicit imports

        // Generate code for implicit top level function definition
    }

    // Ensures that the indentation matches the current level of indentation
    #[allow(dead_code)]
    fn check_indentation(&mut self, depth: usize) -> Option<OldExpr> {
        if self.indentation == depth {
            return None;
        }

        self.write_error("Incorrect indentation level");

        None
    }

    // Parses a variable or constant declaration
    #[allow(unused_variables)]
    fn parse_declaration(&mut self) -> Option<OldExpr> {
        let token = self.next_token();
        match token {
            Identifier(name) => {
                let token1 = self.next_token();

                match token1 {
                    // Punctuation(punc) => {
                    //     match punc {
                    //         punctuation::Assign => {
                    //             self.parse_expression()
                    //         }
                    //         _ => Err(self.write_error("Invalid Punctuation here"))
                    //     }
                    // }
                    Punctuation(Punctuations::Equals) => {
                        return self.parse_expression();
                    },
                    _ => self.write_error("Invalid sequence")
                }
            },
            Error(msg) => self.write_error(&msg),
            _ => self.expect_error("", "a variable name", "something else")
        }

        None
    }

    fn expect_token(&mut self, token: Token) -> bool {
        if self.next_token() != token {
            self.expect_error("", "", "");

            return false;
        }

        true
    }

    // Parses a print function/statement
    fn parse_print_fn(&mut self) -> Option<ExprWrapper> {
        if !self.expect_token(Punctuation(Punctuations::ParenOpen)) {
            return None;
        }

        // Parse 1+ args here

        if !self.expect_token(Punctuation(Punctuations::ParenClose)) {
            return None;
        }

        // Return the ast of the print function
        None
    }

    // Handles top-level keywords to start parsing them
    fn handle_keywords(&mut self, keyword: Keywords) -> Option<ExprWrapper> {
        match keyword {
//            Keywords::Def => {
//                self.parse_declaration()
//            },
            Keywords::Print => {
                self.parse_print_fn()
            },
            _ => {
                self.write_error(&format!("Unsupported keyword {:?}.", keyword));
                None
            }
        }
    }

    fn parse_expression(&self) -> Option<OldExpr> {
        None
    }

    // Parse numbers into their correct representation
    #[allow(dead_code)]
    fn parse_number(&mut self, num: &str) -> Option<OldExpr> {
        println!("{}", num);
        let value = 0;
        // This would normally check the ending on the thing slash information
        // about the number to pick int, uint, or float
        Some(OldExpr::Integer(IntegerAST { val: value }))
    }

    // Parse the file
    #[allow(unused_variables)]
    pub fn parse(&mut self) {
        self.start();

        // Generate AST
        loop {
            // Parse the token into the next node of the AST
            let token = self.next_token();

            // Debug:
            println!("{:?}", token);

            match token {
                Numeric(string, type_) => {
                    panic!("Unimplemented top level token 'Numeric'");
                },
                Identifier(repr) => {
//                    self.parse_expression()
                    panic!("Unimplemented top level token 'Identifier'");
                },
                Indent(depth) => {
                    // ToDo: Keep track of indentation level when preceeding a statement
                    // Might need look ahead to see if next token is Comment or Indent
                    // which means you didn't actually dedent.
                },
                BoolLiteral(lit) => {
                    panic!("Unimplemented top level token 'BoolLiteral'");
                },
                CharLiteral(lit) => {
                    panic!("Unimplemented top level token 'CharLiteral'");
                },
                StrLiteral(lit) => {
                    panic!("Unimplemented top level token 'StrLiteral'");
                },
                Keyword(keyword) => {
                    if let Some(exprwrapper) = self.handle_keywords(keyword) {
                        if let &mut Expr::BlockExpr(ref mut vec) = self.ast_root.get_expr() {
                            vec.push(exprwrapper);
                        }
                    }
                },
                Punctuation(punc) => {
                    panic!("Unimplemented top level token 'Punctuation'");
                },
                Comment(text) => {
                    // ToDo: Docstring, else ignore
                },
                Error(err) => {
                    self.write_error(&err);
                },
                Type(type_) => {
                    panic!("Unimplemented top level token 'Type'");
                },
                EOF => break
            };
        }

        // Do semantic analysis on AST

        // Run codegen when no errors have been found
        if self.run_codegen {
            self.ast_root.gen_code();
        }
    }
}
<commit_msg>Remove OldExpr related code. Silenced some prominent warnings.<commit_after>use syntax::lexer::Tokenizer;
use syntax::core::tokens::Token;
use syntax::core::tokens::Token::*;
use syntax::core::keywords::Keywords;
use syntax::core::punctuation::Punctuations;
use syntax::ast::expr::*;

pub struct Parser<TokType: Tokenizer> {
    lexer: TokType,
    indentation: usize,
    run_codegen: bool,
    ast_root: ExprWrapper
}

impl<TokType: Tokenizer> Parser<TokType> {
    pub fn new(tokenizer: TokType) -> Parser<TokType> {
        let expr = Box::new(Expr::BlockExpr(Vec::new()));
        let wrapper = ExprWrapper::new(expr, 0, 0, 0, 0);

        Parser {
            lexer: tokenizer,
            indentation: 0,
            run_codegen: true,
            ast_root: wrapper
        }
    }

    // Get the next token from the lexer
    fn next_token(&mut self) -> Token {
        self.lexer.get_tok()
    }

    // Create an error from the current lexer's
    // state, and a message
    fn write_error(&mut self, msg: &str) {
        let (start_line, start_column, _, _) = self.lexer.get_error_pos();

        self.run_codegen = false;

        println!("Error on line: {} col: {}, {}", start_line, start_column, msg);

        // Skip to the end of the line (at Indent token) and allow parsing to continue
        loop {
            // Note: This consumes the Indent token, which probably isnt ideal.
            match self.next_token() {
                Indent(_) => break,
                EOF => break,
                _ => ()
            }
        }
    }

    fn expect_error(&mut self, reason: &str, expect: &str, got: &str) {
        self.write_error(&format!("{}. Expected {}, but got {}", reason, expect, got));
    }

    // Perform any necessary on-start actions
    fn start(&self) {
        // Start code gen startup

        // Implicit imports

        // Generate code for implicit top level function definition
    }

    // Ensures that the indentation matches the current level of indentation
    #[allow(dead_code)]
    fn check_indentation(&mut self, depth: usize) -> Option<ExprWrapper> {
        if self.indentation == depth {
            return None;
        }

        self.write_error("Incorrect indentation level");

        None
    }

    // Parses a variable or constant declaration
    #[allow(unused_variables)]
    #[allow(dead_code)]
    fn parse_declaration(&mut self) -> Option<ExprWrapper> {
        let token = self.next_token();
        match token {
            Identifier(name) => {
                let token1 = self.next_token();

                match token1 {
                    // Punctuation(punc) => {
                    //     match punc {
                    //         punctuation::Assign => {
                    //             self.parse_expression()
                    //         }
                    //         _ => Err(self.write_error("Invalid Punctuation here"))
                    //     }
                    // }
                    Punctuation(Punctuations::Equals) => {
                        return self.parse_expression();
                    },
                    _ => self.write_error("Invalid sequence")
                }
            },
            Error(msg) => self.write_error(&msg),
            _ => self.expect_error("", "a variable name", "something else")
        }

        None
    }

    fn expect_token(&mut self, token: Token) -> bool {
        if self.next_token() != token {
            self.expect_error("", "", "");

            return false;
        }

        true
    }

    // Parses a print function/statement
    fn parse_print_fn(&mut self) -> Option<ExprWrapper> {
        if !self.expect_token(Punctuation(Punctuations::ParenOpen)) {
            return None;
        }

        // Parse 1+ args here

        if !self.expect_token(Punctuation(Punctuations::ParenClose)) {
            return None;
        }

        // Return the ast of the print function
        None
    }

    // Handles top-level keywords to start parsing them
    fn handle_keywords(&mut self, keyword: Keywords) -> Option<ExprWrapper> {
        match keyword {
//            Keywords::Def => {
//                self.parse_declaration()
//            },
            Keywords::Print => {
                self.parse_print_fn()
            },
            _ => {
                self.write_error(&format!("Unsupported keyword {:?}.", keyword));
                None
            }
        }
    }

    #[allow(dead_code)]
    fn parse_expression(&self) -> Option<ExprWrapper> {
        None
    }

    // Parse numbers into their correct representation
    #[allow(unused_variables)]
    #[allow(dead_code)]
    fn parse_number(&mut self, num: &str) -> Option<ExprWrapper> {
        println!("{}", num);
        let value = 0;
        // This would normally check the ending on the thing slash information
        // about the number to pick int, uint, or float
        None
    }

    // Parse the file
    #[allow(unused_variables)]
    pub fn parse(&mut self) {
        self.start();

        // Generate AST
        loop {
            // Parse the token into the next node of the AST
            let token = self.next_token();

            // Debug:
            println!("{:?}", token);

            match token {
                Numeric(string, type_) => {
                    panic!("Unimplemented top level token 'Numeric'");
                },
                Identifier(repr) => {
//                    self.parse_expression()
                    panic!("Unimplemented top level token 'Identifier'");
                },
                Indent(depth) => {
                    // ToDo: Keep track of indentation level when preceeding a statement
                    // Might need look ahead to see if next token is Comment or Indent
                    // which means you didn't actually dedent.
                },
                BoolLiteral(lit) => {
                    panic!("Unimplemented top level token 'BoolLiteral'");
                },
                CharLiteral(lit) => {
                    panic!("Unimplemented top level token 'CharLiteral'");
                },
                StrLiteral(lit) => {
                    panic!("Unimplemented top level token 'StrLiteral'");
                },
                Keyword(keyword) => {
                    if let Some(exprwrapper) = self.handle_keywords(keyword) {
                        if let &mut Expr::BlockExpr(ref mut vec) = self.ast_root.get_expr() {
                            vec.push(exprwrapper);
                        }
                    }
                },
                Punctuation(punc) => {
                    panic!("Unimplemented top level token 'Punctuation'");
                },
                Comment(text) => {
                    // ToDo: Docstring, else ignore
                },
                Error(err) => {
                    self.write_error(&err);
                },
                Type(type_) => {
                    panic!("Unimplemented top level token 'Type'");
                },
                EOF => break
            };
        }

        // Do semantic analysis on AST

        // Run codegen when no errors have been found
        if self.run_codegen {
            self.ast_root.gen_code();
        }
    }
}
<|endoftext|>


<commit_before>/// The datatype used for the ioctl number
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_ulong;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_int;

mod consts {
    use ::sys::ioctl::ioctl_num_type;
    #[doc(hidden)]
    pub const VOID: ioctl_num_type = 0x2000_0000;
    #[doc(hidden)]
    pub const OUT: ioctl_num_type = 0x4000_0000;
    #[doc(hidden)]
    pub const IN: ioctl_num_type = 0x8000_0000;
    #[doc(hidden)]
    pub const INOUT: ioctl_num_type = (IN|OUT);
    #[doc(hidden)]
    pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
}

pub use self::consts::*;

#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
    ($inout:expr, $group:expr, $num:expr, $len:expr) => (
        $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type)
    )
}

/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0))
}

/// Generate an ioctl request code for a command that passes an integer
///
/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write_int {
    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, ::std::mem::size_of::<$crate::libc::c_int>()))
}

/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len))
}

/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len))
}

/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len))
}
<commit_msg>Fix a lint on Rust nightly 1.43.0<commit_after>/// The datatype used for the ioctl number
#[doc(hidden)]
pub type ioctl_num_type = ::libc::c_ulong;
/// The datatype used for the 3rd argument
#[doc(hidden)]
pub type ioctl_param_type = ::libc::c_int;

mod consts {
    use ::sys::ioctl::ioctl_num_type;
    #[doc(hidden)]
    pub const VOID: ioctl_num_type = 0x2000_0000;
    #[doc(hidden)]
    pub const OUT: ioctl_num_type = 0x4000_0000;
    #[doc(hidden)]
    pub const IN: ioctl_num_type = 0x8000_0000;
    #[doc(hidden)]
    pub const INOUT: ioctl_num_type = IN|OUT;
    #[doc(hidden)]
    pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
}

pub use self::consts::*;

#[macro_export]
#[doc(hidden)]
macro_rules! ioc {
    ($inout:expr, $group:expr, $num:expr, $len:expr) => (
        $inout | (($len as $crate::sys::ioctl::ioctl_num_type & $crate::sys::ioctl::IOCPARM_MASK) << 16) | (($group as $crate::sys::ioctl::ioctl_num_type) << 8) | ($num as $crate::sys::ioctl::ioctl_num_type)
    )
}

/// Generate an ioctl request code for a command that passes no data.
///
/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_none!()` directly.
///
/// # Example
///
/// ```
/// # #[macro_use] extern crate nix;
/// const KVMIO: u8 = 0xAE;
/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
/// # fn main() {}
/// ```
#[macro_export(local_inner_macros)]
macro_rules! request_code_none {
    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, 0))
}

/// Generate an ioctl request code for a command that passes an integer
///
/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write_int {
    ($g:expr, $n:expr) => (ioc!($crate::sys::ioctl::VOID, $g, $n, ::std::mem::size_of::<$crate::libc::c_int>()))
}

/// Generate an ioctl request code for a command that reads.
///
/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_read!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is reading and the kernel is
/// writing.
#[macro_export(local_inner_macros)]
macro_rules! request_code_read {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::OUT, $g, $n, $len))
}

/// Generate an ioctl request code for a command that writes.
///
/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_write!()` directly.
///
/// The read/write direction is relative to userland, so this
/// command would be userland is writing and the kernel is
/// reading.
#[macro_export(local_inner_macros)]
macro_rules! request_code_write {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::IN, $g, $n, $len))
}

/// Generate an ioctl request code for a command that reads and writes.
///
/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
///
/// You should only use this macro directly if the `ioctl` you're working
/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
#[macro_export(local_inner_macros)]
macro_rules! request_code_readwrite {
    ($g:expr, $n:expr, $len:expr) => (ioc!($crate::sys::ioctl::INOUT, $g, $n, $len))
}
<|endoftext|>


<commit_before>use termbox_simple::Termbox;
use term_input::Key;

use std::collections::HashMap;
use std::collections::HashSet;
use std::convert::From;
use std::rc::Rc;

use rand::Rng;
use rand;
use time::Tm;
use time;

use trie::Trie;
use tui::exit_dialogue::ExitDialogue;
use tui::msg_area::MsgArea;
use tui::style;
use tui::text_field::TextField;
use tui::widget::{WidgetRet, Widget};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area : MsgArea,

    /// Stacked user input fields. Topmost one handles keypresses.
    input_field : Vec<Box<Widget>>,

    width  : i32,
    height : i32,

    // NOTE: Color is encoded in Termbox's 216 colors. (in 256-color mode)
    nick_colors      : HashMap<String, u8>,
    available_colors : HashSet<u8>,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    //
    // Rc to be able to share with dynamic messages.
    nicks : Rc<Trie>,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    pub fn now() -> Timestamp {
        Timestamp::from(time::now())
    }

    fn stamp(&self, msg_area: &mut MsgArea) {
        msg_area.set_style(&style::TIMESTAMP);
        msg_area.add_text(&format!("{:02}:{:02} ", self.hour, self.min));
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp { hour: tm.tm_hour, min: tm.tm_min }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub fn new(width : i32, height : i32) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1),
            input_field: vec![Box::new(TextField::new(width))],
            width: width,
            height: height,
            nick_colors: HashMap::new(),
            available_colors: (16 .. 232).into_iter().collect(),
            nicks: Rc::new(Trie::new()),
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub fn draw(&self, tb: &mut Termbox, pos_x: i32, pos_y: i32) {
        self.msg_area.draw(tb, pos_x, pos_y);
        self.input_field.draw(tb, pos_x, pos_y + self.height - 1);
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('u') => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('d') => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            },

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            },

            Key::Tab => {
                self.input_field.event(Box::new(self.nicks.clone()));
                WidgetRet::KeyHandled
            },

            key => {
                match self.input_field.keypressed(key) {
                    WidgetRet::KeyIgnored => {
                        // self.show_server_msg("KEY IGNORED", format!("{:?}", key).as_ref());
                        WidgetRet::KeyIgnored
                    },
                    WidgetRet::Remove => {
                        self.input_field.pop();
                        WidgetRet::KeyHandled

                    },
                    ret => ret,
                }
            },
        }
    }

    pub fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);
    }

    fn toggle_exit_dialogue(&mut self) {
        assert!(self.input_field.len() > 0);
        // FIXME: This is a bit too fragile I think. Since we only stack an exit
        // dialogue on top of the input field at the moment, checking the len()
        // is fine. If we decide to stack more stuff it'll break.
        if self.input_field.len() == 1 {
            self.input_field.push(Box::new(ExitDialogue::new(self.width)));
        } else {
            self.input_field.pop();
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {

    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            }
        } else {
            ts.stamp(&mut self.msg_area);

        }
        self.last_activity_ts = Some(ts);
    }

    pub fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area.set_style(&style::TOPIC);
        self.msg_area.add_text(&format!("Channel topic is: \"{}\"", topic));

        self.msg_area.flush_line();
    }

    pub fn add_client_err_msg(&mut self, msg : &str) {
        self.reset_activity_line();

        self.msg_area.set_style(&style::ERR_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_client_msg(&mut self, msg : &str) {
        self.reset_activity_line();

        self.msg_area.set_style(&style::USER_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub fn add_privmsg(&mut self, sender: &str, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        let translated = translate_irc_colors(msg);
        let msg = {
            match translated {
                Some(ref str) => &str,
                None => msg,
            }
        };

        self.add_timestamp(ts);

        {
            let nick_color = self.get_nick_color(sender);
            let style = style::Style { fg: nick_color as u16, bg: style::USER_MSG.bg };
            self.msg_area.set_style(&style);
            self.msg_area.add_text(sender);
        }

        self.msg_area.set_style(&style::Style { fg: style::USER_MSG.fg | style::TB_BOLD, bg: style::USER_MSG.bg });
        self.msg_area.add_text(": ");

        // Highlight the message if it mentions us
        // let mentions_us = WordIdxs::new(msg).any(|&(word_left, word_right)|
        //                                          msg[ word_left .. word_right ] == our_nick);
        let mentions_us = false; // TODO

        self.msg_area.set_style(
            if mentions_us {
                &style::HIGHLIGHT
            } else {
                &style::USER_MSG
            });

        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.set_style(&style::USER_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.set_style(&style::ERR_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    fn get_nick_color(&mut self, sender: &str) -> u8 {
        match self.nick_colors.get(sender) {
            Some(color) => {
                return *color;
            }
            None => {},
        }

        let mut rng = rand::thread_rng();
        let ret = {
            if !self.available_colors.is_empty() {
                let ret =
                    *self.available_colors.iter().nth(
                        rng.gen_range(0, self.available_colors.len())).unwrap();
                self.available_colors.remove(&ret);
                ret
            } else {
                rng.gen_range(16, 232)
            }
        };

        self.nick_colors.insert(sender.to_owned(), ret);
        ret
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        Rc::get_mut(&mut self.nicks).unwrap().insert(nick);

        if let Some(ts) = ts {
            let line_idx = self.get_activity_line_idx(ts);
            self.msg_area.modify_line(line_idx, |line| {
                line.set_style(&style::JOIN);
                line.add_char('+');
                line.add_text(nick);
                line.add_char(' ');
            });
        }
    }

    pub fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        Rc::get_mut(&mut self.nicks).unwrap().remove(nick);

        if let Some(ts) = ts {
            let line_idx = self.get_activity_line_idx(ts);
            self.msg_area.modify_line(line_idx, |line| {
                line.set_style(&style::LEAVE);
                line.add_char('-');
                line.add_text(nick);
                line.add_char(' ');
            });
        }
    }

    pub fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        Rc::get_mut(&mut self.nicks).unwrap().remove(old_nick);
        Rc::get_mut(&mut self.nicks).unwrap().insert(new_nick);
        let color = self.nick_colors.remove(old_nick);
        if let Some(color_) = color {
            self.nick_colors.insert(new_nick.to_owned(), color_);
        }

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.set_style(&style::NICK);
            line.add_text(old_nick);
            line.add_text("->");
            line.add_text(new_nick);
            line.add_char(' ');
        });
    }

    pub fn has_nick(&self, nick : &str) -> bool {
        self.nicks.contains(nick)
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        // borrow checkers strikes again
        if let Some(ref mut l) = self.last_activity_line {
            if l.ts == ts {
                return l.line_idx;
            }
        }

        self.add_timestamp(ts);
        let line_idx = self.msg_area.flush_line();
        self.last_activity_line = Some(ActivityLine {
            ts: ts,
            line_idx: line_idx,
        });
        line_idx
    }
}

////////////////////////////////////////////////////////////////////////////////

fn translate_irc_colors(str : &str) -> Option<String> {
    // Most messages won't have any colors, so we have this fast path here
    if str.find(style::IRC_COLOR_PREFIX).is_none() {
        return None;
    }

    let mut ret = String::with_capacity(str.len());

    let mut iter = str.chars();
    while let Some(mut char) = iter.next() {
        if char == style::IRC_COLOR_PREFIX {
            let fg1 = to_dec(iter.next().unwrap());
            let fg2 = to_dec(iter.next().unwrap());
            let fg  = fg1 * 10 + fg2;
            if let Some(char_) = iter.next() {
                if char_ == ',' {
                    let bg1 = to_dec(iter.next().unwrap());
                    let bg2 = to_dec(iter.next().unwrap());
                    let bg  = bg1 * 10 + bg2;
                    ret.push(style::TERMBOX_COLOR_PREFIX);
                    ret.push(0 as char); // style
                    ret.push(irc_color_to_termbox(fg) as char);
                    ret.push(irc_color_to_termbox(bg) as char);
                    continue;
                } else {
                    ret.push(style::TERMBOX_COLOR_PREFIX);
                    ret.push(0 as char); // style
                    ret.push(irc_color_to_termbox(fg) as char);
                    ret.push(irc_color_to_termbox(style::USER_MSG.bg as u8) as char);
                    char = char_;
                }
            } else {
                ret.push(style::TERMBOX_COLOR_PREFIX);
                ret.push(0 as char); // style
                ret.push(irc_color_to_termbox(fg) as char);
                ret.push(irc_color_to_termbox(style::USER_MSG.bg as u8) as char);
                break;
            }
        }

        ret.push(char);
    }

    Some(ret)
}

#[inline]
fn to_dec(ch : char) -> u8 {
    ((ch as u32) - ('0' as u32)) as u8
}

// IRC colors: http://en.wikichip.org/wiki/irc/colors
// Termbox colors: http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
fn irc_color_to_termbox(irc_color : u8) -> u8 {
    match irc_color {
         0 => 15,  // white
         1 => 0,   // black
         2 => 17,  // navy
         3 => 2,   // green
         4 => 9,   // red
         5 => 88,  // maroon
         6 => 5,   // purple
         7 => 130, // olive
         8 => 11,  // yellow
         9 => 10,  // light green
        10 => 6,   // teal
        11 => 14,  // cyan
        12 => 12,  // awful blue
        13 => 13,  // magenta
        14 => 8,   // gray
        15 => 7,   // light gray
         _ => panic!("Unknown irc color: {}", irc_color)
    }
}
<commit_msg>Fix input widget resizing in messaging UI<commit_after>use termbox_simple::Termbox;
use term_input::Key;

use std::collections::HashMap;
use std::collections::HashSet;
use std::convert::From;
use std::rc::Rc;

use rand::Rng;
use rand;
use time::Tm;
use time;

use trie::Trie;
use tui::exit_dialogue::ExitDialogue;
use tui::msg_area::MsgArea;
use tui::style;
use tui::text_field::TextField;
use tui::widget::{WidgetRet, Widget};

/// A messaging screen is just a text field to type messages and msg area to
/// show incoming/sent messages.
pub struct MessagingUI {
    /// Incoming and sent messages appear
    msg_area : MsgArea,

    /// Stacked user input fields. Topmost one handles keypresses.
    input_field : Vec<Box<Widget>>,

    width  : i32,
    height : i32,

    // NOTE: Color is encoded in Termbox's 216 colors. (in 256-color mode)
    nick_colors      : HashMap<String, u8>,
    available_colors : HashSet<u8>,

    // All nicks in the channel. Need to keep this up-to-date to be able to
    // properly highlight mentions.
    //
    // Rc to be able to share with dynamic messages.
    nicks : Rc<Trie>,

    last_activity_line: Option<ActivityLine>,
    last_activity_ts: Option<Timestamp>,
}

/// Like `time::Tm`, but we only care about hour and minute parts.
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Timestamp {
    hour: i32,
    min: i32,
}

impl Timestamp {
    pub fn now() -> Timestamp {
        Timestamp::from(time::now())
    }

    fn stamp(&self, msg_area: &mut MsgArea) {
        msg_area.set_style(&style::TIMESTAMP);
        msg_area.add_text(&format!("{:02}:{:02} ", self.hour, self.min));
    }
}

impl From<Tm> for Timestamp {
    fn from(tm: Tm) -> Timestamp {
        Timestamp { hour: tm.tm_hour, min: tm.tm_min }
    }
}

/// An activity line is just a line that we update on joins / leaves /
/// disconnects. We group activities that happen in the same minute to avoid
/// redundantly showing lines.
struct ActivityLine {
    ts: Timestamp,
    line_idx: usize,
}

impl MessagingUI {
    pub fn new(width : i32, height : i32) -> MessagingUI {
        MessagingUI {
            msg_area: MsgArea::new(width, height - 1),
            input_field: vec![Box::new(TextField::new(width))],
            width: width,
            height: height,
            nick_colors: HashMap::new(),
            available_colors: (16 .. 232).into_iter().collect(),
            nicks: Rc::new(Trie::new()),
            last_activity_line: None,
            last_activity_ts: None,
        }
    }

    pub fn draw(&self, tb: &mut Termbox, pos_x: i32, pos_y: i32) {
        self.msg_area.draw(tb, pos_x, pos_y);
        self.input_field.draw(tb, pos_x, pos_y + self.height - 1);
    }

    pub fn keypressed(&mut self, key : Key) -> WidgetRet {
        match key {
            Key::Ctrl('c') => {
                self.toggle_exit_dialogue();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('u') => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::Ctrl('d') => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            Key::PageUp => {
                self.msg_area.page_up();
                WidgetRet::KeyHandled
            },

            Key::PageDown => {
                self.msg_area.page_down();
                WidgetRet::KeyHandled
            },

            Key::ShiftUp => {
                self.msg_area.scroll_up();
                WidgetRet::KeyHandled
            },

            Key::ShiftDown => {
                self.msg_area.scroll_down();
                WidgetRet::KeyHandled
            },

            Key::Tab => {
                self.input_field.event(Box::new(self.nicks.clone()));
                WidgetRet::KeyHandled
            },

            key => {
                match self.input_field.keypressed(key) {
                    WidgetRet::KeyIgnored => {
                        // self.show_server_msg("KEY IGNORED", format!("{:?}", key).as_ref());
                        WidgetRet::KeyIgnored
                    },
                    WidgetRet::Remove => {
                        self.input_field.pop();
                        WidgetRet::KeyHandled

                    },
                    ret => ret,
                }
            },
        }
    }

    pub fn resize(&mut self, width: i32, height: i32) {
        self.width = width;
        self.height = height;
        self.msg_area.resize(width, height - 1);
        for w in &mut self.input_field {
            w.resize(width, 1);
        }
    }

    fn toggle_exit_dialogue(&mut self) {
        assert!(self.input_field.len() > 0);
        // FIXME: This is a bit too fragile I think. Since we only stack an exit
        // dialogue on top of the input field at the moment, checking the len()
        // is fine. If we decide to stack more stuff it'll break.
        if self.input_field.len() == 1 {
            self.input_field.push(Box::new(ExitDialogue::new(self.width)));
        } else {
            self.input_field.pop();
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Adding new messages

impl MessagingUI {

    fn add_timestamp(&mut self, ts: Timestamp) {
        if let Some(ts_) = self.last_activity_ts {
            if ts_ != ts {
                ts.stamp(&mut self.msg_area);
            }
        } else {
            ts.stamp(&mut self.msg_area);

        }
        self.last_activity_ts = Some(ts);
    }

    pub fn show_topic(&mut self, topic: &str, ts: Timestamp) {
        self.add_timestamp(ts);

        self.msg_area.set_style(&style::TOPIC);
        self.msg_area.add_text(&format!("Channel topic is: \"{}\"", topic));

        self.msg_area.flush_line();
    }

    pub fn add_client_err_msg(&mut self, msg : &str) {
        self.reset_activity_line();

        self.msg_area.set_style(&style::ERR_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_client_msg(&mut self, msg : &str) {
        self.reset_activity_line();

        self.msg_area.set_style(&style::USER_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
        self.reset_activity_line();
    }

    pub fn add_privmsg(&mut self, sender: &str, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        let translated = translate_irc_colors(msg);
        let msg = {
            match translated {
                Some(ref str) => &str,
                None => msg,
            }
        };

        self.add_timestamp(ts);

        {
            let nick_color = self.get_nick_color(sender);
            let style = style::Style { fg: nick_color as u16, bg: style::USER_MSG.bg };
            self.msg_area.set_style(&style);
            self.msg_area.add_text(sender);
        }

        self.msg_area.set_style(&style::Style { fg: style::USER_MSG.fg | style::TB_BOLD, bg: style::USER_MSG.bg });
        self.msg_area.add_text(": ");

        // Highlight the message if it mentions us
        // let mentions_us = WordIdxs::new(msg).any(|&(word_left, word_right)|
        //                                          msg[ word_left .. word_right ] == our_nick);
        let mentions_us = false; // TODO

        self.msg_area.set_style(
            if mentions_us {
                &style::HIGHLIGHT
            } else {
                &style::USER_MSG
            });

        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.set_style(&style::USER_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    pub fn add_err_msg(&mut self, msg: &str, ts: Timestamp) {
        self.reset_activity_line();

        self.add_timestamp(ts);
        self.msg_area.set_style(&style::ERR_MSG);
        self.msg_area.add_text(msg);
        self.msg_area.flush_line();
    }

    fn get_nick_color(&mut self, sender: &str) -> u8 {
        match self.nick_colors.get(sender) {
            Some(color) => {
                return *color;
            }
            None => {},
        }

        let mut rng = rand::thread_rng();
        let ret = {
            if !self.available_colors.is_empty() {
                let ret =
                    *self.available_colors.iter().nth(
                        rng.gen_range(0, self.available_colors.len())).unwrap();
                self.available_colors.remove(&ret);
                ret
            } else {
                rng.gen_range(16, 232)
            }
        };

        self.nick_colors.insert(sender.to_owned(), ret);
        ret
    }
}

////////////////////////////////////////////////////////////////////////////////
// Keeping nick list up-to-date

impl MessagingUI {
    pub fn join(&mut self, nick: &str, ts: Option<Timestamp>) {
        Rc::get_mut(&mut self.nicks).unwrap().insert(nick);

        if let Some(ts) = ts {
            let line_idx = self.get_activity_line_idx(ts);
            self.msg_area.modify_line(line_idx, |line| {
                line.set_style(&style::JOIN);
                line.add_char('+');
                line.add_text(nick);
                line.add_char(' ');
            });
        }
    }

    pub fn part(&mut self, nick: &str, ts: Option<Timestamp>) {
        Rc::get_mut(&mut self.nicks).unwrap().remove(nick);

        if let Some(ts) = ts {
            let line_idx = self.get_activity_line_idx(ts);
            self.msg_area.modify_line(line_idx, |line| {
                line.set_style(&style::LEAVE);
                line.add_char('-');
                line.add_text(nick);
                line.add_char(' ');
            });
        }
    }

    pub fn nick(&mut self, old_nick: &str, new_nick: &str, ts: Timestamp) {
        Rc::get_mut(&mut self.nicks).unwrap().remove(old_nick);
        Rc::get_mut(&mut self.nicks).unwrap().insert(new_nick);
        let color = self.nick_colors.remove(old_nick);
        if let Some(color_) = color {
            self.nick_colors.insert(new_nick.to_owned(), color_);
        }

        let line_idx = self.get_activity_line_idx(ts);
        self.msg_area.modify_line(line_idx, |line| {
            line.set_style(&style::NICK);
            line.add_text(old_nick);
            line.add_text("->");
            line.add_text(new_nick);
            line.add_char(' ');
        });
    }

    pub fn has_nick(&self, nick : &str) -> bool {
        self.nicks.contains(nick)
    }

    fn reset_activity_line(&mut self) {
        self.last_activity_line = None;
    }

    fn get_activity_line_idx(&mut self, ts: Timestamp) -> usize {
        // borrow checkers strikes again
        if let Some(ref mut l) = self.last_activity_line {
            if l.ts == ts {
                return l.line_idx;
            }
        }

        self.add_timestamp(ts);
        let line_idx = self.msg_area.flush_line();
        self.last_activity_line = Some(ActivityLine {
            ts: ts,
            line_idx: line_idx,
        });
        line_idx
    }
}

////////////////////////////////////////////////////////////////////////////////

fn translate_irc_colors(str : &str) -> Option<String> {
    // Most messages won't have any colors, so we have this fast path here
    if str.find(style::IRC_COLOR_PREFIX).is_none() {
        return None;
    }

    let mut ret = String::with_capacity(str.len());

    let mut iter = str.chars();
    while let Some(mut char) = iter.next() {
        if char == style::IRC_COLOR_PREFIX {
            let fg1 = to_dec(iter.next().unwrap());
            let fg2 = to_dec(iter.next().unwrap());
            let fg  = fg1 * 10 + fg2;
            if let Some(char_) = iter.next() {
                if char_ == ',' {
                    let bg1 = to_dec(iter.next().unwrap());
                    let bg2 = to_dec(iter.next().unwrap());
                    let bg  = bg1 * 10 + bg2;
                    ret.push(style::TERMBOX_COLOR_PREFIX);
                    ret.push(0 as char); // style
                    ret.push(irc_color_to_termbox(fg) as char);
                    ret.push(irc_color_to_termbox(bg) as char);
                    continue;
                } else {
                    ret.push(style::TERMBOX_COLOR_PREFIX);
                    ret.push(0 as char); // style
                    ret.push(irc_color_to_termbox(fg) as char);
                    ret.push(irc_color_to_termbox(style::USER_MSG.bg as u8) as char);
                    char = char_;
                }
            } else {
                ret.push(style::TERMBOX_COLOR_PREFIX);
                ret.push(0 as char); // style
                ret.push(irc_color_to_termbox(fg) as char);
                ret.push(irc_color_to_termbox(style::USER_MSG.bg as u8) as char);
                break;
            }
        }

        ret.push(char);
    }

    Some(ret)
}

#[inline]
fn to_dec(ch : char) -> u8 {
    ((ch as u32) - ('0' as u32)) as u8
}

// IRC colors: http://en.wikichip.org/wiki/irc/colors
// Termbox colors: http://www.calmar.ws/vim/256-xterm-24bit-rgb-color-chart.html
fn irc_color_to_termbox(irc_color : u8) -> u8 {
    match irc_color {
         0 => 15,  // white
         1 => 0,   // black
         2 => 17,  // navy
         3 => 2,   // green
         4 => 9,   // red
         5 => 88,  // maroon
         6 => 5,   // purple
         7 => 130, // olive
         8 => 11,  // yellow
         9 => 10,  // light green
        10 => 6,   // teal
        11 => 14,  // cyan
        12 => 12,  // awful blue
        13 => 13,  // magenta
        14 => 8,   // gray
        15 => 7,   // light gray
         _ => panic!("Unknown irc color: {}", irc_color)
    }
}
<|endoftext|>