<commit_before>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{NamedMatch, MatchedSeq, MatchedNonterminal};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, Token, NtTT};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::ops::Add;
use std::rc::Rc;

// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited {
        forest: Lrc<quoted::Delimited>,
        idx: usize,
        span: DelimSpan,
    },
    Sequence {
        forest: Lrc<quoted::SequenceRepetition>,
        idx: usize,
        sep: Option<Token>,
    },
}

impl Frame {
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription. On the other hand, if
/// `src` contains no `TokenTree::{Sequence, MetaVar, MetaVarDecl}`s, `interp` can
/// (and should) be None.
pub fn transcribe(cx: &ExtCtxt<'_>,
                  interp: Option<FxHashMap<Ident, Rc<NamedMatch>>>,
                  src: Vec<quoted::TokenTree>)
                  -> TokenStream {
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];
    let interpolations = interp.unwrap_or_else(FxHashMap::default); /* just a convenience */
    let mut repeats = Vec::new();
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            tree
        } else {
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        // repeat same span, I guess
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue
                }
            }

            match stack.pop().unwrap() {
                Frame::Sequence { .. } => {
                    repeats.pop();
                }
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        return TokenStream::new(result);
                    }
                    let tree = TokenTree::Delimited(
                        span,
                        forest.delim,
                        TokenStream::new(result).into(),
                    );
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue
        };

        match tree {
            quoted::TokenTree::Sequence(sp, seq) => {
                // FIXME(pcwalton): Bad copy.
                match lockstep_iter_size(&quoted::TokenTree::Sequence(sp, seq.clone()),
                                         &interpolations,
                                         &repeats) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(sp.entire(), /* blame macro writer */
                            "attempted to repeat an expression \
                             containing no syntax \
                             variables matched as repeating at this depth");
                    }
                    LockstepIterSize::Contradiction(ref msg) => {
                        // FIXME #2887 blame macro invoker instead
                        cx.span_fatal(sp.entire(), &msg[..]);
                    }
                    LockstepIterSize::Constraint(len, _) => {
                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // FIXME #2887 blame invoker
                                cx.span_fatal(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            repeats.push((0, len));
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }
            // FIXME #2887: think about span stuff here
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                if let Some(cur_matched) = lookup_cur_matched(ident, &interpolations, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        cx.span_fatal(sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident));
                    }
                } else {
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

fn lookup_cur_matched(ident: Ident,
                      interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
                      repeats: &[(usize, usize)])
                      -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

#[derive(Clone)]
enum LockstepIterSize {
    Unconstrained,
    Constraint(usize, Ident),
    Contradiction(String),
}

impl Add for LockstepIterSize {
    type Output = LockstepIterSize;

    fn add(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!("inconsistent lockstep iteration: \
                                       '{}' has {} items, but '{}' has {}",
                                      l_id, l_len, r_id, r_len);
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

fn lockstep_iter_size(tree: &quoted::TokenTree,
                      interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
                      repeats: &[(usize, usize)])
                      -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        },
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        },
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) =>
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained
            },
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<commit_msg>rustfmt<commit_after>use crate::ast::Ident;
use crate::ext::base::ExtCtxt;
use crate::ext::expand::Marker;
use crate::ext::tt::macro_parser::{MatchedNonterminal, MatchedSeq, NamedMatch};
use crate::ext::tt::quoted;
use crate::mut_visit::noop_visit_tt;
use crate::parse::token::{self, NtTT, Token};
use crate::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndJoint};

use smallvec::{smallvec, SmallVec};
use syntax_pos::DUMMY_SP;

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
use std::mem;
use std::ops::Add;
use std::rc::Rc;

/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame {
    Delimited { forest: Lrc<quoted::Delimited>, idx: usize, span: DelimSpan },
    Sequence { forest: Lrc<quoted::SequenceRepetition>, idx: usize, sep: Option<Token> },
}

impl Frame {
    fn new(tts: Vec<quoted::TokenTree>) -> Frame {
        let forest = Lrc::new(quoted::Delimited { delim: token::NoDelim, tts: tts });
        Frame::Delimited { forest: forest, idx: 0, span: DelimSpan::dummy() }
    }
}

impl Iterator for Frame {
    type Item = quoted::TokenTree;

    fn next(&mut self) -> Option<quoted::TokenTree> {
        match *self {
            Frame::Delimited { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
            Frame::Sequence { ref forest, ref mut idx, .. } => {
                *idx += 1;
                forest.tts.get(*idx - 1).cloned()
            }
        }
    }
}

/// This can do Macro-By-Example transcription. On the other hand, if `src` contains no
/// `TokenTree::{Sequence, MetaVar, MetaVarDecl}`s, `interp` can (and should) be `None`.
pub fn transcribe(
    cx: &ExtCtxt<'_>,
    interp: Option<FxHashMap<Ident, Rc<NamedMatch>>>,
    src: Vec<quoted::TokenTree>,
) -> TokenStream {
    let mut stack: SmallVec<[Frame; 1]> = smallvec![Frame::new(src)];
    let interpolations = interp.unwrap_or_else(FxHashMap::default); /* just a convenience */
    let mut repeats = Vec::new();
    let mut result: Vec<TreeAndJoint> = Vec::new();
    let mut result_stack = Vec::new();

    loop {
        let tree = if let Some(tree) = stack.last_mut().unwrap().next() {
            tree
        } else {
            if let Frame::Sequence { ref mut idx, ref sep, .. } = *stack.last_mut().unwrap() {
                let (ref mut repeat_idx, repeat_len) = *repeats.last_mut().unwrap();
                *repeat_idx += 1;
                if *repeat_idx < repeat_len {
                    *idx = 0;
                    if let Some(sep) = sep.clone() {
                        // repeat same span, I guess
                        let prev_span = match result.last() {
                            Some((tt, _)) => tt.span(),
                            None => DUMMY_SP,
                        };
                        result.push(TokenTree::Token(prev_span, sep).into());
                    }
                    continue;
                }
            }

            match stack.pop().unwrap() {
                Frame::Sequence { .. } => {
                    repeats.pop();
                }
                Frame::Delimited { forest, span, .. } => {
                    if result_stack.is_empty() {
                        return TokenStream::new(result);
                    }
                    let tree =
                        TokenTree::Delimited(span, forest.delim, TokenStream::new(result).into());
                    result = result_stack.pop().unwrap();
                    result.push(tree.into());
                }
            }
            continue;
        };

        match tree {
            quoted::TokenTree::Sequence(sp, seq) => {
                // FIXME(pcwalton): Bad copy.
                match lockstep_iter_size(
                    &quoted::TokenTree::Sequence(sp, seq.clone()),
                    &interpolations,
                    &repeats,
                ) {
                    LockstepIterSize::Unconstrained => {
                        cx.span_fatal(
                            sp.entire(), /* blame macro writer */
                            "attempted to repeat an expression \
                             containing no syntax \
                             variables matched as repeating at this depth",
                        );
                    }
                    LockstepIterSize::Contradiction(ref msg) => {
                        // FIXME #2887 blame macro invoker instead
                        cx.span_fatal(sp.entire(), &msg[..]);
                    }
                    LockstepIterSize::Constraint(len, _) => {
                        if len == 0 {
                            if seq.op == quoted::KleeneOp::OneOrMore {
                                // FIXME #2887 blame invoker
                                cx.span_fatal(sp.entire(), "this must repeat at least once");
                            }
                        } else {
                            repeats.push((0, len));
                            stack.push(Frame::Sequence {
                                idx: 0,
                                sep: seq.separator.clone(),
                                forest: seq,
                            });
                        }
                    }
                }
            }
            // FIXME #2887: think about span stuff here
            quoted::TokenTree::MetaVar(mut sp, ident) => {
                if let Some(cur_matched) = lookup_cur_matched(ident, &interpolations, &repeats) {
                    if let MatchedNonterminal(ref nt) = *cur_matched {
                        if let NtTT(ref tt) = **nt {
                            result.push(tt.clone().into());
                        } else {
                            sp = sp.apply_mark(cx.current_expansion.mark);
                            let token = TokenTree::Token(sp, Token::Interpolated(nt.clone()));
                            result.push(token.into());
                        }
                    } else {
                        cx.span_fatal(
                            sp, /* blame the macro writer */
                            &format!("variable '{}' is still repeating at this depth", ident),
                        );
                    }
                } else {
                    let ident =
                        Ident::new(ident.name, ident.span.apply_mark(cx.current_expansion.mark));
                    sp = sp.apply_mark(cx.current_expansion.mark);
                    result.push(TokenTree::Token(sp, token::Dollar).into());
                    result.push(TokenTree::Token(sp, token::Token::from_ast_ident(ident)).into());
                }
            }
            quoted::TokenTree::Delimited(mut span, delimited) => {
                span = span.apply_mark(cx.current_expansion.mark);
                stack.push(Frame::Delimited { forest: delimited, idx: 0, span: span });
                result_stack.push(mem::replace(&mut result, Vec::new()));
            }
            quoted::TokenTree::Token(sp, tok) => {
                let mut marker = Marker(cx.current_expansion.mark);
                let mut tt = TokenTree::Token(sp, tok);
                noop_visit_tt(&mut tt, &mut marker);
                result.push(tt.into());
            }
            quoted::TokenTree::MetaVarDecl(..) => panic!("unexpected `TokenTree::MetaVarDecl"),
        }
    }
}

fn lookup_cur_matched(
    ident: Ident,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> Option<Rc<NamedMatch>> {
    interpolations.get(&ident).map(|matched| {
        let mut matched = matched.clone();
        for &(idx, _) in repeats {
            let m = matched.clone();
            match *m {
                MatchedNonterminal(_) => break,
                MatchedSeq(ref ads, _) => matched = Rc::new(ads[idx].clone()),
            }
        }

        matched
    })
}

#[derive(Clone)]
enum LockstepIterSize {
    Unconstrained,
    Constraint(usize, Ident),
    Contradiction(String),
}

impl Add for LockstepIterSize {
    type Output = LockstepIterSize;

    fn add(self, other: LockstepIterSize) -> LockstepIterSize {
        match self {
            LockstepIterSize::Unconstrained => other,
            LockstepIterSize::Contradiction(_) => self,
            LockstepIterSize::Constraint(l_len, ref l_id) => match other {
                LockstepIterSize::Unconstrained => self,
                LockstepIterSize::Contradiction(_) => other,
                LockstepIterSize::Constraint(r_len, _) if l_len == r_len => self,
                LockstepIterSize::Constraint(r_len, r_id) => {
                    let msg = format!(
                        "inconsistent lockstep iteration: \
                         '{}' has {} items, but '{}' has {}",
                        l_id, l_len, r_id, r_len
                    );
                    LockstepIterSize::Contradiction(msg)
                }
            },
        }
    }
}

fn lockstep_iter_size(
    tree: &quoted::TokenTree,
    interpolations: &FxHashMap<Ident, Rc<NamedMatch>>,
    repeats: &[(usize, usize)],
) -> LockstepIterSize {
    use quoted::TokenTree;
    match *tree {
        TokenTree::Delimited(_, ref delimed) => {
            delimed.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::Sequence(_, ref seq) => {
            seq.tts.iter().fold(LockstepIterSize::Unconstrained, |size, tt| {
                size + lockstep_iter_size(tt, interpolations, repeats)
            })
        }
        TokenTree::MetaVar(_, name) | TokenTree::MetaVarDecl(_, name, _) => {
            match lookup_cur_matched(name, interpolations, repeats) {
                Some(matched) => match *matched {
                    MatchedNonterminal(_) => LockstepIterSize::Unconstrained,
                    MatchedSeq(ref ads, _) => LockstepIterSize::Constraint(ads.len(), name),
                },
                _ => LockstepIterSize::Unconstrained,
            }
        }
        TokenTree::Token(..) => LockstepIterSize::Unconstrained,
    }
}
<|endoftext|>


<commit_before>use crate::syscalls;
use core::ptr;

#[must_use = "Shared memory risks being dropped too early. Drop it manually."]
pub struct SharedMemory<'a> {
    driver_number: usize,
    allow_number: usize,
    buffer_to_share: &'a mut [u8],
}

impl<'a> SharedMemory<'a> {
    pub fn new(
        driver_number: usize,
        allow_number: usize,
        buffer_to_share: &'a mut [u8],
    ) -> SharedMemory<'a> {
        SharedMemory {
            driver_number,
            allow_number,
            buffer_to_share,
        }
    }

    pub fn read_bytes<T: AsMut<[u8]>>(&self, mut destination: T) {
        safe_copy(self.buffer_to_share, destination.as_mut());
    }

    pub fn write_bytes<T: AsRef<[u8]>>(&mut self, source: T) {
        safe_copy(source.as_ref(), self.buffer_to_share);
    }
}

impl<'a> Drop for SharedMemory<'a> {
    fn drop(&mut self) {
        unsafe {
            syscalls::raw::allow(self.driver_number, self.allow_number, ptr::null_mut(), 0);
        }
    }
}

fn safe_copy(origin: &[u8], destination: &mut [u8]) {
    let amount = origin.len().min(destination.len());
    let origin = &origin[0..amount];
    let destination = &mut destination[0..amount];
    destination.clone_from_slice(origin);
}
<commit_msg>Replace 'clone_from_slice' with 'copy_from_slice' Currently, 'shared_memory::safe_copy' uses 'clone_from_slice' to perform memory copy. 'clone_from_slice' iterates over each element of slice to invoke 'clone_from'. According to docs of 'clone_from_slice', it can be more performant to use 'copy_from_slice' instead when target slice implements 'Copy'(https://doc.rust-lang.org/std/primitive.slice.html#method.clone_from_slice). Since '&[u8]' implements 'Copy', I thought replacing 'clone_from_slice' with 'copy_from_slice' would be a helpful change regarding performance.<commit_after>use crate::syscalls;
use core::ptr;

#[must_use = "Shared memory risks being dropped too early. Drop it manually."]
pub struct SharedMemory<'a> {
    driver_number: usize,
    allow_number: usize,
    buffer_to_share: &'a mut [u8],
}

impl<'a> SharedMemory<'a> {
    pub fn new(
        driver_number: usize,
        allow_number: usize,
        buffer_to_share: &'a mut [u8],
    ) -> SharedMemory<'a> {
        SharedMemory {
            driver_number,
            allow_number,
            buffer_to_share,
        }
    }

    pub fn read_bytes<T: AsMut<[u8]>>(&self, mut destination: T) {
        safe_copy(self.buffer_to_share, destination.as_mut());
    }

    pub fn write_bytes<T: AsRef<[u8]>>(&mut self, source: T) {
        safe_copy(source.as_ref(), self.buffer_to_share);
    }
}

impl<'a> Drop for SharedMemory<'a> {
    fn drop(&mut self) {
        unsafe {
            syscalls::raw::allow(self.driver_number, self.allow_number, ptr::null_mut(), 0);
        }
    }
}

fn safe_copy(origin: &[u8], destination: &mut [u8]) {
    let amount = origin.len().min(destination.len());
    let origin = &origin[0..amount];
    let destination = &mut destination[0..amount];
    destination.copy_from_slice(origin);
}
<|endoftext|>


<commit_before>pub use self::download::download_document;
pub use self::search::search_documents;
pub use self::upload::upload_document;

mod download {
    use utils::console::*;

    use hyper::Client;
    use hyper::client::Response;
    use hyper::header::{ContentType, ContentDisposition, ContentLength, DispositionParam, Authorization, Bearer};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use std::fs::File;
    use std::io;
    use std::io::{Read, Write};
    use std::str;

    error_chain! {
        errors {
            HttpDownloadCallFailed {
                description("failed to make http download call")
                display("failed to make http download call")
            }

            FailedSetFilename {
                description("failed set filename from cli parameter and content disposition header")
                display("failed set filename from cli parameter and content disposition header")
            }

            FailedContentLength {
                description("failed get content length from response header")
                display("failed get content length from response header")
            }
        }
    }

    pub fn download_document(
        access_token: &str,
        filename: Option<&str>,
        document_id: &str) -> Result<()> {
        do_download_document(access_token, filename, document_id)
            .chain_err(|| ErrorKind::HttpDownloadCallFailed)
    }

    fn do_download_document(
        access_token: &str,
        filename: Option<&str>,
        document_id: &str) -> Result<()> {
        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let url = format!("https://api.centerdevice.de/v2/document/{}", document_id);
        let request = client.get(&url)
            .header(Authorization(Bearer { token: access_token.to_string() }))
            .header(ContentType(mime!(Star/Star)));

        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let filename = get_filename(filename, &response)?;
        let size = get_content_length(&response)? as usize;

        verbose(format!("Retrieving {} bytes ", size));
        let w_size = write_to_file(&filename, &mut response)?;
        verboseln("done.");

        if w_size != size {
            bail!("Retrieved number of bytes ({}) does not match expected bytes ({}).", w_size, size);
        }

        Ok(())
    }

    fn get_filename(filename: Option<&str>, response: &Response) -> Result<String> {
        let filename = if let Some(_filename) = filename {
            _filename
        } else {
            let mut _filename = None;
            let content_disposition: &ContentDisposition = response.headers.get()
                .ok_or(ErrorKind::FailedSetFilename)?;
            for cp in &content_disposition.parameters {
                if let DispositionParam::Filename(_, _, ref f) = *cp {
                    _filename = Some(
                        str::from_utf8(f).chain_err(|| ErrorKind::FailedSetFilename)?);
                    break;
                }
            }
            _filename.ok_or(ErrorKind::FailedSetFilename)?
        };

        Ok(filename.to_string())
    }

    fn get_content_length(response: &Response) -> Result<u64> {
        let content_length: &ContentLength = response.headers.get()
            .ok_or(ErrorKind::FailedContentLength)?;
        let &ContentLength(size) = content_length;
        Ok(size)
    }

    fn write_to_file(filename: &str, response: &mut Response) -> Result<usize> {
        let mut out = File::create(filename).chain_err(|| "Failed to create destination file")?;

        let mut buf = [0; 128 * 1024];
        let mut written: usize = 0;
        let mut mega_bytes: usize = 0;
        loop {
            let len = match response.read(&mut buf) {
                Ok(0) => break,  // EOF.
                Ok(len) => len,
                Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
                Err(_) => bail!("Could not read from server response")
            };
            written += len;
            if (written / (1024 * 1024)) - mega_bytes > 0 {
                mega_bytes += 1;
                verbose(".");
            }
            out.write_all(&buf[..len]).chain_err(|| "Could not write to file")?;
        }

        Ok(written)
    }
}

mod search {
    use utils::console::*;

    use hyper::Client;
    use hyper::header::{ContentType, Authorization, Bearer, Accept, qitem};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use serde_json;
    use std::io::Read;
    use std::str;

    error_chain! {
errors {
HttpSearchCallFailed {
description("failed to make http search call")
display("failed to make http search call")
}
}
}

    #[derive(Serialize, Debug)]
    struct Search<'a> {
        action: &'a str,
        params: Params<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Params<'a> {
        query: Query<'a>,
        filter: Filter<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Query<'a> {
        #[serde(skip_serializing_if = "Option::is_none")] text: Option<&'a str>,
    }

    #[derive(Serialize, Debug)]
    struct Filter<'a> {
        #[serde(skip_serializing_if = "Option::is_none")] filenames: Option<Vec<&'a str>>,
        #[serde(skip_serializing_if = "Option::is_none")] tags: Option<Vec<&'a str>>,
    }

    impl<'a> Search<'a> {
        pub fn new(filenames: Option<Vec<&'a str>>, tags: Option<Vec<&'a str>>, fulltext: Option<&'a str>) -> Self {
            let filter = Filter { filenames: filenames, tags: tags };
            let query = Query { text: fulltext };
            let params = Params { query: query, filter: filter };

            Search { action: "search", params: params }
        }
    }

    pub fn search_documents(
        access_token: &str,
        filenames: Option<Vec<&str>>,
        tags: Option<Vec<&str>>,
        fulltext: Option<&str>) -> Result<String> {
        do_search_documents(access_token, filenames, tags, fulltext)
            .chain_err(|| ErrorKind::HttpSearchCallFailed)
    }

    fn do_search_documents(
        access_token: &str,
        filenames: Option<Vec<&str>>,
        tags: Option<Vec<&str>>,
        fulltext: Option<&str>) -> Result<String> {
        let search = Search::new(filenames, tags, fulltext);
        let search_json = serde_json::to_string(&search).chain_err(|| "JSON serialization failed")?;

        verboseln(format!("search = '{:?}'", search_json));

        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let request = client.post("https://api.centerdevice.de/v2/documents")
            .header(Authorization(Bearer { token: access_token.to_string() }))
            .header(ContentType(mime!(Application / Json)))
            .header(Accept(vec![qitem(mime!(Application/ Json; Charset = Utf8))]))
            .body(&search_json);

        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let mut body = Vec::new();
        response.read_to_end(&mut body).chain_err(|| "Failed to read server response")?;
        let response_body = String::from_utf8_lossy(&body).to_string();

        Ok(response_body)
    }
}

mod upload {
    use utils::console::*;

    use crypto::digest::Digest;
    use crypto::sha2::Sha256;
    use hyper::client::Request;
    use hyper::method::Method;
    use hyper::header::{Headers, ContentDisposition, DispositionParam, DispositionType, ContentType,
                        Authorization, Bearer, Accept, qitem};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use mime::Mime;
    use mime_multipart::{Node, Part, FilePart, write_multipart};
    use serde_json;
    use std::fs;
    use std::io::Read;
    use std::path::Path;
    use std::str;

    error_chain! {
errors {
HttpUploadCallFailed {
description("failed to make http upload call")
display("failed to make http upload call")
}
}
}

    #[derive(Serialize, Debug)]
    struct DocumentMetadata<'a> {
        metadata: Metadata<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Metadata<'a> {
        document: Document<'a>,
        #[serde(skip_serializing_if = "Option::is_none")] actions: Option<Actions<'a>>,
    }

    #[derive(Serialize, Debug)]
    struct Document<'a> {
        filename: &'a str,
        size: u64,
        #[serde(skip_serializing_if = "Option::is_none")] title: Option<&'a str>,
        #[serde(skip_serializing_if = "Option::is_none")] author: Option<&'a str>,
    }

    #[derive(Serialize, Debug)]
    struct Actions<'a> {
        #[serde(skip_serializing_if = "Option::is_none", rename(serialize = "add-tag"))] tags: Option<Vec<&'a str>>
    }

    impl<'a> DocumentMetadata<'a> {
        pub fn new(filename: &'a str, size: u64, title: Option<&'a str>, tags: Option<Vec<&'a str>>) -> Self {
            let document = Document { filename: filename, size: size, title: title, author: None };
            let actions = Actions { tags: tags };
            let metadata = Metadata { document: document, actions: Some(actions) };
            DocumentMetadata { metadata: metadata }
        }
    }

    pub fn upload_document(
        access_token: &str,
        file: &Path,
        filename: &str,
        mime: Mime,
        title: Option<&str>,
        tags: Option<Vec<&str>>)
        -> Result<String> {
        do_upload_document(access_token, file, filename, mime, title, tags)
            .chain_err(|| ErrorKind::HttpUploadCallFailed)
    }

    fn do_upload_document(
        access_token: &str,
        file: &Path,
        filename: &str,
        mime: Mime,
        title: Option<&str>,
        tags: Option<Vec<&str>>) -> Result<String> {
        let doc_metadata_json_bytes = create_doc_metadata(file, filename, title, tags)?.into_bytes();
        let boundary = generate_boundary(&doc_metadata_json_bytes);
        let boundary_bytes = boundary.clone().into_bytes();
        let nodes = create_multipart_nodes(
            doc_metadata_json_bytes, filename.to_string(), file, mime).chain_err(|| "Failed to create form-data")?;

        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let url = ::hyper::Url::parse("https://api.centerdevice.de/v2/documents").chain_err(|| "Failed to parse URL")?;
        let mut client = Request::with_connector(Method::Post, url, &connector).chain_err(|| "Failed to create client")?;
        client.headers_mut().set(Authorization(Bearer { token: access_token.to_string() }));
        client.headers_mut().set(ContentType(mime!(Multipart / FormData; Boundary = (boundary))));
        client.headers_mut().set(Accept(vec![qitem(mime!(Application / Json; Charset = Utf8))]));

        let mut request = client.start().chain_err(|| "Failed to start HTTP connection")?;
        write_multipart(&mut request, &boundary_bytes, &nodes).chain_err(|| "Failed to send multipart form-data")?;
        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let mut body = Vec::new();
        response.read_to_end(&mut body).chain_err(|| "Failed to read server response")?;
        let response_body = String::from_utf8_lossy(&body).to_string();

        Ok(response_body)
    }

    fn create_doc_metadata(file: &Path, filename: &str, title: Option<&str>, tags: Option<Vec<&str>>) -> Result<String> {
        let size = fs::metadata(file).chain_err(|| "Failed to get metadata for file")?.len();
        let doc_metadata = DocumentMetadata::new(filename, size, title, tags);
        let doc_metadata_json = serde_json::to_string(&doc_metadata).chain_err(|| "JSON serialization failed")?;

        verboseln(format!("document metadata = '{:?}'", doc_metadata_json));

        Ok(doc_metadata_json)
    }

    fn create_multipart_nodes(json_bytes: Vec<u8>, filename: String, file: &Path, mime: Mime) -> Result<Vec<Node>> {
        let mut nodes: Vec<Node> = Vec::with_capacity(2);

        let mut h = Headers::new();
        h.set(ContentType(mime!(Application / Json)));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_string()),
            parameters: vec![DispositionParam::Ext("name".to_string(), "metadata".to_string())],
        });
        nodes.push(Node::Part(Part {
            headers: h,
            body: json_bytes
        }));

        let mut h = Headers::new();
        h.set(ContentType(mime));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_string()),
            parameters: vec![DispositionParam::Ext("name".to_string(), "document".to_string()),
                             DispositionParam::Ext("filename".to_string(), filename)],
        });
        nodes.push(Node::File(FilePart::new(h, file)));

        Ok(nodes)
    }

    // CenterDevice / Jersey does not accept special characters in boundary; thus, we build it ourselves.
    fn generate_boundary(seed: &[u8]) -> String {
        let mut sha = Sha256::new();
        sha.input(seed);
        let output = format!("Boundary_{}", sha.result_str());

        output
    }
}<commit_msg>Formatting<commit_after>pub use self::download::download_document;
pub use self::search::search_documents;
pub use self::upload::upload_document;

mod download {
    use utils::console::*;

    use hyper::Client;
    use hyper::client::Response;
    use hyper::header::{ContentType, ContentDisposition, ContentLength, DispositionParam, Authorization, Bearer};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use std::fs::File;
    use std::io;
    use std::io::{Read, Write};
    use std::str;

    error_chain! {
        errors {
            HttpDownloadCallFailed {
                description("failed to make http download call")
                display("failed to make http download call")
            }

            FailedSetFilename {
                description("failed set filename from cli parameter and content disposition header")
                display("failed set filename from cli parameter and content disposition header")
            }

            FailedContentLength {
                description("failed get content length from response header")
                display("failed get content length from response header")
            }
        }
    }

    pub fn download_document(
        access_token: &str,
        filename: Option<&str>,
        document_id: &str) -> Result<()> {
        do_download_document(access_token, filename, document_id)
            .chain_err(|| ErrorKind::HttpDownloadCallFailed)
    }

    fn do_download_document(
        access_token: &str,
        filename: Option<&str>,
        document_id: &str) -> Result<()> {
        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let url = format!("https://api.centerdevice.de/v2/document/{}", document_id);
        let request = client.get(&url)
            .header(Authorization(Bearer { token: access_token.to_string() }))
            .header(ContentType(mime!(Star/Star)));

        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let filename = get_filename(filename, &response)?;
        let size = get_content_length(&response)? as usize;

        verbose(format!("Retrieving {} bytes ", size));
        let w_size = write_to_file(&filename, &mut response)?;
        verboseln("done.");

        if w_size != size {
            bail!("Retrieved number of bytes ({}) does not match expected bytes ({}).", w_size, size);
        }

        Ok(())
    }

    fn get_filename(filename: Option<&str>, response: &Response) -> Result<String> {
        let filename = if let Some(_filename) = filename {
            _filename
        } else {
            let mut _filename = None;
            let content_disposition: &ContentDisposition = response.headers.get()
                .ok_or(ErrorKind::FailedSetFilename)?;
            for cp in &content_disposition.parameters {
                if let DispositionParam::Filename(_, _, ref f) = *cp {
                    _filename = Some(
                        str::from_utf8(f).chain_err(|| ErrorKind::FailedSetFilename)?);
                    break;
                }
            }
            _filename.ok_or(ErrorKind::FailedSetFilename)?
        };

        Ok(filename.to_string())
    }

    fn get_content_length(response: &Response) -> Result<u64> {
        let content_length: &ContentLength = response.headers.get()
            .ok_or(ErrorKind::FailedContentLength)?;
        let &ContentLength(size) = content_length;
        Ok(size)
    }

    fn write_to_file(filename: &str, response: &mut Response) -> Result<usize> {
        let mut out = File::create(filename).chain_err(|| "Failed to create destination file")?;

        let mut buf = [0; 128 * 1024];
        let mut written: usize = 0;
        let mut mega_bytes: usize = 0;
        loop {
            let len = match response.read(&mut buf) {
                Ok(0) => break,  // EOF.
                Ok(len) => len,
                Err(ref err) if err.kind() == io::ErrorKind::Interrupted => continue,
                Err(_) => bail!("Could not read from server response")
            };
            written += len;
            if (written / (1024 * 1024)) - mega_bytes > 0 {
                mega_bytes += 1;
                verbose(".");
            }
            out.write_all(&buf[..len]).chain_err(|| "Could not write to file")?;
        }

        Ok(written)
    }
}

mod search {
    use utils::console::*;

    use hyper::Client;
    use hyper::header::{ContentType, Authorization, Bearer, Accept, qitem};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use serde_json;
    use std::io::Read;
    use std::str;

    error_chain! {
        errors {
            HttpSearchCallFailed {
                description("failed to make http search call")
                display("failed to make http search call")
            }
        }
    }

    #[derive(Serialize, Debug)]
    struct Search<'a> {
        action: &'a str,
        params: Params<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Params<'a> {
        query: Query<'a>,
        filter: Filter<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Query<'a> {
        #[serde(skip_serializing_if = "Option::is_none")] text: Option<&'a str>,
    }

    #[derive(Serialize, Debug)]
    struct Filter<'a> {
        #[serde(skip_serializing_if = "Option::is_none")] filenames: Option<Vec<&'a str>>,
        #[serde(skip_serializing_if = "Option::is_none")] tags: Option<Vec<&'a str>>,
    }

    impl<'a> Search<'a> {
        pub fn new(filenames: Option<Vec<&'a str>>, tags: Option<Vec<&'a str>>, fulltext: Option<&'a str>) -> Self {
            let filter = Filter { filenames: filenames, tags: tags };
            let query = Query { text: fulltext };
            let params = Params { query: query, filter: filter };

            Search { action: "search", params: params }
        }
    }

    pub fn search_documents(
        access_token: &str,
        filenames: Option<Vec<&str>>,
        tags: Option<Vec<&str>>,
        fulltext: Option<&str>) -> Result<String> {
        do_search_documents(access_token, filenames, tags, fulltext)
            .chain_err(|| ErrorKind::HttpSearchCallFailed)
    }

    fn do_search_documents(
        access_token: &str,
        filenames: Option<Vec<&str>>,
        tags: Option<Vec<&str>>,
        fulltext: Option<&str>) -> Result<String> {
        let search = Search::new(filenames, tags, fulltext);
        let search_json = serde_json::to_string(&search).chain_err(|| "JSON serialization failed")?;

        verboseln(format!("search = '{:?}'", search_json));

        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let client = Client::with_connector(connector);

        let request = client.post("https://api.centerdevice.de/v2/documents")
            .header(Authorization(Bearer { token: access_token.to_string() }))
            .header(ContentType(mime!(Application / Json)))
            .header(Accept(vec![qitem(mime!(Application/ Json; Charset = Utf8))]))
            .body(&search_json);

        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let mut body = Vec::new();
        response.read_to_end(&mut body).chain_err(|| "Failed to read server response")?;
        let response_body = String::from_utf8_lossy(&body).to_string();

        Ok(response_body)
    }
}

mod upload {
    use utils::console::*;

    use crypto::digest::Digest;
    use crypto::sha2::Sha256;
    use hyper::client::Request;
    use hyper::method::Method;
    use hyper::header::{Headers, ContentDisposition, DispositionParam, DispositionType, ContentType,
                        Authorization, Bearer, Accept, qitem};
    use hyper::net::HttpsConnector;
    use hyper_native_tls::NativeTlsClient;
    use mime::Mime;
    use mime_multipart::{Node, Part, FilePart, write_multipart};
    use serde_json;
    use std::fs;
    use std::io::Read;
    use std::path::Path;
    use std::str;

    error_chain! {
        errors {
            HttpUploadCallFailed {
                description("failed to make http upload call")
                display("failed to make http upload call")
            }
        }
    }

    #[derive(Serialize, Debug)]
    struct DocumentMetadata<'a> {
        metadata: Metadata<'a>,
    }

    #[derive(Serialize, Debug)]
    struct Metadata<'a> {
        document: Document<'a>,
        #[serde(skip_serializing_if = "Option::is_none")] actions: Option<Actions<'a>>,
    }

    #[derive(Serialize, Debug)]
    struct Document<'a> {
        filename: &'a str,
        size: u64,
        #[serde(skip_serializing_if = "Option::is_none")] title: Option<&'a str>,
        #[serde(skip_serializing_if = "Option::is_none")] author: Option<&'a str>,
    }

    #[derive(Serialize, Debug)]
    struct Actions<'a> {
        #[serde(skip_serializing_if = "Option::is_none", rename(serialize = "add-tag"))] tags: Option<Vec<&'a str>>
    }

    impl<'a> DocumentMetadata<'a> {
        pub fn new(filename: &'a str, size: u64, title: Option<&'a str>, tags: Option<Vec<&'a str>>) -> Self {
            let document = Document { filename: filename, size: size, title: title, author: None };
            let actions = Actions { tags: tags };
            let metadata = Metadata { document: document, actions: Some(actions) };
            DocumentMetadata { metadata: metadata }
        }
    }

    pub fn upload_document(
        access_token: &str,
        file: &Path,
        filename: &str,
        mime: Mime,
        title: Option<&str>,
        tags: Option<Vec<&str>>)
        -> Result<String> {
        do_upload_document(access_token, file, filename, mime, title, tags)
            .chain_err(|| ErrorKind::HttpUploadCallFailed)
    }

    fn do_upload_document(
        access_token: &str,
        file: &Path,
        filename: &str,
        mime: Mime,
        title: Option<&str>,
        tags: Option<Vec<&str>>) -> Result<String> {
        let doc_metadata_json_bytes = create_doc_metadata(file, filename, title, tags)?.into_bytes();
        let boundary = generate_boundary(&doc_metadata_json_bytes);
        let boundary_bytes = boundary.clone().into_bytes();
        let nodes = create_multipart_nodes(
            doc_metadata_json_bytes, filename.to_string(), file, mime).chain_err(|| "Failed to create form-data")?;

        let ssl = NativeTlsClient::new().chain_err(|| "Failed to create TLS client")?;
        let connector = HttpsConnector::new(ssl);
        let url = ::hyper::Url::parse("https://api.centerdevice.de/v2/documents").chain_err(|| "Failed to parse URL")?;
        let mut client = Request::with_connector(Method::Post, url, &connector).chain_err(|| "Failed to create client")?;
        client.headers_mut().set(Authorization(Bearer { token: access_token.to_string() }));
        client.headers_mut().set(ContentType(mime!(Multipart / FormData; Boundary = (boundary))));
        client.headers_mut().set(Accept(vec![qitem(mime!(Application / Json; Charset = Utf8))]));

        let mut request = client.start().chain_err(|| "Failed to start HTTP connection")?;
        write_multipart(&mut request, &boundary_bytes, &nodes).chain_err(|| "Failed to send multipart form-data")?;
        let mut response = request.send().chain_err(|| "Failed to finish http request")?;

        let mut body = Vec::new();
        response.read_to_end(&mut body).chain_err(|| "Failed to read server response")?;
        let response_body = String::from_utf8_lossy(&body).to_string();

        Ok(response_body)
    }

    fn create_doc_metadata(file: &Path, filename: &str, title: Option<&str>, tags: Option<Vec<&str>>) -> Result<String> {
        let size = fs::metadata(file).chain_err(|| "Failed to get metadata for file")?.len();
        let doc_metadata = DocumentMetadata::new(filename, size, title, tags);
        let doc_metadata_json = serde_json::to_string(&doc_metadata).chain_err(|| "JSON serialization failed")?;

        verboseln(format!("document metadata = '{:?}'", doc_metadata_json));

        Ok(doc_metadata_json)
    }

    fn create_multipart_nodes(json_bytes: Vec<u8>, filename: String, file: &Path, mime: Mime) -> Result<Vec<Node>> {
        let mut nodes: Vec<Node> = Vec::with_capacity(2);

        let mut h = Headers::new();
        h.set(ContentType(mime!(Application / Json)));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_string()),
            parameters: vec![DispositionParam::Ext("name".to_string(), "metadata".to_string())],
        });
        nodes.push(Node::Part(Part {
            headers: h,
            body: json_bytes
        }));

        let mut h = Headers::new();
        h.set(ContentType(mime));
        h.set(ContentDisposition {
            disposition: DispositionType::Ext("form-data".to_string()),
            parameters: vec![DispositionParam::Ext("name".to_string(), "document".to_string()),
                             DispositionParam::Ext("filename".to_string(), filename)],
        });
        nodes.push(Node::File(FilePart::new(h, file)));

        Ok(nodes)
    }

    // CenterDevice / Jersey does not accept special characters in boundary; thus, we build it ourselves.
    fn generate_boundary(seed: &[u8]) -> String {
        let mut sha = Sha256::new();
        sha.input(seed);
        let output = format!("Boundary_{}", sha.result_str());

        output
    }
}<|endoftext|>


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

extern crate xkpwgen;

use std::collections::HashSet;
use std::ffi::OsStr;
use std::process::{Command, ExitStatus, Output};
use xkpwgen::wordlist;

struct Result {
    stdout: String,
    stderr: String,
    status: ExitStatus,
}

impl From<Output> for Result {
    fn from(output: Output) -> Result {
        Result {
            status: output.status,
            stdout: String::from_utf8(output.stdout).expect("Failed to decode stdout"),
            stderr: String::from_utf8(output.stderr).expect("Failed to decode stderr"),
        }
    }
}

fn run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--")
        .args(args)
        .output()
        .expect("Failed to run xkpwgen for testing")
        .into()
}

fn assert_run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    let result = run(args);
    assert!(
        result.status.success(),
        "xkpwgen failed with status {} and output:
stdout:
{}
stderr:
{}
",
        result.status,
        result.stdout,
        result.stderr
    );
    result
}

fn all_words<'a>(s: &'a str, sep: &str) -> Vec<&'a str> {
    s.lines().flat_map(|w| w.split(sep)).collect()
}

macro_rules! repeat_run {
    ($result:ident, $command:expr, $body:block) => {
        {
            for _ in 0..10 {
                let $result = assert_run($command);
                $body;
            }
        }
    };

    ($result:ident, $body:block) => {
        {
        let command: Vec<String> = Vec::new();
        repeat_run!($result, &command, $body);
        }
    };
}

#[test]
fn it_includes_eff_copyright_in_help() {
    let s = assert_run(&["--help"]).stdout;
    assert!(
        s.contains(
            "wordlist copyright (C) 2016 EFF <https://www.eff.org/copyright>",
        ),
        "EFF wordlist copyright missing from --help:
{}",
        s
    );
}

#[test]
fn it_uses_only_words_from_the_wordlist() {
    let words = wordlist::builtin_words()
        .into_iter()
        .collect::<HashSet<_>>();
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(words.contains(word), "Word {} not in EFF wordlist!", word);
        }
    });
}

#[test]
fn it_generates_five_phrases_by_default() {
    repeat_run!(result, {
        assert_eq!(result.stdout.lines().count(), 5);
    })
}

#[test]
fn it_generates_the_given_number_of_passwords() {
    repeat_run!(result, &["-n", "10"], {
        assert_eq!(result.stdout.lines().count(), 10);
    })
}

#[test]
fn it_generates_four_words_per_password_by_default() {
    repeat_run!(result, {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 4);
        }
    })
}

#[test]
fn it_generates_the_given_number_of_words_per_password() {
    repeat_run!(result, &["-l", "9"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 9)
        }
    });
}

#[test]
fn it_uses_the_given_separator() {
    repeat_run!(result, &["-s", ":"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, ":").len(), 4);
        }
    });
}

#[test]
fn it_generates_different_passwords() {
    repeat_run!(result, &["-n", "100"], {
        let mut seen_passwords = HashSet::with_capacity(100);
        let mut duplicate_passwords = HashSet::new();
        for password in result.stdout.lines() {
            if !seen_passwords.insert(password) {
                duplicate_passwords.insert(password);
            }
        }

        assert!(
            duplicate_passwords.is_empty(),
            "Duplicate passwords found: {}",
            duplicate_passwords.into_iter().collect::<Vec<_>>().join(
                ", ",
            )
        );

    });
}

#[test]
fn it_uses_the_original_eff_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(
        stdout.lines().collect::<Vec<_>>(),
        wordlist::builtin_words()
    );
}
<commit_msg>Remove CLI tests<commit_after><|endoftext|>


<commit_before>//! Tests!
extern crate fern;
#[macro_use]
extern crate log;
extern crate tempdir;

use std::io::prelude::*;
use std::{fmt, fs, io};

#[test]
fn test1_basic_usage_global_logger() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    // Create a basic logger configuration
    fern::Dispatch::new()
        .format(|out, msg, record| {
            out.finish(format_args!("[{}] {}", record.level(), msg))
        })
        // Only log messages Info and above
        .level(log::LevelFilter::Info)
        // Output to stdout and the log file in the temporary directory we made above to test
        .chain(io::stdout())
        .chain(fern::log_file(log_file).expect("Failed to open log file"))
        .apply()
        .expect("Failed to initialize logger: global logger already set!");

    trace!("SHOULD NOT DISPLAY");
    debug!("SHOULD NOT DISPLAY");
    info!("Test information message");
    warn!("Test warning message");
    error!("Test error message");

    // ensure all File objects are dropped and OS buffers are flushed.
    log::logger().flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert!(
            !result.contains("SHOULD NOT DISPLAY"),
            "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[INFO] Test information message"),
            "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[WARN] Test warning message"),
            "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[ERROR] Test error message"),
            "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
            result
        );
    }

    // Just to make sure this goes smoothly - it dose this automatically if we don't .close()
    // manually, but it will ignore any errors when doing it automatically.
    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

/// Utility to manually enter a log message into a logger. All extra metadata (target,
/// line number, etc) will be blank.
fn manual_log<T>(logger: &log::Log, level: log::Level, message: T)
where
    T: fmt::Display,
{
    logger.log(&log::RecordBuilder::new()
        .args(format_args!("{}", message))
        .level(level)
        .build());
}

#[test]
fn test2_line_seps() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    // Create a basic logger configuration
    let (_, l) = fern::Dispatch::new()
        // default format is just the message if not specified
        // default log level is 'trace' if not specified (logs all messages)
        // output to the log file with the "\r\n" line separator.
        .chain(fern::Output::file(fern::log_file(&log_file).expect("Failed to open log file"), "\r\n"))
        .into_log();

    manual_log(&*l, log::Level::Info, "message1");
    manual_log(&*l, log::Level::Info, "message2");

    // ensure all File objects are dropped and OS buffers are flushed.
    l.flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert_eq!(&result, "message1\r\nmessage2\r\n");
    }

    // Just to make sure this goes smoothly - it dose this automatically if we don't .close()
    // manually, but it will ignore any errors when doing it automatically.
    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[test]
fn test3_channel_logging() {
    use std::sync::mpsc;
    // Create the channel
    let (send, recv) = mpsc::channel();

    let (_, l) = fern::Dispatch::new().chain(send).into_log();

    manual_log(&*l, log::Level::Info, "message1");
    manual_log(&*l, log::Level::Info, "message2");

    l.flush();

    assert_eq!(recv.recv().unwrap(), "message1\n");
    assert_eq!(recv.recv().unwrap(), "message2\n");
}
<commit_msg>Split out tests for global logger and file logger<commit_after>//! Tests!
extern crate fern;
#[macro_use]
extern crate log;
extern crate tempdir;

use std::sync::{Arc, Mutex};
use std::io::prelude::*;
use std::{fmt, fs, io};

use log::Level::*;

#[test]
fn test_global_logger() {
    // custom logger to verify behavior
    struct LogVerify {
        info: bool,
        warn: bool,
        error: bool,
    }
    impl LogVerify {
        fn new() -> Self {
            LogVerify {
                info: false,
                warn: false,
                error: false,
            }
        }
        fn log(&mut self, record: &log::Record) {
            let formatted_message = format!("{}", record.args());
            match &*formatted_message {
                "[INFO] Test information message" => {
                    assert_eq!(self.info, false, "expected only one info message");
                    self.info = true;
                }
                "[WARN] Test warning message" => {
                    assert_eq!(self.warn, false, "expected only one warn message");
                    self.warn = true;
                }
                "[ERROR] Test error message" => {
                    assert_eq!(self.error, false, "expected only one error message");
                    self.error = true;
                }
                other => panic!("unexpected message: '{}'", other),
            }
        }
    }
    #[derive(Clone)]
    struct LogVerifyWrapper(Arc<Mutex<LogVerify>>);
    impl log::Log for LogVerifyWrapper {
        fn enabled(&self, _: &log::Metadata) -> bool {
            true
        }
        fn flush(&self) {}
        fn log(&self, record: &log::Record) {
            self.0.lock().unwrap().log(record);
        }
    }

    let verify = LogVerifyWrapper(Arc::new(Mutex::new(LogVerify::new())));

    // Create a basic logger configuration
    fern::Dispatch::new()
        .format(|out, msg, record| {
            out.finish(format_args!("[{}] {}", record.level(), msg))
        })
        // Only log messages Info and above
        .level(log::LevelFilter::Info)
        // Output to our verification logger for verification
        .chain(Box::new(verify.clone()) as Box<log::Log>)
        .apply()
        .expect("Failed to initialize logger: global logger already set!");

    trace!("SHOULD NOT DISPLAY");
    debug!("SHOULD NOT DISPLAY");
    info!("Test information message");
    warn!("Test warning message");
    error!("Test error message");

    // ensure all buffers are flushed.
    log::logger().flush();

    let verify_acquired = verify.0.lock().unwrap();
    assert_eq!(
        verify_acquired.info,
        true,
        "expected info message to be received"
    );
    assert_eq!(
        verify_acquired.warn,
        true,
        "expected warn message to be received"
    );
    assert_eq!(
        verify_acquired.error,
        true,
        "expected error message to be received"
    );
}

#[test]
fn basic_logging_file_logging() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test.log");

    // Create a basic logger configuration
    let l = fern::Dispatch::new()
        .format(|out, msg, record| {
            out.finish(format_args!("[{}] {}", record.level(), msg))
        })
        // Only log messages Info and above
        .level(log::LevelFilter::Info)
        // Output to stdout and the log file in the temporary directory we made above to test
        .chain(io::stdout())
        .chain(fern::log_file(log_file).expect("Failed to open log file"))
        .into_log().1;

    manual_log(&*l, Trace, "SHOULD NOT DISPLAY");
    manual_log(&*l, Debug, "SHOULD NOT DISPLAY");
    manual_log(&*l, Info, "Test information message");
    manual_log(&*l, Warn, "Test warning message");
    manual_log(&*l, Error, "Test error message");

    // ensure all File objects are dropped and OS buffers are flushed.
    log::logger().flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert!(
            !result.contains("SHOULD NOT DISPLAY"),
            "expected result not including \"SHOULD_NOT_DISPLAY\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[INFO] Test information message"),
            "expected result including \"[INFO] Test information message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[WARN] Test warning message"),
            "expected result including \"[WARN] Test warning message\", found:\n```\n{}\n```\n",
            result
        );
        assert!(
            result.contains("[ERROR] Test error message"),
            "expected result to not include \"[ERROR] Test error message\", found:\n```\n{}\n```\n",
            result
        );
    }

    drop(l); // close before tempdir closes for Windows support.

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

/// Utility to manually enter a log message into a logger. All extra metadata (target,
/// line number, etc) will be blank.
fn manual_log<T>(logger: &log::Log, level: log::Level, message: T)
where
    T: fmt::Display,
{
    logger.log(&log::RecordBuilder::new()
        .args(format_args!("{}", message))
        .level(level)
        .build());
}

#[test]
fn test2_line_seps() {
    // Create a temporary directory to put a log file into for testing
    let temp_log_dir = tempdir::TempDir::new("fern").expect("Failed to set up temporary directory");
    let log_file = temp_log_dir.path().join("test_custom_line_sep.log");

    // Create a basic logger configuration
    let (_, l) = fern::Dispatch::new()
        // default format is just the message if not specified
        // default log level is 'trace' if not specified (logs all messages)
        // output to the log file with the "\r\n" line separator.
        .chain(fern::Output::file(fern::log_file(&log_file).expect("Failed to open log file"), "\r\n"))
        .into_log();

    manual_log(&*l, Info, "message1");
    manual_log(&*l, Info, "message2");

    // ensure all File objects are dropped and OS buffers are flushed.
    l.flush();

    {
        let result = {
            let mut log_read = fs::File::open(&temp_log_dir.path().join("test_custom_line_sep.log")).unwrap();
            let mut buf = String::new();
            log_read.read_to_string(&mut buf).unwrap();
            buf
        };
        assert_eq!(&result, "message1\r\nmessage2\r\n");
    }

    temp_log_dir
        .close()
        .expect("Failed to clean up temporary directory");
}

#[test]
fn test3_channel_logging() {
    use std::sync::mpsc;
    // Create the channel
    let (send, recv) = mpsc::channel();

    let (_, l) = fern::Dispatch::new().chain(send).into_log();

    manual_log(&*l, Info, "message1");
    manual_log(&*l, Info, "message2");

    l.flush();

    assert_eq!(recv.recv().unwrap(), "message1\n");
    assert_eq!(recv.recv().unwrap(), "message2\n");
}
<|endoftext|>


<commit_before>extern crate nom_sql;

use std::fs::File;
use std::io::Read;
use std::path::Path;

fn parse_queryset(queries: Vec<String>) -> (i32, i32) {
    let mut parsed_ok = Vec::new();
    let mut parsed_err = 0;
    for query in queries.iter() {
        println!("Trying to parse '{}': ", &query);
        match nom_sql::parser::parse_query(&query) {
            Ok(_) => {
                println!("ok");
                parsed_ok.push(query);
            }
            Err(_) => {
                println!("failed");
                parsed_err += 1;
            }
        }
    }

    println!("\nParsing failed: {} queries", parsed_err);
    println!("Parsed successfully: {} queries", parsed_ok.len());
    println!("\nSuccessfully parsed queries:");
    for q in parsed_ok.iter() {
        println!("{:?}", q);
    }

    (parsed_ok.len() as i32, parsed_err)
}

fn test_queries_from_file(f: &Path, name: &str) -> Result<i32, i32> {
    let mut f = File::open(f).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<String> = s
        .lines()
        .filter(|l| !l.is_empty() && !l.starts_with("#") && !l.starts_with("--"))
        .map(|l| {
            if !(l.ends_with("\n") || l.ends_with(";")) {
                String::from(l) + "\n"
            } else {
                String::from(l)
            }
        }).collect();
    println!("\nLoaded {} {} queries", lines.len(), name);

    // Try parsing them all
    let (ok, err) = parse_queryset(lines);

    if err > 0 {
        return Err(err);
    }
    Ok(ok)
}

fn parse_file(path: &str) -> (i32, i32) {
    let mut f = File::open(Path::new(path)).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<&str> = s
        .lines()
        .map(str::trim)
        .filter(|l| {
            !l.is_empty()
                && !l.starts_with("#")
                && !l.starts_with("--")
                && !l.starts_with("DROP")
                && !l.starts_with("/*")
        }).collect();
    let mut q = String::new();
    let mut queries = Vec::new();
    for l in lines {
        if !l.ends_with(";") {
            q.push_str(l);
        } else {
            // end of query
            q.push_str(l);
            queries.push(q.clone());
            q = String::new();
        }
    }
    println!("Loaded {} table definitions", queries.len());

    // Try parsing them all
    parse_queryset(queries)
}

#[test] #[ignore]
fn hotcrp_queries() {
    assert!(test_queries_from_file(Path::new("tests/hotcrp-queries.txt"), "HotCRP").is_ok());
}

#[test]
fn hyrise_test_queries() {
    assert!(test_queries_from_file(Path::new("tests/hyrise-test-queries.txt"), "HyRise").is_ok());
}

#[test]
fn tpcw_test_queries() {
    assert!(test_queries_from_file(Path::new("tests/tpc-w-queries.txt"), "TPC-W").is_ok());
}

#[test]
fn tpcw_test_tables() {
    let res = test_queries_from_file(Path::new("tests/tpc-w-tables.txt"), "TPC-W tables");
    assert!(res.is_ok());
    // There are 10 tables
    assert_eq!(res.unwrap(), 10);
}

#[test]
fn finkelstein82_test_queries() {
    let res = test_queries_from_file(Path::new("tests/finkelstein82.txt"), "Finkelstein 1982");
    assert!(res.is_ok());
    // There are 3 tables and 6 queries
    assert_eq!(res.unwrap(), 9);
}

#[test]
fn hotcrp_schema() {
    let mut f = File::open(Path::new("tests/hotcrp-schema.txt")).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<&str> = s
        .lines()
        .map(str::trim)
        .filter(|l| {
            !l.is_empty() && !l.starts_with("#") && !l.starts_with("--") && !l.starts_with("DROP")
        }).collect();
    let mut q = String::new();
    let mut queries = Vec::new();
    for l in lines {
        // remove inline comments, bah
        let l = match l.find("#") {
            None => l,
            Some(pos) => &l[0..pos - 1],
        };
        if !l.ends_with(";") {
            q.push_str(l);
        } else {
            // end of query
            q.push_str(l);
            queries.push(q.clone());
            q = String::new();
        }
    }
    println!("Loaded {} table definitions", queries.len());

    // Try parsing them all
    let (ok, fail) = parse_queryset(queries);

    // There are 24 CREATE TABLE queries in the schema
    assert_eq!(ok, 24);
    assert_eq!(fail, 0);
}

#[test]
fn mediawiki_schema() {
    let (ok, fail) = parse_file("tests/mediawiki-schema.txt");

    // There are 17 CREATE TABLE queries in the schema
    assert_eq!(ok, 17);
    assert_eq!(fail, 0);
}

#[test]
fn parse_comments() {
    let (ok, fail) = parse_file("tests/comments.txt");

    // There are 2 CREATE TABLE queries in the schema
    assert_eq!(ok, 2);
    assert_eq!(fail, 0);
}

#[test]
fn parse_autoincrement() {
    let (ok, fail) = parse_file("tests/autoincrement.txt");

    // There is 1 CREATE TABLE queries in the schema
    assert_eq!(ok, 1);
    assert_eq!(fail, 0);
}

#[test]
fn parse_select() {
    let (ok, fail) = parse_file("tests/select.txt");
    assert_eq!(fail, 0);
    assert_eq!(ok, 24);
}
<commit_msg>/* for single-line only<commit_after>extern crate nom_sql;

use std::fs::File;
use std::io::Read;
use std::path::Path;

fn parse_queryset(queries: Vec<String>) -> (i32, i32) {
    let mut parsed_ok = Vec::new();
    let mut parsed_err = 0;
    for query in queries.iter() {
        println!("Trying to parse '{}': ", &query);
        match nom_sql::parser::parse_query(&query) {
            Ok(_) => {
                println!("ok");
                parsed_ok.push(query);
            }
            Err(_) => {
                println!("failed");
                parsed_err += 1;
            }
        }
    }

    println!("\nParsing failed: {} queries", parsed_err);
    println!("Parsed successfully: {} queries", parsed_ok.len());
    println!("\nSuccessfully parsed queries:");
    for q in parsed_ok.iter() {
        println!("{:?}", q);
    }

    (parsed_ok.len() as i32, parsed_err)
}

fn test_queries_from_file(f: &Path, name: &str) -> Result<i32, i32> {
    let mut f = File::open(f).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<String> = s
        .lines()
        .filter(|l| {
            !l.is_empty()
                && !l.starts_with("#")
                && !l.starts_with("--")
                && !(l.starts_with("/*") && l.ends_with("*/;"))
        })
        .map(|l| {
            if !(l.ends_with("\n") || l.ends_with(";")) {
                String::from(l) + "\n"
            } else {
                String::from(l)
            }
        }).collect();
    println!("\nLoaded {} {} queries", lines.len(), name);

    // Try parsing them all
    let (ok, err) = parse_queryset(lines);

    if err > 0 {
        return Err(err);
    }
    Ok(ok)
}

fn parse_file(path: &str) -> (i32, i32) {
    let mut f = File::open(Path::new(path)).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<&str> = s
        .lines()
        .map(str::trim)
        .filter(|l| {
            !l.is_empty()
                && !l.starts_with("#")
                && !l.starts_with("--")
                && !l.starts_with("DROP")
                && !(l.starts_with("/*") && l.ends_with("*/;"))
        }).collect();
    let mut q = String::new();
    let mut queries = Vec::new();
    for l in lines {
        if !l.ends_with(";") {
            q.push_str(l);
        } else {
            // end of query
            q.push_str(l);
            queries.push(q.clone());
            q = String::new();
        }
    }
    println!("Loaded {} table definitions", queries.len());

    // Try parsing them all
    parse_queryset(queries)
}

#[test] #[ignore]
fn hotcrp_queries() {
    assert!(test_queries_from_file(Path::new("tests/hotcrp-queries.txt"), "HotCRP").is_ok());
}

#[test]
fn hyrise_test_queries() {
    assert!(test_queries_from_file(Path::new("tests/hyrise-test-queries.txt"), "HyRise").is_ok());
}

#[test]
fn tpcw_test_queries() {
    assert!(test_queries_from_file(Path::new("tests/tpc-w-queries.txt"), "TPC-W").is_ok());
}

#[test]
fn tpcw_test_tables() {
    let res = test_queries_from_file(Path::new("tests/tpc-w-tables.txt"), "TPC-W tables");
    assert!(res.is_ok());
    // There are 10 tables
    assert_eq!(res.unwrap(), 10);
}

#[test]
fn finkelstein82_test_queries() {
    let res = test_queries_from_file(Path::new("tests/finkelstein82.txt"), "Finkelstein 1982");
    assert!(res.is_ok());
    // There are 3 tables and 6 queries
    assert_eq!(res.unwrap(), 9);
}

#[test]
fn hotcrp_schema() {
    let mut f = File::open(Path::new("tests/hotcrp-schema.txt")).unwrap();
    let mut s = String::new();

    // Load queries
    f.read_to_string(&mut s).unwrap();
    let lines: Vec<&str> = s
        .lines()
        .map(str::trim)
        .filter(|l| {
            !l.is_empty() && !l.starts_with("#") && !l.starts_with("--") && !l.starts_with("DROP")
        }).collect();
    let mut q = String::new();
    let mut queries = Vec::new();
    for l in lines {
        // remove inline comments, bah
        let l = match l.find("#") {
            None => l,
            Some(pos) => &l[0..pos - 1],
        };
        if !l.ends_with(";") {
            q.push_str(l);
        } else {
            // end of query
            q.push_str(l);
            queries.push(q.clone());
            q = String::new();
        }
    }
    println!("Loaded {} table definitions", queries.len());

    // Try parsing them all
    let (ok, fail) = parse_queryset(queries);

    // There are 24 CREATE TABLE queries in the schema
    assert_eq!(ok, 24);
    assert_eq!(fail, 0);
}

#[test]
fn mediawiki_schema() {
    let (ok, fail) = parse_file("tests/mediawiki-schema.txt");

    // There are 17 CREATE TABLE queries in the schema
    assert_eq!(ok, 17);
    assert_eq!(fail, 0);
}

#[test]
fn parse_comments() {
    let (ok, fail) = parse_file("tests/comments.txt");

    // There are 2 CREATE TABLE queries in the schema
    assert_eq!(ok, 2);
    assert_eq!(fail, 0);
}

#[test]
fn parse_autoincrement() {
    let (ok, fail) = parse_file("tests/autoincrement.txt");

    // There is 1 CREATE TABLE queries in the schema
    assert_eq!(ok, 1);
    assert_eq!(fail, 0);
}

#[test]
fn parse_select() {
    let (ok, fail) = parse_file("tests/select.txt");
    assert_eq!(fail, 0);
    assert_eq!(ok, 24);
}
<|endoftext|>


<commit_before>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]
#![feature(test)]

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

extern crate test;

use svm::slist::Stack;
use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;

macro_rules! impl_test {
    ($name:ident, $it:expr, $exp:expr) => {
        #[test]
        fn $name() {
            assert_eq!(
                svm::eval_program(scheme::compile($it)
                    .unwrap(), true)
                    .unwrap()
                    .peek(),
                Some($exp)
            )
        }

    }
}

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ==> (10 . 20)
/// ```
impl_test!( test_list_construction,
    "(cons 10 (cons 20 nil))",
    &ListCell( box list!(
        AtomCell(SInt(10)),
        AtomCell(SInt(20))
    ))
);

/// Test for simple list construction and deconstruction
///
/// ```lisp
/// (car (cons 20 (cons 10 nil)))
/// ==> 20
/// ```
impl_test!( test_list_car,
    "(car (cons 20 (cons 10 nil)))",
    &AtomCell(SInt(20))
);

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 20 (cons 10 nil)))
/// ==> (10)
/// ```
impl_test!( test_list_cdr,
    "(cdr (cons 20 (cons 10 nil)))",
    &ListCell(box list!(AtomCell(SInt(10))))
);

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ==> 20
/// ```
#[test]
fn run_simple_add() {
    assert_eq!(
        svm::eval_program(scheme::compile("(+ 10 10)").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(20)))
    )
}

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ==> 10
/// ```
#[test]
fn run_nested_arith() {
    assert_eq!(
        svm::eval_program(scheme::compile("(- 20 (+ 5 5))").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(10)))
    )
}

/// Test for basic branching with `if` expressions.
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ==> #t
/// ```
#[test]
fn run_basic_branching_1() {
    assert_eq!(
        svm::eval_program(scheme::compile("(if (= 0 (- 1 1)) #t #f)").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(1)))
    )
}

/// Test for basic branching with `if` expressions.
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ==> 20
/// ```
#[test]
fn run_basic_branching_2() {
    assert_eq!(
        svm::eval_program(scheme::compile("(+ 10 (if (nil? nil) 10 20))").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(20)))
    )
}

/// Test for applying a lambda expression
///
/// ```lisp
/// ((lambda (x y) (+ x y)) 2 3)
/// ==> 5
/// ```
#[test]
fn run_lambda_ap() {
    assert_eq!(
        svm::eval_program(scheme::compile("((lambda (x y) (+ x y)) 2 3)").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(5)))
    )
}

/// Test for applying an expression with nested lambdas
///
/// ```lisp
/// ((lambda (z) ((lambda (x y) (+ (- x y) z)) 3 5)) 6)
/// ==> 4
/// ```
#[test]
fn run_nested_lambda() {
    assert_eq!(
        svm::eval_program(
            scheme::compile("((lambda (z) ((lambda (x y) (+ (- x y) z)) 3 5)) 6)").unwrap(), true)
            .unwrap()
            .peek(),
        Some(&AtomCell(SInt(4)))
    )
}
<commit_msg>All integration targets are now macros<commit_after>#![feature(box_syntax,box_patterns)]
#![feature(compile)]
#![feature(scheme)]
#![feature(test)]

#[macro_use]
extern crate seax_svm as svm;
extern crate seax_scheme as scheme;

extern crate test;

use svm::slist::Stack;
use svm::slist::List::{Cons,Nil};
use svm::cell::Atom::*;
use svm::cell::SVMCell::*;

macro_rules! impl_test {
    ($name:ident, $it:expr, $exp:expr) => {
        #[test]
        fn $name() {
            assert_eq!(
                svm::eval_program(scheme::compile($it)
                    .unwrap(), true)
                    .unwrap()
                    .peek(),
                Some($exp)
            )
        }

    }
}

/// Test for simple list construction through CONS.
///
/// ```lisp
/// (cons 10 (cons 20 nil))
/// ==> (10 . 20)
/// ```
impl_test!( test_list_construction,
    "(cons 10 (cons 20 nil))",
    &ListCell( box list!(
        AtomCell(SInt(10)),
        AtomCell(SInt(20))
    ))
);

/// Test for simple list construction and deconstruction
///
/// ```lisp
/// (car (cons 20 (cons 10 nil)))
/// ==> 20
/// ```
impl_test!( test_list_car,
    "(car (cons 20 (cons 10 nil)))",
    &AtomCell(SInt(20))
);

/// Test for simple list construction and destructuring
///
/// ```lisp
/// (cdr (cons 20 (cons 10 nil)))
/// ==> (10)
/// ```
impl_test!( test_list_cdr,
    "(cdr (cons 20 (cons 10 nil)))",
    &ListCell(box list!(AtomCell(SInt(10))))
);

/// Test for simple mathematics application
///
/// ```lisp
/// (+ 10 10)
/// ==> 20
/// ```
impl_test!( test_simple_add,
    "(+ 10 10)",
    &AtomCell(SInt(20))
);

/// Test for nested arithmetic
///
/// ```lisp
/// (- 20 (+ 5 5))
/// ==> 10
/// ```
impl_test!( test_nested_arith,
    "(- 20 (+ 5 5))",
    &AtomCell(SInt(10))
);

/// Test for basic branching with `if` expressions.
///
/// ```lisp
/// ((if (= 0 (- 1 1)) #t #f)
/// ==> #t
/// ```
impl_test!( test_basic_branching_1,
    "(if (= 0 (- 1 1)) #t #f)",
    &AtomCell(SInt(1))
);

/// Test for basic branching with `if` expressions.
///
/// ```lisp
/// (+ 10 (if (nil? nil) 10 20))
/// ==> 20
/// ```
impl_test!( test_basic_branching_2,
    "(+ 10 (if (nil? nil) 10 20))",
    &AtomCell(SInt(20))
);

/// Test for applying a lambda expression
///
/// ```lisp
/// ((lambda (x y) (+ x y)) 2 3)
/// ==> 5
/// ```
impl_test!( test_lambda_ap,
    "((lambda (x y) (+ x y)) 2 3)",
    &AtomCell(SInt(5))
);

/// Test for applying an expression with nested lambdas
///
/// ```lisp
/// ((lambda (z) ((lambda (x y) (+ (- x y) z)) 3 5)) 6)
/// ==> 4
/// ```
impl_test!( test_nested_lambda,
    "((lambda (z) ((lambda (x y) (+ (- x y) z)) 3 5)) 6)",
    &AtomCell(SInt(4))
);
<|endoftext|>


<commit_before>use std::env;
use std::process::Command;

#[derive(Debug)]
pub struct NiceOutput {
    pub status: i32,
    pub stdout: String,
    pub stderr: String,
}

fn run(args: Vec<&str>) -> NiceOutput {
    let mut directory = env::current_exe().unwrap();
            directory.pop(); // chop off exe name but leave "debug"
    let name = format!("to{}", env::consts::EXE_SUFFIX);
    let binary = directory.join(&name);

    let mut command = Command::new(binary);
            command.args(&args);
    let output = command.output().unwrap();
    let stdout = trim(output.stdout);
    let stderr = trim(output.stderr);

    return NiceOutput {
        status: output.status.code().unwrap(),
        stdout: stdout,
        stderr: stderr,
    };
}

fn trim(output: Vec<u8>) -> String {
    let string = String::from_utf8(output).unwrap();
    return String::from(string.trim());
}

#[test]
fn help_long() {
    let result = run(vec!["--help"]);
    assert_eq!(result.status, 0);
}

#[test]
fn help_short() {
    let result = run(vec!["-h"]);
    assert_eq!(result.status, 0);
}

#[test]
fn version_long() {
    let result = run(vec!["--version"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
    assert_eq!(result.status, 0);
}

#[test]
fn version_short() {
    let result = run(vec!["-V"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
    assert_eq!(result.status, 0);
}
<commit_msg>Adds --save test.<commit_after>use std::env;
use std::process::Command;

#[derive(Debug)]
pub struct NiceOutput {
    pub status: i32,
    pub stdout: String,
    pub stderr: String,
}

fn run(args: Vec<&str>) -> NiceOutput {
    let mut directory = env::current_exe().unwrap();
            directory.pop(); // chop off exe name but leave "debug"
    let name = format!("to{}", env::consts::EXE_SUFFIX);
    let binary = directory.join(&name);

    let mut command = Command::new(binary);
            command.args(&args);
    let output = command.output().unwrap();
    let stdout = trim(output.stdout);
    let stderr = trim(output.stderr);

    return NiceOutput {
        status: output.status.code().unwrap(),
        stdout: stdout,
        stderr: stderr,
    };
}

fn trim(output: Vec<u8>) -> String {
    let string = String::from_utf8(output).unwrap();
    return String::from(string.trim());
}

#[test]
fn help_long() {
    let result = run(vec!["--help"]);
    assert_eq!(result.status, 0);
}

#[test]
fn help_short() {
    let result = run(vec!["-h"]);
    assert_eq!(result.status, 0);
}

#[test]
fn version_long() {
    let result = run(vec!["--version"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
    assert_eq!(result.status, 0);
}

#[test]
fn version_short() {
    let result = run(vec!["-V"]);
    let version = env!("CARGO_PKG_VERSION");
    let expected = format!("to {}", version);

    assert_eq!(result.stdout, expected);
    assert_eq!(result.status, 0);
}

#[test]
fn save_long() {
    let result = run(vec!["--save"]);
    assert_eq!(result.status, 0);
}
<|endoftext|>