<commit_before>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    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/>.
*/

use ansi_term::Style;
use ansi_term::Colour::{Red, Cyan, Yellow, Green, Blue};

#[derive(Clone, Copy)]
pub struct EsrPrinter {
    is_tty: bool,
}

impl EsrPrinter {
    pub fn new_with_term(is_tty: bool) -> Self {
        Self { is_tty }
    }

    fn bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Style::new().bold().paint(val))
        } else {
            val.into()
        }
    }

    fn yellow_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Yellow.bold().paint(val))
        } else {
            val.into()
        }
    }

    fn green_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Green.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn red_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Red.bold().paint(val))
        } else {
            val.into()
        }
    }

    fn cyan_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Cyan.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn blue_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Blue.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn msg_pair(&self, msg: &str, val: &str) -> String {
        format!("{}: {}",
                self.cyan_bold(msg),
                self.bold(val))
    }

    pub fn score_error(&self, msg: &str) -> String {
        format!("{}: {}",
                self.red_bold(msg),
                self.bold("Error"))
    }

    pub fn score_na(&self, msg: &str) -> String {
        self.msg_pair(msg, "N/A")
    }

    pub fn score_overview(&self, msg: &str, pos: f64, neg: f64) -> String {
        format!("{}: {} ({} / {})",
                self.cyan_bold(msg),
                self.yellow_bold(&format!("{:.3}", pos + neg)),
                self.green_bold(&format!("+{:.3}", pos)),
                self.red_bold(&format!("{:.3}", neg)))
    }

    pub fn score_details(&self, msg: &str, table: &[(String, String, String)]) -> String {
        let msg = format!("{: ^49}", msg);
        let frame = format!("{: ^49}", "-".repeat(msg.len()));

        let mut score_str = String::with_capacity(4096);
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*frame)));
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*msg)));
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*frame)));

        for line in table {
            if line.2.starts_with('-') {
                let line_str = format!("{} | {} | {}\n",
                                       self.yellow_bold(&*line.0),
                                       self.red_bold(&*line.1),
                                       self.red_bold(&*line.2));
                score_str.push_str(&line_str);

            } else {
                let line_str = format!("{} | {} | {}\n",
                                       self.yellow_bold(&*line.0),
                                       self.green_bold(&*line.1),
                                       self.green_bold(&*("+".to_string() + &*line.2)));
                score_str.push_str(&line_str);
            }
        }
        score_str
    }

    pub fn crate_no_score(&self, id: &str) {
        let msg = format!("Failed to get scores for crate \"{}\". Maybe it does not exist!", id);
        println!("{}", self.red_bold(&msg));
    }

    pub fn search_no_results(&self, search_pattern: &str) {
        let msg = format!("Searching for \"{}\" returned no results.", search_pattern);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn search_failed(&self, search_pattern: &str) {
        let msg = format!("Search for \"{}\" failed.", search_pattern);
        println!("{}", self.red_bold(&msg));
    }

    pub fn limit_out_of_range(&self, limit: u8, min: u8, max: u8) {
        let msg = format!("{} is out of the range of valid limits. \
                          Please pass a value between {} and {}.", limit, min, max);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn limit_invalid(&self, limit: &str) {
        let msg = format!("\"{}\" is an invalid limit value.", limit);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn no_token(&self) {
        let msg = "Accessing GitHub's API wothout hitting rate-limits requires providing an access\
                   token.\n\n\
                   You can pass a token via -g/--gh-token option.\n\
                   Or by setting the variable CARGO_ESR_GH_TOKEN in the environment.\n\n\
                   To a acquire an access token, visit: <https://github.com/settings/tokens/new>\n\n\
                   Alternatively, you can pass -o/--crate-only to skip getting repository info.";
        println!("{}", self.yellow_bold(msg));
    }
}
<commit_msg>esr_printer: Fix printing zero scores in green when the weight is negative<commit_after>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    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/>.
*/

use ansi_term::Style;
use ansi_term::Colour::{Red, Cyan, Yellow, Green, Blue};

#[derive(Clone, Copy)]
pub struct EsrPrinter {
    is_tty: bool,
}

impl EsrPrinter {
    pub fn new_with_term(is_tty: bool) -> Self {
        Self { is_tty }
    }

    fn bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Style::new().bold().paint(val))
        } else {
            val.into()
        }
    }

    fn yellow_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Yellow.bold().paint(val))
        } else {
            val.into()
        }
    }

    fn green_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Green.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn red_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Red.bold().paint(val))
        } else {
            val.into()
        }
    }

    fn cyan_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Cyan.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn blue_bold(&self, val: &str) -> String {
        if self.is_tty {
            format!("{}", Blue.bold().paint(val))
        } else {
            val.into()
        }
    }

    pub fn msg_pair(&self, msg: &str, val: &str) -> String {
        format!("{}: {}",
                self.cyan_bold(msg),
                self.bold(val))
    }

    pub fn score_error(&self, msg: &str) -> String {
        format!("{}: {}",
                self.red_bold(msg),
                self.bold("Error"))
    }

    pub fn score_na(&self, msg: &str) -> String {
        self.msg_pair(msg, "N/A")
    }

    pub fn score_overview(&self, msg: &str, pos: f64, neg: f64) -> String {
        format!("{}: {} ({} / {})",
                self.cyan_bold(msg),
                self.yellow_bold(&format!("{:.3}", pos + neg)),
                self.green_bold(&format!("+{:.3}", pos)),
                self.red_bold(&format!("{:.3}", neg)))
    }

    pub fn score_details(&self, msg: &str, table: &[(String, String, String)]) -> String {
        let msg = format!("{: ^49}", msg);
        let frame = format!("{: ^49}", "-".repeat(msg.len()));

        let mut score_str = String::with_capacity(4096);
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*frame)));
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*msg)));
        score_str.push_str(&format!("{}\n", self.cyan_bold(&*frame)));

        for line in table {
            if line.1.find("* -").is_some() {
                let line_str = format!("{} | {} | {}\n",
                                       self.yellow_bold(&*line.0),
                                       self.red_bold(&*line.1),
                                       self.red_bold(&*line.2));
                score_str.push_str(&line_str);

            } else {
                let line_str = format!("{} | {} | {}\n",
                                       self.yellow_bold(&*line.0),
                                       self.green_bold(&*line.1),
                                       self.green_bold(&*("+".to_string() + &*line.2)));
                score_str.push_str(&line_str);
            }
        }
        score_str
    }

    pub fn crate_no_score(&self, id: &str) {
        let msg = format!("Failed to get scores for crate \"{}\". Maybe it does not exist!", id);
        println!("{}", self.red_bold(&msg));
    }

    pub fn search_no_results(&self, search_pattern: &str) {
        let msg = format!("Searching for \"{}\" returned no results.", search_pattern);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn search_failed(&self, search_pattern: &str) {
        let msg = format!("Search for \"{}\" failed.", search_pattern);
        println!("{}", self.red_bold(&msg));
    }

    pub fn limit_out_of_range(&self, limit: u8, min: u8, max: u8) {
        let msg = format!("{} is out of the range of valid limits. \
                          Please pass a value between {} and {}.", limit, min, max);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn limit_invalid(&self, limit: &str) {
        let msg = format!("\"{}\" is an invalid limit value.", limit);
        println!("{}", self.yellow_bold(&msg));
    }

    pub fn no_token(&self) {
        let msg = "Accessing GitHub's API wothout hitting rate-limits requires providing an access\
                   token.\n\n\
                   You can pass a token via -g/--gh-token option.\n\
                   Or by setting the variable CARGO_ESR_GH_TOKEN in the environment.\n\n\
                   To a acquire an access token, visit: <https://github.com/settings/tokens/new>\n\n\
                   Alternatively, you can pass -o/--crate-only to skip getting repository info.";
        println!("{}", self.yellow_bold(msg));
    }
}
<|endoftext|>


<commit_before>use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_char;
use std::os::raw;
use std::ptr;

/// An enum identifying supported signature algorithms. Currently only ECDSA with SHA256 (ES256) and
/// RSASSA-PSS with SHA-256 (PS256) are supported. Note that with PS256, the salt length is defined
/// to be 32 bytes.
#[derive(Debug)]
pub enum SignatureAlgorithm {
    ES256,
    PS256,
}

type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS

#[repr(C)]
struct SECItem {
    typ: SECItemType,
    data: *const u8,
    len: raw::c_uint,
}

impl SECItem {
    fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
        if data.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data.as_ptr(),
            len: data.len() as u32,
        })
    }

    fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
        if len > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data,
            len: len as u32,
        })
    }
}

/// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
/// To represent this, we define another type of mutable SECItem.
#[repr(C)]
struct SECItemMut<'a> {
    typ: SECItemType,
    data: *mut u8,
    len: raw::c_uint,
    _marker: PhantomData<&'a mut Vec<u8>>,
}

impl<'a> SECItemMut<'a> {
    /// Given a mutable reference to a Vec<u8> that has a particular allocated size, create a
    /// SECItemMut that points to the vec and has the same capacity.
    /// The input vec is not expected to have any actual contents, and in any case is cleared.
    fn maybe_from_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
        if vec.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        vec.clear();
        Ok(SECItemMut {
            typ: SI_BUFFER,
            data: vec.as_mut_ptr(),
            len: vec.capacity() as u32,
            _marker: PhantomData,
        })
    }
}

#[repr(C)]
struct CkRsaPkcsPssParams {
    // Called CK_RSA_PKCS_PSS_PARAMS in NSS
    hash_alg: CkMechanismType, // Called hashAlg in NSS
    mgf: CkRsaPkcsMgfType,
    s_len: raw::c_ulong, // Called sLen in NSS
}

impl CkRsaPkcsPssParams {
    fn new() -> CkRsaPkcsPssParams {
        CkRsaPkcsPssParams {
            hash_alg: CKM_SHA256,
            mgf: CKG_MGF1_SHA256,
            s_len: 32,
        }
    }

    fn get_params_item(&self) -> Result<SECItem, NSSError> {
        // This isn't entirely NSS' fault, but it mostly is.
        let params_ptr: *const CkRsaPkcsPssParams = self;
        let params_ptr: *const u8 = params_ptr as *const u8;
        let params_secitem =
            SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
        Ok(params_secitem)
    }
}

// TODO: link to NSS source where these are defined
type SECOidTag = raw::c_uint; // TODO: actually an enum - is this the right size?
const SEC_OID_SHA256: SECOidTag = 191;

type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
const CKM_ECDSA: CkMechanismType = 0x0000_1041;
const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
const CKM_SHA256: CkMechanismType = 0x0000_0250;

type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;

type SECStatus = raw::c_int; // TODO: enum - right size?
const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS

enum SECKEYPublicKey {}
enum SECKEYPrivateKey {}
enum PK11SlotInfo {}
enum CERTCertificate {}
enum CERTCertDBHandle {}

const SHA256_LENGTH: usize = 32;

// TODO: ugh this will probably have a platform-specific name...
#[link(name = "nss3")]
extern "C" {
    fn PK11_HashBuf(
        hashAlg: SECOidTag,
        out: *mut u8,
        data_in: *const u8, // called "in" in NSS
        len: raw::c_int,
    ) -> SECStatus;
    fn PK11_VerifyWithMechanism(
        key: *const SECKEYPublicKey,
        mechanism: CkMechanismType,
        param: *const SECItem,
        sig: *const SECItem,
        hash: *const SECItem,
        wincx: *const raw::c_void,
    ) -> SECStatus;

    fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);

    fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
    fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
    fn CERT_NewTempCertificate(
        handle: *const CERTCertDBHandle,
        derCert: *const SECItem,
        nickname: *const c_char,
        isperm: bool,
        copyDER: bool,
    ) -> *mut CERTCertificate;
    fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;

    fn PK11_ImportDERPrivateKeyInfoAndReturnKey(
        slot: *mut PK11SlotInfo,
        derPKI: *const SECItem,
        nickname: *const SECItem,
        publicValue: *const SECItem,
        isPerm: bool,
        isPrivate: bool,
        keyUsage: u32,
        privk: *mut *mut SECKEYPrivateKey,
        wincx: *const u8,
    ) -> SECStatus;
    fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
    fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
    fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
    fn PK11_SignWithMechanism(
        key: *const SECKEYPrivateKey,
        mech: CkMechanismType,
        param: *const SECItem,
        sig: *mut SECItemMut,
        hash: *const SECItem,
    ) -> SECStatus;
}

/// An error type describing errors that may be encountered during verification.
#[derive(Debug, PartialEq)]
pub enum NSSError {
    ImportCertError,
    DecodingPKCS8Failed,
    InputTooLarge,
    LibraryFailure,
    SignatureVerificationFailed,
    SigningFailed,
    ExtractPublicKeyFailed,
}

// XXX: make this work with other hashe algos.
fn hash(payload: &[u8]) -> Result<Vec<u8>, NSSError> {
    if payload.len() > raw::c_int::max_value() as usize {
        return Err(NSSError::InputTooLarge);
    }
    let mut hash_buf = vec![0; SHA256_LENGTH];
    let len: raw::c_int = payload.len() as raw::c_int;
    let hash_result =
        unsafe { PK11_HashBuf(SEC_OID_SHA256, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
    if hash_result != SEC_SUCCESS {
        return Err(NSSError::LibraryFailure);
    }
    Ok(hash_buf)
}

/// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
/// info, a payload, and a signature over the payload, returns a result based on the outcome of
/// decoding the subject public key info and running the signature verification algorithm on the
/// signed data.
pub fn verify_signature(
    signature_algorithm: &SignatureAlgorithm,
    cert: &[u8],
    payload: &[u8],
    signature: &[u8],
) -> Result<(), NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });

    let hash_buf = hash(payload).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;

    // Import DER cert into NSS.
    let der_cert = SECItem::maybe_new(cert)?;
    let db_handle = unsafe { CERT_GetDefaultCertDB() };
    if db_handle.is_null() {
        // TODO #28
        return Err(NSSError::LibraryFailure);
    }
    let nss_cert =
        unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
    if nss_cert.is_null() {
        return Err(NSSError::ImportCertError);
    }
    defer!(unsafe {
        CERT_DestroyCertificate(nss_cert);
    });

    let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
    if key.is_null() {
        return Err(NSSError::ExtractPublicKeyFailed);
    }
    defer!(unsafe {
        SECKEY_DestroyPublicKey(key);
    });
    let signature_item = SECItem::maybe_new(signature)?;
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let null_cx_ptr: *const raw::c_void = ptr::null();
    let result = unsafe {
        PK11_VerifyWithMechanism(
            key,
            mechanism,
            params_item,
            &signature_item,
            &hash_item,
            null_cx_ptr,
        )
    };
    match result {
        SEC_SUCCESS => Ok(()),
        SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
        _ => Err(NSSError::LibraryFailure),
    }
}

pub fn sign(
    signature_algorithm: &SignatureAlgorithm,
    pk8: &[u8],
    payload: &[u8],
) -> Result<Vec<u8>, NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });
    let pkcs8item = SECItem::maybe_new(pk8)?;
    let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
    let ku_all = 0xFF;
    let rv = unsafe {
        PK11_ImportDERPrivateKeyInfoAndReturnKey(
            slot,
            &pkcs8item,
            ptr::null(),
            ptr::null(),
            false,
            false,
            ku_all,
            &mut key,
            ptr::null(),
        )
    };
    if rv != SEC_SUCCESS || key.is_null() {
        return Err(NSSError::DecodingPKCS8Failed);
    }
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let signature_len = unsafe { PK11_SignatureLen(key) };
    // Allocate enough space for the signature.
    let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
    let hash_buf = hash(payload).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
    {
        // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
        // fill the SECItem's buf with the bytes of the signature.
        let mut signature_item = SECItemMut::maybe_from_vec(&mut signature)?;
        let rv = unsafe {
            PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
        };
        if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
            return Err(NSSError::SigningFailed);
        }
    }
    unsafe {
        // Now that the bytes of the signature have been filled out, set its length.
        signature.set_len(signature_len);
    }
    Ok(signature)
}
<commit_msg>more descriptive name for making SECItemMuts<commit_after>use std::marker::PhantomData;
use std::mem;
use std::os::raw::c_char;
use std::os::raw;
use std::ptr;

/// An enum identifying supported signature algorithms. Currently only ECDSA with SHA256 (ES256) and
/// RSASSA-PSS with SHA-256 (PS256) are supported. Note that with PS256, the salt length is defined
/// to be 32 bytes.
#[derive(Debug)]
pub enum SignatureAlgorithm {
    ES256,
    PS256,
}

type SECItemType = raw::c_uint; // TODO: actually an enum - is this the right size?
const SI_BUFFER: SECItemType = 0; // called siBuffer in NSS

#[repr(C)]
struct SECItem {
    typ: SECItemType,
    data: *const u8,
    len: raw::c_uint,
}

impl SECItem {
    fn maybe_new(data: &[u8]) -> Result<SECItem, NSSError> {
        if data.len() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data.as_ptr(),
            len: data.len() as u32,
        })
    }

    fn maybe_from_parts(data: *const u8, len: usize) -> Result<SECItem, NSSError> {
        if len > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        Ok(SECItem {
            typ: SI_BUFFER,
            data: data,
            len: len as u32,
        })
    }
}

/// Many NSS APIs take constant data input as SECItems. Some, however, output data as SECItems.
/// To represent this, we define another type of mutable SECItem.
#[repr(C)]
struct SECItemMut<'a> {
    typ: SECItemType,
    data: *mut u8,
    len: raw::c_uint,
    _marker: PhantomData<&'a mut Vec<u8>>,
}

impl<'a> SECItemMut<'a> {
    /// Given a mutable reference to a Vec<u8> that has a particular allocated capacity, create a
    /// SECItemMut that points to the vec and has the same capacity.
    /// The input vec is not expected to have any actual contents, and in any case is cleared.
    fn maybe_from_empty_preallocated_vec(vec: &'a mut Vec<u8>) -> Result<SECItemMut<'a>, NSSError> {
        if vec.capacity() > u32::max_value() as usize {
            return Err(NSSError::InputTooLarge);
        }
        vec.clear();
        Ok(SECItemMut {
            typ: SI_BUFFER,
            data: vec.as_mut_ptr(),
            len: vec.capacity() as u32,
            _marker: PhantomData,
        })
    }
}

#[repr(C)]
struct CkRsaPkcsPssParams {
    // Called CK_RSA_PKCS_PSS_PARAMS in NSS
    hash_alg: CkMechanismType, // Called hashAlg in NSS
    mgf: CkRsaPkcsMgfType,
    s_len: raw::c_ulong, // Called sLen in NSS
}

impl CkRsaPkcsPssParams {
    fn new() -> CkRsaPkcsPssParams {
        CkRsaPkcsPssParams {
            hash_alg: CKM_SHA256,
            mgf: CKG_MGF1_SHA256,
            s_len: 32,
        }
    }

    fn get_params_item(&self) -> Result<SECItem, NSSError> {
        // This isn't entirely NSS' fault, but it mostly is.
        let params_ptr: *const CkRsaPkcsPssParams = self;
        let params_ptr: *const u8 = params_ptr as *const u8;
        let params_secitem =
            SECItem::maybe_from_parts(params_ptr, mem::size_of::<CkRsaPkcsPssParams>())?;
        Ok(params_secitem)
    }
}

// TODO: link to NSS source where these are defined
type SECOidTag = raw::c_uint; // TODO: actually an enum - is this the right size?
const SEC_OID_SHA256: SECOidTag = 191;

type CkMechanismType = raw::c_ulong; // called CK_MECHANISM_TYPE in NSS
const CKM_ECDSA: CkMechanismType = 0x0000_1041;
const CKM_RSA_PKCS_PSS: CkMechanismType = 0x0000_000D;
const CKM_SHA256: CkMechanismType = 0x0000_0250;

type CkRsaPkcsMgfType = raw::c_ulong; // called CK_RSA_PKCS_MGF_TYPE in NSS
const CKG_MGF1_SHA256: CkRsaPkcsMgfType = 0x0000_0002;

type SECStatus = raw::c_int; // TODO: enum - right size?
const SEC_SUCCESS: SECStatus = 0; // Called SECSuccess in NSS
const SEC_FAILURE: SECStatus = -1; // Called SECFailure in NSS

enum SECKEYPublicKey {}
enum SECKEYPrivateKey {}
enum PK11SlotInfo {}
enum CERTCertificate {}
enum CERTCertDBHandle {}

const SHA256_LENGTH: usize = 32;

// TODO: ugh this will probably have a platform-specific name...
#[link(name = "nss3")]
extern "C" {
    fn PK11_HashBuf(
        hashAlg: SECOidTag,
        out: *mut u8,
        data_in: *const u8, // called "in" in NSS
        len: raw::c_int,
    ) -> SECStatus;
    fn PK11_VerifyWithMechanism(
        key: *const SECKEYPublicKey,
        mechanism: CkMechanismType,
        param: *const SECItem,
        sig: *const SECItem,
        hash: *const SECItem,
        wincx: *const raw::c_void,
    ) -> SECStatus;

    fn SECKEY_DestroyPublicKey(pubk: *const SECKEYPublicKey);

    fn CERT_GetDefaultCertDB() -> *const CERTCertDBHandle;
    fn CERT_DestroyCertificate(cert: *mut CERTCertificate);
    fn CERT_NewTempCertificate(
        handle: *const CERTCertDBHandle,
        derCert: *const SECItem,
        nickname: *const c_char,
        isperm: bool,
        copyDER: bool,
    ) -> *mut CERTCertificate;
    fn CERT_ExtractPublicKey(cert: *const CERTCertificate) -> *const SECKEYPublicKey;

    fn PK11_ImportDERPrivateKeyInfoAndReturnKey(
        slot: *mut PK11SlotInfo,
        derPKI: *const SECItem,
        nickname: *const SECItem,
        publicValue: *const SECItem,
        isPerm: bool,
        isPrivate: bool,
        keyUsage: u32,
        privk: *mut *mut SECKEYPrivateKey,
        wincx: *const u8,
    ) -> SECStatus;
    fn PK11_GetInternalSlot() -> *mut PK11SlotInfo;
    fn PK11_FreeSlot(slot: *mut PK11SlotInfo);
    fn PK11_SignatureLen(key: *const SECKEYPrivateKey) -> usize;
    fn PK11_SignWithMechanism(
        key: *const SECKEYPrivateKey,
        mech: CkMechanismType,
        param: *const SECItem,
        sig: *mut SECItemMut,
        hash: *const SECItem,
    ) -> SECStatus;
}

/// An error type describing errors that may be encountered during verification.
#[derive(Debug, PartialEq)]
pub enum NSSError {
    ImportCertError,
    DecodingPKCS8Failed,
    InputTooLarge,
    LibraryFailure,
    SignatureVerificationFailed,
    SigningFailed,
    ExtractPublicKeyFailed,
}

// XXX: make this work with other hashe algos.
fn hash(payload: &[u8]) -> Result<Vec<u8>, NSSError> {
    if payload.len() > raw::c_int::max_value() as usize {
        return Err(NSSError::InputTooLarge);
    }
    let mut hash_buf = vec![0; SHA256_LENGTH];
    let len: raw::c_int = payload.len() as raw::c_int;
    let hash_result =
        unsafe { PK11_HashBuf(SEC_OID_SHA256, hash_buf.as_mut_ptr(), payload.as_ptr(), len) };
    if hash_result != SEC_SUCCESS {
        return Err(NSSError::LibraryFailure);
    }
    Ok(hash_buf)
}

/// Main entrypoint for verification. Given a signature algorithm, the bytes of a subject public key
/// info, a payload, and a signature over the payload, returns a result based on the outcome of
/// decoding the subject public key info and running the signature verification algorithm on the
/// signed data.
pub fn verify_signature(
    signature_algorithm: &SignatureAlgorithm,
    cert: &[u8],
    payload: &[u8],
    signature: &[u8],
) -> Result<(), NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });

    let hash_buf = hash(payload).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;

    // Import DER cert into NSS.
    let der_cert = SECItem::maybe_new(cert)?;
    let db_handle = unsafe { CERT_GetDefaultCertDB() };
    if db_handle.is_null() {
        // TODO #28
        return Err(NSSError::LibraryFailure);
    }
    let nss_cert =
        unsafe { CERT_NewTempCertificate(db_handle, &der_cert, ptr::null(), false, true) };
    if nss_cert.is_null() {
        return Err(NSSError::ImportCertError);
    }
    defer!(unsafe {
        CERT_DestroyCertificate(nss_cert);
    });

    let key = unsafe { CERT_ExtractPublicKey(nss_cert) };
    if key.is_null() {
        return Err(NSSError::ExtractPublicKeyFailed);
    }
    defer!(unsafe {
        SECKEY_DestroyPublicKey(key);
    });
    let signature_item = SECItem::maybe_new(signature)?;
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let null_cx_ptr: *const raw::c_void = ptr::null();
    let result = unsafe {
        PK11_VerifyWithMechanism(
            key,
            mechanism,
            params_item,
            &signature_item,
            &hash_item,
            null_cx_ptr,
        )
    };
    match result {
        SEC_SUCCESS => Ok(()),
        SEC_FAILURE => Err(NSSError::SignatureVerificationFailed),
        _ => Err(NSSError::LibraryFailure),
    }
}

pub fn sign(
    signature_algorithm: &SignatureAlgorithm,
    pk8: &[u8],
    payload: &[u8],
) -> Result<Vec<u8>, NSSError> {
    let slot = unsafe { PK11_GetInternalSlot() };
    if slot.is_null() {
        return Err(NSSError::LibraryFailure);
    }
    defer!(unsafe {
        PK11_FreeSlot(slot);
    });
    let pkcs8item = SECItem::maybe_new(pk8)?;
    let mut key: *mut SECKEYPrivateKey = ptr::null_mut();
    let ku_all = 0xFF;
    let rv = unsafe {
        PK11_ImportDERPrivateKeyInfoAndReturnKey(
            slot,
            &pkcs8item,
            ptr::null(),
            ptr::null(),
            false,
            false,
            ku_all,
            &mut key,
            ptr::null(),
        )
    };
    if rv != SEC_SUCCESS || key.is_null() {
        return Err(NSSError::DecodingPKCS8Failed);
    }
    let mechanism = match *signature_algorithm {
        SignatureAlgorithm::ES256 => CKM_ECDSA,
        SignatureAlgorithm::PS256 => CKM_RSA_PKCS_PSS,
    };
    let rsa_pss_params = CkRsaPkcsPssParams::new();
    let rsa_pss_params_item = rsa_pss_params.get_params_item()?;
    let params_item = match *signature_algorithm {
        SignatureAlgorithm::ES256 => ptr::null(),
        SignatureAlgorithm::PS256 => &rsa_pss_params_item,
    };
    let signature_len = unsafe { PK11_SignatureLen(key) };
    // Allocate enough space for the signature.
    let mut signature: Vec<u8> = Vec::with_capacity(signature_len);
    let hash_buf = hash(payload).unwrap();
    let hash_item = SECItem::maybe_new(hash_buf.as_slice())?;
    {
        // Get a mutable SECItem on the preallocated signature buffer. PK11_SignWithMechanism will
        // fill the SECItem's buf with the bytes of the signature.
        let mut signature_item = SECItemMut::maybe_from_empty_preallocated_vec(&mut signature)?;
        let rv = unsafe {
            PK11_SignWithMechanism(key, mechanism, params_item, &mut signature_item, &hash_item)
        };
        if rv != SEC_SUCCESS || signature_item.len as usize != signature_len {
            return Err(NSSError::SigningFailed);
        }
    }
    unsafe {
        // Now that the bytes of the signature have been filled out, set its length.
        signature.set_len(signature_len);
    }
    Ok(signature)
}
<|endoftext|>


<commit_before>#[derive(Copy, Clone)]
pub struct FixedPoint16 {
    inner: i16,
}

impl FixedPoint16 {
    #[inline]
    pub const fn raw(value: i16) -> Self {
        FixedPoint16 { inner: value }
    }

    #[inline]
    pub const fn to_inner(self) -> i16 {
        self.inner
    }

    #[inline]
    pub const fn integer(self) -> i16 {
        self.inner >> 8
    }

    #[inline]
    pub const fn fractional(self) -> u16 {
        (self.inner & 0xFF) as u16
    }
}

impl Default for FixedPoint16 {
    fn default() -> Self {
        Self::raw(0)
    }
}

impl std::ops::Add for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn add(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_add(other.inner))
    }
}

impl std::ops::AddAssign for FixedPoint16 {
    #[inline]
    fn add_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_add(other.inner);
    }
}

impl std::ops::Sub for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn sub(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_sub(other.inner))
    }
}

impl std::ops::SubAssign for FixedPoint16 {
    #[inline]
    fn sub_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_sub(other.inner);
    }
}

impl std::ops::Mul for FixedPoint16 {
    type Output = Self;
    #[inline]
    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_mul(other.inner) >> 8)
    }
}

impl std::ops::MulAssign for FixedPoint16 {
    #[inline]
    #[allow(clippy::suspicious_op_assign_impl)]
    fn mul_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_mul(other.inner) >> 8;
    }
}

impl std::ops::Neg for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn neg(self) -> Self {
        FixedPoint16::raw(-self.inner)
    }
}

#[derive(Copy, Clone)]
pub struct FixedPoint32 {
    inner: i32,
}

impl FixedPoint32 {
    #[inline]
    pub const fn raw(value: i32) -> Self {
        FixedPoint32 { inner: value }
    }

    #[inline]
    pub const fn to_inner(self) -> i32 {
        self.inner
    }

    #[inline]
    pub const fn integer(self) -> i32 {
        self.inner >> 8
    }

    #[inline]
    pub const fn fractional(self) -> u32 {
        (self.inner & 0xFF) as u32
    }
}

impl Default for FixedPoint32 {
    fn default() -> Self {
        Self::raw(0)
    }
}

impl std::convert::From<u32> for FixedPoint32 {
    #[inline]
    fn from(original: u32) -> Self {
        FixedPoint32::raw((original as i32) << 8)
    }
}

impl std::convert::From<u16> for FixedPoint32 {
    #[inline]
    fn from(original: u16) -> Self {
        FixedPoint32::raw((original as u32 as i32) << 8)
    }
}

impl std::convert::From<i32> for FixedPoint32 {
    fn from(original: i32) -> Self {
        FixedPoint32::raw(original << 8)
    }
}

impl std::convert::From<FixedPoint16> for FixedPoint32 {
    #[inline]
    fn from(original: FixedPoint16) -> Self {
        FixedPoint32::raw(original.inner as i32)
    }
}

impl std::ops::Add for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn add(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_add(other.inner))
    }
}

impl std::ops::AddAssign for FixedPoint32 {
    #[inline]
    fn add_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_add(other.inner);
    }
}

impl std::ops::Sub for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn sub(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_sub(other.inner))
    }
}

impl std::ops::SubAssign for FixedPoint32 {
    #[inline]
    fn sub_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_sub(other.inner);
    }
}

impl std::ops::Mul for FixedPoint32 {
    type Output = Self;
    #[inline]
    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_mul(other.inner) >> 8)
    }
}

impl std::ops::MulAssign for FixedPoint32 {
    #[inline]
    #[allow(clippy::suspicious_op_assign_impl)]
    fn mul_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_mul(other.inner) >> 8;
    }
}

impl std::ops::Div for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn div(self, other: Self) -> Self {
        FixedPoint32::raw((self.inner << 16) / (other.inner << 8))
    }
}

impl std::ops::DivAssign for FixedPoint32 {
    #[inline]
    fn div_assign(&mut self, other: Self) {
        self.inner = (self.inner << 16) / (other.inner << 8);
    }
}

impl std::ops::Neg for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn neg(self) -> Self {
        FixedPoint32::raw(-self.inner)
    }
}

impl std::fmt::Debug for FixedPoint32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp32", self.integer(), self.fractional())
    }
}

impl std::fmt::Display for FixedPoint32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp32", self.integer(), self.fractional())
    }
}
<commit_msg>Add display/debug implementations for 16bit fixed point integers.<commit_after>#[derive(Copy, Clone)]
pub struct FixedPoint16 {
    inner: i16,
}

impl FixedPoint16 {
    #[inline]
    pub const fn raw(value: i16) -> Self {
        FixedPoint16 { inner: value }
    }

    #[inline]
    pub const fn to_inner(self) -> i16 {
        self.inner
    }

    #[inline]
    pub const fn integer(self) -> i16 {
        self.inner >> 8
    }

    #[inline]
    pub const fn fractional(self) -> u16 {
        (self.inner & 0xFF) as u16
    }
}

impl Default for FixedPoint16 {
    fn default() -> Self {
        Self::raw(0)
    }
}

impl std::ops::Add for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn add(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_add(other.inner))
    }
}

impl std::ops::AddAssign for FixedPoint16 {
    #[inline]
    fn add_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_add(other.inner);
    }
}

impl std::ops::Sub for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn sub(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_sub(other.inner))
    }
}

impl std::ops::SubAssign for FixedPoint16 {
    #[inline]
    fn sub_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_sub(other.inner);
    }
}

impl std::ops::Mul for FixedPoint16 {
    type Output = Self;
    #[inline]
    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, other: Self) -> Self {
        FixedPoint16::raw(self.inner.wrapping_mul(other.inner) >> 8)
    }
}

impl std::ops::MulAssign for FixedPoint16 {
    #[inline]
    #[allow(clippy::suspicious_op_assign_impl)]
    fn mul_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_mul(other.inner) >> 8;
    }
}

impl std::ops::Neg for FixedPoint16 {
    type Output = Self;
    #[inline]
    fn neg(self) -> Self {
        FixedPoint16::raw(-self.inner)
    }
}

impl std::fmt::Debug for FixedPoint16 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp16", self.integer(), self.fractional())
    }
}

impl std::fmt::Display for FixedPoint16 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp16", self.integer(), self.fractional())
    }
}

#[derive(Copy, Clone)]
pub struct FixedPoint32 {
    inner: i32,
}

impl FixedPoint32 {
    #[inline]
    pub const fn raw(value: i32) -> Self {
        FixedPoint32 { inner: value }
    }

    #[inline]
    pub const fn to_inner(self) -> i32 {
        self.inner
    }

    #[inline]
    pub const fn integer(self) -> i32 {
        self.inner >> 8
    }

    #[inline]
    pub const fn fractional(self) -> u32 {
        (self.inner & 0xFF) as u32
    }
}

impl Default for FixedPoint32 {
    fn default() -> Self {
        Self::raw(0)
    }
}

impl std::convert::From<u32> for FixedPoint32 {
    #[inline]
    fn from(original: u32) -> Self {
        FixedPoint32::raw((original as i32) << 8)
    }
}

impl std::convert::From<u16> for FixedPoint32 {
    #[inline]
    fn from(original: u16) -> Self {
        FixedPoint32::raw((original as u32 as i32) << 8)
    }
}

impl std::convert::From<i32> for FixedPoint32 {
    fn from(original: i32) -> Self {
        FixedPoint32::raw(original << 8)
    }
}

impl std::convert::From<FixedPoint16> for FixedPoint32 {
    #[inline]
    fn from(original: FixedPoint16) -> Self {
        FixedPoint32::raw(original.inner as i32)
    }
}

impl std::ops::Add for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn add(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_add(other.inner))
    }
}

impl std::ops::AddAssign for FixedPoint32 {
    #[inline]
    fn add_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_add(other.inner);
    }
}

impl std::ops::Sub for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn sub(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_sub(other.inner))
    }
}

impl std::ops::SubAssign for FixedPoint32 {
    #[inline]
    fn sub_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_sub(other.inner);
    }
}

impl std::ops::Mul for FixedPoint32 {
    type Output = Self;
    #[inline]
    #[allow(clippy::suspicious_arithmetic_impl)]
    fn mul(self, other: Self) -> Self {
        FixedPoint32::raw(self.inner.wrapping_mul(other.inner) >> 8)
    }
}

impl std::ops::MulAssign for FixedPoint32 {
    #[inline]
    #[allow(clippy::suspicious_op_assign_impl)]
    fn mul_assign(&mut self, other: Self) {
        self.inner = self.inner.wrapping_mul(other.inner) >> 8;
    }
}

impl std::ops::Div for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn div(self, other: Self) -> Self {
        FixedPoint32::raw((self.inner << 16) / (other.inner << 8))
    }
}

impl std::ops::DivAssign for FixedPoint32 {
    #[inline]
    fn div_assign(&mut self, other: Self) {
        self.inner = (self.inner << 16) / (other.inner << 8);
    }
}

impl std::ops::Neg for FixedPoint32 {
    type Output = Self;
    #[inline]
    fn neg(self) -> Self {
        FixedPoint32::raw(-self.inner)
    }
}

impl std::fmt::Debug for FixedPoint32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp32", self.integer(), self.fractional())
    }
}

impl std::fmt::Display for FixedPoint32 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}.{}fp32", self.integer(), self.fractional())
    }
}
<|endoftext|>


<commit_before>use common::{ApiError, Body, Credentials, Query, discovery_api};
use hyper::method::Method::{Get, Post};
use serde_json::Value;

pub fn detail(creds: &Credentials,
              env_id: &str,
              collection_id: &str,
              document_id: &str)
              -> Result<Value, ApiError> {
    let path = "/v1/environments/".to_string() + env_id + "/collections/" +
               collection_id +
               "/documents/" + document_id;
    Ok(discovery_api(creds, Get, &path, Query::None, &Body::None)?)
}

pub fn create(creds: &Credentials,
              env_id: &str,
              collection_id: &str,
              configuration_id: Option<&str>,
              document_id: Option<&str>,
              filename: &str)
              -> Result<Value, ApiError> {
    let path = match document_id {
        Some(id) => {
            "/v1/environments/".to_string() + env_id + "/collections/" +
            collection_id + "/documents/" + id
        }
        None => {
            "/v1/environments/".to_string() + env_id + "/collections/" +
            collection_id + "/documents"
        }
    };
    let q = match configuration_id {
        Some(id) => Query::Config(id.to_string()),
        None => Query::None,
    };
    Ok(discovery_api(creds, Post, &path, q, &Body::Filename(filename))?)
}
<commit_msg>Add document::delete<commit_after>use common::{ApiError, Body, Credentials, Query, discovery_api};
use hyper::method::Method::{Delete, Get, Post};
use serde_json::Value;

pub fn detail(creds: &Credentials,
              env_id: &str,
              collection_id: &str,
              document_id: &str)
              -> Result<Value, ApiError> {
    let path = "/v1/environments/".to_string() + env_id + "/collections/" +
               collection_id +
               "/documents/" + document_id;
    Ok(discovery_api(creds, Get, &path, Query::None, &Body::None)?)
}

pub fn delete(creds: &Credentials,
              env_id: &str,
              collection_id: &str,
              document_id: &str)
              -> Result<Value, ApiError> {
    let path = "/v1/environments/".to_string() + env_id + "/collections/" +
               collection_id +
               "/documents/" + document_id;
    Ok(discovery_api(creds, Delete, &path, Query::None, &Body::None)?)
}

pub fn create(creds: &Credentials,
              env_id: &str,
              collection_id: &str,
              configuration_id: Option<&str>,
              document_id: Option<&str>,
              filename: &str)
              -> Result<Value, ApiError> {
    let path = match document_id {
        Some(id) => {
            "/v1/environments/".to_string() + env_id + "/collections/" +
            collection_id + "/documents/" + id
        }
        None => {
            "/v1/environments/".to_string() + env_id + "/collections/" +
            collection_id + "/documents"
        }
    };
    let q = match configuration_id {
        Some(id) => Query::Config(id.to_string()),
        None => Query::None,
    };
    Ok(discovery_api(creds, Post, &path, q, &Body::Filename(filename))?)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

use mustache;

use error::{FerrumError, FerrumResult};
use parser;

pub type Header = HashMap<String, String>;

#[derive(PartialEq, Debug)]
pub struct Document {
    data: Header,
    content: String,
}

impl Document {
    pub fn new(header: Header, content: &str) -> Document {
        Document { data: header, content: content.to_string() }
    }

    pub fn as_html(&self) -> FerrumResult<String> {
        let template = mustache::compile_str(&self.content);

        // Write the template to memory, then retrieve it as a string.
        let mut buf = Vec::<u8>::new();
        template.render(&mut buf, &self.data).is_ok();

        String::from_utf8(buf).map_err(FerrumError::from)
    }

    pub fn render_to_file(&self, file_path: &Path, templates: &HashMap<String, String>) -> FerrumResult<()> {
        let template_path = try!(self.template());
        let template = try!(templates.get(&template_path.to_string())
            .ok_or(FerrumError::missing_template()));

        fs::create_dir_all(&file_path.parent().unwrap()).is_ok();

        let mut file = try!(File::create(file_path));
        let mut data = HashMap::new();

        data.insert("content", try!(self.as_html()));

        for (key, value) in self.data.iter() {
            data.insert(&key, value.clone());
        }

        let template = mustache::compile_str(&template);

        template.render(&mut file, &data).is_ok();

        info!("Created {}", file_path.display());
        Ok(())
    }

    fn template(&self) -> FerrumResult<&String> {
        self.data.get(&"template".to_string())
            .ok_or(FerrumError::missing_template_field())
    }
}

pub fn load_documents_from_disk<F>(documents_path: &Path, mut criteria: F) -> FerrumResult<HashMap<PathBuf, Document>>
    where F : FnMut(&Path) -> bool
{
    use util;

    let mut documents = HashMap::new();

    try!(util::walk_dir(
        documents_path,
        &mut |path| {
            if !criteria(&path) {
                return Ok(());
            }

            // Read the document from the disk.
            let content = {
                use std::io::Read;

                let mut buf = String::new();
                let mut file = try!(File::open(&path));
                try!(file.read_to_string(&mut buf));
                buf
            };
            let document = try!(parser::parse_document(&content));
            let relative_path = try!(path.strip_prefix(documents_path));

            documents.insert(relative_path.with_extension("").to_path_buf(), document);

            Ok(())
        },
    ));

    Ok(documents)
}
<commit_msg>bug: fix non-file document reading issue<commit_after>use std::collections::HashMap;
use std::fs::{self, File};
use std::path::{Path, PathBuf};

use mustache;

use error::{FerrumError, FerrumResult};
use parser;

pub type Header = HashMap<String, String>;

#[derive(PartialEq, Debug)]
pub struct Document {
    data: Header,
    content: String,
}

impl Document {
    pub fn new(header: Header, content: &str) -> Document {
        Document { data: header, content: content.to_string() }
    }

    pub fn as_html(&self) -> FerrumResult<String> {
        let template = mustache::compile_str(&self.content);

        // Write the template to memory, then retrieve it as a string.
        let mut buf = Vec::<u8>::new();
        template.render(&mut buf, &self.data).is_ok();

        String::from_utf8(buf).map_err(FerrumError::from)
    }

    pub fn render_to_file(&self, file_path: &Path, templates: &HashMap<String, String>) -> FerrumResult<()> {
        let template_path = try!(self.template());
        let template = try!(templates.get(&template_path.to_string())
            .ok_or(FerrumError::missing_template()));

        fs::create_dir_all(&file_path.parent().unwrap()).is_ok();

        let mut file = try!(File::create(file_path));
        let mut data = HashMap::new();

        data.insert("content", try!(self.as_html()));

        for (key, value) in self.data.iter() {
            data.insert(&key, value.clone());
        }

        let template = mustache::compile_str(&template);

        template.render(&mut file, &data).is_ok();

        info!("Created {}", file_path.display());
        Ok(())
    }

    fn template(&self) -> FerrumResult<&String> {
        self.data.get(&"template".to_string())
            .ok_or(FerrumError::missing_template_field())
    }
}

pub fn load_documents_from_disk<F>(documents_path: &Path, mut criteria: F) -> FerrumResult<HashMap<PathBuf, Document>>
    where F : FnMut(&Path) -> bool
{
    use util;

    let mut documents = HashMap::new();

    try!(util::walk_dir(
        documents_path,
        &mut |path| {
            if !criteria(&path) || !path.is_file() {
                return Ok(());
            }

            // Read the document from the disk.
            let content = {
                use std::io::Read;

                let mut buf = String::new();
                let mut file = try!(File::open(&path));
                try!(file.read_to_string(&mut buf));
                buf
            };
            let document = try!(parser::parse_document(&content));
            let relative_path = try!(path.strip_prefix(documents_path));

            documents.insert(relative_path.with_extension("").to_path_buf(), document);

            Ok(())
        },
    ));

    Ok(documents)
}
<|endoftext|>


<commit_before>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    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/>.
*/

use serde::{Deserialize, de::DeserializeOwned};
use reqwest::Client;
use bytes::Bytes;
use async_trait::async_trait;
use futures::future;

use crate::esr_errors::Result;

#[derive(Deserialize, Debug, Clone)]
pub struct Meta {
    total: usize,
}

#[async_trait]
pub trait EsrFromMulti: EsrFrom + Sync + Send + 'static {
    type Inner: Clone;
    type Inner2: Clone;

    async fn from_url_multi(url: &str, multi_page: bool) -> Result<Self> {
        let mut initial_self = Self::from_url(url).await?;
        let total = initial_self.total_from_meta();

        // per_page=100 is the maximum number allowed.
        // If total > 100, GET all pages and append all results to initial_self inner vec
        if multi_page && total > 100 {
            let num_pages = (total as f64 / 100.0).ceil() as usize;

            let more_pages_iter = (2..=num_pages)
                .map(|page| url.to_owned() + &format!("&page={}", page))
                .map(|page_url| Self::from_url_owned(page_url));

            for page_res in future::join_all(more_pages_iter).await {
                let page = page_res?;
                initial_self.get_inner_mut().extend_from_slice(&*page.get_inner());

                // If inner2 exists
                match (initial_self.get_inner2_mut_opt(), page.get_inner2_opt()) {
                    (Some(initial_inner2_mut), Some(page_inner2)) => initial_inner2_mut.extend_from_slice(&*page_inner2),
                    (Some(_), None) | (None, Some(_)) => Err("Wrong trait implementation!")?,
                    (None, None) => (),
                }
            }

        }

        if multi_page && initial_self.get_inner().len() != total {
            Err("Total no. of results does not match total reported")?;
        }

        if let Some(inner2) = initial_self.get_inner2_opt() {
            if inner2.len() != initial_self.get_inner().len() {
                Err("Inner/Inner2 length mismatch")?;
            }
        }

        Ok(initial_self)
    }

    // Owned arguments variants to allow use in task::spawn
    async fn from_url_multi_owned(url: String, multi_page: bool) -> Result<Self> {
        Self::from_url_multi(&*url, multi_page).await
    }

    fn total_from_meta(&self) -> usize {
        self.get_meta().total
    }

    fn get_meta(&self) -> &Meta;
    fn get_inner(&self) -> &Vec<Self::Inner>;
    fn get_inner_mut(&mut self) -> &mut Vec<Self::Inner>;

    fn get_inner2_opt(&self) -> Option<&Vec<Self::Inner2>> {
        None
    }

    fn get_inner2_mut_opt(&mut self) -> Option<&mut Vec<Self::Inner2>> {
        None
    }
}

#[async_trait]
pub trait EsrFrom: Sized + Sync + Send + DeserializeOwned {
    // url=id by default
    fn url_from_id(id: &str) -> String {
        String::from(id)
    }

    fn url_from_id_and_token(id: &str, token: &str) -> String {
        let url = Self::url_from_id(id);
        if url.find('?').is_some() {
            url + "&access_token=" + token
        } else {
            url + "?access_token=" + token
        }
    }

    async fn bytes_from_url(url: &str) -> Result<Bytes> {
        let client = Client::builder().build()?;

        // Creating an outgoing request.
        let ret = client.get(url)
            .header("user-agent", "cargo-esr/0.1")
            .send()
            .await?
            .bytes()
            .await?;

        Ok(ret)
    }

    async fn bytes_from_id(id: &str) -> Result<Bytes> {
        Self::bytes_from_url(&*Self::url_from_id(id)).await
    }

    async fn bytes_from_id_with_token(id: &str, token: &str) -> Result<Bytes> {
        Self::bytes_from_url(&*Self::url_from_id_and_token(id, token)).await
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        // Deserialize
        let info = serde_json::from_slice(bytes)?;
        Ok(info)
    }

    async fn from_url(url: &str) -> Result<Self> {
        let bytes = Self::bytes_from_url(url).await?;
        Self::from_bytes(&*bytes)
    }

    async fn from_id(id: &str) -> Result<Self> {
        Self::from_url(&*Self::url_from_id(id)).await
    }

    async fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        Self::from_url(&*Self::url_from_id_and_token(id, token)).await
    }

    // Owned arguments variants to allow use in task::spawn
    async fn from_url_owned(url: String) -> Result<Self> {
        Self::from_url(&*url).await
    }

    async fn from_id_owned(id: String) -> Result<Self> {
        Self::from_id(&*id).await
    }

    async fn from_id_with_token_owned(id: String, token: String) -> Result<Self> {
        Self::from_id_with_token(&*id, &*token).await
    }
}
<commit_msg>esr_from/bytes_from_url(): Err if HTTP response status is an error<commit_after>/*
    This file is a part of cargo-esr.

    Copyright (C) 2017 Mohammad AlSaleh <CE.Mohammad.AlSaleh at gmail.com>
    https://github.com/rust-alt/cargo-esr

    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/>.
*/

use serde::{Deserialize, de::DeserializeOwned};
use reqwest::Client;
use bytes::Bytes;
use async_trait::async_trait;
use futures::future;

use crate::esr_errors::Result;

#[derive(Deserialize, Debug, Clone)]
pub struct Meta {
    total: usize,
}

#[async_trait]
pub trait EsrFromMulti: EsrFrom + Sync + Send + 'static {
    type Inner: Clone;
    type Inner2: Clone;

    async fn from_url_multi(url: &str, multi_page: bool) -> Result<Self> {
        let mut initial_self = Self::from_url(url).await?;
        let total = initial_self.total_from_meta();

        // per_page=100 is the maximum number allowed.
        // If total > 100, GET all pages and append all results to initial_self inner vec
        if multi_page && total > 100 {
            let num_pages = (total as f64 / 100.0).ceil() as usize;

            let more_pages_iter = (2..=num_pages)
                .map(|page| url.to_owned() + &format!("&page={}", page))
                .map(|page_url| Self::from_url_owned(page_url));

            for page_res in future::join_all(more_pages_iter).await {
                let page = page_res?;
                initial_self.get_inner_mut().extend_from_slice(&*page.get_inner());

                // If inner2 exists
                match (initial_self.get_inner2_mut_opt(), page.get_inner2_opt()) {
                    (Some(initial_inner2_mut), Some(page_inner2)) => initial_inner2_mut.extend_from_slice(&*page_inner2),
                    (Some(_), None) | (None, Some(_)) => Err("Wrong trait implementation!")?,
                    (None, None) => (),
                }
            }

        }

        if multi_page && initial_self.get_inner().len() != total {
            Err("Total no. of results does not match total reported")?;
        }

        if let Some(inner2) = initial_self.get_inner2_opt() {
            if inner2.len() != initial_self.get_inner().len() {
                Err("Inner/Inner2 length mismatch")?;
            }
        }

        Ok(initial_self)
    }

    // Owned arguments variants to allow use in task::spawn
    async fn from_url_multi_owned(url: String, multi_page: bool) -> Result<Self> {
        Self::from_url_multi(&*url, multi_page).await
    }

    fn total_from_meta(&self) -> usize {
        self.get_meta().total
    }

    fn get_meta(&self) -> &Meta;
    fn get_inner(&self) -> &Vec<Self::Inner>;
    fn get_inner_mut(&mut self) -> &mut Vec<Self::Inner>;

    fn get_inner2_opt(&self) -> Option<&Vec<Self::Inner2>> {
        None
    }

    fn get_inner2_mut_opt(&mut self) -> Option<&mut Vec<Self::Inner2>> {
        None
    }
}

#[async_trait]
pub trait EsrFrom: Sized + Sync + Send + DeserializeOwned {
    // url=id by default
    fn url_from_id(id: &str) -> String {
        String::from(id)
    }

    fn url_from_id_and_token(id: &str, token: &str) -> String {
        let url = Self::url_from_id(id);
        if url.find('?').is_some() {
            url + "&access_token=" + token
        } else {
            url + "?access_token=" + token
        }
    }

    async fn bytes_from_url(url: &str) -> Result<Bytes> {
        let client = Client::builder().build()?;

        // Creating an outgoing request.
        let ret = client.get(url)
            .header("user-agent", "cargo-esr/0.1")
            .send()
            .await?
            .error_for_status()?
            .bytes()
            .await?;

        Ok(ret)
    }

    async fn bytes_from_id(id: &str) -> Result<Bytes> {
        Self::bytes_from_url(&*Self::url_from_id(id)).await
    }

    async fn bytes_from_id_with_token(id: &str, token: &str) -> Result<Bytes> {
        Self::bytes_from_url(&*Self::url_from_id_and_token(id, token)).await
    }

    fn from_bytes(bytes: &[u8]) -> Result<Self> {
        // Deserialize
        let info = serde_json::from_slice(bytes)?;
        Ok(info)
    }

    async fn from_url(url: &str) -> Result<Self> {
        let bytes = Self::bytes_from_url(url).await?;
        Self::from_bytes(&*bytes)
    }

    async fn from_id(id: &str) -> Result<Self> {
        Self::from_url(&*Self::url_from_id(id)).await
    }

    async fn from_id_with_token(id: &str, token: &str) -> Result<Self> {
        Self::from_url(&*Self::url_from_id_and_token(id, token)).await
    }

    // Owned arguments variants to allow use in task::spawn
    async fn from_url_owned(url: String) -> Result<Self> {
        Self::from_url(&*url).await
    }

    async fn from_id_owned(id: String) -> Result<Self> {
        Self::from_id(&*id).await
    }

    async fn from_id_with_token_owned(id: String, token: String) -> Result<Self> {
        Self::from_id_with_token(&*id, &*token).await
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod test;

mod bin_exp;

use ast::{BinOp, Expr, Statement, Value};
use self::bin_exp::{arith_exp, bool_exp, eq_exp, ineq_exp};
use error::{Error, Result};
use state::State;

use stepper::Stepper;

impl Statement {
    pub fn eval(&self, state: &mut State) -> Result<Option<Value>> {
        match *self {
            Statement::ArrayElemAssign(ref var, ref index, ref indexes, ref exp) => {
                let mut array_vec = match state.lookup(var) {
                    Some(&Value::Array(ref vec)) => vec.clone(),
                    Some(ref val) => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, val.type_string_with_article(), self)),
                    None => return Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so {} doesn't make sense", var, self)),
                };

                let index_val = try!(index.eval(state));
                let mut index_int = match index_val {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                };

                if index_int < 0 {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense",
                        index, index_int, self))
                }

                if index_int as usize >= array_vec.len() {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                        var, array_vec.len(), self))
                }

                let mut repr = format!("{}[{}]", var, index);

                for idx in indexes {
                    repr.push_str(&format!("[{}]", idx));

                    array_vec = match array_vec[index_int as usize] {
                        Value::Array(ref vec) => vec.clone(),
                        ref val => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", repr, val.type_string_with_article(), self)),
                    };

                    let index_val = try!(idx.eval(state));

                    index_int = match index_val {
                        Value::Int(i) => i,
                        _ => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                    };

                    if index_int < 0 {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense",
                            index, index_int, self))
                    }

                    if index_int as usize >= array_vec.len() {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                            var, array_vec.len(), self))
                    }
                }

                let exp_val = try!(exp.eval(state));
                array_vec[index_int as usize] = exp_val;
                state.assign(var, Value::Array(array_vec)).map(|_| None)
            }
            Statement::Defun(ref t, ref name, ref params, ref body) =>
                state.define_func(t, name, params, body).map(|_| None),
            Statement::For(ref var, ref exp, ref block) => {
                let val = try!(exp.eval(state));
                let vec = match val {
                    Value::Array(vec) => vec,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `for {} in {} ...` doesn't make sense", exp, val.type_string_with_article(), var, exp)),
                };

                for array_val in vec {
                    state.define_var(var, array_val);

                    for stmt in block {
                        try!(stmt.eval(state));
                    }
                }

                Ok(None)
            }
            Statement::If(ref exp, ref block1, ref block2) => {
                let val = try!(exp.eval(state));
                let block = match val {
                    Value::Bool(true) => block1,
                    Value::Bool(false) => block2,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `if ({}) ...` doesn't make sense", exp, val.type_string_with_article(), exp)),
                };

                for stmt in block.iter() {
                    if let v @ Some(_) = try!(stmt.eval(state)) {
                        return Ok(v);
                    }
                }

                Ok(None)
            }
            Statement::Let(ref var, ref exp) => {
                let val = try!(exp.eval(state));
                state.define_var(var, val);
                Ok(None)
            }
            Statement::Print(ref exp) => {
                println!("{}", try!(exp.eval(state)));
                Ok(None)
            }
            Statement::Return(ref exp) => exp.eval(state).map(Some),
            Statement::VarAssign(ref var, ref exp) => {
                let val = try!(exp.eval(state));
                state.assign(var, val).map(|_| None)
            }
            Statement::VoidCall(ref name, ref args) => state.call_function(name, args).map(|_| None),
            Statement::While(ref exp, ref block) => {
                let val = try!(exp.eval(state));
                match val {
                    Value::Bool(true) => (),
                    Value::Bool(false) => return Ok(None),
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `while ({}) ...` doesn't make sense", exp, val.type_string_with_article(), exp)),
                };

                for stmt in block.iter() {
                    if let v @ Some(_) = try!(stmt.eval(state)) {
                        return Ok(v);
                    }
                }

                self.eval(state)
            }
        }
    }
}

impl Expr {
    pub fn eval(&self, state: &mut State) -> Result<Value> {
        match *self {
            Expr::Array(ref vec) => {
                let mut out = Vec::new();

                for ref exp in vec {
                    out.push(try!(exp.eval(state)));
                }

                Ok(Value::Array(out))
            }
            Expr::ArrayElement(ref var, ref index, ref indexes) => {
                let mut array_vec = match state.lookup(var) {
                    Some(&Value::Array(ref vec)) => vec.clone(),
                    Some(ref val) => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, val.type_string_with_article(), self)),
                    None => return Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so {} doesn't make sense", var, self)),
                };

                let index_val = try!(index.eval(state));
                let mut index_int = match index_val {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                };

                if index_int < 0 {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense",
                        index, index_int, self))
                }

                if index_int as usize >= array_vec.len() {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                        var, array_vec.len(), self))
                }

                let mut repr = format!("{}[{}]", var, index);

                for idx in indexes {
                    repr.push_str(&format!("[{}]", idx));

                    array_vec = match array_vec[index_int as usize] {
                        Value::Array(ref vec) => vec.clone(),
                        ref val => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", repr, val.type_string_with_article(), self)),
                    };

                    let index_val = try!(idx.eval(state));

                    index_int = match index_val {
                        Value::Int(i) => i,
                        _ => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                    };

                    if index_int < 0 {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense",
                            index, index_int, self))
                    }

                    if index_int as usize >= array_vec.len() {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                            var, array_vec.len(), self))
                    }
                }

                Ok(array_vec[index_int as usize].clone())
            }
            Expr::BinExp(ref exp1, ref op, ref exp2) => {
                let val1 = try!(exp1.eval(state));
                let val2 = try!(exp2.eval(state));

                match *op {
                    BinOp::And => bool_exp(self, val1, val2, |x, y| x && y),
                    BinOp::Or =>  bool_exp(self, val1, val2, |x, y| x || y),
                    BinOp::Equal => eq_exp(self, val1, val2, |x, y| x == y),
                    BinOp::NotEqual => eq_exp(self, val1, val2, |x, y| x != y),
                    BinOp::GreaterOrEqual => ineq_exp(self, val1, val2, |x, y| x >= y),
                    BinOp::GreaterThan => ineq_exp(self, val1, val2, |x, y| x > y),
                    BinOp::LessOrEqual => ineq_exp(self, val1, val2, |x, y| x <= y),
                    BinOp::LessThan => ineq_exp(self, val1, val2, |x, y| x < y),
                    BinOp::Plus => arith_exp(self, val1, val2, |x, y| x + y),
                    BinOp::Minus => arith_exp(self, val1, val2, |x, y| x - y),
                    BinOp::Times => arith_exp(self, val1, val2, |x, y| x * y),
                    BinOp::Divide => arith_exp(self, val1, val2, |x, y| x / y),
                    BinOp::Modulus => arith_exp(self, val1, val2, |x, y| x % y),
                }
            }
            Expr::Call(ref name, ref args) => {
                match state.call_function(name, args) {
                    Ok(Some(val)) => Ok(val),
                    Ok(None) => Error::type_error(
                        &format!("The function {} doesn't return anything, so {} doesn't make sense", name, self)),
                    Err(e) => Err(e),
                }
            }
            Expr::Not(ref exp) => {
                match try!(exp.eval(state)) {
                    Value::Bool(b) => Ok(Value::Bool(!b)),
                    _ => Error::type_error(
                        &format!("`{}` is not a boolean, so `!{}` doesn't make sense", exp, exp)),
                }
            }
            Expr::Range(ref start, ref end) => {
                let start_int = match try!(start.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", start, self)),
                };

                let end_int = match try!(end.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", end, self)),
                };

                // `stepper` ranges are not end-inclusive.
                let (fixed_end, step_int) = if end_int >= start_int {
                    (end_int + 1, 1)
                } else {
                    (end_int - 1, -1)
                };

                let vec: Vec<_> = step!(start_int => fixed_end; step_int).into_iter().map(|i| Value::Int(i)).collect();
                Ok(Value::Array(vec))
            }
            Expr::Step(ref start, ref end, ref step) => {
                let start_int = match try!(start.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", start, self)),
                };

                let end_int = match try!(end.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", end, self)),
                };

                let step_int = match try!(step.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", step, self)),
                };

                if start_int < end_int && step_int < 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping down from {} will never reach {}",
                            start, start_int, end, end_int, step, step_int, start_int, end_int));
                }

                if start_int > end_int && step_int > 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping up from {} will never reach {}",
                            start, start_int, end, end_int, step, step_int, start_int, end_int));
                }

                if step_int == 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping by 0 will never get anywhere",
                            start, start_int, end, end_int, step, step_int));
                }

                // `stepper` ranges are not end-inclusive.
                let fixed_end = end_int + step_int.signum();

                let vec: Vec<_> = step!(start_int => fixed_end; step_int).into_iter().map(|i| Value::Int(i)).collect();
                Ok(Value::Array(vec))
            }
            Expr::Value(ref val) => Ok(val.clone()),
            Expr::Var(ref var) => {
                match state.lookup(var) {
                    Some(val) => Ok(val.clone()),
                    None => Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so it can't be used in an expression", var))
                }
            }
        }
    }
}
<commit_msg>clippy fix<commit_after>#[cfg(test)]
mod test;

mod bin_exp;

use ast::{BinOp, Expr, Statement, Value};
use self::bin_exp::{arith_exp, bool_exp, eq_exp, ineq_exp};
use error::{Error, Result};
use state::State;

use stepper::Stepper;

impl Statement {
    pub fn eval(&self, state: &mut State) -> Result<Option<Value>> {
        match *self {
            Statement::ArrayElemAssign(ref var, ref index, ref indexes, ref exp) => {
                let mut array_vec = match state.lookup(var) {
                    Some(&Value::Array(ref vec)) => vec.clone(),
                    Some(ref val) => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, val.type_string_with_article(), self)),
                    None => return Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so {} doesn't make sense", var, self)),
                };

                let index_val = try!(index.eval(state));
                let mut index_int = match index_val {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                };

                if index_int < 0 {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense",
                        index, index_int, self))
                }

                if index_int as usize >= array_vec.len() {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                        var, array_vec.len(), self))
                }

                let mut repr = format!("{}[{}]", var, index);

                for idx in indexes {
                    repr.push_str(&format!("[{}]", idx));

                    array_vec = match array_vec[index_int as usize] {
                        Value::Array(ref vec) => vec.clone(),
                        ref val => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", repr, val.type_string_with_article(), self)),
                    };

                    let index_val = try!(idx.eval(state));

                    index_int = match index_val {
                        Value::Int(i) => i,
                        _ => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                    };

                    if index_int < 0 {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense",
                            index, index_int, self))
                    }

                    if index_int as usize >= array_vec.len() {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                            var, array_vec.len(), self))
                    }
                }

                let exp_val = try!(exp.eval(state));
                array_vec[index_int as usize] = exp_val;
                state.assign(var, Value::Array(array_vec)).map(|_| None)
            }
            Statement::Defun(ref t, ref name, ref params, ref body) =>
                state.define_func(t, name, params, body).map(|_| None),
            Statement::For(ref var, ref exp, ref block) => {
                let val = try!(exp.eval(state));
                let vec = match val {
                    Value::Array(vec) => vec,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `for {} in {} ...` doesn't make sense", exp, val.type_string_with_article(), var, exp)),
                };

                for array_val in vec {
                    state.define_var(var, array_val);

                    for stmt in block {
                        try!(stmt.eval(state));
                    }
                }

                Ok(None)
            }
            Statement::If(ref exp, ref block1, ref block2) => {
                let val = try!(exp.eval(state));
                let block = match val {
                    Value::Bool(true) => block1,
                    Value::Bool(false) => block2,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `if ({}) ...` doesn't make sense", exp, val.type_string_with_article(), exp)),
                };

                for stmt in block.iter() {
                    if let v @ Some(_) = try!(stmt.eval(state)) {
                        return Ok(v);
                    }
                }

                Ok(None)
            }
            Statement::Let(ref var, ref exp) => {
                let val = try!(exp.eval(state));
                state.define_var(var, val);
                Ok(None)
            }
            Statement::Print(ref exp) => {
                println!("{}", try!(exp.eval(state)));
                Ok(None)
            }
            Statement::Return(ref exp) => exp.eval(state).map(Some),
            Statement::VarAssign(ref var, ref exp) => {
                let val = try!(exp.eval(state));
                state.assign(var, val).map(|_| None)
            }
            Statement::VoidCall(ref name, ref args) => state.call_function(name, args).map(|_| None),
            Statement::While(ref exp, ref block) => {
                let val = try!(exp.eval(state));
                match val {
                    Value::Bool(true) => (),
                    Value::Bool(false) => return Ok(None),
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `while ({}) ...` doesn't make sense", exp, val.type_string_with_article(), exp)),
                };

                for stmt in block.iter() {
                    if let v @ Some(_) = try!(stmt.eval(state)) {
                        return Ok(v);
                    }
                }

                self.eval(state)
            }
        }
    }
}

impl Expr {
    pub fn eval(&self, state: &mut State) -> Result<Value> {
        match *self {
            Expr::Array(ref vec) => {
                let mut out = Vec::new();

                for ref exp in vec {
                    out.push(try!(exp.eval(state)));
                }

                Ok(Value::Array(out))
            }
            Expr::ArrayElement(ref var, ref index, ref indexes) => {
                let mut array_vec = match state.lookup(var) {
                    Some(&Value::Array(ref vec)) => vec.clone(),
                    Some(ref val) => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, val.type_string_with_article(), self)),
                    None => return Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so {} doesn't make sense", var, self)),
                };

                let index_val = try!(index.eval(state));
                let mut index_int = match index_val {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                };

                if index_int < 0 {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` is {}, so `{}` doesn't make sense",
                        index, index_int, self))
                }

                if index_int as usize >= array_vec.len() {
                    return Error::array_index_out_of_bounds_error(
                        &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                        var, array_vec.len(), self))
                }

                let mut repr = format!("{}[{}]", var, index);

                for idx in indexes {
                    repr.push_str(&format!("[{}]", idx));

                    array_vec = match array_vec[index_int as usize] {
                        Value::Array(ref vec) => vec.clone(),
                        ref val => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", repr, val.type_string_with_article(), self)),
                    };

                    let index_val = try!(idx.eval(state));

                    index_int = match index_val {
                        Value::Int(i) => i,
                        _ => return Error::type_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense", var, index_val.type_string_with_article(), self))
                    };

                    if index_int < 0 {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` is {}, so `{}` doesn't make sense",
                            index, index_int, self))
                    }

                    if index_int as usize >= array_vec.len() {
                        return Error::array_index_out_of_bounds_error(
                            &format!("`{}` has {} elements in it, so `{}` doesn't make sense",
                            var, array_vec.len(), self))
                    }
                }

                Ok(array_vec[index_int as usize].clone())
            }
            Expr::BinExp(ref exp1, ref op, ref exp2) => {
                let val1 = try!(exp1.eval(state));
                let val2 = try!(exp2.eval(state));

                match *op {
                    BinOp::And => bool_exp(self, val1, val2, |x, y| x && y),
                    BinOp::Or =>  bool_exp(self, val1, val2, |x, y| x || y),
                    BinOp::Equal => eq_exp(self, val1, val2, |x, y| x == y),
                    BinOp::NotEqual => eq_exp(self, val1, val2, |x, y| x != y),
                    BinOp::GreaterOrEqual => ineq_exp(self, val1, val2, |x, y| x >= y),
                    BinOp::GreaterThan => ineq_exp(self, val1, val2, |x, y| x > y),
                    BinOp::LessOrEqual => ineq_exp(self, val1, val2, |x, y| x <= y),
                    BinOp::LessThan => ineq_exp(self, val1, val2, |x, y| x < y),
                    BinOp::Plus => arith_exp(self, val1, val2, |x, y| x + y),
                    BinOp::Minus => arith_exp(self, val1, val2, |x, y| x - y),
                    BinOp::Times => arith_exp(self, val1, val2, |x, y| x * y),
                    BinOp::Divide => arith_exp(self, val1, val2, |x, y| x / y),
                    BinOp::Modulus => arith_exp(self, val1, val2, |x, y| x % y),
                }
            }
            Expr::Call(ref name, ref args) => {
                match state.call_function(name, args) {
                    Ok(Some(val)) => Ok(val),
                    Ok(None) => Error::type_error(
                        &format!("The function {} doesn't return anything, so {} doesn't make sense", name, self)),
                    Err(e) => Err(e),
                }
            }
            Expr::Not(ref exp) => {
                match try!(exp.eval(state)) {
                    Value::Bool(b) => Ok(Value::Bool(!b)),
                    _ => Error::type_error(
                        &format!("`{}` is not a boolean, so `!{}` doesn't make sense", exp, exp)),
                }
            }
            Expr::Range(ref start, ref end) => {
                let start_int = match try!(start.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", start, self)),
                };

                let end_int = match try!(end.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", end, self)),
                };

                // `stepper` ranges are not end-inclusive.
                let (fixed_end, step_int) = if end_int >= start_int {
                    (end_int + 1, 1)
                } else {
                    (end_int - 1, -1)
                };

                let vec: Vec<_> = step!(start_int => fixed_end; step_int).into_iter().map(Value::Int).collect();
                Ok(Value::Array(vec))
            }
            Expr::Step(ref start, ref end, ref step) => {
                let start_int = match try!(start.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", start, self)),
                };

                let end_int = match try!(end.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", end, self)),
                };

                let step_int = match try!(step.eval(state)) {
                    Value::Int(i) => i,
                    _ => return Error::type_error(
                        &format!("`{}` is not a int, so `{}` doesn't make sense", step, self)),
                };

                if start_int < end_int && step_int < 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping down from {} will never reach {}",
                            start, start_int, end, end_int, step, step_int, start_int, end_int));
                }

                if start_int > end_int && step_int > 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping up from {} will never reach {}",
                            start, start_int, end, end_int, step, step_int, start_int, end_int));
                }

                if step_int == 0 {
                    return Error::step_error(
                        &format!("`{}` = {}, `{}` = {}, and `{}` = {}; stepping by 0 will never get anywhere",
                            start, start_int, end, end_int, step, step_int));
                }

                // `stepper` ranges are not end-inclusive.
                let fixed_end = end_int + step_int.signum();

                let vec: Vec<_> = step!(start_int => fixed_end; step_int).into_iter().map(Value::Int).collect();
                Ok(Value::Array(vec))
            }
            Expr::Value(ref val) => Ok(val.clone()),
            Expr::Var(ref var) => {
                match state.lookup(var) {
                    Some(val) => Ok(val.clone()),
                    None => Error::undef_var_error(
                        &format!("The variable `{}` is not defined, so it can't be used in an expression", var))
                }
            }
        }
    }
}
<|endoftext|>