<commit_before>//! SMTP client

use bufstream::BufStream;
use nom::ErrorKind as NomErrorKind;
use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::{Credentials, Mechanism};
use smtp::client::net::{ClientTlsParameters, Connector, NetworkStream, Timeout};
use smtp::commands::*;
use smtp::error::{Error, SmtpResult};
use smtp::response::Response;
use std::fmt::{Debug, Display};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::ToSocketAddrs;
use std::string::String;
use std::time::Duration;

pub mod net;
pub mod mock;

/// The codec used for transparency
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct ClientCodec {
    escape_count: u8,
}

impl ClientCodec {
    /// Creates a new client codec
    pub fn new() -> Self {
        ClientCodec::default()
    }
}

impl ClientCodec {
    /// Adds transparency
    /// TODO: replace CR and LF by CRLF
    fn encode(&mut self, frame: &[u8], buf: &mut Vec<u8>) -> Result<(), Error> {
        match frame.len() {
            0 => {
                match self.escape_count {
                    0 => buf.write_all(b"\r\n.\r\n")?,
                    1 => buf.write_all(b"\n.\r\n")?,
                    2 => buf.write_all(b".\r\n")?,
                    _ => unreachable!(),
                }
                self.escape_count = 0;
                Ok(())
            }
            _ => {
                let mut start = 0;
                for (idx, byte) in frame.iter().enumerate() {
                    match self.escape_count {
                        0 => self.escape_count = if *byte == b'\r' { 1 } else { 0 },
                        1 => self.escape_count = if *byte == b'\n' { 2 } else { 0 },
                        2 => self.escape_count = if *byte == b'.' { 3 } else { 0 },
                        _ => unreachable!(),
                    }
                    if self.escape_count == 3 {
                        self.escape_count = 0;
                        buf.write_all(&frame[start..idx])?;
                        buf.write_all(b".")?;
                        start = idx;
                    }
                }
                buf.write_all(&frame[start..])?;
                Ok(())
            }
        }
    }
}

/// Returns the string replacing all the CRLF with "\<CRLF\>"
/// Used for debug displays
fn escape_crlf(string: &str) -> String {
    string.replace(CRLF, "<CRLF>")
}

/// Structure that implements the SMTP client
#[derive(Debug, Default)]
pub struct Client<S: Write + Read = NetworkStream> {
    /// TCP stream between client and server
    /// Value is None before connection
    stream: Option<BufStream<S>>,
}

macro_rules! return_err (
    ($err: expr, $client: ident) => ({
        return Err(From::from($err))
    })
);

#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
impl<S: Write + Read> Client<S> {
    /// Creates a new SMTP client
    ///
    /// It does not connects to the server, but only creates the `Client`
    pub fn new() -> Client<S> {
        Client { stream: None }
    }
}

impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
    /// Closes the SMTP transaction if possible
    pub fn close(&mut self) {
        let _ = self.command(QuitCommand);
        self.stream = None;
    }

    /// Sets the underlying stream
    pub fn set_stream(&mut self, stream: S) {
        self.stream = Some(BufStream::new(stream));
    }

    /// Upgrades the underlying connection to SSL/TLS
    pub fn upgrade_tls_stream(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()> {
        match self.stream {
            Some(ref mut stream) => stream.get_mut().upgrade_tls(tls_parameters),
            None => Ok(()),
        }
    }

    /// Tells if the underlying stream is currently encrypted
    pub fn is_encrypted(&self) -> bool {
        match self.stream {
            Some(ref stream) => stream.get_ref().is_encrypted(),
            None => false,
        }
    }

    /// Set timeout
    pub fn set_timeout(&mut self, duration: Option<Duration>) -> io::Result<()> {
        match self.stream {
            Some(ref mut stream) => {
                stream.get_mut().set_read_timeout(duration)?;
                stream.get_mut().set_read_timeout(duration)?;
                Ok(())
            }
            None => Ok(()),
        }
    }

    /// Connects to the configured server
    pub fn connect<A: ToSocketAddrs>(
        &mut self,
        addr: &A,
        tls_parameters: Option<&ClientTlsParameters>,
    ) -> SmtpResult {
        // Connect should not be called when the client is already connected
        if self.stream.is_some() {
            return_err!("The connection is already established", self);
        }

        let mut addresses = addr.to_socket_addrs()?;

        let server_addr = match addresses.next() {
            Some(addr) => addr,
            None => return_err!("Could not resolve hostname", self),
        };

        debug!("connecting to {}", server_addr);

        // Try to connect
        self.set_stream(Connector::connect(&server_addr, tls_parameters)?);

        self.read_response()
    }

    /// Checks if the server is connected using the NOOP SMTP command
    #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
    pub fn is_connected(&mut self) -> bool {
        self.command(NoopCommand).is_ok()
    }

    /// Sends an AUTH command with the given mechanism, and handles challenge if needed
    pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult {
        // TODO
        let mut challenges = 10;
        let mut response = self.command(AuthCommand::new(mechanism, credentials.clone(), None)?)?;

        while challenges > 0 && response.has_code(334) {
            challenges -= 1;
            response = self.command(AuthCommand::new_from_response(
                mechanism,
                credentials.clone(),
                &response,
            )?)?;
        }

        if challenges == 0 {
            Err(Error::ResponseParsing("Unexpected number of challenges"))
        } else {
            Ok(response)
        }
    }

    /// Sends the message content
    pub fn message<T: Read>(&mut self, mut message: Box<T>) -> SmtpResult {
        let mut out_buf: Vec<u8> = vec![];
        let mut codec = ClientCodec::new();
        let mut message_reader = BufReader::new(message.as_mut());

        loop {
            out_buf.clear();

            let consumed = match message_reader.fill_buf() {
                Ok(bytes) => {
                    codec.encode(bytes, &mut out_buf)?;
                    bytes.len()
                }
                Err(ref err) => panic!("Failed with: {}", err),
            };
            message_reader.consume(consumed);

            if consumed == 0 {
                break;
            }

            self.write(out_buf.as_slice())?;
        }

        self.write(MESSAGE_ENDING.as_bytes())?;
        self.read_response()
    }

    /// Sends an SMTP command
    pub fn command<C: Display>(&mut self, command: C) -> SmtpResult {
        self.write(command.to_string().as_bytes())?;
        self.read_response()
    }

    /// Writes a string to the server
    fn write(&mut self, string: &[u8]) -> Result<(), Error> {
        if self.stream.is_none() {
            return Err(From::from("Connection closed"));
        }

        self.stream.as_mut().unwrap().write_all(string)?;
        self.stream.as_mut().unwrap().flush()?;

        debug!(
            "Wrote: {}",
            escape_crlf(String::from_utf8_lossy(string).as_ref())
        );
        Ok(())
    }

    /// Gets the SMTP response
    fn read_response(&mut self) -> SmtpResult {
        let mut raw_response = String::new();
        let mut response = raw_response.parse::<Response>();

        while response.is_err() {
            if response.as_ref().err().unwrap() != &NomErrorKind::Complete {
                break;
            }
            // TODO read more than one line
            self.stream.as_mut().unwrap().read_line(&mut raw_response)?;
            response = raw_response.parse::<Response>();
        }

        debug!("Read: {}", escape_crlf(raw_response.as_ref()));

        let final_response = response?;

        if final_response.is_positive() {
            Ok(final_response)
        } else {
            Err(From::from(final_response))
        }
    }
}

#[cfg(test)]
mod test {
    use super::{escape_crlf, ClientCodec};

    #[test]
    fn test_codec() {
        let mut codec = ClientCodec::new();
        let mut buf: Vec<u8> = vec![];

        assert!(codec.encode(b"test\r\n", &mut buf).is_ok());
        assert!(codec.encode(b".\r\n", &mut buf).is_ok());
        assert!(codec.encode(b"\r\ntest", &mut buf).is_ok());
        assert!(codec.encode(b"te\r\n.\r\nst", &mut buf).is_ok());
        assert!(codec.encode(b"test", &mut buf).is_ok());
        assert!(codec.encode(b"test.", &mut buf).is_ok());
        assert!(codec.encode(b"test\n", &mut buf).is_ok());
        assert!(codec.encode(b".test\n", &mut buf).is_ok());
        assert!(codec.encode(b"test", &mut buf).is_ok());
        assert_eq!(
            String::from_utf8(buf).unwrap(),
            "test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest"
        );
    }

    #[test]
    fn test_escape_crlf() {
        assert_eq!(escape_crlf("\r\n"), "<CRLF>");
        assert_eq!(escape_crlf("EHLO my_name\r\n"), "EHLO my_name<CRLF>");
        assert_eq!(
            escape_crlf("EHLO my_name\r\nSIZE 42\r\n"),
            "EHLO my_name<CRLF>SIZE 42<CRLF>"
        );
    }
}
<commit_msg>fix(transport): Write timeout is not set in smtp transport<commit_after>//! SMTP client

use bufstream::BufStream;
use nom::ErrorKind as NomErrorKind;
use smtp::{CRLF, MESSAGE_ENDING};
use smtp::authentication::{Credentials, Mechanism};
use smtp::client::net::{ClientTlsParameters, Connector, NetworkStream, Timeout};
use smtp::commands::*;
use smtp::error::{Error, SmtpResult};
use smtp::response::Response;
use std::fmt::{Debug, Display};
use std::io::{self, BufRead, BufReader, Read, Write};
use std::net::ToSocketAddrs;
use std::string::String;
use std::time::Duration;

pub mod net;
pub mod mock;

/// The codec used for transparency
#[derive(Default, Clone, Copy, Debug)]
#[cfg_attr(feature = "serde-impls", derive(Serialize, Deserialize))]
pub struct ClientCodec {
    escape_count: u8,
}

impl ClientCodec {
    /// Creates a new client codec
    pub fn new() -> Self {
        ClientCodec::default()
    }
}

impl ClientCodec {
    /// Adds transparency
    /// TODO: replace CR and LF by CRLF
    fn encode(&mut self, frame: &[u8], buf: &mut Vec<u8>) -> Result<(), Error> {
        match frame.len() {
            0 => {
                match self.escape_count {
                    0 => buf.write_all(b"\r\n.\r\n")?,
                    1 => buf.write_all(b"\n.\r\n")?,
                    2 => buf.write_all(b".\r\n")?,
                    _ => unreachable!(),
                }
                self.escape_count = 0;
                Ok(())
            }
            _ => {
                let mut start = 0;
                for (idx, byte) in frame.iter().enumerate() {
                    match self.escape_count {
                        0 => self.escape_count = if *byte == b'\r' { 1 } else { 0 },
                        1 => self.escape_count = if *byte == b'\n' { 2 } else { 0 },
                        2 => self.escape_count = if *byte == b'.' { 3 } else { 0 },
                        _ => unreachable!(),
                    }
                    if self.escape_count == 3 {
                        self.escape_count = 0;
                        buf.write_all(&frame[start..idx])?;
                        buf.write_all(b".")?;
                        start = idx;
                    }
                }
                buf.write_all(&frame[start..])?;
                Ok(())
            }
        }
    }
}

/// Returns the string replacing all the CRLF with "\<CRLF\>"
/// Used for debug displays
fn escape_crlf(string: &str) -> String {
    string.replace(CRLF, "<CRLF>")
}

/// Structure that implements the SMTP client
#[derive(Debug, Default)]
pub struct Client<S: Write + Read = NetworkStream> {
    /// TCP stream between client and server
    /// Value is None before connection
    stream: Option<BufStream<S>>,
}

macro_rules! return_err (
    ($err: expr, $client: ident) => ({
        return Err(From::from($err))
    })
);

#[cfg_attr(feature = "cargo-clippy", allow(new_without_default_derive))]
impl<S: Write + Read> Client<S> {
    /// Creates a new SMTP client
    ///
    /// It does not connects to the server, but only creates the `Client`
    pub fn new() -> Client<S> {
        Client { stream: None }
    }
}

impl<S: Connector + Write + Read + Timeout + Debug> Client<S> {
    /// Closes the SMTP transaction if possible
    pub fn close(&mut self) {
        let _ = self.command(QuitCommand);
        self.stream = None;
    }

    /// Sets the underlying stream
    pub fn set_stream(&mut self, stream: S) {
        self.stream = Some(BufStream::new(stream));
    }

    /// Upgrades the underlying connection to SSL/TLS
    pub fn upgrade_tls_stream(&mut self, tls_parameters: &ClientTlsParameters) -> io::Result<()> {
        match self.stream {
            Some(ref mut stream) => stream.get_mut().upgrade_tls(tls_parameters),
            None => Ok(()),
        }
    }

    /// Tells if the underlying stream is currently encrypted
    pub fn is_encrypted(&self) -> bool {
        match self.stream {
            Some(ref stream) => stream.get_ref().is_encrypted(),
            None => false,
        }
    }

    /// Set timeout
    pub fn set_timeout(&mut self, duration: Option<Duration>) -> io::Result<()> {
        match self.stream {
            Some(ref mut stream) => {
                stream.get_mut().set_read_timeout(duration)?;
                stream.get_mut().set_write_timeout(duration)?;
                Ok(())
            }
            None => Ok(()),
        }
    }

    /// Connects to the configured server
    pub fn connect<A: ToSocketAddrs>(
        &mut self,
        addr: &A,
        tls_parameters: Option<&ClientTlsParameters>,
    ) -> SmtpResult {
        // Connect should not be called when the client is already connected
        if self.stream.is_some() {
            return_err!("The connection is already established", self);
        }

        let mut addresses = addr.to_socket_addrs()?;

        let server_addr = match addresses.next() {
            Some(addr) => addr,
            None => return_err!("Could not resolve hostname", self),
        };

        debug!("connecting to {}", server_addr);

        // Try to connect
        self.set_stream(Connector::connect(&server_addr, tls_parameters)?);

        self.read_response()
    }

    /// Checks if the server is connected using the NOOP SMTP command
    #[cfg_attr(feature = "cargo-clippy", allow(wrong_self_convention))]
    pub fn is_connected(&mut self) -> bool {
        self.command(NoopCommand).is_ok()
    }

    /// Sends an AUTH command with the given mechanism, and handles challenge if needed
    pub fn auth(&mut self, mechanism: Mechanism, credentials: &Credentials) -> SmtpResult {
        // TODO
        let mut challenges = 10;
        let mut response = self.command(AuthCommand::new(mechanism, credentials.clone(), None)?)?;

        while challenges > 0 && response.has_code(334) {
            challenges -= 1;
            response = self.command(AuthCommand::new_from_response(
                mechanism,
                credentials.clone(),
                &response,
            )?)?;
        }

        if challenges == 0 {
            Err(Error::ResponseParsing("Unexpected number of challenges"))
        } else {
            Ok(response)
        }
    }

    /// Sends the message content
    pub fn message<T: Read>(&mut self, mut message: Box<T>) -> SmtpResult {
        let mut out_buf: Vec<u8> = vec![];
        let mut codec = ClientCodec::new();
        let mut message_reader = BufReader::new(message.as_mut());

        loop {
            out_buf.clear();

            let consumed = match message_reader.fill_buf() {
                Ok(bytes) => {
                    codec.encode(bytes, &mut out_buf)?;
                    bytes.len()
                }
                Err(ref err) => panic!("Failed with: {}", err),
            };
            message_reader.consume(consumed);

            if consumed == 0 {
                break;
            }

            self.write(out_buf.as_slice())?;
        }

        self.write(MESSAGE_ENDING.as_bytes())?;
        self.read_response()
    }

    /// Sends an SMTP command
    pub fn command<C: Display>(&mut self, command: C) -> SmtpResult {
        self.write(command.to_string().as_bytes())?;
        self.read_response()
    }

    /// Writes a string to the server
    fn write(&mut self, string: &[u8]) -> Result<(), Error> {
        if self.stream.is_none() {
            return Err(From::from("Connection closed"));
        }

        self.stream.as_mut().unwrap().write_all(string)?;
        self.stream.as_mut().unwrap().flush()?;

        debug!(
            "Wrote: {}",
            escape_crlf(String::from_utf8_lossy(string).as_ref())
        );
        Ok(())
    }

    /// Gets the SMTP response
    fn read_response(&mut self) -> SmtpResult {
        let mut raw_response = String::new();
        let mut response = raw_response.parse::<Response>();

        while response.is_err() {
            if response.as_ref().err().unwrap() != &NomErrorKind::Complete {
                break;
            }
            // TODO read more than one line
            self.stream.as_mut().unwrap().read_line(&mut raw_response)?;
            response = raw_response.parse::<Response>();
        }

        debug!("Read: {}", escape_crlf(raw_response.as_ref()));

        let final_response = response?;

        if final_response.is_positive() {
            Ok(final_response)
        } else {
            Err(From::from(final_response))
        }
    }
}

#[cfg(test)]
mod test {
    use super::{escape_crlf, ClientCodec};

    #[test]
    fn test_codec() {
        let mut codec = ClientCodec::new();
        let mut buf: Vec<u8> = vec![];

        assert!(codec.encode(b"test\r\n", &mut buf).is_ok());
        assert!(codec.encode(b".\r\n", &mut buf).is_ok());
        assert!(codec.encode(b"\r\ntest", &mut buf).is_ok());
        assert!(codec.encode(b"te\r\n.\r\nst", &mut buf).is_ok());
        assert!(codec.encode(b"test", &mut buf).is_ok());
        assert!(codec.encode(b"test.", &mut buf).is_ok());
        assert!(codec.encode(b"test\n", &mut buf).is_ok());
        assert!(codec.encode(b".test\n", &mut buf).is_ok());
        assert!(codec.encode(b"test", &mut buf).is_ok());
        assert_eq!(
            String::from_utf8(buf).unwrap(),
            "test\r\n..\r\n\r\ntestte\r\n..\r\nsttesttest.test\n.test\ntest"
        );
    }

    #[test]
    fn test_escape_crlf() {
        assert_eq!(escape_crlf("\r\n"), "<CRLF>");
        assert_eq!(escape_crlf("EHLO my_name\r\n"), "EHLO my_name<CRLF>");
        assert_eq!(
            escape_crlf("EHLO my_name\r\nSIZE 42\r\n"),
            "EHLO my_name<CRLF>SIZE 42<CRLF>"
        );
    }
}
<|endoftext|>


<commit_before>#![feature(io)]

extern crate compiler;
extern crate argparse;
extern crate walkdir;
extern crate term;

use argparse::ArgumentParser;
use walkdir::WalkDir;
use std::process::Command;
use std::borrow::Borrow;

mod tool
{
    /// A constant.
    #[derive(Copy,Clone,Debug,PartialEq,Eq)]
    pub enum Constant
    {
        /// The path of the test that is being run.
        TestPath,
    }

    impl Constant
    {
        /// Maps a constant name to a constant.
        /// Returns `None` if no mapping exists.
        pub fn lookup(name: &str) -> Option<Constant> {
            match name {
                "file" => Some(Constant::TestPath),
                _ => None,
            }
        }
    }

    /// An argument to a tool.
    #[derive(Clone,Debug,PartialEq,Eq)]
    pub enum Argument
    {
        Normal(String),
        Substitute(Constant),
    }

    impl Argument
    {
        /// Parses an argument to a tool.
        ///
        /// If it is prefixed with `@`, then it will be taken
        /// as a constant substitution, otherwise it will
        /// be read verbatim as a tool argument.
        pub fn parse(string: String) -> Result<Self,String> {
            // check if we are parsing a substitution
            if string.chars().next().unwrap() == '@' {
                let name: String = string.chars().skip(1).collect();

                match Constant::lookup(&name) {
                    Some(constant) => Ok(Argument::Substitute(constant)),
                    None => Err(format!("constant does not exist: {}", name)),
                }
            } else { // it is a plain old argument
                Ok(Argument::Normal(string))
            }
        }

        pub fn resolve(&self, test: &super::Test) -> String {
            match *self {
                Argument::Normal(ref s) => s.clone(),
                Argument::Substitute(constant) => match constant {
                    Constant::TestPath => test.path.clone(),
                },
            }
        }
    }

    /// A tool invocation.
    #[derive(Clone,Debug,PartialEq,Eq)]
    pub struct Invocation
    {
        pub executable: String,
        pub arguments: Vec<Argument>,
    }

    impl Invocation
    {
        /// Parses a tool invocation.
        ///
        /// It is in the format:
        ///
        /// ``` bash
        /// <tool-name> [arg1] [arg2] ...
        /// ```
        pub fn parse<'a,I>(mut words: I) -> Result<Self,String>
            where I: Iterator<Item=&'a str> {
            let executable = match words.next() {
                Some(exec) => exec,
                None => return Err("no executable specified".into()),
            }.into();

            let mut arguments = Vec::new();

            for arg_str in words {
                let arg = try!(Argument::parse(arg_str.into()));
                arguments.push(arg);
            }

            Ok(Invocation {
                executable: executable,
                arguments: arguments,
            })
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub enum Directive
{
    Run(tool::Invocation),
}

impl Directive
{
    pub fn maybe_parse(string: String) -> Option<Result<Self,String>> {
        let directive_str = string.split_whitespace().next().unwrap();
        let inner_words = string.split_whitespace().skip(1);

        match directive_str {
            // FIXME: better message if we have 'RUN :'
            "RUN:" => {
                let invocation = match tool::Invocation::parse(inner_words) {
                    Ok(i) => i,
                    Err(e) => return Some(Err(e)),
                };

                Some(Ok(Directive::Run(invocation)))
            },
            _ => {
                if directive_str.ends_with(':') {
                    Some(Err(format!("directive '{}' not known", directive_str)))
                } else {
                    None
                }
            },
        }
    }
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct Test
{
    pub path: String,
    pub directives: Vec<Directive>,
}

impl Test
{
    pub fn is_empty(&self) -> bool {
        self.directives.is_empty()
    }
}

pub enum TestResultKind
{
    Pass,
    Fail(String, String),
    Skip,
}

pub struct TestResult
{
    pub path: String,
    pub kind: TestResultKind,
}

#[derive(Clone,Debug,PartialEq,Eq)]
pub struct Context
{
    pub exec_search_dirs: Vec<String>,
    pub tests: Vec<Test>,
}

impl Context
{
    pub fn new() -> Self {
        Context {
            exec_search_dirs: Vec::new(),
            tests: Vec::new(),
        }
    }

    pub fn test(mut self, test: Test) -> Self {
        self.tests.push(test);
        self
    }

    pub fn add_search_dir(&mut self, dir: String) {
        self.exec_search_dirs.push(dir);
    }

    pub fn find_in_search_dir(&self, path: &str)
        -> Option<String> {
        for dir in self.exec_search_dirs.iter() {
            for entry in std::fs::read_dir(dir).unwrap() {
                let entry = entry.unwrap();
                let cur_path = entry.path();

                if std::fs::metadata(&cur_path).unwrap().is_file() {
                    if cur_path.file_name().unwrap() == path {
                        return Some(cur_path.to_str().unwrap().to_owned());
                    }
                }

            }
        }
        None
    }

    pub fn executable_path(&self, path: &str) -> String {
        match self.find_in_search_dir(path) {
            Some(p) => p,
            None => path.to_owned(),
        }
    }
}

fn main() {
    let mut files: Vec<String> = Vec::new();

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Runs tests");

        ap.refer(&mut files)
            .add_argument("files", argparse::List,
                          r#"Files to test"#);
        ap.parse_args_or_exit();
    }

    let paths: Vec<&str> = files.iter()
                                 .map(|s| s.borrow())
                                 .collect();

    if paths.is_empty() {
        util::abort("no filenames given")
    }

    let test_paths = find_tests(&paths);

    let mut context = test_paths.into_iter().fold(Context::new(), |c,file| {
        let test = read_test(&file).unwrap();
        c.test(test)
    });

    match util::tool_dir() {
        Some(dir) => context.add_search_dir(dir),
        None => print::warning("could not find tool directory"),
    }

    let results = run_tests(&context);

    for result in results.iter() {
        print_result(result)
    }
}

fn print_result(result: &TestResult) {
    match result.kind {
        TestResultKind::Pass => {
            print::success(format!("PASS :: {}", result.path));
        },
        TestResultKind::Skip => {
            print::line();
            print::warning(format!(
                "SKIP :: {} (test does not contain any directives)",
                     result.path));
            print::line();
        },
        TestResultKind::Fail(ref msg, ref desc) => {
            print::line();

            print::failure(format!("FAIL :: {}", result.path));
            print::text(msg.clone());

            // Only print stderr if there was output
            if !desc.is_empty() {
                print::line();
                print::text("stderr:");
                print::line();
                print::text(desc.clone());
            }

            print::line();
        },
    }
}

/// Recursively finds tests for the given paths.
fn find_tests(paths: &[&str]) -> Vec<String> {
    paths.iter().flat_map(|path| {
                    find_tests_in_path(path).into_iter()
                })
                .collect()
}

fn find_tests_in_path(path: &str) -> Vec<String> {
    let metadata = match std::fs::metadata(path) {
        Ok(meta) => meta,
        Err(e) => util::abort(format!("failed to open '{}': {}",
                                      path, e)),
    };

    if metadata.is_dir() {
        find_tests_in_dir(path)
    } else {
        vec![path.to_owned()]
    }
}

fn find_tests_in_dir(path: &str) -> Vec<String> {
    find_files_in_dir(path).into_iter()
                           .filter(|f| f.ends_with(".ir"))
                           .collect()
}

fn find_files_in_dir(path: &str) -> Vec<String> {
    let mut dir_tests = Vec::new();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();

        // don't go into an infinite loop
        if entry.path().to_str().unwrap() == path {
            continue;
        }

        if entry.metadata().unwrap().is_file() {
            dir_tests.push(entry.path().to_str().unwrap().to_owned());
        }
    }

    dir_tests
}

fn read_test(path: &str) -> Result<Test,String> {
    // TODO: redundant indirection
    util::parse_test(path)
}

fn run_tests(context: &Context) -> Vec<TestResult> {
    context.tests.iter().map(|test| run_test(test, context)).collect()
}

fn run_test(test: &Test, context: &Context) -> TestResult {
    if test.is_empty() {
        println!("SKIP :: {} (it includes no test directives", test.path);
        return TestResult {
            path: test.path.clone(),
            kind: TestResultKind::Skip,
        }
    }

    for directive in test.directives.iter() {
        let kind = run_directive(directive, test, context);

        match kind {
            TestResultKind::Pass => continue,
            TestResultKind::Skip => continue,
            TestResultKind::Fail(msg, desc) => {
                return TestResult {
                    path: test.path.clone(),
                    kind: TestResultKind::Fail(msg, desc),
                }
            },
        }
    }

    TestResult {
        path: test.path.clone(),
        kind: TestResultKind::Pass,
    }
}

fn run_directive(directive: &Directive, test: &Test, context: &Context)
    -> TestResultKind {
    match *directive {
        Directive::Run(ref invocation) => {
            let exe_path = context.executable_path(&invocation.executable);
            let mut cmd = Command::new(&exe_path);

            for arg in invocation.arguments.iter() {
                let arg_str = arg.resolve(test);
                cmd.arg(arg_str);
            }

            let output = match cmd.output() {
                Ok(o) => o,
                Err(e) => match e.kind() {
                    std::io::ErrorKind::NotFound => {
                        return TestResultKind::Fail(
                            format!("executable not found: {}",
                                    exe_path), "".to_owned());
                    },
                    _ => {
                        return TestResultKind::Fail(
                            format!("could not execute: '{}', {}",
                                    exe_path, e), "".to_owned());
                    },
                },
            };

            if output.status.success() {
                TestResultKind::Pass
            } else {
                let stderr = String::from_utf8(output.stderr).unwrap();

                TestResultKind::Fail(format!(
                    "{} exited with code {}", exe_path,
                    output.status.code().unwrap()),
                    stderr
                    )
            }
        },
    }
}

mod util
{
    use super::{Test,Directive};

    use std::error::Error;
    use std::io::Read;
    use compiler::ir;
    use std;

    pub fn tool_dir() -> Option<String> {
        let current_exec = match std::env::current_exe() {
            Ok(e) => e,
            Err(e) => abort(
                format!("failed to get current executable path: {}", e)),
        };

        current_exec.parent().map(|p| p.to_str().unwrap().to_owned())
    }

    pub fn parse_test(file_name: &str) -> Result<Test,String> {
        use compiler::ir::read::Token;

        let chars = open_file(file_name).chars().map(|c| c.unwrap());

        let tokenizer = ir::read::Tokenizer::new(chars).preserve_comments();
        let tokens = tokenizer.map(|result| result.unwrap());

        let mut directives = Vec::new();

        for token in tokens {
            match token {
                Token::Comment { ref text, .. } => {
                    match maybe_parse_directive(text) {
                        Some(Ok(directive)) => directives.push(directive),
                        Some(Err(e)) => {
                            return Err(format!(
                                "could not parse directive: {}", e)
                            );
                        },
                        None => continue,
                    }
                },
                _ => continue,
            };
        }

        Ok(Test {
            path: file_name.to_owned(),
            directives: directives,
        })
    }

    pub fn maybe_parse_directive(comment_text: &str)
        -> Option<Result<Directive,String>> {
        Directive::maybe_parse(comment_text.trim().to_owned())
    }

    fn open_file(path: &str) -> std::fs::File {
        match std::fs::File::open(path) {
            Ok(f) => f,
            Err(e) => abort(format!("could not open {}: {}",
                                    path, e.description())),
        }
    }
    pub fn abort<S>(msg: S) -> !
        where S: Into<String> {
        super::print::failure(format!("error: {}", msg.into()));

        std::process::exit(1);
    }
}

pub mod print
{
    use std;
    use term;

    pub fn line() {
        with("\n",
             term::stdout().unwrap(),
             term::color::WHITE);
    }

    pub fn text<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::WHITE);
    }

    pub fn success<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::GREEN);
    }

    pub fn warning<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::YELLOW);
    }

    pub fn failure<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stderr().unwrap(),
             term::color::RED);
    }

    pub fn with<S,W>(msg: S,
                           mut term: Box<term::Terminal<Output=W>>,
                           color: term::color::Color)
        where S: Into<String>, W: std::io::Write {

        term.fg(color).unwrap();
        write!(term, "{}", msg.into()).unwrap();
    }
}
<commit_msg>[test] Separate into modules<commit_after>#![feature(io)]

extern crate compiler;
extern crate argparse;
extern crate walkdir;
extern crate term;

use argparse::ArgumentParser;
use walkdir::WalkDir;
use std::process::Command;
use std::borrow::Borrow;

mod tool
{
    use super::test::Test;

    /// A constant.
    #[derive(Copy,Clone,Debug,PartialEq,Eq)]
    pub enum Constant
    {
        /// The path of the test that is being run.
        TestPath,
    }

    impl Constant
    {
        /// Maps a constant name to a constant.
        /// Returns `None` if no mapping exists.
        pub fn lookup(name: &str) -> Option<Constant> {
            match name {
                "file" => Some(Constant::TestPath),
                _ => None,
            }
        }
    }

    /// An argument to a tool.
    #[derive(Clone,Debug,PartialEq,Eq)]
    pub enum Argument
    {
        Normal(String),
        Substitute(Constant),
    }

    impl Argument
    {
        /// Parses an argument to a tool.
        ///
        /// If it is prefixed with `@`, then it will be taken
        /// as a constant substitution, otherwise it will
        /// be read verbatim as a tool argument.
        pub fn parse(string: String) -> Result<Self,String> {
            // check if we are parsing a substitution
            if string.chars().next().unwrap() == '@' {
                let name: String = string.chars().skip(1).collect();

                match Constant::lookup(&name) {
                    Some(constant) => Ok(Argument::Substitute(constant)),
                    None => Err(format!("constant does not exist: {}", name)),
                }
            } else { // it is a plain old argument
                Ok(Argument::Normal(string))
            }
        }

        pub fn resolve(&self, test: &Test) -> String {
            match *self {
                Argument::Normal(ref s) => s.clone(),
                Argument::Substitute(constant) => match constant {
                    Constant::TestPath => test.path.clone(),
                },
            }
        }
    }

    /// A tool invocation.
    #[derive(Clone,Debug,PartialEq,Eq)]
    pub struct Invocation
    {
        pub executable: String,
        pub arguments: Vec<Argument>,
    }

    impl Invocation
    {
        /// Parses a tool invocation.
        ///
        /// It is in the format:
        ///
        /// ``` bash
        /// <tool-name> [arg1] [arg2] ...
        /// ```
        pub fn parse<'a,I>(mut words: I) -> Result<Self,String>
            where I: Iterator<Item=&'a str> {
            let executable = match words.next() {
                Some(exec) => exec,
                None => return Err("no executable specified".into()),
            }.into();

            let mut arguments = Vec::new();

            for arg_str in words {
                let arg = try!(Argument::parse(arg_str.into()));
                arguments.push(arg);
            }

            Ok(Invocation {
                executable: executable,
                arguments: arguments,
            })
        }
    }
}

pub mod test
{
    use super::tool;
    use std;

    #[derive(Clone,Debug,PartialEq,Eq)]
    pub enum Directive
    {
        Run(tool::Invocation),
    }

    impl Directive
    {
        pub fn maybe_parse(string: String) -> Option<Result<Self,String>> {
            let directive_str = string.split_whitespace().next().unwrap();
            let inner_words = string.split_whitespace().skip(1);

            match directive_str {
                // FIXME: better message if we have 'RUN :'
                "RUN:" => {
                    let invocation = match tool::Invocation::parse(inner_words) {
                        Ok(i) => i,
                        Err(e) => return Some(Err(e)),
                    };

                    Some(Ok(Directive::Run(invocation)))
                },
                _ => {
                    if directive_str.ends_with(':') {
                        Some(Err(format!("directive '{}' not known", directive_str)))
                    } else {
                        None
                    }
                },
            }
        }
    }

    #[derive(Clone,Debug,PartialEq,Eq)]
    pub struct Test
    {
        pub path: String,
        pub directives: Vec<Directive>,
    }

    impl Test
    {
        pub fn is_empty(&self) -> bool {
            self.directives.is_empty()
        }
    }

    pub enum TestResultKind
    {
        Pass,
        Fail(String, String),
        Skip,
    }

    pub struct TestResult
    {
        pub path: String,
        pub kind: TestResultKind,
    }

    #[derive(Clone,Debug,PartialEq,Eq)]
    pub struct Context
    {
        pub exec_search_dirs: Vec<String>,
        pub tests: Vec<Test>,
    }

    impl Context
    {
        pub fn new() -> Self {
            Context {
                exec_search_dirs: Vec::new(),
                tests: Vec::new(),
            }
        }

        pub fn test(mut self, test: Test) -> Self {
            self.tests.push(test);
            self
        }

        pub fn add_search_dir(&mut self, dir: String) {
            self.exec_search_dirs.push(dir);
        }

        pub fn find_in_search_dir(&self, path: &str)
            -> Option<String> {
            for dir in self.exec_search_dirs.iter() {
                for entry in std::fs::read_dir(dir).unwrap() {
                    let entry = entry.unwrap();
                    let cur_path = entry.path();

                    if std::fs::metadata(&cur_path).unwrap().is_file() {
                        if cur_path.file_name().unwrap() == path {
                            return Some(cur_path.to_str().unwrap().to_owned());
                        }
                    }

                }
            }
            None
        }

        pub fn executable_path(&self, path: &str) -> String {
            match self.find_in_search_dir(path) {
                Some(p) => p,
                None => path.to_owned(),
            }
        }
    }
}

use test::{Test,Directive,TestResult,TestResultKind,Context};

fn main() {
    let mut files: Vec<String> = Vec::new();

    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Runs tests");

        ap.refer(&mut files)
            .add_argument("files", argparse::List,
                          r#"Files to test"#);
        ap.parse_args_or_exit();
    }

    let paths: Vec<&str> = files.iter()
                                 .map(|s| s.borrow())
                                 .collect();

    if paths.is_empty() {
        util::abort("no filenames given")
    }

    let test_paths = find_tests(&paths);

    let mut context = test_paths.into_iter().fold(Context::new(), |c,file| {
        let test = read_test(&file).unwrap();
        c.test(test)
    });

    match util::tool_dir() {
        Some(dir) => context.add_search_dir(dir),
        None => print::warning("could not find tool directory"),
    }

    let results = run_tests(&context);

    for result in results.iter() {
        print_result(result)
    }
}

fn print_result(result: &TestResult) {
    match result.kind {
        TestResultKind::Pass => {
            print::success(format!("PASS :: {}", result.path));
        },
        TestResultKind::Skip => {
            print::line();
            print::warning(format!(
                "SKIP :: {} (test does not contain any directives)",
                     result.path));
            print::line();
        },
        TestResultKind::Fail(ref msg, ref desc) => {
            print::line();

            print::failure(format!("FAIL :: {}", result.path));
            print::text(msg.clone());

            // Only print stderr if there was output
            if !desc.is_empty() {
                print::line();
                print::text("stderr:");
                print::line();
                print::text(desc.clone());
            }

            print::line();
        },
    }
}

/// Recursively finds tests for the given paths.
fn find_tests(paths: &[&str]) -> Vec<String> {
    paths.iter().flat_map(|path| {
                    find_tests_in_path(path).into_iter()
                })
                .collect()
}

fn find_tests_in_path(path: &str) -> Vec<String> {
    let metadata = match std::fs::metadata(path) {
        Ok(meta) => meta,
        Err(e) => util::abort(format!("failed to open '{}': {}",
                                      path, e)),
    };

    if metadata.is_dir() {
        find_tests_in_dir(path)
    } else {
        vec![path.to_owned()]
    }
}

fn find_tests_in_dir(path: &str) -> Vec<String> {
    find_files_in_dir(path).into_iter()
                           .filter(|f| f.ends_with(".ir"))
                           .collect()
}

fn find_files_in_dir(path: &str) -> Vec<String> {
    let mut dir_tests = Vec::new();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();

        // don't go into an infinite loop
        if entry.path().to_str().unwrap() == path {
            continue;
        }

        if entry.metadata().unwrap().is_file() {
            dir_tests.push(entry.path().to_str().unwrap().to_owned());
        }
    }

    dir_tests
}

fn read_test(path: &str) -> Result<Test,String> {
    // TODO: redundant indirection
    util::parse_test(path)
}

fn run_tests(context: &Context) -> Vec<TestResult> {
    context.tests.iter().map(|test| run_test(test, context)).collect()
}

fn run_test(test: &Test, context: &Context) -> TestResult {
    if test.is_empty() {
        println!("SKIP :: {} (it includes no test directives", test.path);
        return TestResult {
            path: test.path.clone(),
            kind: TestResultKind::Skip,
        }
    }

    for directive in test.directives.iter() {
        let kind = run_directive(directive, test, context);

        match kind {
            TestResultKind::Pass => continue,
            TestResultKind::Skip => continue,
            TestResultKind::Fail(msg, desc) => {
                return TestResult {
                    path: test.path.clone(),
                    kind: TestResultKind::Fail(msg, desc),
                }
            },
        }
    }

    TestResult {
        path: test.path.clone(),
        kind: TestResultKind::Pass,
    }
}

fn run_directive(directive: &Directive, test: &Test, context: &Context)
    -> TestResultKind {
    match *directive {
        Directive::Run(ref invocation) => {
            let exe_path = context.executable_path(&invocation.executable);
            let mut cmd = Command::new(&exe_path);

            for arg in invocation.arguments.iter() {
                let arg_str = arg.resolve(test);
                cmd.arg(arg_str);
            }

            let output = match cmd.output() {
                Ok(o) => o,
                Err(e) => match e.kind() {
                    std::io::ErrorKind::NotFound => {
                        return TestResultKind::Fail(
                            format!("executable not found: {}",
                                    exe_path), "".to_owned());
                    },
                    _ => {
                        return TestResultKind::Fail(
                            format!("could not execute: '{}', {}",
                                    exe_path, e), "".to_owned());
                    },
                },
            };

            if output.status.success() {
                TestResultKind::Pass
            } else {
                let stderr = String::from_utf8(output.stderr).unwrap();

                TestResultKind::Fail(format!(
                    "{} exited with code {}", exe_path,
                    output.status.code().unwrap()),
                    stderr
                    )
            }
        },
    }
}

mod util
{
    use super::test::{Test,Directive};

    use std::error::Error;
    use std::io::Read;
    use compiler::ir;
    use std;

    pub fn tool_dir() -> Option<String> {
        let current_exec = match std::env::current_exe() {
            Ok(e) => e,
            Err(e) => abort(
                format!("failed to get current executable path: {}", e)),
        };

        current_exec.parent().map(|p| p.to_str().unwrap().to_owned())
    }

    pub fn parse_test(file_name: &str) -> Result<Test,String> {
        use compiler::ir::read::Token;

        let chars = open_file(file_name).chars().map(|c| c.unwrap());

        let tokenizer = ir::read::Tokenizer::new(chars).preserve_comments();
        let tokens = tokenizer.map(|result| result.unwrap());

        let mut directives = Vec::new();

        for token in tokens {
            match token {
                Token::Comment { ref text, .. } => {
                    match maybe_parse_directive(text) {
                        Some(Ok(directive)) => directives.push(directive),
                        Some(Err(e)) => {
                            return Err(format!(
                                "could not parse directive: {}", e)
                            );
                        },
                        None => continue,
                    }
                },
                _ => continue,
            };
        }

        Ok(Test {
            path: file_name.to_owned(),
            directives: directives,
        })
    }

    pub fn maybe_parse_directive(comment_text: &str)
        -> Option<Result<Directive,String>> {
        Directive::maybe_parse(comment_text.trim().to_owned())
    }

    fn open_file(path: &str) -> std::fs::File {
        match std::fs::File::open(path) {
            Ok(f) => f,
            Err(e) => abort(format!("could not open {}: {}",
                                    path, e.description())),
        }
    }
    pub fn abort<S>(msg: S) -> !
        where S: Into<String> {
        super::print::failure(format!("error: {}", msg.into()));

        std::process::exit(1);
    }
}

pub mod print
{
    use std;
    use term;

    pub fn line() {
        with("\n",
             term::stdout().unwrap(),
             term::color::WHITE);
    }

    pub fn text<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::WHITE);
    }

    pub fn success<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::GREEN);
    }

    pub fn warning<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stdout().unwrap(),
             term::color::YELLOW);
    }

    pub fn failure<S>(msg: S)
        where S: Into<String> {
        with(format!("{}\n", msg.into()),
             term::stderr().unwrap(),
             term::color::RED);
    }

    pub fn with<S,W>(msg: S,
                           mut term: Box<term::Terminal<Output=W>>,
                           color: term::color::Color)
        where S: Into<String>, W: std::io::Write {

        term.fg(color).unwrap();
        write!(term, "{}", msg.into()).unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate proc_macro;
use self::proc_macro::TokenStream;

use quote::{quote, ToTokens};
use std::collections::HashSet as Set;
use syn::fold::{self, Fold};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{parse_macro_input, parse_quote, Expr, Ident, ItemFn, Local, Pat, Stmt, Token};

/// Parses a list of variable names separated by commas.
///
///     a, b, c
///
/// This is how the compiler passes in arguments to our attribute -- it is
/// everything inside the delimiters after the attribute name.
///
///     #[trace_var(a, b, c)]
///                 ^^^^^^^
struct Args {
    vars: Set<Ident>,
}

impl Parse for Args {
    fn parse(input: ParseStream) -> Result<Self> {
        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            vars: vars.into_iter().collect(),
        })
    }
}

impl Args {
    /// Determines whether the given `Expr` is a path referring to one of the
    /// variables we intend to print. Expressions are used as the left-hand side
    /// of the assignment operator.
    fn should_print_expr(&self, e: &Expr) -> bool {
        match *e {
            Expr::Path(ref e) => {
                if e.path.leading_colon.is_some() {
                    false
                } else if e.path.segments.len() != 1 {
                    false
                } else {
                    let first = e.path.segments.first().unwrap();
                    let segment = first.value();
                    self.vars.contains(&segment.ident) && segment.arguments.is_empty()
                }
            }
            _ => false,
        }
    }

    /// Determines whether the given `Pat` is an identifier equal to one of the
    /// variables we intend to print. Patterns are used as the left-hand side of
    /// a `let` binding.
    fn should_print_pat(&self, p: &Punctuated<Pat, Token![|]>) -> bool {
        if p.len() != 1 {
            return false;
        }
        match p[0] {
            Pat::Ident(ref p) => self.vars.contains(&p.ident),
            _ => false,
        }
    }

    /// Produces an expression that assigns the right-hand side to the left-hand
    /// side and then prints the value.
    ///
    ///     // Before
    ///     VAR = INIT
    ///
    ///     // After
    ///     { VAR = INIT; println!("VAR = {:?}", VAR); }
    fn assign_and_print(&mut self, left: Expr, op: &ToTokens, right: Expr) -> Expr {
        let right = fold::fold_expr(self, right);
        parse_quote!({
            #left #op #right;
            println!(concat!(stringify!(#left), " = {:?}"), #left);
        })
    }

    /// Produces a let-binding that assigns the right-hand side to the left-hand
    /// side and then prints the value.
    ///
    ///     // Before
    ///     let VAR = INIT;
    ///
    ///     // After
    ///     let VAR = { let VAR = INIT; println!("VAR = {:?}", VAR); VAR };
    fn let_and_print(&mut self, local: Local) -> Stmt {
        let Local { pats, ty, init, .. } = local;
        let pat = &pats[0];
        let ty = ty.map(|(colon_token, ty)| quote!(#colon_token #ty));
        let init = self.fold_expr(*init.unwrap().1);
        let ident = match *pat {
            Pat::Ident(ref p) => &p.ident,
            _ => unreachable!(),
        };
        parse_quote! {
            let #pat #ty = {
                #[allow(unused_mut)]
                let #pat = #init;
                println!(concat!(stringify!(#ident), " = {:?}"), #ident);
                #ident
            };
        }
    }
}

/// The `Fold` trait is a way to traverse an owned syntax tree and replace some
/// of its nodes.
///
/// Syn provides two other syntax tree traversal traits: `Visit` which walks a
/// shared borrow of a syntax tree, and `VisitMut` which walks an exclusive
/// borrow of a syntax tree and can mutate it in place.
///
/// All three traits have a method corresponding to each type of node in Syn's
/// syntax tree. All of these methods have default no-op implementations that
/// simply recurse on any child nodes. We can override only those methods for
/// which we want non-default behavior. In this case the traversal needs to
/// transform `Expr` and `Stmt` nodes.
impl Fold for Args {
    fn fold_expr(&mut self, e: Expr) -> Expr {
        match e {
            Expr::Assign(e) => {
                if self.should_print_expr(&e.left) {
                    self.assign_and_print(*e.left, &e.eq_token, *e.right)
                } else {
                    Expr::Assign(fold::fold_expr_assign(self, e))
                }
            }
            Expr::AssignOp(e) => {
                if self.should_print_expr(&e.left) {
                    self.assign_and_print(*e.left, &e.op, *e.right)
                } else {
                    Expr::AssignOp(fold::fold_expr_assign_op(self, e))
                }
            }
            _ => fold::fold_expr(self, e),
        }
    }

    fn fold_stmt(&mut self, s: Stmt) -> Stmt {
        match s {
            Stmt::Local(s) => {
                if s.init.is_some() && self.should_print_pat(&s.pats) {
                    self.let_and_print(s)
                } else {
                    Stmt::Local(fold::fold_local(self, s))
                }
            }
            _ => fold::fold_stmt(self, s),
        }
    }
}

/// Attribute to print the value of the given variables each time they are
/// reassigned.
///
/// # Example
///
/// ```
/// #[trace_var(p, n)]
/// fn factorial(mut n: u64) -> u64 {
///     let mut p = 1;
///     while n > 1 {
///         p *= n;
///         n -= 1;
///     }
///     p
/// }
/// ```
#[proc_macro_attribute]
pub fn trace_var(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);

    // Parse the list of variables the user wanted to print.
    let mut args = parse_macro_input!(args as Args);

    // Use a syntax tree traversal to transform the function body.
    let output = args.fold_item_fn(input);

    // Hand the resulting function body back to the compiler.
    TokenStream::from(quote!(#output))
}
<commit_msg>Fix bare_trait_objects lint<commit_after>extern crate proc_macro;
use self::proc_macro::TokenStream;

use quote::{quote, ToTokens};
use std::collections::HashSet as Set;
use syn::fold::{self, Fold};
use syn::parse::{Parse, ParseStream, Result};
use syn::punctuated::Punctuated;
use syn::{parse_macro_input, parse_quote, Expr, Ident, ItemFn, Local, Pat, Stmt, Token};

/// Parses a list of variable names separated by commas.
///
///     a, b, c
///
/// This is how the compiler passes in arguments to our attribute -- it is
/// everything inside the delimiters after the attribute name.
///
///     #[trace_var(a, b, c)]
///                 ^^^^^^^
struct Args {
    vars: Set<Ident>,
}

impl Parse for Args {
    fn parse(input: ParseStream) -> Result<Self> {
        let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?;
        Ok(Args {
            vars: vars.into_iter().collect(),
        })
    }
}

impl Args {
    /// Determines whether the given `Expr` is a path referring to one of the
    /// variables we intend to print. Expressions are used as the left-hand side
    /// of the assignment operator.
    fn should_print_expr(&self, e: &Expr) -> bool {
        match *e {
            Expr::Path(ref e) => {
                if e.path.leading_colon.is_some() {
                    false
                } else if e.path.segments.len() != 1 {
                    false
                } else {
                    let first = e.path.segments.first().unwrap();
                    let segment = first.value();
                    self.vars.contains(&segment.ident) && segment.arguments.is_empty()
                }
            }
            _ => false,
        }
    }

    /// Determines whether the given `Pat` is an identifier equal to one of the
    /// variables we intend to print. Patterns are used as the left-hand side of
    /// a `let` binding.
    fn should_print_pat(&self, p: &Punctuated<Pat, Token![|]>) -> bool {
        if p.len() != 1 {
            return false;
        }
        match p[0] {
            Pat::Ident(ref p) => self.vars.contains(&p.ident),
            _ => false,
        }
    }

    /// Produces an expression that assigns the right-hand side to the left-hand
    /// side and then prints the value.
    ///
    ///     // Before
    ///     VAR = INIT
    ///
    ///     // After
    ///     { VAR = INIT; println!("VAR = {:?}", VAR); }
    fn assign_and_print(&mut self, left: Expr, op: &dyn ToTokens, right: Expr) -> Expr {
        let right = fold::fold_expr(self, right);
        parse_quote!({
            #left #op #right;
            println!(concat!(stringify!(#left), " = {:?}"), #left);
        })
    }

    /// Produces a let-binding that assigns the right-hand side to the left-hand
    /// side and then prints the value.
    ///
    ///     // Before
    ///     let VAR = INIT;
    ///
    ///     // After
    ///     let VAR = { let VAR = INIT; println!("VAR = {:?}", VAR); VAR };
    fn let_and_print(&mut self, local: Local) -> Stmt {
        let Local { pats, ty, init, .. } = local;
        let pat = &pats[0];
        let ty = ty.map(|(colon_token, ty)| quote!(#colon_token #ty));
        let init = self.fold_expr(*init.unwrap().1);
        let ident = match *pat {
            Pat::Ident(ref p) => &p.ident,
            _ => unreachable!(),
        };
        parse_quote! {
            let #pat #ty = {
                #[allow(unused_mut)]
                let #pat = #init;
                println!(concat!(stringify!(#ident), " = {:?}"), #ident);
                #ident
            };
        }
    }
}

/// The `Fold` trait is a way to traverse an owned syntax tree and replace some
/// of its nodes.
///
/// Syn provides two other syntax tree traversal traits: `Visit` which walks a
/// shared borrow of a syntax tree, and `VisitMut` which walks an exclusive
/// borrow of a syntax tree and can mutate it in place.
///
/// All three traits have a method corresponding to each type of node in Syn's
/// syntax tree. All of these methods have default no-op implementations that
/// simply recurse on any child nodes. We can override only those methods for
/// which we want non-default behavior. In this case the traversal needs to
/// transform `Expr` and `Stmt` nodes.
impl Fold for Args {
    fn fold_expr(&mut self, e: Expr) -> Expr {
        match e {
            Expr::Assign(e) => {
                if self.should_print_expr(&e.left) {
                    self.assign_and_print(*e.left, &e.eq_token, *e.right)
                } else {
                    Expr::Assign(fold::fold_expr_assign(self, e))
                }
            }
            Expr::AssignOp(e) => {
                if self.should_print_expr(&e.left) {
                    self.assign_and_print(*e.left, &e.op, *e.right)
                } else {
                    Expr::AssignOp(fold::fold_expr_assign_op(self, e))
                }
            }
            _ => fold::fold_expr(self, e),
        }
    }

    fn fold_stmt(&mut self, s: Stmt) -> Stmt {
        match s {
            Stmt::Local(s) => {
                if s.init.is_some() && self.should_print_pat(&s.pats) {
                    self.let_and_print(s)
                } else {
                    Stmt::Local(fold::fold_local(self, s))
                }
            }
            _ => fold::fold_stmt(self, s),
        }
    }
}

/// Attribute to print the value of the given variables each time they are
/// reassigned.
///
/// # Example
///
/// ```
/// #[trace_var(p, n)]
/// fn factorial(mut n: u64) -> u64 {
///     let mut p = 1;
///     while n > 1 {
///         p *= n;
///         n -= 1;
///     }
///     p
/// }
/// ```
#[proc_macro_attribute]
pub fn trace_var(args: TokenStream, input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as ItemFn);

    // Parse the list of variables the user wanted to print.
    let mut args = parse_macro_input!(args as Args);

    // Use a syntax tree traversal to transform the function body.
    let output = args.fold_item_fn(input);

    // Hand the resulting function body back to the compiler.
    TokenStream::from(quote!(#output))
}
<|endoftext|>


<commit_before>#![crate_type = "dylib"]
#![feature(type_ascription)]

extern crate intercom;
use intercom::*;
extern crate winapi;

// Declare available COM classes.
com_library!(class HelloWorld);

#[com_interface]
trait IHelloWorld
{
    fn get_hello(&self) -> ComResult<String>;
}

#[com_class(clsid = "{25ccb3f6-b782-4b2d-933e-54ab447da0aa}", IHelloWorld)]
#[derive(Default)]
pub struct HelloWorld {}

impl HelloWorld
{
    pub fn new() -> HelloWorld
    {
        HelloWorld {}
    }
}

#[com_impl]
impl IHelloWorld for HelloWorld
{
    fn get_hello(&self) -> ComResult<String>
    {
        Ok("Hello World!".to_string())
    }
}

#[test]
fn hello_world_returns_hello_world()
{
    let hello = HelloWorld::new();
    assert_eq!(hello.get_hello().unwrap(), "Hello World!");
}
<commit_msg>Make multilib stable compatible<commit_after>#![crate_type = "dylib"]

extern crate intercom;
use intercom::*;
extern crate winapi;

// Declare available COM classes.
com_library!(class HelloWorld);

#[com_interface]
trait IHelloWorld
{
    fn get_hello(&self) -> ComResult<String>;
}

#[com_class(clsid = "{25ccb3f6-b782-4b2d-933e-54ab447da0aa}", IHelloWorld)]
#[derive(Default)]
pub struct HelloWorld {}

impl HelloWorld
{
    pub fn new() -> HelloWorld
    {
        HelloWorld {}
    }
}

#[com_impl]
impl IHelloWorld for HelloWorld
{
    fn get_hello(&self) -> ComResult<String>
    {
        Ok("Hello World!".to_string())
    }
}

#[test]
fn hello_world_returns_hello_world()
{
    let hello = HelloWorld::new();
    assert_eq!(hello.get_hello().unwrap(), "Hello World!");
}
<|endoftext|>


<commit_before>use std::ops::Not;

use serde::ser::{Serialize, Serializer};

use types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ReplyMarkup {
    InlineKeyboardMarkup(InlineKeyboardMarkup),
    ReplyKeyboardMarkup(ReplyKeyboardMarkup),
    ReplyKeyboardRemove(ReplyKeyboardRemove),
    ForceReply(ForceReply),
}

impl From<InlineKeyboardMarkup> for ReplyMarkup {
    fn from(value: InlineKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardMarkup> for ReplyMarkup {
    fn from(value: ReplyKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardRemove> for ReplyMarkup {
    fn from(value: ReplyKeyboardRemove) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardRemove(value)
    }
}

impl From<ForceReply> for ReplyMarkup {
    fn from(value: ForceReply) -> ReplyMarkup {
        ReplyMarkup::ForceReply(value)
    }
}

impl Serialize for ReplyMarkup {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        match *self {
            ReplyMarkup::InlineKeyboardMarkup(ref value) => value.serialize(serializer),
            ReplyMarkup::ReplyKeyboardMarkup(ref value) => value.serialize(serializer),
            ReplyMarkup::ReplyKeyboardRemove(ref value) => value.serialize(serializer),
            ReplyMarkup::ForceReply(ref value) => value.serialize(serializer),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardMarkup {
    pub keyboard: Vec<Vec<KeyboardButton>>,
    #[serde(skip_serializing_if = "Not::not")]
    pub resize_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    pub one_time_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool
}

impl ReplyKeyboardMarkup {
    pub fn new() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: false,
            one_time_keyboard: false,
            selective: false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct KeyboardButton {
    pub text: String,
    #[serde(skip_serializing_if = "Not::not")]
    pub request_contact: bool,
    #[serde(skip_serializing_if = "Not::not")]
    pub request_location: bool,
}

impl<'a> From<&'a str> for KeyboardButton {
    fn from(value: &'a str) -> KeyboardButton {
        KeyboardButton {
            text: value.to_string(),
            request_contact: false,
            request_location: false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardRemove {
    pub remove_keyboard: True,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardMarkup {
    pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InlineKeyboardButton {
    pub text: String,
    pub kind: InlineKeyboardButtonKind
}

impl Serialize for InlineKeyboardButton {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        use self::InlineKeyboardButtonKind::*;

        let mut raw = InlineKeyboardButtonRaw {
            text: &self.text,
            url: None,
            callback_data: None,
            switch_inline_query: None,
            switch_inline_query_current_chat: None,
//            callback_game: None,
        };

        match self.kind {
            Url(ref data) => raw.url = Some(data),
            CallbackData(ref data) => raw.callback_data = Some(data),
            SwitchInlineQuery(ref data) => raw.switch_inline_query = Some(data),
            SwitchInlineQueryCurrentChat(ref data) => raw.switch_inline_query_current_chat = Some(data),
//            CallbackGame(ref data) => raw.callback_game = Some(data),
        }

        Serialize::serialize(&raw, serializer)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum InlineKeyboardButtonKind {
    Url(String), // TODO(knsd): Url?
    CallbackData(String),  //TODO(knsd) Validate size?
    SwitchInlineQuery(String),
    SwitchInlineQueryCurrentChat(String),
//    CallbackGame(CallbackGame),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
struct InlineKeyboardButtonRaw<'a> {
    text: &'a str,
    #[serde(skip_serializing_if = "Option::is_none")]
    url: Option<&'a str>, // TODO(knsd): Url?
    #[serde(skip_serializing_if = "Option::is_none")]
    callback_data: Option<&'a str>, //TODO(knsd) Validate size?
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query: Option<&'a str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query_current_chat: Option<&'a str>,
//    callback_game: Option<CallbackGame>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ForceReply {
    pub force_reply: True,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool,
}
<commit_msg>feat: hide KeyboardButton's fields<commit_after>use std::ops::Not;

use serde::ser::{Serialize, Serializer};

use types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum ReplyMarkup {
    InlineKeyboardMarkup(InlineKeyboardMarkup),
    ReplyKeyboardMarkup(ReplyKeyboardMarkup),
    ReplyKeyboardRemove(ReplyKeyboardRemove),
    ForceReply(ForceReply),
}

impl From<InlineKeyboardMarkup> for ReplyMarkup {
    fn from(value: InlineKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::InlineKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardMarkup> for ReplyMarkup {
    fn from(value: ReplyKeyboardMarkup) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardMarkup(value)
    }
}

impl From<ReplyKeyboardRemove> for ReplyMarkup {
    fn from(value: ReplyKeyboardRemove) -> ReplyMarkup {
        ReplyMarkup::ReplyKeyboardRemove(value)
    }
}

impl From<ForceReply> for ReplyMarkup {
    fn from(value: ForceReply) -> ReplyMarkup {
        ReplyMarkup::ForceReply(value)
    }
}

impl Serialize for ReplyMarkup {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        match *self {
            ReplyMarkup::InlineKeyboardMarkup(ref value) => value.serialize(serializer),
            ReplyMarkup::ReplyKeyboardMarkup(ref value) => value.serialize(serializer),
            ReplyMarkup::ReplyKeyboardRemove(ref value) => value.serialize(serializer),
            ReplyMarkup::ForceReply(ref value) => value.serialize(serializer),
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardMarkup {
    pub keyboard: Vec<Vec<KeyboardButton>>,
    #[serde(skip_serializing_if = "Not::not")]
    pub resize_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    pub one_time_keyboard: bool,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool
}

impl ReplyKeyboardMarkup {
    pub fn new() -> Self {
        ReplyKeyboardMarkup {
            keyboard: Vec::new(),
            resize_keyboard: false,
            one_time_keyboard: false,
            selective: false,
        }
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct KeyboardButton {
    text: String,
    #[serde(skip_serializing_if = "Not::not")]
    request_contact: bool,
    #[serde(skip_serializing_if = "Not::not")]
    request_location: bool,
}

impl KeyboardButton {
    pub fn new(text: &str) -> Self {
        Self {
            text: text.to_string(),
            request_contact: false,
            request_location: false,
        }
    }

    pub fn request_contact(mut self) -> Self {
        self.request_contact = true;
        self
    }

    pub fn request_location(mut self) -> Self {
        self.request_location = true;
        self
    }
}

impl<'a> From<&'a str> for KeyboardButton {
    fn from(value: &'a str) -> KeyboardButton {
        KeyboardButton::new(value)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ReplyKeyboardRemove {
    pub remove_keyboard: True,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct InlineKeyboardMarkup {
    pub inline_keyboard: Vec<Vec<InlineKeyboardButton>>
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InlineKeyboardButton {
    pub text: String,
    pub kind: InlineKeyboardButtonKind
}

impl Serialize for InlineKeyboardButton {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
        use self::InlineKeyboardButtonKind::*;

        let mut raw = InlineKeyboardButtonRaw {
            text: &self.text,
            url: None,
            callback_data: None,
            switch_inline_query: None,
            switch_inline_query_current_chat: None,
//            callback_game: None,
        };

        match self.kind {
            Url(ref data) => raw.url = Some(data),
            CallbackData(ref data) => raw.callback_data = Some(data),
            SwitchInlineQuery(ref data) => raw.switch_inline_query = Some(data),
            SwitchInlineQueryCurrentChat(ref data) => raw.switch_inline_query_current_chat = Some(data),
//            CallbackGame(ref data) => raw.callback_game = Some(data),
        }

        Serialize::serialize(&raw, serializer)
    }
}

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum InlineKeyboardButtonKind {
    Url(String), // TODO(knsd): Url?
    CallbackData(String),  //TODO(knsd) Validate size?
    SwitchInlineQuery(String),
    SwitchInlineQueryCurrentChat(String),
//    CallbackGame(CallbackGame),
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
struct InlineKeyboardButtonRaw<'a> {
    text: &'a str,
    #[serde(skip_serializing_if = "Option::is_none")]
    url: Option<&'a str>, // TODO(knsd): Url?
    #[serde(skip_serializing_if = "Option::is_none")]
    callback_data: Option<&'a str>, //TODO(knsd) Validate size?
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query: Option<&'a str>,
    #[serde(skip_serializing_if = "Option::is_none")]
    switch_inline_query_current_chat: Option<&'a str>,
//    callback_game: Option<CallbackGame>,
}

#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize)]
pub struct ForceReply {
    pub force_reply: True,
    #[serde(skip_serializing_if = "Not::not")]
    pub selective: bool,
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting, Versioning};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use model::v1::*;
use model;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String
}

impl ApiEndPoint {

pub fn root(&self) -> rustless::Api {
    Api::build(|api| {
        api.get("", |endpoint| {
            endpoint.handle(|client, _params| {
                let desc = EndPoint { description: "autocomplete service".to_string() };
                render(client, desc)
            })
        });

        api.error_formatter(|error, _media| {
            let err = if error.is::<rustless::errors::Validation>() {
                let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                // TODO better message, we shouldn't use {:?} but access the `path`
                // and `detail` of all errrors in val_err.reason
                CustomError {
                    short: "validation error".to_string(),
                    long: format!("invalid arguments {:?}", val_err.reason),
                }
            } else {
                CustomError {
                    short: "bad_request".to_string(),
                    long: format!("bad request, error: {}", error),
                }
            };
            let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                    Box::new(serde_json::to_string(&err).unwrap()));
            resp.set_json_content_type();
            Some(resp)
        });
        api.mount(self.v1());
    })
}

fn v1(&self) -> rustless::Api {
    Api::build(|api| {
        api.version("v1", Versioning::Path);

        api.get("", |endpoint| {
            endpoint.desc("api interface version v1");
            endpoint.handle(|client, _params| {
                render(client,
                       V1Reponse::Response { description: "api version 1".to_string() })
            })
        });
        api.mount(self.status());
        api.mount(self.autocomplete());
    })
}

fn status(&self) -> rustless::Api {
    Api::build(|api| {
        api.get("status", |endpoint| {
            endpoint.handle(|client, _params| {
                let status = Status {
                    version: "14".to_string(),
                    status: "good".to_string(),
                };
                render(client, status)
            })
        });
    })
}

fn autocomplete(&self) -> rustless::Namespace {
    rustless::Namespace::build("autocomplete", |ns| {
        ns.get("", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("q", json_dsl::string());

                params.opt("lon", |lon| {
                    lon.coerce(json_dsl::f64());
                    lon.validate_with(|val, path| {
                        check_bound(val, path, -180f64, 180f64, "lon is not a valid longitude")
                    });
                });

                params.opt("lat", |lat| {
                    lat.coerce(json_dsl::f64());
                    lat.validate_with(|val, path| {
                        check_bound(val, path, -90f64, 90f64, "lat is not a valid latitude")
                    });
                });
                params.validate_with(|val, path| {
                    // if we have a lat we should have a lon (and the opposite)
                    if let json::Json::Object(ref obj) = *val {
                        let has_lon = obj.get("lon").is_some();
                        let has_lat = obj.get("lat").is_some();
                        if has_lon ^ has_lat {
                            Err(vec![Box::new(json_dsl::errors::WrongValue {
                                         path: path.to_string(),
                                         detail: Some("you need to provide a lon AND a lat if \
                                                       you provide one of them"
                                                          .to_string()),
                                     })])
                        } else {
                            Ok(())
                        }
                    } else {
                        panic!("should never happen, already checked");
                    }
                });
            });
            let cnx = self.es_cnx_string.clone();
            endpoint.handle(move |client, params| {
                let q = params.find("q").unwrap().as_string().unwrap().to_string();
                let lon = params.find("lon").and_then(|p| p.as_f64());
                let lat = params.find("lat").and_then(|p| p.as_f64());
                // we have already checked that if there is a lon, lat is not None so we can unwrap
                let coord = lon.and_then(|lon| {
                    Some(model::Coord {
                        lon: lon,
                        lat: lat.unwrap(),
                    })
                });
                let model_autocomplete = query::autocomplete(q, coord, &cnx);

                let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                render(client, response)
            })
        });
    })
}
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        panic!("should never happen, already checked");
    }
}
<commit_msg>remove the API versioning<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io
use rustless;
use serde;
use serde_json;
use rustc_serialize::json;
use rustless::server::status;
use rustless::{Api, Nesting};
use valico::json_dsl;
use valico::common::error as valico_error;
use super::query;
use model::v1::*;
use model;

fn render<T>(mut client: rustless::Client,
             obj: T)
             -> Result<rustless::Client, rustless::ErrorResponse>
    where T: serde::Serialize
{
    client.set_json_content_type();
    client.text(serde_json::to_string(&obj).unwrap())
}

pub struct ApiEndPoint {
    pub es_cnx_string: String
}

impl ApiEndPoint {

pub fn root(&self) -> rustless::Api {
    Api::build(|api| {
        api.get("", |endpoint| {
            endpoint.handle(|client, _params| {
                let desc = EndPoint { description: "autocomplete service".to_string() };
                render(client, desc)
            })
        });

        api.error_formatter(|error, _media| {
            let err = if error.is::<rustless::errors::Validation>() {
                let val_err = error.downcast::<rustless::errors::Validation>().unwrap();
                // TODO better message, we shouldn't use {:?} but access the `path`
                // and `detail` of all errrors in val_err.reason
                CustomError {
                    short: "validation error".to_string(),
                    long: format!("invalid arguments {:?}", val_err.reason),
                }
            } else {
                CustomError {
                    short: "bad_request".to_string(),
                    long: format!("bad request, error: {}", error),
                }
            };
            let mut resp = rustless::Response::from(status::StatusCode::BadRequest,
                                                    Box::new(serde_json::to_string(&err).unwrap()));
            resp.set_json_content_type();
            Some(resp)
        });
        api.mount(self.v1());
    })
}

fn v1(&self) -> rustless::Api {
    Api::build(|api| {
        api.mount(self.status());
        api.mount(self.autocomplete());
    })
}

fn status(&self) -> rustless::Api {
    Api::build(|api| {
        api.get("status", |endpoint| {
            endpoint.handle(|client, _params| {
                let status = Status {
                    version: "14".to_string(),
                    status: "good".to_string(),
                };
                render(client, status)
            })
        });
    })
}

fn autocomplete(&self) -> rustless::Api {
    Api::build(|api| {
        api.get("autocomplete", |endpoint| {
            endpoint.params(|params| {
                params.req_typed("q", json_dsl::string());

                params.opt("lon", |lon| {
                    lon.coerce(json_dsl::f64());
                    lon.validate_with(|val, path| {
                        check_bound(val, path, -180f64, 180f64, "lon is not a valid longitude")
                    });
                });

                params.opt("lat", |lat| {
                    lat.coerce(json_dsl::f64());
                    lat.validate_with(|val, path| {
                        check_bound(val, path, -90f64, 90f64, "lat is not a valid latitude")
                    });
                });
                params.validate_with(|val, path| {
                    // if we have a lat we should have a lon (and the opposite)
                    if let json::Json::Object(ref obj) = *val {
                        let has_lon = obj.get("lon").is_some();
                        let has_lat = obj.get("lat").is_some();
                        if has_lon ^ has_lat {
                            Err(vec![Box::new(json_dsl::errors::WrongValue {
                                         path: path.to_string(),
                                         detail: Some("you need to provide a lon AND a lat if \
                                                       you provide one of them"
                                                          .to_string()),
                                     })])
                        } else {
                            Ok(())
                        }
                    } else {
                        panic!("should never happen, already checked");
                    }
                });
            });
            let cnx = self.es_cnx_string.clone();
            endpoint.handle(move |client, params| {
                let q = params.find("q").unwrap().as_string().unwrap().to_string();
                let lon = params.find("lon").and_then(|p| p.as_f64());
                let lat = params.find("lat").and_then(|p| p.as_f64());
                // we have already checked that if there is a lon, lat is not None so we can unwrap
                let coord = lon.and_then(|lon| {
                    Some(model::Coord {
                        lon: lon,
                        lat: lat.unwrap(),
                    })
                });
                let model_autocomplete = query::autocomplete(q, coord, &cnx);

                let response = model::v1::AutocompleteResponse::from(model_autocomplete);
                render(client, response)
            })
        });
    })
}
}

fn check_bound(val: &json::Json,
               path: &str,
               min: f64,
               max: f64,
               error_msg: &str)
               -> Result<(), valico_error::ValicoErrors> {
    if let json::Json::F64(lon) = *val {
        if min <= lon && lon <= max {
            Ok(())
        } else {
            Err(vec![Box::new(json_dsl::errors::WrongValue {
                         path: path.to_string(),
                         detail: Some(error_msg.to_string()),
                     })])
        }
    } else {
        panic!("should never happen, already checked");
    }
}
<|endoftext|>


<commit_before>use std::thread;

use tokio::sync::{mpsc, oneshot};
use tokio::runtime::Handle;
use serde::{Serialize, de::DeserializeOwned};
use ipc_channel::ipc::{IpcReceiver, IpcError};

/// Asynchronous wrapper over `IpcReceiver` that yields `Result<T, IpcError>`.
///
/// The `ipc_channel::ipc::IpcReceiver` type can't be shared between threads, so it can't be used
/// from `tokio::task::spawn_blocking`. This type uses a separate thread to manage receiving
/// values asynchronously. It implements the `Stream` trait, which allows it to be polled over
/// and over again for each received value.
#[derive(Debug)]
pub struct AsyncIpcReceiver<T: Serialize + DeserializeOwned + Send + 'static> {
    /// Each AsyncIpcReceiver::recv() call sends a oneshot channel that collects the result when
    /// it is ready. This ensures proper ordering even if the future from `recv` is dropped and has
    /// the added benefit of allowing `recv()` to take `&self` instead of `&mut self`. The second
    /// benefit allows some operations that use this struct to be lock-free.
    channel_sender: mpsc::UnboundedSender<oneshot::Sender<Result<T, IpcError>>>,
}

impl<T: Serialize + DeserializeOwned + Send + 'static> AsyncIpcReceiver<T> {
    pub fn new(ipc_receiver: IpcReceiver<T>) -> Self {
        let (channel_sender, mut receiver) = mpsc::unbounded_channel();

        let handle = Handle::current();
        thread::spawn(move || {
            loop {
                // This loop continues even if this call produces an error saying that IPC was
                // disconnected. This allows callers of `recv` to detect the disconnection.
                let next_value = ipc_receiver.recv();

                let value_sender: oneshot::Sender<_> = match handle.block_on(receiver.recv()) {
                    Some(value_sender) => value_sender,
                    // The sender for this channel only drops when the main thread has stopped,
                    // so it's pretty safe to stop here
                    None => break,
                };
                // The send may fail if the future was dropped, but that is not a fatal error
                value_sender.send(next_value).unwrap_or(());
            }
        });

        Self {channel_sender}
    }

    /// Receives the next value from the IPC receiver
    pub async fn recv(&self) -> Result<T, IpcError> {
        let (sender, receiver) = oneshot::channel();

        // The channels should never return errors because the spawned thread runs as long as this
        // thread is still running
        self.channel_sender.send(sender)
            .unwrap_or_else(|_| panic!("bug: thread managing IPC receiver terminated before main thread"));

        receiver.await
            .expect("bug: thread managing IPC receiver terminated before main thread")
    }
}
<commit_msg>AsyncIpcReceiver does not implement Stream<commit_after>use std::thread;

use tokio::sync::{mpsc, oneshot};
use tokio::runtime::Handle;
use serde::{Serialize, de::DeserializeOwned};
use ipc_channel::ipc::{IpcReceiver, IpcError};

/// Asynchronous wrapper over `IpcReceiver` that yields `Result<T, IpcError>`.
///
/// The `ipc_channel::ipc::IpcReceiver` type can't be shared between threads, so it can't be used
/// from `tokio::task::spawn_blocking`. This type uses a separate thread to manage receiving
/// values asynchronously.
#[derive(Debug)]
pub struct AsyncIpcReceiver<T: Serialize + DeserializeOwned + Send + 'static> {
    /// Each AsyncIpcReceiver::recv() call sends a oneshot channel that collects the result when
    /// it is ready. This ensures proper ordering even if the future from `recv` is dropped and has
    /// the added benefit of allowing `recv()` to take `&self` instead of `&mut self`. The second
    /// benefit allows some operations that use this struct to be lock-free.
    channel_sender: mpsc::UnboundedSender<oneshot::Sender<Result<T, IpcError>>>,
}

impl<T: Serialize + DeserializeOwned + Send + 'static> AsyncIpcReceiver<T> {
    pub fn new(ipc_receiver: IpcReceiver<T>) -> Self {
        let (channel_sender, mut receiver) = mpsc::unbounded_channel();

        let handle = Handle::current();
        thread::spawn(move || {
            loop {
                // This loop continues even if this call produces an error saying that IPC was
                // disconnected. This allows callers of `recv` to detect the disconnection.
                let next_value = ipc_receiver.recv();

                let value_sender: oneshot::Sender<_> = match handle.block_on(receiver.recv()) {
                    Some(value_sender) => value_sender,
                    // The sender for this channel only drops when the main thread has stopped,
                    // so it's pretty safe to stop here
                    None => break,
                };
                // The send may fail if the future was dropped, but that is not a fatal error
                value_sender.send(next_value).unwrap_or(());
            }
        });

        Self {channel_sender}
    }

    /// Receives the next value from the IPC receiver
    pub async fn recv(&self) -> Result<T, IpcError> {
        let (sender, receiver) = oneshot::channel();

        // The channels should never return errors because the spawned thread runs as long as this
        // thread is still running
        self.channel_sender.send(sender)
            .unwrap_or_else(|_| panic!("bug: thread managing IPC receiver terminated before main thread"));

        receiver.await
            .expect("bug: thread managing IPC receiver terminated before main thread")
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;

use light_arena::Allocator;

use bsdf::{BxDF, BxDFHolder, FourierBSDF, FourierBSDFTable, BSDF};
use interaction::SurfaceInteraction;
use material::{Material, TransportMode};
use paramset::TextureParams;
use texture::TextureFloat;

#[derive(Debug)]
pub struct FourierMaterial {
    bsdf_table: Box<FourierBSDFTable>,
    bump_map: Option<Arc<TextureFloat>>,
}

impl FourierMaterial {
    pub fn create(mp: &TextureParams) -> Arc<Material> {
        let bump_map = mp.get_float_texture_or_none("bumpmap");
        let filename = mp.find_filename("bsdffile", "");
        let bsdf_table = Box::new(FourierBSDFTable::read(filename).unwrap()); // TODO error
        Arc::new(FourierMaterial {
            bsdf_table,
            bump_map,
        })
    }
}

impl Material for FourierMaterial {
    fn compute_scattering_functions<'a, 'b>(
        &self,
        si: &mut SurfaceInteraction<'a, 'b>,
        mode: TransportMode,
        allow_multiple_lobes: bool,
        arena: &'b Allocator,
    ) {
        let mut bxdfs = BxDFHolder::new(arena);

        if let Some(ref bump) = self.bump_map {
            super::bump(bump, si);
        }
        bxdfs.add(arena <- FourierBSDF::new(&self.bsdf_table, mode));
        let bsdf = BSDF::new(si, 1.0, bxdfs.into_slice());
        si.bsdf = Some(Arc::new(bsdf));
    }
}
<commit_msg>Remove some warnings<commit_after>use std::sync::Arc;

use light_arena::Allocator;

use bsdf::{BxDFHolder, FourierBSDF, FourierBSDFTable, BSDF};
use interaction::SurfaceInteraction;
use material::{Material, TransportMode};
use paramset::TextureParams;
use texture::TextureFloat;

#[derive(Debug)]
pub struct FourierMaterial {
    bsdf_table: Box<FourierBSDFTable>,
    bump_map: Option<Arc<TextureFloat>>,
}

impl FourierMaterial {
    pub fn create(mp: &TextureParams) -> Arc<Material> {
        let bump_map = mp.get_float_texture_or_none("bumpmap");
        let filename = mp.find_filename("bsdffile", "");
        let bsdf_table = Box::new(FourierBSDFTable::read(filename).unwrap()); // TODO error
        Arc::new(FourierMaterial {
            bsdf_table,
            bump_map,
        })
    }
}

impl Material for FourierMaterial {
    fn compute_scattering_functions<'a, 'b>(
        &self,
        si: &mut SurfaceInteraction<'a, 'b>,
        mode: TransportMode,
        _allow_multiple_lobes: bool,
        arena: &'b Allocator,
    ) {
        let mut bxdfs = BxDFHolder::new(arena);

        if let Some(ref bump) = self.bump_map {
            super::bump(bump, si);
        }
        bxdfs.add(arena <- FourierBSDF::new(&self.bsdf_table, mode));
        let bsdf = BSDF::new(si, 1.0, bxdfs.into_slice());
        si.bsdf = Some(Arc::new(bsdf));
    }
}
<|endoftext|>