<commit_before>use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::{Command, Output, Stdio};

use failure::Fail;
use jobserver::Client;
use shell_escape::escape;

use crate::util::{process_error, CargoResult, CargoResultExt, read2};

/// A builder object for an external process, similar to `std::process::Command`.
#[derive(Clone, Debug)]
pub struct ProcessBuilder {
    /// The program to execute.
    program: OsString,
    /// A list of arguments to pass to the program.
    args: Vec<OsString>,
    /// Any environment variables that should be set for the program.
    env: HashMap<String, Option<OsString>>,
    /// Which directory to run the program from.
    cwd: Option<OsString>,
    /// The `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    jobserver: Option<Client>,
}

impl fmt::Display for ProcessBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "`{}", self.program.to_string_lossy())?;

        for arg in &self.args {
            write!(f, " {}", escape(arg.to_string_lossy()))?;
        }

        write!(f, "`")
    }
}

impl ProcessBuilder {
    /// (chainable) Set the executable for the process.
    pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
        self.program = program.as_ref().to_os_string();
        self
    }

    /// (chainable) Add an arg to the args list.
    pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
        self.args.push(arg.as_ref().to_os_string());
        self
    }

    /// (chainable) Add many args to the args list.
    pub fn args<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args
            .extend(arguments.iter().map(|t| t.as_ref().to_os_string()));
        self
    }

    /// (chainable) Replace args with new args list
    pub fn args_replace<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args = arguments
            .iter()
            .map(|t| t.as_ref().to_os_string())
            .collect();
        self
    }

    /// (chainable) Set the current working directory of the process
    pub fn cwd<T: AsRef<OsStr>>(&mut self, path: T) -> &mut ProcessBuilder {
        self.cwd = Some(path.as_ref().to_os_string());
        self
    }

    /// (chainable) Set an environment variable for the process.
    pub fn env<T: AsRef<OsStr>>(&mut self, key: &str, val: T) -> &mut ProcessBuilder {
        self.env
            .insert(key.to_string(), Some(val.as_ref().to_os_string()));
        self
    }

    /// (chainable) Unset an environment variable for the process.
    pub fn env_remove(&mut self, key: &str) -> &mut ProcessBuilder {
        self.env.insert(key.to_string(), None);
        self
    }

    /// Get the executable name.
    pub fn get_program(&self) -> &OsString {
        &self.program
    }

    /// Get the program arguments
    pub fn get_args(&self) -> &[OsString] {
        &self.args
    }

    /// Get the current working directory for the process
    pub fn get_cwd(&self) -> Option<&Path> {
        self.cwd.as_ref().map(Path::new)
    }

    /// Get an environment variable as the process will see it (will inherit from environment
    /// unless explicitally unset).
    pub fn get_env(&self, var: &str) -> Option<OsString> {
        self.env
            .get(var)
            .cloned()
            .or_else(|| Some(env::var_os(var)))
            .and_then(|s| s)
    }

    /// Get all environment variables explicitly set or unset for the process (not inherited
    /// vars).
    pub fn get_envs(&self) -> &HashMap<String, Option<OsString>> {
        &self.env
    }

    /// Set the `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    pub fn inherit_jobserver(&mut self, jobserver: &Client) -> &mut Self {
        self.jobserver = Some(jobserver.clone());
        self
    }

    /// Run the process, waiting for completion, and mapping non-success exit codes to an error.
    pub fn exec(&self) -> CargoResult<()> {
        let mut command = self.build_command();
        let exit = command.status().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if exit.success() {
            Ok(())
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(exit),
                None,
            ).into())
        }
    }

    /// Replaces the current process with the target process.
    ///
    /// On Unix, this executes the process using the unix syscall `execvp`, which will block
    /// this process, and will only return if there is an error.
    ///
    /// On Windows this isn't technically possible. Instead we emulate it to the best of our
    /// ability. One aspect we fix here is that we specify a handler for the ctrl-c handler.
    /// In doing so (and by effectively ignoring it) we should emulate proxying ctrl-c
    /// handling to the application at hand, which will either terminate or handle it itself.
    /// According to microsoft's documentation at:
    /// https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals
    /// the ctrl-c signal is sent to all processes attached to a terminal, which should
    /// include our child process. If the child terminates then we'll reap them in Cargo
    /// pretty quickly, and if the child handles the signal then we won't terminate
    /// (and we shouldn't!) until the process itself later exits.
    pub fn exec_replace(&self) -> CargoResult<()> {
        imp::exec_replace(self)
    }

    /// Execute the process, returning the stdio output, or an error if non-zero exit status.
    pub fn exec_with_output(&self) -> CargoResult<Output> {
        let mut command = self.build_command();

        let output = command.output().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if output.status.success() {
            Ok(output)
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(output.status),
                Some(&output),
            ).into())
        }
    }

    /// Execute a command, passing each line of stdout and stderr to the supplied callbacks, which
    /// can mutate the string data.
    ///
    /// If any invocations of these function return an error, it will be propagated.
    ///
    /// Optionally, output can be passed to errors using `print_output`
    pub fn exec_with_streaming(
        &self,
        on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
        on_stderr_line: &mut FnMut(&str) -> CargoResult<()>,
        capture_output: bool,
    ) -> CargoResult<Output> {
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();

        let mut cmd = self.build_command();
        cmd.stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .stdin(Stdio::null());

        let mut callback_error = None;
        let status = (|| {
            let mut child = cmd.spawn()?;
            let out = child.stdout.take().unwrap();
            let err = child.stderr.take().unwrap();
            read2(out, err, &mut |is_out, data, eof| {
                let idx = if eof {
                    data.len()
                } else {
                    match data.iter().rposition(|b| *b == b'\n') {
                        Some(i) => i + 1,
                        None => return,
                    }
                };
                { // scope for new_lines
                    let new_lines = if capture_output {
                        let dst = if is_out { &mut stdout } else { &mut stderr };
                        let start = dst.len();
                        let data = data.drain(..idx);
                        dst.extend(data);
                        &dst[start..]
                    } else {
                        &data[..idx]
                    };
                    for line in String::from_utf8_lossy(new_lines).lines() {
                        if callback_error.is_some() {
                            break;
                        }
                        let callback_result = if is_out {
                            on_stdout_line(line)
                        } else {
                            on_stderr_line(line)
                        };
                        if let Err(e) = callback_result {
                            callback_error = Some(e);
                        }
                    }
                }
                if !capture_output {
                    data.drain(..idx);
                }
            })?;
            child.wait()
        })()
            .chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;
        let output = Output {
            stdout,
            stderr,
            status,
        };

        {
            let to_print = if capture_output { Some(&output) } else { None };
            if let Some(e) = callback_error {
                let cx = process_error(
                    &format!("failed to parse process output: {}", self),
                    Some(output.status),
                    to_print,
                );
                return Err(cx.context(e).into());
            } else if !output.status.success() {
                return Err(process_error(
                    &format!("process didn't exit successfully: {}", self),
                    Some(output.status),
                    to_print,
                )
                .into());
            }
        }

        Ok(output)
    }

    /// Converts ProcessBuilder into a `std::process::Command`, and handles the jobserver if
    /// present.
    pub fn build_command(&self) -> Command {
        let mut command = Command::new(&self.program);
        if let Some(cwd) = self.get_cwd() {
            command.current_dir(cwd);
        }
        for arg in &self.args {
            command.arg(arg);
        }
        for (k, v) in &self.env {
            match *v {
                Some(ref v) => {
                    command.env(k, v);
                }
                None => {
                    command.env_remove(k);
                }
            }
        }
        if let Some(ref c) = self.jobserver {
            c.configure(&mut command);
        }
        command
    }
}

/// A helper function to create a `ProcessBuilder`.
pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.as_ref().to_os_string(),
        args: Vec::new(),
        cwd: None,
        env: HashMap::new(),
        jobserver: None,
    }
}

#[cfg(unix)]
mod imp {
    use crate::CargoResult;
    use std::os::unix::process::CommandExt;
    use crate::util::{process_error, ProcessBuilder};

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        let mut command = process_builder.build_command();
        let error = command.exec();
        Err(crate::util::CargoError::from(error)
            .context(process_error(
                &format!("could not execute process {}", process_builder),
                None,
                None,
            ))
            .into())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use CargoResult;
    use util::{process_error, ProcessBuilder};
    use self::winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
    use self::winapi::um::consoleapi::SetConsoleCtrlHandler;

    unsafe extern "system" fn ctrlc_handler(_: DWORD) -> BOOL {
        // Do nothing. Let the child process handle it.
        TRUE
    }

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        unsafe {
            if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {
                return Err(process_error(
                    "Could not set Ctrl-C handler.",
                    None,
                    None).into());
            }
        }

        // Just exec the process as normal.
        process_builder.exec()
    }
}
<commit_msg>Fix Rust 2018 code in Windows code<commit_after>use std::collections::HashMap;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::{Command, Output, Stdio};

use failure::Fail;
use jobserver::Client;
use shell_escape::escape;

use crate::util::{process_error, CargoResult, CargoResultExt, read2};

/// A builder object for an external process, similar to `std::process::Command`.
#[derive(Clone, Debug)]
pub struct ProcessBuilder {
    /// The program to execute.
    program: OsString,
    /// A list of arguments to pass to the program.
    args: Vec<OsString>,
    /// Any environment variables that should be set for the program.
    env: HashMap<String, Option<OsString>>,
    /// Which directory to run the program from.
    cwd: Option<OsString>,
    /// The `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    jobserver: Option<Client>,
}

impl fmt::Display for ProcessBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "`{}", self.program.to_string_lossy())?;

        for arg in &self.args {
            write!(f, " {}", escape(arg.to_string_lossy()))?;
        }

        write!(f, "`")
    }
}

impl ProcessBuilder {
    /// (chainable) Set the executable for the process.
    pub fn program<T: AsRef<OsStr>>(&mut self, program: T) -> &mut ProcessBuilder {
        self.program = program.as_ref().to_os_string();
        self
    }

    /// (chainable) Add an arg to the args list.
    pub fn arg<T: AsRef<OsStr>>(&mut self, arg: T) -> &mut ProcessBuilder {
        self.args.push(arg.as_ref().to_os_string());
        self
    }

    /// (chainable) Add many args to the args list.
    pub fn args<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args
            .extend(arguments.iter().map(|t| t.as_ref().to_os_string()));
        self
    }

    /// (chainable) Replace args with new args list
    pub fn args_replace<T: AsRef<OsStr>>(&mut self, arguments: &[T]) -> &mut ProcessBuilder {
        self.args = arguments
            .iter()
            .map(|t| t.as_ref().to_os_string())
            .collect();
        self
    }

    /// (chainable) Set the current working directory of the process
    pub fn cwd<T: AsRef<OsStr>>(&mut self, path: T) -> &mut ProcessBuilder {
        self.cwd = Some(path.as_ref().to_os_string());
        self
    }

    /// (chainable) Set an environment variable for the process.
    pub fn env<T: AsRef<OsStr>>(&mut self, key: &str, val: T) -> &mut ProcessBuilder {
        self.env
            .insert(key.to_string(), Some(val.as_ref().to_os_string()));
        self
    }

    /// (chainable) Unset an environment variable for the process.
    pub fn env_remove(&mut self, key: &str) -> &mut ProcessBuilder {
        self.env.insert(key.to_string(), None);
        self
    }

    /// Get the executable name.
    pub fn get_program(&self) -> &OsString {
        &self.program
    }

    /// Get the program arguments
    pub fn get_args(&self) -> &[OsString] {
        &self.args
    }

    /// Get the current working directory for the process
    pub fn get_cwd(&self) -> Option<&Path> {
        self.cwd.as_ref().map(Path::new)
    }

    /// Get an environment variable as the process will see it (will inherit from environment
    /// unless explicitally unset).
    pub fn get_env(&self, var: &str) -> Option<OsString> {
        self.env
            .get(var)
            .cloned()
            .or_else(|| Some(env::var_os(var)))
            .and_then(|s| s)
    }

    /// Get all environment variables explicitly set or unset for the process (not inherited
    /// vars).
    pub fn get_envs(&self) -> &HashMap<String, Option<OsString>> {
        &self.env
    }

    /// Set the `make` jobserver. See the [jobserver crate][jobserver_docs] for
    /// more information.
    ///
    /// [jobserver_docs]: https://docs.rs/jobserver/0.1.6/jobserver/
    pub fn inherit_jobserver(&mut self, jobserver: &Client) -> &mut Self {
        self.jobserver = Some(jobserver.clone());
        self
    }

    /// Run the process, waiting for completion, and mapping non-success exit codes to an error.
    pub fn exec(&self) -> CargoResult<()> {
        let mut command = self.build_command();
        let exit = command.status().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if exit.success() {
            Ok(())
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(exit),
                None,
            ).into())
        }
    }

    /// Replaces the current process with the target process.
    ///
    /// On Unix, this executes the process using the unix syscall `execvp`, which will block
    /// this process, and will only return if there is an error.
    ///
    /// On Windows this isn't technically possible. Instead we emulate it to the best of our
    /// ability. One aspect we fix here is that we specify a handler for the ctrl-c handler.
    /// In doing so (and by effectively ignoring it) we should emulate proxying ctrl-c
    /// handling to the application at hand, which will either terminate or handle it itself.
    /// According to microsoft's documentation at:
    /// https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals
    /// the ctrl-c signal is sent to all processes attached to a terminal, which should
    /// include our child process. If the child terminates then we'll reap them in Cargo
    /// pretty quickly, and if the child handles the signal then we won't terminate
    /// (and we shouldn't!) until the process itself later exits.
    pub fn exec_replace(&self) -> CargoResult<()> {
        imp::exec_replace(self)
    }

    /// Execute the process, returning the stdio output, or an error if non-zero exit status.
    pub fn exec_with_output(&self) -> CargoResult<Output> {
        let mut command = self.build_command();

        let output = command.output().chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;

        if output.status.success() {
            Ok(output)
        } else {
            Err(process_error(
                &format!("process didn't exit successfully: {}", self),
                Some(output.status),
                Some(&output),
            ).into())
        }
    }

    /// Execute a command, passing each line of stdout and stderr to the supplied callbacks, which
    /// can mutate the string data.
    ///
    /// If any invocations of these function return an error, it will be propagated.
    ///
    /// Optionally, output can be passed to errors using `print_output`
    pub fn exec_with_streaming(
        &self,
        on_stdout_line: &mut FnMut(&str) -> CargoResult<()>,
        on_stderr_line: &mut FnMut(&str) -> CargoResult<()>,
        capture_output: bool,
    ) -> CargoResult<Output> {
        let mut stdout = Vec::new();
        let mut stderr = Vec::new();

        let mut cmd = self.build_command();
        cmd.stdout(Stdio::piped())
            .stderr(Stdio::piped())
            .stdin(Stdio::null());

        let mut callback_error = None;
        let status = (|| {
            let mut child = cmd.spawn()?;
            let out = child.stdout.take().unwrap();
            let err = child.stderr.take().unwrap();
            read2(out, err, &mut |is_out, data, eof| {
                let idx = if eof {
                    data.len()
                } else {
                    match data.iter().rposition(|b| *b == b'\n') {
                        Some(i) => i + 1,
                        None => return,
                    }
                };
                { // scope for new_lines
                    let new_lines = if capture_output {
                        let dst = if is_out { &mut stdout } else { &mut stderr };
                        let start = dst.len();
                        let data = data.drain(..idx);
                        dst.extend(data);
                        &dst[start..]
                    } else {
                        &data[..idx]
                    };
                    for line in String::from_utf8_lossy(new_lines).lines() {
                        if callback_error.is_some() {
                            break;
                        }
                        let callback_result = if is_out {
                            on_stdout_line(line)
                        } else {
                            on_stderr_line(line)
                        };
                        if let Err(e) = callback_result {
                            callback_error = Some(e);
                        }
                    }
                }
                if !capture_output {
                    data.drain(..idx);
                }
            })?;
            child.wait()
        })()
            .chain_err(|| {
            process_error(
                &format!("could not execute process {}", self),
                None,
                None,
            )
        })?;
        let output = Output {
            stdout,
            stderr,
            status,
        };

        {
            let to_print = if capture_output { Some(&output) } else { None };
            if let Some(e) = callback_error {
                let cx = process_error(
                    &format!("failed to parse process output: {}", self),
                    Some(output.status),
                    to_print,
                );
                return Err(cx.context(e).into());
            } else if !output.status.success() {
                return Err(process_error(
                    &format!("process didn't exit successfully: {}", self),
                    Some(output.status),
                    to_print,
                )
                .into());
            }
        }

        Ok(output)
    }

    /// Converts ProcessBuilder into a `std::process::Command`, and handles the jobserver if
    /// present.
    pub fn build_command(&self) -> Command {
        let mut command = Command::new(&self.program);
        if let Some(cwd) = self.get_cwd() {
            command.current_dir(cwd);
        }
        for arg in &self.args {
            command.arg(arg);
        }
        for (k, v) in &self.env {
            match *v {
                Some(ref v) => {
                    command.env(k, v);
                }
                None => {
                    command.env_remove(k);
                }
            }
        }
        if let Some(ref c) = self.jobserver {
            c.configure(&mut command);
        }
        command
    }
}

/// A helper function to create a `ProcessBuilder`.
pub fn process<T: AsRef<OsStr>>(cmd: T) -> ProcessBuilder {
    ProcessBuilder {
        program: cmd.as_ref().to_os_string(),
        args: Vec::new(),
        cwd: None,
        env: HashMap::new(),
        jobserver: None,
    }
}

#[cfg(unix)]
mod imp {
    use crate::CargoResult;
    use std::os::unix::process::CommandExt;
    use crate::util::{process_error, ProcessBuilder};

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        let mut command = process_builder.build_command();
        let error = command.exec();
        Err(crate::util::CargoError::from(error)
            .context(process_error(
                &format!("could not execute process {}", process_builder),
                None,
                None,
            ))
            .into())
    }
}

#[cfg(windows)]
mod imp {
    extern crate winapi;

    use crate::CargoResult;
    use crate::util::{process_error, ProcessBuilder};
    use self::winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE};
    use self::winapi::um::consoleapi::SetConsoleCtrlHandler;

    unsafe extern "system" fn ctrlc_handler(_: DWORD) -> BOOL {
        // Do nothing. Let the child process handle it.
        TRUE
    }

    pub fn exec_replace(process_builder: &ProcessBuilder) -> CargoResult<()> {
        unsafe {
            if SetConsoleCtrlHandler(Some(ctrlc_handler), TRUE) == FALSE {
                return Err(process_error(
                    "Could not set Ctrl-C handler.",
                    None,
                    None).into());
            }
        }

        // Just exec the process as normal.
        process_builder.exec()
    }
}
<|endoftext|>


<commit_before>//! Utilities to do with connections between a Minecraft or Forge client and Netherrack

extern crate chrono;
use self::chrono::duration::Duration;

use super::packet::PacketHeader;

use std::collections::VecDeque;

use std::net::{Shutdown, TcpStream};

use std::io::Read;

use super::deque_buffer::DequeBuffer;

/// An enum describing the current connection's state
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ConnectionState {

    /// Connection is in handshake mode
    HANDSHAKE,

    /// Connection is in login mode
    LOGIN,

    /// Connection is in status mode
    STATUS,

    /// Connection is in play mode
    PLAY

}

/// A struct defining a game connection
#[derive(Debug)]
pub struct GameConnection {

    /// The TCP stream bound to this GameConnection
    pub stream: TcpStream,

    /// The state of this GameConnection
    pub state: ConnectionState

}

impl GameConnection {

    /// Creates a new GameConnection
    pub fn new(stream: TcpStream) -> GameConnection {
        GameConnection {
            stream: stream,
            state: ConnectionState::HANDSHAKE
        }
    }

    pub fn disconnect(&mut self) {
        //TODO: Check to see if in ConnectionState::PLAY, otherwise
        let result = self.stream.shutdown(Shutdown::Both);

        if result.is_ok() == false {
            error!("Error disconnecting: {:?}", result);
        }

    }

    /// Starts listening on the connection
    pub fn start_listening(&mut self) {

        info!("Got a new client from {}! Oh my!", self.stream.peer_addr().unwrap());

        let mut timeout_duration = Duration::zero();

        loop {

            if timeout_duration.num_seconds() >= 10 {
                info!("Client timed out");
                self.disconnect();
                break;
            }

            let mut raw_buffer = vec![0u8; 512];

            match self.stream.read(&mut raw_buffer) {

                Err(error) => {
                    // Connection dropped out or something broke
                    // In the future, we use the customized NetherrackStream to see if we're in GAME mode, and if so, get the attached player and start saving their information and get rid of them
                    // For now, we just exit the loop
                    error!("Got an error! {}", error);

                    self.disconnect();
                    break;
                }
                Ok(count) => {
                    if count == 0 {
                        //Nothing in stream, let's sleep for a few milliseconds and try again
                        timeout_duration = timeout_duration + Duration::milliseconds(2);
                        ::std::thread::sleep_ms(2);
                    } else {

                        timeout_duration = Duration::zero();

                        // Now truncate the raw buffer - Don't worry, it will reallocate to 512 on the next networking loop
                        raw_buffer.truncate(count);

                        let mut backing: VecDeque<u8> = VecDeque::<u8>::with_capacity(count);

                        for b in raw_buffer {

                            backing.push_back(b);
                        }

                        let mut buffer = DequeBuffer::from(backing);

                        //drop(raw_buffer);

                        let length_result = buffer.read_unsigned_varint_32();
                        let id_result = buffer.read_unsigned_varint_32();

                        if length_result.is_err() || id_result.is_err() {
                            error!("Error while reading packet ID or length! Oh no, dropping connection");

                            self.disconnect();
                            break;
                        }

                        let packet_header = PacketHeader { length: length_result.unwrap(), id: id_result.unwrap() };

                        info!("Read {} bytes, length is {}, id is {}", count, packet_header.length, packet_header.id);

                        info!("Remaining buffer size is {}", buffer.remaining());

                        //TODO: Move to a MinecraftClientConnection, determine if the client is Forge or Vanilla, and set the GameState to HANDSHAKE

                        if packet_header.id == 0 {
                            if packet_header.length == 1 {

                                //This is a request packet
                                debug!("Received a Server Status Request packet");

                            } else {

                                //This is a handshake packet
                                debug!("Received a Handshake packet");

                                let protocol_result = buffer.read_unsigned_varint_32();

                                if protocol_result.is_err() {
                                    error!("Error reading protocol version");
                                    self.disconnect();
                                    break;
                                }

                                let hostname: String = buffer.read_utf8_string();

                                let port: u16 = buffer.read_unsigned_short();

                                debug!("Protocol version {}, hostname: {}, port: {}", protocol_result.unwrap(), hostname, port);

                            }
                        }

                        // This is a memory leak. We need to get the time of last packet and check to see if it's more than 20 seconds ago
                    }
                }

            }

        }

        info!("Connection closed");

    }

}<commit_msg>Start treating each connection state differently<commit_after>//! Utilities to do with connections between a Minecraft or Forge client and Netherrack

extern crate chrono;
use self::chrono::duration::Duration;

use super::packet::PacketHeader;

use std::collections::VecDeque;

use std::net::{Shutdown, TcpStream};

use std::io::Read;

use super::deque_buffer::DequeBuffer;

/// An enum describing the current connection's state
#[derive(PartialEq, Clone, Copy, Debug)]
pub enum ConnectionState {

    /// Connection is in handshake mode
    HANDSHAKE,

    /// Connection is in login mode
    LOGIN,

    /// Connection is in status mode
    STATUS,

    /// Connection is in play mode
    PLAY

}

/// A struct defining a game connection
#[derive(Debug)]
pub struct GameConnection {

    /// The TCP stream bound to this GameConnection
    pub stream: TcpStream,

    /// The state of this GameConnection
    pub state: ConnectionState

}

impl GameConnection {

    /// Creates a new GameConnection
    pub fn new(stream: TcpStream) -> GameConnection {
        GameConnection {
            stream: stream,
            state: ConnectionState::HANDSHAKE
        }
    }

    pub fn disconnect(&mut self) {
        //TODO: Check to see if in ConnectionState::PLAY, otherwise
        let result = self.stream.shutdown(Shutdown::Both);

        if result.is_ok() == false {
            error!("Error disconnecting: {:?}", result);
        }

    }

    /// Starts listening on the connection
    pub fn start_listening(&mut self) {

        info!("Got a new client from {}! Oh my!", self.stream.peer_addr().unwrap());

        let mut timeout_duration = Duration::zero();

        loop {

            if timeout_duration.num_seconds() >= 10 {
                info!("Client timed out");
                self.disconnect();
                break;
            }

            let mut raw_buffer = vec![0u8; 512];

            match self.stream.read(&mut raw_buffer) {

                Err(error) => {
                    // Connection dropped out or something broke
                    // In the future, we use the customized NetherrackStream to see if we're in GAME mode, and if so, get the attached player and start saving their information and get rid of them
                    // For now, we just exit the loop
                    error!("Got an error! {}", error);

                    self.disconnect();
                    break;
                }
                Ok(count) => {
                    if count == 0 {
                        //Nothing in stream, let's sleep for a few milliseconds and try again
                        timeout_duration = timeout_duration + Duration::milliseconds(2);
                        ::std::thread::sleep_ms(2);
                    } else {

                        timeout_duration = Duration::zero();

                        // Now truncate the raw buffer - Don't worry, it will reallocate to 512 on the next networking loop
                        raw_buffer.truncate(count);

                        let mut backing: VecDeque<u8> = VecDeque::<u8>::with_capacity(count);

                        for b in raw_buffer {

                            backing.push_back(b);
                        }

                        let mut buffer = DequeBuffer::from(backing);

                        //drop(raw_buffer);

                        let length_result = buffer.read_unsigned_varint_32();
                        let id_result = buffer.read_unsigned_varint_32();

                        if length_result.is_err() || id_result.is_err() {
                            error!("Error while reading packet ID or length! Oh no, dropping connection");

                            self.disconnect();
                            break;
                        }

                        let packet_header = PacketHeader { length: length_result.unwrap(), id: id_result.unwrap() };

                        info!("Read {} bytes, length is {}, id is {}", count, packet_header.length, packet_header.id);

                        info!("Remaining buffer size is {}", buffer.remaining());

                        if self.state == ConnectionState::HANDSHAKE {

                            debug!("Incoming handshake packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length);

                        } else if self.state == ConnectionState::LOGIN {

                            debug!("Incoming login packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length);

                        } else if self.state == ConnectionState::STATUS {

                            debug!("Incoming status packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length);

                        } else if self.state == ConnectionState::PLAY {

                            debug!("Incoming play packet unhandled; ID: {}, Length: {}", packet_header.id, packet_header.length);

                        }
                        /*
                        if packet_header.id == 0 {
                            if packet_header.length == 1 {

                                //This is a request packet
                                debug!("Received a Server Status Request packet");

                            } else {

                                //This is a handshake packet
                                debug!("Received a Handshake packet");

                                let protocol_result = buffer.read_unsigned_varint_32();

                                if protocol_result.is_err() {
                                    error!("Error reading protocol version");
                                    self.disconnect();
                                    break;
                                }

                                let hostname: String = buffer.read_utf8_string();

                                let port: u16 = buffer.read_unsigned_short();

                                debug!("Protocol version {}, hostname: {}, port: {}", protocol_result.unwrap(), hostname, port);

                            }
                        }*/

                        // This is a memory leak. We need to get the time of last packet and check to see if it's more than 20 seconds ago
                    }
                }

            }

        }

        info!("Connection closed");

    }

}<|endoftext|>


<commit_before>// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::collections::HashMap;

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map:expr, $name:expr, $short:expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map:expr, $name:expr, $short:expr, $long:expr) => {
        $map.insert($name, Help {
            short: $short,
            long: concat!($long, "\n ")
        });
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&["f", "file", "d", "directory", "l", "symlink"])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num"),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path").multiple(true))
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped).");
    doc!(h, "no-ignore"
        , "Do not respect .(git)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.*ignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "file-type"
        , "Filter by type: f(ile), d(irectory), (sym)l(ink)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to\
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");

    h
}
<commit_msg>Run updated version of rustfmt<commit_after>// Copyright (c) 2017 fd developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0>
// or the MIT license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::collections::HashMap;

use clap::{App, AppSettings, Arg};

struct Help {
    short: &'static str,
    long: &'static str,
}

macro_rules! doc {
    ($map: expr, $name: expr, $short: expr) => {
        doc!($map, $name, $short, $short)
    };
    ($map: expr, $name: expr, $short: expr, $long: expr) => {
        $map.insert(
            $name,
            Help {
                short: $short,
                long: concat!($long, "\n "),
            },
        );
    };
}

pub fn build_app() -> App<'static, 'static> {
    let helps = usage();
    let arg = |name| {
        Arg::with_name(name)
            .help(helps[name].short)
            .long_help(helps[name].long)
    };

    App::new("fd")
        .version(crate_version!())
        .usage("fd [FLAGS/OPTIONS] [<pattern>] [<path>...]")
        .setting(AppSettings::ColoredHelp)
        .setting(AppSettings::DeriveDisplayOrder)
        .arg(arg("hidden").long("hidden").short("H"))
        .arg(arg("no-ignore").long("no-ignore").short("I"))
        .arg(arg("no-ignore-vcs").long("no-ignore-vcs"))
        .arg(
            arg("rg-alias-hidden-ignore")
                .short("u")
                .multiple(true)
                .hidden(true),
        )
        .arg(
            arg("case-sensitive")
                .long("case-sensitive")
                .short("s")
                .overrides_with("ignore-case"),
        )
        .arg(
            arg("ignore-case")
                .long("ignore-case")
                .short("i")
                .overrides_with("case-sensitive"),
        )
        .arg(
            arg("fixed-strings")
                .long("fixed-strings")
                .short("F")
                .alias("literal"),
        )
        .arg(arg("absolute-path").long("absolute-path").short("a"))
        .arg(arg("follow").long("follow").short("L").alias("dereference"))
        .arg(arg("full-path").long("full-path").short("p"))
        .arg(arg("null_separator").long("print0").short("0"))
        .arg(arg("depth").long("max-depth").short("d").takes_value(true))
        .arg(
            arg("file-type")
                .long("type")
                .short("t")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("filetype")
                .possible_values(&["f", "file", "d", "directory", "l", "symlink"])
                .hide_possible_values(true),
        )
        .arg(
            arg("extension")
                .long("extension")
                .short("e")
                .multiple(true)
                .number_of_values(1)
                .takes_value(true)
                .value_name("ext"),
        )
        .arg(
            arg("exec")
                .long("exec")
                .short("x")
                .min_values(1)
                .allow_hyphen_values(true)
                .value_terminator(";")
                .value_name("cmd"),
        )
        .arg(
            arg("exclude")
                .long("exclude")
                .short("E")
                .takes_value(true)
                .value_name("pattern")
                .number_of_values(1)
                .multiple(true),
        )
        .arg(
            arg("color")
                .long("color")
                .short("c")
                .takes_value(true)
                .value_name("when")
                .possible_values(&["never", "auto", "always"])
                .hide_possible_values(true),
        )
        .arg(
            arg("threads")
                .long("threads")
                .short("j")
                .takes_value(true)
                .value_name("num"),
        )
        .arg(
            arg("max-buffer-time")
                .long("max-buffer-time")
                .takes_value(true)
                .hidden(true),
        )
        .arg(arg("pattern"))
        .arg(arg("path").multiple(true))
}

#[cfg_attr(rustfmt, rustfmt_skip)]
fn usage() -> HashMap<&'static str, Help> {
    let mut h = HashMap::new();
    doc!(h, "hidden"
        , "Search hidden files and directories"
        , "Include hidden directories and files in the search results (default: hidden files \
           and directories are skipped).");
    doc!(h, "no-ignore"
        , "Do not respect .(git)ignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.*ignore' files.");
    doc!(h, "no-ignore-vcs"
        , "Do not respect .gitignore files"
        , "Show search results from files and directories that would otherwise be ignored by \
            '.gitignore' files.");
    doc!(h, "case-sensitive"
        , "Case-sensitive search (default: smart case)"
        , "Perform a case-sensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "ignore-case"
        , "Case-insensitive search (default: smart case)"
        , "Perform a case-insensitive search. By default, fd uses case-insensitive searches, \
           unless the pattern contains an uppercase character (smart case).");
    doc!(h, "fixed-strings"
        , "Treat the pattern as a literal string"
        , "Treat the pattern as a literal string instead of a regular expression.");
    doc!(h, "absolute-path"
        , "Show absolute instead of relative paths"
        , "Shows the full path starting from the root as opposed to relative paths.");
    doc!(h, "follow"
        , "Follow symbolic links"
        , "By default, fd does not descend into symlinked directories. Using this flag, symbolic \
           links are also traversed.");
    doc!(h, "full-path"
        , "Search full path (default: file-/dirname only)"
        , "By default, the search pattern is only matched against the filename (or directory \
           name). Using this flag, the pattern is matched against the full path.");
    doc!(h, "null_separator"
        , "Separate results by the null character"
        , "Separate search results by the null character (instead of newlines). Useful for \
           piping results to 'xargs'.");
    doc!(h, "depth"
        , "Set maximum search depth (default: none)"
        , "Limit the directory traversal to a given depth. By default, there is no limit \
           on the search depth.");
    doc!(h, "file-type"
        , "Filter by type: f(ile), d(irectory), (sym)l(ink)"
        , "Filter the search by type (multiple allowable filetypes can be specified):\n  \
             'f' or 'file':         regular files\n  \
             'd' or 'directory':    directories\n  \
             'l' or 'symlink':      symbolic links");
    doc!(h, "extension"
        , "Filter by file extension"
        , "(Additionally) filter search results by their file extension. Multiple allowable file \
           extensions can be specified.");
    doc!(h, "exec"
        , "Execute a command for each search result"
        , "Execute a command for each search result.\n\
           All arguments following --exec are taken to be arguments to the command until the \
           argument ';' is encountered.\n\
           Each occurrence of the following placeholders is substituted by a path derived from the \
           current search result before the command is executed:\n  \
             '{}':   path\n  \
             '{/}':  basename\n  \
             '{//}': parent directory\n  \
             '{.}':  path without file extension\n  \
             '{/.}': basename without file extension");
    doc!(h, "exclude"
        , "Exclude entries that match the given glob pattern"
        , "Exclude files/directories that match the given glob pattern. This overrides any \
           other ignore logic. Multiple exclude patterns can be specified.");
    doc!(h, "color"
        , "When to use colors: never, *auto*, always"
        , "Declare when to use color for the pattern match output:\n  \
             'auto':      show colors if the output goes to an interactive console (default)\n  \
             'never':     do not use colorized output\n  \
             'always':    always use colorized output");
    doc!(h, "threads"
        , "Set number of threads to use for searching & executing"
        , "Set number of threads to use for searching & executing (default: number of available \
           CPU cores)");
    doc!(h, "max-buffer-time"
        , "the time (in ms) to buffer, before streaming to the console"
        , "Amount of time in milliseconds to buffer, before streaming the search results to\
           the console.");
    doc!(h, "pattern"
        , "the search pattern, a regular expression (optional)");
    doc!(h, "path"
        , "the root directory for the filesystem search (optional)"
        , "The directory where the filesystem search is rooted (optional). \
           If omitted, search the current working directory.");
    doc!(h, "rg-alias-hidden-ignore"
        , "Alias for no-ignore and/or hidden"
        , "Alias for no-ignore ('u') and no-ignore and hidden ('uu')");

    h
}
<|endoftext|>


<commit_before>use std::fmt::{Display, Error, Formatter};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinOp {
    And,
    Ge,
    Gt,
    Eql,       // ==
    EqlStrict, // ===
    Le,
    Lt,
    Minus,
    Neq,       // !=
    NeqStrict, // !==
    Or,
    Plus,
    Slash,
    Star,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 110,
    Sign = 100,
    Inc = 90,
    Mult = 70,
    Add = 60,
    Equality = 50,
    And = 40,
    Or = 30,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::And => Precedence::And,
            BinOp::Ge | BinOp::Gt | BinOp::Eql | BinOp::Le | BinOp::Lt |
                BinOp::Neq | BinOp::EqlStrict | BinOp::NeqStrict
                => Precedence::Equality,
            BinOp::Or => Precedence::Or,
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::And => write!(fmt, "&&"),
            BinOp::Ge => write!(fmt, ">="),
            BinOp::Gt => write!(fmt, ">"),
            BinOp::Eql => write!(fmt, "=="),
            BinOp::EqlStrict => write!(fmt, "==="),
            BinOp::Le => write!(fmt, "<="),
            BinOp::Lt => write!(fmt, "<"),
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Neq => write!(fmt, "!="),
            BinOp::NeqStrict => write!(fmt, "!=="),
            BinOp::Or => write!(fmt, "||"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Bool(bool),
    Call(Box<Exp>, Vec<Box<Exp>>),
    Defun(Option<String>, Vec<String>, Vec<Stmt>),
    Float(f64),
    InstanceVar(Box<Exp>, String),
    Neg(Box<Exp>),
    Null,
    NewObject(Box<Exp>, Vec<Box<Exp>>),
    Object(Vec<(String, Box<Exp>)>),
    Pos(Box<Exp>),
    PostDec(Box<Exp>),
    PostInc(Box<Exp>),
    PreDec(Box<Exp>),
    PreInc(Box<Exp>),
    TypeOf(Box<Exp>),
    Str(String),
    Undefined,
    Var(String),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Neg(_) | Exp::Pos(_) => Precedence::Sign,
            Exp::PostDec(_) | Exp::PostInc(_) | Exp::PreDec(_) | Exp::PreInc(_) => Precedence::Inc,
            _ => Precedence::Const
        }
    }
}

macro_rules! group {
    ($e:expr, $p:expr) => {
        if $e.precedence() < $p {
            format!("({})", $e)
        } else {
            format!("{}", $e)
        }
    }
}

impl Exp {
    fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Bool(b) => write!(fmt, "{}", b),
            Exp::Call(ref func, ref args) => {
                try!(write!(fmt, "{}(", func));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg));
                }

                write!(fmt, ")")
            }
            Exp::Defun(ref opt, ref params, ref body) => {
                try!(write!(fmt, "function"));

                if let &Some(ref func) = opt {
                    try!(write!(fmt, " {}", func));
                }

                try!(write!(fmt, "("));

                for (i, param) in params.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", param));
                }

                try!(write!(fmt, ") {{\n"));
                stmt_block!(body);

                let indent : String = (0..indent_level).map(|_| " ").collect();

                write!(fmt, "{}}}", indent)
            }
            Exp::Float(f) => write!(fmt, "{}", f),
            Exp::InstanceVar(ref obj, ref name) => write!(fmt, "{}.{}", obj, name),
            Exp::Neg(ref e) => write!(fmt, "-{}", group!(e, Precedence::Sign)),
            Exp::NewObject(ref name, ref args) => {
                try!(write!(fmt, "new {}(", name));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg))
                }

                write!(fmt, ")")
            }
            Exp::Null => write!(fmt, "null"),
            Exp::Object(ref properties) => {
                if properties.is_empty() {
                    return write!(fmt, "{{}}");
                }

                try!(writeln!(fmt, "{{"));

                let indent : String = (0..indent_level + 2).map(|_| " ").collect();

                for (i, &(ref name, ref prop)) in properties.iter().enumerate() {
                    if i != 0 {
                        try!(writeln!(fmt, ","));
                    }

                    try!(write!(fmt, "{}{}: ", indent, name));
                    try!(prop.fmt_helper(&mut fmt, indent_level + 2));
                }

                write!(fmt, "\n}}")
            }
            Exp::Pos(ref e) => write!(fmt, "+{}", group!(e, Precedence::Sign)),
            Exp::PostDec(ref e) => write!(fmt, "{}--", group!(e, Precedence::Inc)),
            Exp::PostInc(ref e) => write!(fmt, "{}++", group!(e, Precedence::Inc)),
            Exp::PreDec(ref e) => write!(fmt, "--{}", group!(e, Precedence::Inc)),
            Exp::PreInc(ref e) => write!(fmt, "++{}", group!(e, Precedence::Inc)),
            Exp::TypeOf(ref e) => write!(fmt, "typeof {}", e),
            Exp::Str(ref s) => write!(fmt, "\"{}\"", s),
            Exp::Undefined => write!(fmt, "undefined"),
            Exp::Var(ref v) => write!(fmt, "{}", v),
        }
    }
}

impl Display for Exp {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}

#[derive(Clone, Debug)]
pub enum Stmt {
    Assign(String, Exp),
    BareExp(Exp),
    Decl(String, Exp),
    Empty,
    If(Exp, Vec<Stmt>, Vec<Stmt>),
    Ret(Exp),
    Seq(Box<Stmt>, Box<Stmt>),
    Try(Vec<Stmt>, Vec<Stmt>, String, Vec<Stmt>), // try block, catch block, catch variable, finally block
    Throw(Box<Exp>),
    While(Exp, Vec<Stmt>),
}

impl Stmt {
    pub fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        macro_rules! indented_stmt {
            ($s:expr) => {
                try!($s.fmt_helper(&mut fmt, indent_level + 2))
            }
        }

        macro_rules! exp_semi {
            ($exp:expr) => {{
                try!($exp.fmt_helper(&mut fmt, indent_level));
                writeln!(fmt, ";")
            }}
        }

        let indent : String = (0..indent_level).map(|_| " ").collect();

        match *self {
            Stmt::Assign(ref v, ref exp) => {
                try!(write!(fmt, "{}{} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::BareExp(ref exp) => {
                try!(write!(fmt, "{}", indent));
                exp_semi!(exp)
            }
            Stmt::Decl(ref v, ref exp) => {
                try!(write!(fmt, "{}var {} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::Empty => Ok(()),
            Stmt::If(ref e, ref s, ref els) => {
                try!(write!(fmt, "{}if (", indent));
                try!(e.fmt_helper(&mut fmt, indent_level + 2));
                try!(writeln!(fmt, ") {{\n"));
                stmt_block!(s);

                if els.len() > 0 {
                    stmt_block!(s);
                }

                //if let &Some(ref stmt) = els {
                //    try!(write!(fmt, "{}else {{\n", indent));
                //    indented_stmt!(stmt);
                //    try!(write!(fmt, "{}}}\n", indent));
                //}

                Ok(())
            }
            Stmt::Ret(ref e) => {
                try!(write!(fmt, "{}return ", indent));
                exp_semi!(e)
            }
            Stmt::Seq(ref s1, ref s2) => {
                try!(s1.fmt_helper(&mut fmt, indent_level));
                s2.fmt_helper(&mut fmt, indent_level)
            }
            Stmt::Throw(ref e) => {
                write!(fmt, "{}throw {}", indent, e)
            }
            Stmt::Try(ref stmt, ref catch_block, ref finally_block) => {
                try!(write!(fmt, "{}try {{\n", indent));
                stmt_block!(stmt);
                try!(write!(fmt, "{}}}\n", indent));

                if catch_block.len() > 0 {
                    try!(write!(fmt, "{}catch {{\n", indent));
                    stmt_block!(catch_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                if finally_block.len() > 0 {
                    try!(write!(fmt, "{}finally {{\n", indent));
                    stmt_block!(finally_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                Ok(())
            }
            Stmt::While(ref exp, ref stmt) => {
                try!(write!(fmt, "{}while ({}) {{\n", indent, exp));
                stmt_block!(stmt);
                write!(fmt, "{}}}\n", indent)
            }
        }
    }
}

impl Display for Stmt {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}
<commit_msg>...finish adding try/catch binding<commit_after>use std::fmt::{Display, Error, Formatter};

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum BinOp {
    And,
    Ge,
    Gt,
    Eql,       // ==
    EqlStrict, // ===
    Le,
    Lt,
    Minus,
    Neq,       // !=
    NeqStrict, // !==
    Or,
    Plus,
    Slash,
    Star,
}

#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Precedence {
    Const = 110,
    Sign = 100,
    Inc = 90,
    Mult = 70,
    Add = 60,
    Equality = 50,
    And = 40,
    Or = 30,
}

impl BinOp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            BinOp::And => Precedence::And,
            BinOp::Ge | BinOp::Gt | BinOp::Eql | BinOp::Le | BinOp::Lt |
                BinOp::Neq | BinOp::EqlStrict | BinOp::NeqStrict
                => Precedence::Equality,
            BinOp::Or => Precedence::Or,
            BinOp::Minus | BinOp::Plus => Precedence::Add,
            BinOp::Slash | BinOp::Star => Precedence::Mult,
        }
    }

    pub fn is_commutative(&self) -> bool {
        match *self {
            BinOp::Minus | BinOp::Slash => false,
            _ => true
        }
    }
}

impl Display for BinOp {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        match *self {
            BinOp::And => write!(fmt, "&&"),
            BinOp::Ge => write!(fmt, ">="),
            BinOp::Gt => write!(fmt, ">"),
            BinOp::Eql => write!(fmt, "=="),
            BinOp::EqlStrict => write!(fmt, "==="),
            BinOp::Le => write!(fmt, "<="),
            BinOp::Lt => write!(fmt, "<"),
            BinOp::Minus => write!(fmt, "-"),
            BinOp::Neq => write!(fmt, "!="),
            BinOp::NeqStrict => write!(fmt, "!=="),
            BinOp::Or => write!(fmt, "||"),
            BinOp::Plus => write!(fmt, "+"),
            BinOp::Slash => write!(fmt, "/"),
            BinOp::Star => write!(fmt, "*"),
        }
    }
}

#[derive(Clone, Debug)]
pub enum Exp {
    BinExp(Box<Exp>, BinOp, Box<Exp>),
    Bool(bool),
    Call(Box<Exp>, Vec<Box<Exp>>),
    Defun(Option<String>, Vec<String>, Vec<Stmt>),
    Float(f64),
    InstanceVar(Box<Exp>, String),
    Neg(Box<Exp>),
    Null,
    NewObject(Box<Exp>, Vec<Box<Exp>>),
    Object(Vec<(String, Box<Exp>)>),
    Pos(Box<Exp>),
    PostDec(Box<Exp>),
    PostInc(Box<Exp>),
    PreDec(Box<Exp>),
    PreInc(Box<Exp>),
    TypeOf(Box<Exp>),
    Str(String),
    Undefined,
    Var(String),
}

impl Exp {
    pub fn precedence(&self) -> Precedence {
        match *self {
            Exp::BinExp(_, ref o, _) => o.precedence(),
            Exp::Neg(_) | Exp::Pos(_) => Precedence::Sign,
            Exp::PostDec(_) | Exp::PostInc(_) | Exp::PreDec(_) | Exp::PreInc(_) => Precedence::Inc,
            _ => Precedence::Const
        }
    }
}

macro_rules! group {
    ($e:expr, $p:expr) => {
        if $e.precedence() < $p {
            format!("({})", $e)
        } else {
            format!("{}", $e)
        }
    }
}

impl Exp {
    fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        match *self {
            Exp::BinExp(ref e1, ref o, ref e2) => {
                let prec = self.precedence();

                // Put grouping parentheses if the left subexpression has a lower-precedence
                // operator, e.g. (1 + 2) * 3
                let left = if prec > e1.precedence() {
                    format!("({})", e1)
                } else {
                    format!("{}", e1)
                };

                let right_prec = e2.precedence();

                // Put grouping parentheses around the right subexpression if it has a
                // lower-precedence operator,  __OR__ if `o` is not commutative and the precedence
                // is the same, e.g. (1 + 2) * 3 __OR__ 1 - (2 + 3)
                let right = if prec > right_prec || (!o.is_commutative() && prec == right_prec) {
                    format!("({})", e2)
                } else {
                    format!("{}", e2)
                };

                write!(fmt, "{} {} {}", left, o, right)
            }
            Exp::Bool(b) => write!(fmt, "{}", b),
            Exp::Call(ref func, ref args) => {
                try!(write!(fmt, "{}(", func));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg));
                }

                write!(fmt, ")")
            }
            Exp::Defun(ref opt, ref params, ref body) => {
                try!(write!(fmt, "function"));

                if let &Some(ref func) = opt {
                    try!(write!(fmt, " {}", func));
                }

                try!(write!(fmt, "("));

                for (i, param) in params.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", param));
                }

                try!(write!(fmt, ") {{\n"));
                stmt_block!(body);

                let indent : String = (0..indent_level).map(|_| " ").collect();

                write!(fmt, "{}}}", indent)
            }
            Exp::Float(f) => write!(fmt, "{}", f),
            Exp::InstanceVar(ref obj, ref name) => write!(fmt, "{}.{}", obj, name),
            Exp::Neg(ref e) => write!(fmt, "-{}", group!(e, Precedence::Sign)),
            Exp::NewObject(ref name, ref args) => {
                try!(write!(fmt, "new {}(", name));

                for (i, arg) in args.iter().enumerate() {
                    if i != 0 {
                        try!(write!(fmt, ", "));
                    }

                    try!(write!(fmt, "{}", arg))
                }

                write!(fmt, ")")
            }
            Exp::Null => write!(fmt, "null"),
            Exp::Object(ref properties) => {
                if properties.is_empty() {
                    return write!(fmt, "{{}}");
                }

                try!(writeln!(fmt, "{{"));

                let indent : String = (0..indent_level + 2).map(|_| " ").collect();

                for (i, &(ref name, ref prop)) in properties.iter().enumerate() {
                    if i != 0 {
                        try!(writeln!(fmt, ","));
                    }

                    try!(write!(fmt, "{}{}: ", indent, name));
                    try!(prop.fmt_helper(&mut fmt, indent_level + 2));
                }

                write!(fmt, "\n}}")
            }
            Exp::Pos(ref e) => write!(fmt, "+{}", group!(e, Precedence::Sign)),
            Exp::PostDec(ref e) => write!(fmt, "{}--", group!(e, Precedence::Inc)),
            Exp::PostInc(ref e) => write!(fmt, "{}++", group!(e, Precedence::Inc)),
            Exp::PreDec(ref e) => write!(fmt, "--{}", group!(e, Precedence::Inc)),
            Exp::PreInc(ref e) => write!(fmt, "++{}", group!(e, Precedence::Inc)),
            Exp::TypeOf(ref e) => write!(fmt, "typeof {}", e),
            Exp::Str(ref s) => write!(fmt, "\"{}\"", s),
            Exp::Undefined => write!(fmt, "undefined"),
            Exp::Var(ref v) => write!(fmt, "{}", v),
        }
    }
}

impl Display for Exp {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}

#[derive(Clone, Debug)]
pub enum Stmt {
    Assign(String, Exp),
    BareExp(Exp),
    Decl(String, Exp),
    Empty,
    If(Exp, Vec<Stmt>, Vec<Stmt>),
    Ret(Exp),
    Seq(Box<Stmt>, Box<Stmt>),
    // try block, catch variable, catch block, finally block
    Try(Vec<Stmt>, String, Vec<Stmt>, Vec<Stmt>),
    Throw(Box<Exp>),
    While(Exp, Vec<Stmt>),
}

impl Stmt {
    pub fn fmt_helper(&self, mut fmt: &mut Formatter, indent_level: i32) -> Result<(), Error> {
        macro_rules! stmt_block {
            ($stmt:expr) => {
                for s in $stmt {
                    try!(s.fmt_helper(&mut fmt, indent_level + 2))
                }
            }
        }

        macro_rules! indented_stmt {
            ($s:expr) => {
                try!($s.fmt_helper(&mut fmt, indent_level + 2))
            }
        }

        macro_rules! exp_semi {
            ($exp:expr) => {{
                try!($exp.fmt_helper(&mut fmt, indent_level));
                writeln!(fmt, ";")
            }}
        }

        let indent : String = (0..indent_level).map(|_| " ").collect();

        match *self {
            Stmt::Assign(ref v, ref exp) => {
                try!(write!(fmt, "{}{} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::BareExp(ref exp) => {
                try!(write!(fmt, "{}", indent));
                exp_semi!(exp)
            }
            Stmt::Decl(ref v, ref exp) => {
                try!(write!(fmt, "{}var {} = ", indent, v));
                exp_semi!(exp)
            }
            Stmt::Empty => Ok(()),
            Stmt::If(ref e, ref s, ref els) => {
                try!(write!(fmt, "{}if (", indent));
                try!(e.fmt_helper(&mut fmt, indent_level + 2));
                try!(writeln!(fmt, ") {{\n"));
                stmt_block!(s);

                if els.len() > 0 {
                    stmt_block!(s);
                }

                //if let &Some(ref stmt) = els {
                //    try!(write!(fmt, "{}else {{\n", indent));
                //    indented_stmt!(stmt);
                //    try!(write!(fmt, "{}}}\n", indent));
                //}

                Ok(())
            }
            Stmt::Ret(ref e) => {
                try!(write!(fmt, "{}return ", indent));
                exp_semi!(e)
            }
            Stmt::Seq(ref s1, ref s2) => {
                try!(s1.fmt_helper(&mut fmt, indent_level));
                s2.fmt_helper(&mut fmt, indent_level)
            }
            Stmt::Throw(ref e) => {
                write!(fmt, "{}throw {}", indent, e)
            }
            Stmt::Try(ref stmt, ref catch_var, ref catch_block, ref finally_block) => {
                try!(write!(fmt, "{}try {{\n", indent));
                stmt_block!(stmt);
                try!(write!(fmt, "{}}}\n", indent));

                if catch_block.len() > 0 {
                    try!(write!(fmt, "{}catch ({}) {{\n", catch_var, indent));
                    stmt_block!(catch_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                if finally_block.len() > 0 {
                    try!(write!(fmt, "{}finally {{\n", indent));
                    stmt_block!(finally_block);
                    try!(write!(fmt, "{}}}\n", indent));
                }

                Ok(())
            }
            Stmt::While(ref exp, ref stmt) => {
                try!(write!(fmt, "{}while ({}) {{\n", indent, exp));
                stmt_block!(stmt);
                write!(fmt, "{}}}\n", indent)
            }
        }
    }
}

impl Display for Stmt {
    fn fmt(&self, mut fmt: &mut Formatter) -> Result<(), Error> {
        self.fmt_helper(&mut fmt, 0)
    }
}
<|endoftext|>


<commit_before>use std::thread::{self};
use std::sync::mpsc::{channel,Sender,Receiver};
use std::collections::HashMap;

use messages::{Command, Topic};

#[derive(Clone)]
pub enum Message {
  Subscribe(Topic, Sender<Message>),
  SubscribeOk,
  Msg(Command)
}

impl Message {
  pub fn display(&self) {
      match *self {
        Message::Subscribe(_, _) => println!("Subscribe"),
        Message::SubscribeOk     => println!("SubscribeOk"),
        Message::Msg(ref c)      => {
          println!("{:?}", c.get_topics());
          println!("{:?}", c)
        }
      }
  }
}

pub fn start_bus() -> Sender<Message> {
  let (tx,rx) = channel::<Message>();

  thread::spawn(move|| {
    let mut bus = Bus {
      rx: rx,
      senders: HashMap::new()
    };

    bus.loop_message();
  });
  tx
}

struct Bus {
  rx: Receiver<Message>,
  senders: HashMap<Topic, Vec<Sender<Message>>>
}

impl Bus {
  fn loop_message(&mut self) {
    loop {
      if ! self.handle_message() {
        break;
      }
    }
  }

  fn handle_message(&mut self) -> bool {
    match self.rx.recv() {
      Ok(Message::Subscribe(ref tag, ref tx)) => {
        self.subscribe(tag, tx);
        println!("SUBSCRIBED");
        true
      },
      Ok(Message::Msg(ref c)) => {
        println!("GOT MSG");
        let topics = c.get_topics();

        for t in topics {
          if let &Some(v) = &self.senders.get(&t) {
            println!("GOT MSG 2");
            for tx in v {
              println!("GOT MSG 3");
              // ToDo use result?
              let _ = tx.send(Message::Msg(c.clone()));
              println!("{:?}", c);
            }
          }
        }
        true
      },
      Err(_) => {
        println!("the bus's channel is closed, exiting");
        false
      }
      Ok(_) => {
        println!("invalid message");
        // FIXME: maybe we should not stop if there's an invalid message
        false
      }
    }
  }

  fn subscribe(&mut self, t: &Topic, tx: &Sender<Message>) {
    println!("X");
    if ! &self.senders.contains_key(t) {
      let v = Vec::new();
      let t2 = t.clone();
      &self.senders.insert(t2, v);
      println!("Y");
    }
    if let Some(ref mut v) = self.senders.get_mut(t) {
      v.push(tx.clone());
      // ToDo use result?
      let _ = tx.send(Message::SubscribeOk);
      println!("Z");
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use messages::{Command,HttpFront,Topic};
  use std::sync::mpsc::{channel};

  #[test]
  fn bus_test() {
    let tx = start_bus();

    let (tx2,rx2) = channel();
    println!("AA");
    let _ = tx.send(Message::Subscribe(Topic::HttpProxyConfig, tx2));
    println!("BB");

    if let Ok(Message::SubscribeOk) = rx2.recv() {
      println!("successfully subscribed");
      let command = Command::AddHttpFront(
        HttpFront {
          app_id: String::new() + "app_e74eb0d4-e01a-4a09-af46-7ecab7157d32",
          hostname: String::new() + "cltdl.fr",
          path_begin: String::new() + ""
        }
      );
      let _ = tx.send(Message::Msg(command.clone()));

      if let Ok(Message::Msg(c)) = rx2.recv() {
        assert_eq!(c, command);
      } else {
        assert!(false);
      }
    } else {
      assert!(false);
    }
  }
}

<commit_msg>implement Display for Message<commit_after>use std::thread::{self};
use std::sync::mpsc::{channel,Sender,Receiver};
use std::collections::HashMap;
use std::fmt;

use messages::{Command, Topic};

#[derive(Clone)]
pub enum Message {
  Subscribe(Topic, Sender<Message>),
  SubscribeOk,
  Msg(Command)
}

impl fmt::Display for Message {
  fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
      match *self {
        Message::Subscribe(_, _) => write!(f, "Subscribe"),
        Message::SubscribeOk     => write!(f, "SubscribeOk"),
        Message::Msg(ref c)      => {
          (write!(f, "{:?}", c.get_topics())).and(
            write!(f, "{:?}", c))
        }
      }
  }
}

pub fn start_bus() -> Sender<Message> {
  let (tx,rx) = channel::<Message>();

  thread::spawn(move|| {
    let mut bus = Bus {
      rx: rx,
      senders: HashMap::new()
    };

    bus.loop_message();
  });
  tx
}

struct Bus {
  rx: Receiver<Message>,
  senders: HashMap<Topic, Vec<Sender<Message>>>
}

impl Bus {
  fn loop_message(&mut self) {
    loop {
      if ! self.handle_message() {
        break;
      }
    }
  }

  fn handle_message(&mut self) -> bool {
    match self.rx.recv() {
      Ok(Message::Subscribe(ref tag, ref tx)) => {
        self.subscribe(tag, tx);
        println!("SUBSCRIBED");
        true
      },
      Ok(Message::Msg(ref c)) => {
        println!("GOT MSG");
        let topics = c.get_topics();

        for t in topics {
          if let &Some(v) = &self.senders.get(&t) {
            println!("GOT MSG 2");
            for tx in v {
              println!("GOT MSG 3");
              // ToDo use result?
              let _ = tx.send(Message::Msg(c.clone()));
              println!("{:?}", c);
            }
          }
        }
        true
      },
      Err(_) => {
        println!("the bus's channel is closed, exiting");
        false
      }
      Ok(_) => {
        println!("invalid message");
        // FIXME: maybe we should not stop if there's an invalid message
        false
      }
    }
  }

  fn subscribe(&mut self, t: &Topic, tx: &Sender<Message>) {
    println!("X");
    if ! &self.senders.contains_key(t) {
      let v = Vec::new();
      let t2 = t.clone();
      &self.senders.insert(t2, v);
      println!("Y");
    }
    if let Some(ref mut v) = self.senders.get_mut(t) {
      v.push(tx.clone());
      // ToDo use result?
      let _ = tx.send(Message::SubscribeOk);
      println!("Z");
    }
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use messages::{Command,HttpFront,Topic};
  use std::sync::mpsc::{channel};

  #[test]
  fn bus_test() {
    let tx = start_bus();

    let (tx2,rx2) = channel();
    println!("AA");
    let _ = tx.send(Message::Subscribe(Topic::HttpProxyConfig, tx2));
    println!("BB");

    if let Ok(Message::SubscribeOk) = rx2.recv() {
      println!("successfully subscribed");
      let command = Command::AddHttpFront(
        HttpFront {
          app_id: String::new() + "app_e74eb0d4-e01a-4a09-af46-7ecab7157d32",
          hostname: String::new() + "cltdl.fr",
          path_begin: String::new() + ""
        }
      );
      let _ = tx.send(Message::Msg(command.clone()));

      if let Ok(Message::Msg(c)) = rx2.recv() {
        assert_eq!(c, command);
      } else {
        assert!(false);
      }
    } else {
      assert!(false);
    }
  }
}

<|endoftext|>


<commit_before>#![ warn( missing_docs ) ]
#![ allow( dead_code ) ]
// #![no_std]

//!
//! Tools for writing tests and runnint tests.
//!
//! # Sample
//! ``` rust
//! use wtest::test_suite;
//!
//! //
//!
//! fn _pass1()
//! {
//!   assert_eq!( true, true );
//! }
//!
//! //
//!
//! fn _pass2()
//! {
//!   assert_eq!( 1, 1 );
//! }
//!
//! //
//!
//! test_suite!
//! {
//!   pass1,
//!   pass2,
//! }
//!
//! ```

pub use test_basic as basic;
pub use basic::*;
<commit_msg>publishing module::wtest<commit_after>#![ warn( missing_docs ) ]
#![ allow( dead_code ) ]
// #![no_std]

//!
//! Tools for writing tests and runnint tests.
//!
//! # Sample
//! ``` rust
//! use wtest::test_suite;
//!
//! //
//!
//! fn _pass1()
//! {
//!   assert_eq!( true, true );
//! }
//!
//! //
//!
//! fn _pass2()
//! {
//!   assert_eq!( 1, 1 );
//! }
//!
//! //
//!
//! test_suite!
//! {
//!   pass1,
//!   pass2,
//! }
//!
//! ```

pub use wtest_basic as basic;
pub use basic::*;
<|endoftext|>