<commit_before>// .env parsing

extern crate dotenv;
// command line argument parsing

extern crate clap;
// logging macros

#[macro_use]
extern crate log;
// console logging output

extern crate chrono;
extern crate fern;
// sockets

extern crate futures;
extern crate tokio_core;
extern crate websocket;
// Screeps API

extern crate screeps_api;
// json pretty printing

extern crate serde_json;

use std::borrow::Cow;

use futures::{future, stream, Future, Sink, Stream};

use websocket::OwnedMessage;

use screeps_api::{RoomName, TokenStorage};
use screeps_api::websocket::{Channel, ChannelUpdate, ScreepsMessage, SockjsMessage};

/// Set up dotenv and retrieve a specific variable, informatively panicking if it does not exist.
fn env(var: &str) -> String {
    dotenv::dotenv().ok();
    match ::std::env::var(var) {
        Ok(value) => value,
        Err(e) => panic!("must have `{}` defined (err: {:?})", var, e),
    }
}

fn setup_logging(verbosity: u64) {
    let log_level = match verbosity {
        0 => log::LevelFilter::Warn,
        1 => log::LevelFilter::Info,
        _ => log::LevelFilter::Debug,
    };
    fern::Dispatch::new()
        .level(log_level)
        .level_for("rustls", log::LevelFilter::Warn)
        .level_for("hyper", log::LevelFilter::Warn)
        .format(|out, message, record| {
            let now = chrono::Local::now();

            out.finish(format_args!("[{}][{}] {}: {}",
                                    now.format("%H:%M:%S"),
                                    record.level(),
                                    record.target(),
                                    message));
        })
        .chain(std::io::stdout())
        .apply()
        // ignore errors
        .unwrap_or(());
}

#[derive(Clone, Debug)]
struct Config {
    cpu: bool,
    messages: bool,
    credits: bool,
    console: bool,
    shard: Option<Cow<'static, str>>,
    rooms: Vec<RoomName>,
    map_view: Vec<RoomName>,
    url: Cow<'static, str>,
}

impl Config {
    fn new<'a>(args: &'a clap::ArgMatches) -> Result<Self, screeps_api::data::room_name::RoomNameParseError<'a>> {
        Ok(Config {
            cpu: args.is_present("cpu"),
            messages: args.is_present("messages"),
            credits: args.is_present("credits"),
            console: args.is_present("console"),
            shard: args.value_of("shard")
                .map(|v| {
                    if "none".eq_ignore_ascii_case(v) {
                        None
                    } else {
                        Some(v.to_owned().into())
                    }
                })
                .unwrap_or_else(|| Some("shard0".into())),
            rooms: args.values_of("room")
                .map(|it| it.map(|v| RoomName::new(v)).collect::<Result<_, _>>())
                .unwrap_or_else(|| Ok(Vec::new()))?,
            map_view: args.values_of("map-view")
                .map(|it| it.map(|v| RoomName::new(v)).collect::<Result<_, _>>())
                .unwrap_or_else(|| Ok(Vec::new()))?,
            url: args.value_of("url")
                .map(|v| v.to_owned().into())
                .unwrap_or_else(|| screeps_api::DEFAULT_OFFICIAL_API_URL.into()),
        })
    }

    fn subscribe_with(&self, id: &str) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        use screeps_api::websocket::subscribe;

        let mut messages = Vec::with_capacity(
            1 + self.cpu as usize + self.messages as usize + self.credits as usize + self.console as usize
                + self.rooms.len() + self.map_view.len(),
        );

        messages.push(subscribe(&Channel::ServerMessages));

        if self.cpu {
            messages.push(subscribe(&Channel::user_cpu(id)));
        }

        if self.messages {
            messages.push(subscribe(&Channel::user_messages(id)));
            messages.push(subscribe(&Channel::user_conversation(
                id,
                "57fb16b6e4dd183b746435b0",
            )));
        }

        if self.credits {
            messages.push(subscribe(&Channel::user_credits(id)));
        }

        if self.console {
            messages.push(subscribe(&Channel::user_console(id)));
        }

        for room_name in &self.rooms {
            messages.push(subscribe(&Channel::room_detail(
                *room_name,
                self.shard.as_ref().map(AsRef::as_ref),
            )));
        }

        for room_name in &self.map_view {
            messages.push(subscribe(&Channel::room_map_view(
                *room_name,
                self.shard.as_ref().map(AsRef::as_ref),
            )));
        }

        Box::new(stream::iter_ok(
            messages
                .into_iter()
                .map(|string| OwnedMessage::Text(string)),
        ))
    }
}

fn setup() -> Config {
    let cmd_arguments = clap::App::new("ws-debug")
        .arg(
            clap::Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .multiple(true)
                .help("Enables verbose logging"),
        )
        .arg(
            clap::Arg::with_name("cpu")
                .short("p")
                .long("cpu")
                .help("Subscribe to user cpu and memory updates"),
        )
        .arg(
            clap::Arg::with_name("credits")
                .short("c")
                .long("credits")
                .help("Subscribe to per-tick user credit updates"),
        )
        .arg(
            clap::Arg::with_name("console")
                .short("o")
                .long("console")
                .help("Subscribe to user console messages"),
        )
        .arg(
            clap::Arg::with_name("messages")
                .short("e")
                .long("messages")
                .help("Subscribe to user message alerts"),
        )
        .arg(
            clap::Arg::with_name("shard")
                .short("s")
                .long("shard")
                .value_name("SHARD_NAME")
                .help("Sets the shard (default shard0, use 'None' for no shard)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("room")
                .short("r")
                .long("room")
                .value_name("ROOM_NAME")
                .help("Subscribes to a room")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            clap::Arg::with_name("map-view")
                .short("m")
                .long("map-view")
                .value_name("ROOM_NAME")
                .help("Subscribes to a map-view room")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            clap::Arg::with_name("url")
                .short("u")
                .long("url")
                .value_name("API_URL")
                .help("Server url to connect to")
                .takes_value(true),
        )
        .get_matches();

    setup_logging(cmd_arguments.occurrences_of("verbose"));

    match Config::new(&cmd_arguments) {
        Ok(v) => v,
        Err(e) => clap::Error {
            message: e.to_string(),
            kind: clap::ErrorKind::InvalidValue,
            info: None,
        }.exit(),
    }
}

fn main() {
    let config = setup();

    let token_storage = screeps_api::RcTokenStorage::default();

    let mut client = screeps_api::SyncConfig::new()
        .unwrap()
        .url(&config.url)
        .tokens(token_storage.clone())
        .build()
        .unwrap();

    // Login using the API client - this will storage the auth token in token_storage.
    client
        .login(env("SCREEPS_API_USERNAME"), env("SCREEPS_API_PASSWORD"))
        .expect("failed to login");

    let my_info = client.my_info().unwrap();

    info!(
        "Logged in as {}, attempting to connect to stream.",
        my_info.username
    );

    let mut core = tokio_core::reactor::Core::new().expect("expected IO core to start up without issue.");

    let handle = core.handle();

    let ws_url = screeps_api::websocket::connecting::transform_url(&config.url)
        .expect("expected server api url to parse into websocket url.");

    let connection = websocket::ClientBuilder::from_url(&ws_url).async_connect(None, &handle);

    core.run(connection.then(|result| {
        let (client, _) = result.expect("connecting to server failed");

        let (sink, stream) = client.split();

        sink.send(OwnedMessage::Text(screeps_api::websocket::authenticate(
            &token_storage.take_token().unwrap(),
        ))).and_then(|sink| {
            let handler = Handler::new(token_storage, my_info, config);

            sink.send_all(
                (stream
                    .and_then(move |data| future::ok(handler.handle_data(data)))
                    .or_else(|err| {
                        warn!("error occurred: {}", err);

                        future::ok::<_, websocket::WebSocketError>(Box::new(stream::empty())
                            as Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>>)
                    })
                    .flatten()),
            )
        })
    })).expect("expected websocket connection to complete successfully, but an error occurred");
}

struct Handler<T>
where
    T: screeps_api::TokenStorage,
{
    tokens: T,
    info: screeps_api::MyInfo,
    config: Config,
}

impl<T> Handler<T>
where
    T: screeps_api::TokenStorage,
{
    fn new(tokens: T, info: screeps_api::MyInfo, config: Config) -> Self {
        Handler {
            tokens: tokens,
            info: info,
            config: config,
        }
    }

    fn handle_data(&self, data: OwnedMessage) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        match data {
            OwnedMessage::Text(string) => {
                let data =
                    SockjsMessage::parse(&string).expect("expected a correct SockJS message, found a parse error.");

                match data {
                    SockjsMessage::Open => debug!("SockJS connection opened"),
                    SockjsMessage::Heartbeat => debug!("SockJS heartbeat."),
                    SockjsMessage::Close { .. } => debug!("SockJS close"),
                    SockjsMessage::Message(message) => {
                        return Box::new(self.handle_parsed_message(message));
                    }
                    SockjsMessage::Messages(messages) => {
                        let results = messages
                            .into_iter()
                            .map(|message| Ok(self.handle_parsed_message(message)))
                            .collect::<Vec<_>>();

                        return Box::new(stream::iter_result::<_, _, websocket::WebSocketError>(results).flatten());
                    }
                }
            }
            OwnedMessage::Binary(data) => warn!("ignoring binary data from websocket: {:?}", data),
            OwnedMessage::Close(data) => warn!("connection closing: {:?}", data),
            OwnedMessage::Ping(data) => return Box::new(stream::once(Ok(OwnedMessage::Pong(data)))),
            OwnedMessage::Pong(_) => (),
        }

        Box::new(stream::empty())
    }

    fn handle_parsed_message(
        &self,
        message: screeps_api::websocket::parsing::ScreepsMessage,
    ) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        match message {
            ScreepsMessage::AuthFailed => panic!("authentication with stored token failed!"),
            ScreepsMessage::AuthOk { new_token } => {
                info!(
                    "authentication succeeded, now authenticated as {}.",
                    self.info.username
                );
                // return the token to the token storage in case we need it again - we won't in this example
                // program, but this is a good practice
                //
                // TODO: find an efficient way to do this automatically in the handler.
                self.tokens.return_token(new_token);

                return Box::new(
                    self.config.subscribe_with(&self.info.user_id).chain(
                        stream::futures_unordered(vec![
                            future::lazy(|| {
                                warn!("subscribed!");
                                future::ok::<_, websocket::WebSocketError>(stream::empty())
                            }),
                        ]).flatten(),
                    ),
                ) as Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>>;
            }
            ScreepsMessage::ChannelUpdate { update } => {
                self.handle_update(update);
            }
            ScreepsMessage::ServerProtocol { protocol } => {
                info!("server protocol: {}", protocol);
            }
            ScreepsMessage::ServerTime { time } => {
                info!("server time: {}", time);
            }
            ScreepsMessage::ServerPackage { package } => {
                info!("server package: {}", package);
            }
            ScreepsMessage::Other(other) => {
                warn!("ScreepsMessage::Other: {}", other);
            }
        }

        Box::new(stream::empty())
    }

    fn handle_update(&self, update: ChannelUpdate) {
        match update {
            ChannelUpdate::UserCpu { user_id, update } => info!("CPU: [{}] {:#?}", user_id, update),
            ChannelUpdate::RoomMapView {
                room_name,
                shard_name,
                update,
            } => {
                info!(
                    "Map View: [{}/{}] {:?}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    update
                );
            }
            ChannelUpdate::RoomDetail {
                room_name,
                shard_name,
                update,
            } => {
                debug!(
                    "Room Detail: [{}/{}] {:?}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    update
                );
                info!(
                    "Room {}/{}: {}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    serde_json::to_string_pretty(&serde_json::Value::Object(
                        update.objects.iter().cloned().collect()
                    )).expect("expected to_string to succeed on plain map.")
                );
            }
            ChannelUpdate::NoRoomDetail {
                room_name,
                shard_name,
            } => {
                info!(
                    "Room Skipped: {}/{}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name
                );
            }
            ChannelUpdate::UserConsole { user_id, update } => {
                info!("Console: [{}] {:#?}", user_id, update);
            }
            ChannelUpdate::UserCredits { user_id, update } => {
                info!("Credits: [{}] {}", user_id, update);
            }
            ChannelUpdate::UserMessage { user_id, update } => {
                info!("New message: [{}] {:#?}", user_id, update);
            }
            ChannelUpdate::UserConversation {
                user_id,
                target_user_id,
                update,
            } => {
                info!(
                    "Conversation update: [{}->{}] {:#?}",
                    user_id, target_user_id, update
                );
            }
            ChannelUpdate::Other { channel, update } => {
                warn!(
                    "ChannelUpdate::Other: {}\n{}",
                    channel,
                    serde_json::to_string_pretty(&update).expect("failed to serialize json string")
                );
            }
        }
    }
}
<commit_msg>Re-add AsciiExt import that is necessary for building examples on stable rustc.<commit_after>// .env parsing

extern crate dotenv;
// command line argument parsing

extern crate clap;
// logging macros

#[macro_use]
extern crate log;
// console logging output

extern crate chrono;
extern crate fern;
// sockets

extern crate futures;
extern crate tokio_core;
extern crate websocket;
// Screeps API

extern crate screeps_api;
// json pretty printing

extern crate serde_json;

use std::borrow::Cow;

use futures::{future, stream, Future, Sink, Stream};

use websocket::OwnedMessage;

use screeps_api::{RoomName, TokenStorage};
use screeps_api::websocket::{Channel, ChannelUpdate, ScreepsMessage, SockjsMessage};

/// Set up dotenv and retrieve a specific variable, informatively panicking if it does not exist.
fn env(var: &str) -> String {
    dotenv::dotenv().ok();
    match ::std::env::var(var) {
        Ok(value) => value,
        Err(e) => panic!("must have `{}` defined (err: {:?})", var, e),
    }
}

fn setup_logging(verbosity: u64) {
    let log_level = match verbosity {
        0 => log::LevelFilter::Warn,
        1 => log::LevelFilter::Info,
        _ => log::LevelFilter::Debug,
    };
    fern::Dispatch::new()
        .level(log_level)
        .level_for("rustls", log::LevelFilter::Warn)
        .level_for("hyper", log::LevelFilter::Warn)
        .format(|out, message, record| {
            let now = chrono::Local::now();

            out.finish(format_args!("[{}][{}] {}: {}",
                                    now.format("%H:%M:%S"),
                                    record.level(),
                                    record.target(),
                                    message));
        })
        .chain(std::io::stdout())
        .apply()
        // ignore errors
        .unwrap_or(());
}

#[derive(Clone, Debug)]
struct Config {
    cpu: bool,
    messages: bool,
    credits: bool,
    console: bool,
    shard: Option<Cow<'static, str>>,
    rooms: Vec<RoomName>,
    map_view: Vec<RoomName>,
    url: Cow<'static, str>,
}

impl Config {
    fn new<'a>(args: &'a clap::ArgMatches) -> Result<Self, screeps_api::data::room_name::RoomNameParseError<'a>> {
        #[allow(unused_imports)] // becomes unnecessary for rust 1.23+
        use std::ascii::AsciiExt;

        Ok(Config {
            cpu: args.is_present("cpu"),
            messages: args.is_present("messages"),
            credits: args.is_present("credits"),
            console: args.is_present("console"),
            shard: args.value_of("shard")
                .map(|v| {
                    if "none".eq_ignore_ascii_case(v) {
                        None
                    } else {
                        Some(v.to_owned().into())
                    }
                })
                .unwrap_or_else(|| Some("shard0".into())),
            rooms: args.values_of("room")
                .map(|it| it.map(|v| RoomName::new(v)).collect::<Result<_, _>>())
                .unwrap_or_else(|| Ok(Vec::new()))?,
            map_view: args.values_of("map-view")
                .map(|it| it.map(|v| RoomName::new(v)).collect::<Result<_, _>>())
                .unwrap_or_else(|| Ok(Vec::new()))?,
            url: args.value_of("url")
                .map(|v| v.to_owned().into())
                .unwrap_or_else(|| screeps_api::DEFAULT_OFFICIAL_API_URL.into()),
        })
    }

    fn subscribe_with(&self, id: &str) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        use screeps_api::websocket::subscribe;

        let mut messages = Vec::with_capacity(
            1 + self.cpu as usize + self.messages as usize + self.credits as usize + self.console as usize
                + self.rooms.len() + self.map_view.len(),
        );

        messages.push(subscribe(&Channel::ServerMessages));

        if self.cpu {
            messages.push(subscribe(&Channel::user_cpu(id)));
        }

        if self.messages {
            messages.push(subscribe(&Channel::user_messages(id)));
            messages.push(subscribe(&Channel::user_conversation(
                id,
                "57fb16b6e4dd183b746435b0",
            )));
        }

        if self.credits {
            messages.push(subscribe(&Channel::user_credits(id)));
        }

        if self.console {
            messages.push(subscribe(&Channel::user_console(id)));
        }

        for room_name in &self.rooms {
            messages.push(subscribe(&Channel::room_detail(
                *room_name,
                self.shard.as_ref().map(AsRef::as_ref),
            )));
        }

        for room_name in &self.map_view {
            messages.push(subscribe(&Channel::room_map_view(
                *room_name,
                self.shard.as_ref().map(AsRef::as_ref),
            )));
        }

        Box::new(stream::iter_ok(
            messages
                .into_iter()
                .map(|string| OwnedMessage::Text(string)),
        ))
    }
}

fn setup() -> Config {
    let cmd_arguments = clap::App::new("ws-debug")
        .arg(
            clap::Arg::with_name("verbose")
                .short("v")
                .long("verbose")
                .multiple(true)
                .help("Enables verbose logging"),
        )
        .arg(
            clap::Arg::with_name("cpu")
                .short("p")
                .long("cpu")
                .help("Subscribe to user cpu and memory updates"),
        )
        .arg(
            clap::Arg::with_name("credits")
                .short("c")
                .long("credits")
                .help("Subscribe to per-tick user credit updates"),
        )
        .arg(
            clap::Arg::with_name("console")
                .short("o")
                .long("console")
                .help("Subscribe to user console messages"),
        )
        .arg(
            clap::Arg::with_name("messages")
                .short("e")
                .long("messages")
                .help("Subscribe to user message alerts"),
        )
        .arg(
            clap::Arg::with_name("shard")
                .short("s")
                .long("shard")
                .value_name("SHARD_NAME")
                .help("Sets the shard (default shard0, use 'None' for no shard)")
                .takes_value(true),
        )
        .arg(
            clap::Arg::with_name("room")
                .short("r")
                .long("room")
                .value_name("ROOM_NAME")
                .help("Subscribes to a room")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            clap::Arg::with_name("map-view")
                .short("m")
                .long("map-view")
                .value_name("ROOM_NAME")
                .help("Subscribes to a map-view room")
                .takes_value(true)
                .multiple(true),
        )
        .arg(
            clap::Arg::with_name("url")
                .short("u")
                .long("url")
                .value_name("API_URL")
                .help("Server url to connect to")
                .takes_value(true),
        )
        .get_matches();

    setup_logging(cmd_arguments.occurrences_of("verbose"));

    match Config::new(&cmd_arguments) {
        Ok(v) => v,
        Err(e) => clap::Error {
            message: e.to_string(),
            kind: clap::ErrorKind::InvalidValue,
            info: None,
        }.exit(),
    }
}

fn main() {
    let config = setup();

    let token_storage = screeps_api::RcTokenStorage::default();

    let mut client = screeps_api::SyncConfig::new()
        .unwrap()
        .url(&config.url)
        .tokens(token_storage.clone())
        .build()
        .unwrap();

    // Login using the API client - this will storage the auth token in token_storage.
    client
        .login(env("SCREEPS_API_USERNAME"), env("SCREEPS_API_PASSWORD"))
        .expect("failed to login");

    let my_info = client.my_info().unwrap();

    info!(
        "Logged in as {}, attempting to connect to stream.",
        my_info.username
    );

    let mut core = tokio_core::reactor::Core::new().expect("expected IO core to start up without issue.");

    let handle = core.handle();

    let ws_url = screeps_api::websocket::connecting::transform_url(&config.url)
        .expect("expected server api url to parse into websocket url.");

    let connection = websocket::ClientBuilder::from_url(&ws_url).async_connect(None, &handle);

    core.run(connection.then(|result| {
        let (client, _) = result.expect("connecting to server failed");

        let (sink, stream) = client.split();

        sink.send(OwnedMessage::Text(screeps_api::websocket::authenticate(
            &token_storage.take_token().unwrap(),
        ))).and_then(|sink| {
            let handler = Handler::new(token_storage, my_info, config);

            sink.send_all(
                (stream
                    .and_then(move |data| future::ok(handler.handle_data(data)))
                    .or_else(|err| {
                        warn!("error occurred: {}", err);

                        future::ok::<_, websocket::WebSocketError>(Box::new(stream::empty())
                            as Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>>)
                    })
                    .flatten()),
            )
        })
    })).expect("expected websocket connection to complete successfully, but an error occurred");
}

struct Handler<T>
where
    T: screeps_api::TokenStorage,
{
    tokens: T,
    info: screeps_api::MyInfo,
    config: Config,
}

impl<T> Handler<T>
where
    T: screeps_api::TokenStorage,
{
    fn new(tokens: T, info: screeps_api::MyInfo, config: Config) -> Self {
        Handler {
            tokens: tokens,
            info: info,
            config: config,
        }
    }

    fn handle_data(&self, data: OwnedMessage) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        match data {
            OwnedMessage::Text(string) => {
                let data =
                    SockjsMessage::parse(&string).expect("expected a correct SockJS message, found a parse error.");

                match data {
                    SockjsMessage::Open => debug!("SockJS connection opened"),
                    SockjsMessage::Heartbeat => debug!("SockJS heartbeat."),
                    SockjsMessage::Close { .. } => debug!("SockJS close"),
                    SockjsMessage::Message(message) => {
                        return Box::new(self.handle_parsed_message(message));
                    }
                    SockjsMessage::Messages(messages) => {
                        let results = messages
                            .into_iter()
                            .map(|message| Ok(self.handle_parsed_message(message)))
                            .collect::<Vec<_>>();

                        return Box::new(stream::iter_result::<_, _, websocket::WebSocketError>(results).flatten());
                    }
                }
            }
            OwnedMessage::Binary(data) => warn!("ignoring binary data from websocket: {:?}", data),
            OwnedMessage::Close(data) => warn!("connection closing: {:?}", data),
            OwnedMessage::Ping(data) => return Box::new(stream::once(Ok(OwnedMessage::Pong(data)))),
            OwnedMessage::Pong(_) => (),
        }

        Box::new(stream::empty())
    }

    fn handle_parsed_message(
        &self,
        message: screeps_api::websocket::parsing::ScreepsMessage,
    ) -> Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>> {
        match message {
            ScreepsMessage::AuthFailed => panic!("authentication with stored token failed!"),
            ScreepsMessage::AuthOk { new_token } => {
                info!(
                    "authentication succeeded, now authenticated as {}.",
                    self.info.username
                );
                // return the token to the token storage in case we need it again - we won't in this example
                // program, but this is a good practice
                //
                // TODO: find an efficient way to do this automatically in the handler.
                self.tokens.return_token(new_token);

                return Box::new(
                    self.config.subscribe_with(&self.info.user_id).chain(
                        stream::futures_unordered(vec![
                            future::lazy(|| {
                                warn!("subscribed!");
                                future::ok::<_, websocket::WebSocketError>(stream::empty())
                            }),
                        ]).flatten(),
                    ),
                ) as Box<Stream<Item = OwnedMessage, Error = websocket::WebSocketError>>;
            }
            ScreepsMessage::ChannelUpdate { update } => {
                self.handle_update(update);
            }
            ScreepsMessage::ServerProtocol { protocol } => {
                info!("server protocol: {}", protocol);
            }
            ScreepsMessage::ServerTime { time } => {
                info!("server time: {}", time);
            }
            ScreepsMessage::ServerPackage { package } => {
                info!("server package: {}", package);
            }
            ScreepsMessage::Other(other) => {
                warn!("ScreepsMessage::Other: {}", other);
            }
        }

        Box::new(stream::empty())
    }

    fn handle_update(&self, update: ChannelUpdate) {
        match update {
            ChannelUpdate::UserCpu { user_id, update } => info!("CPU: [{}] {:#?}", user_id, update),
            ChannelUpdate::RoomMapView {
                room_name,
                shard_name,
                update,
            } => {
                info!(
                    "Map View: [{}/{}] {:?}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    update
                );
            }
            ChannelUpdate::RoomDetail {
                room_name,
                shard_name,
                update,
            } => {
                debug!(
                    "Room Detail: [{}/{}] {:?}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    update
                );
                info!(
                    "Room {}/{}: {}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name,
                    serde_json::to_string_pretty(&serde_json::Value::Object(
                        update.objects.iter().cloned().collect()
                    )).expect("expected to_string to succeed on plain map.")
                );
            }
            ChannelUpdate::NoRoomDetail {
                room_name,
                shard_name,
            } => {
                info!(
                    "Room Skipped: {}/{}",
                    shard_name.as_ref().map(AsRef::as_ref).unwrap_or("<None>"),
                    room_name
                );
            }
            ChannelUpdate::UserConsole { user_id, update } => {
                info!("Console: [{}] {:#?}", user_id, update);
            }
            ChannelUpdate::UserCredits { user_id, update } => {
                info!("Credits: [{}] {}", user_id, update);
            }
            ChannelUpdate::UserMessage { user_id, update } => {
                info!("New message: [{}] {:#?}", user_id, update);
            }
            ChannelUpdate::UserConversation {
                user_id,
                target_user_id,
                update,
            } => {
                info!(
                    "Conversation update: [{}->{}] {:#?}",
                    user_id, target_user_id, update
                );
            }
            ChannelUpdate::Other { channel, update } => {
                warn!(
                    "ChannelUpdate::Other: {}\n{}",
                    channel,
                    serde_json::to_string_pretty(&update).expect("failed to serialize json string")
                );
            }
        }
    }
}
<|endoftext|>


<commit_before>// During tests, we disable the renderer and that causes a bunch of warnings that we just want
// to get rid of.
// See Cargo.toml for an explanation of this attribute
#![cfg_attr(any(feature = "test", test), allow(unused_imports))]

use std::env;
use std::thread;
use std::process::{self, Stdio};
use std::sync::mpsc;

use query::{Query, Response};
use messenger;

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(Stdio::piped())
            .stdout(Stdio::piped())
            .stderr(Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            // Continously read responses from the renderer process
            // This is in its own thread because it uses blocking IO and we don't want to block
            // the main thread waiting for the renderer process
            messenger::read_forever(
                renderer_stdout,
                "bug: unable to read data from renderer process",
                "bug: failed to read response from renderer process",
                |resp| response_tx.send(resp).map_err(|_| ()),
            );
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        messenger::send(
            match self.process.stdin {
                Some(ref mut stdin) => stdin,
                None => unreachable!("bug: renderer process was not opened with stdin"),
            },
            &query,
            "bug: unable to write final newline when sending query"
        ).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        self.response_channel.recv().unwrap_or_else(|_| {
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            self.exit_process()
        })
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        let status = self.thread_handle.take().ok_or_else(|| {
            unreachable!("bug: the thread handle was used but the process did not end");
        }).and_then(|handle| {
            // First check if the other thread panicked before it quit
            handle.join().map_err(|_| ())
        }).and_then(|_| {
            // Then check if the renderer process ended normally
            self.process.wait()
                .map_err(|_| unreachable!("bug: renderer process never ran even though we exited"))
                .and_then(|status| if status.success() { Ok(()) } else { Err(()) })
        });

        match status {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(1),
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::Event>, mpsc::Receiver<::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<commit_msg>Instead of using cfg_attr to get rid of possibly too many errors, we're just using cfg on each line that needs it<commit_after>use std::sync::mpsc;
#[cfg(not(any(feature = "test", test)))]
use std::{env, thread, process};

use query::{Query, Response};
#[cfg(not(any(feature = "test", test)))]
use messenger;

/// Manages the renderer process and all communication with it
#[cfg(not(any(feature = "test", test)))]
pub struct RendererProcess {
    process: process::Child,
    thread_handle: Option<thread::JoinHandle<()>>,
    /// Channel for receiving responses from the rendering process
    response_channel: mpsc::Receiver<Response>,
}

#[cfg(not(any(feature = "test", test)))]
impl RendererProcess {
    /// Spawn the renderer process and also a thread for communicating with that process
    pub fn new() -> Self {
        let current_exe = env::current_exe()
            .expect("Could not read path of the currently running executable")
            .into_os_string();
        let mut renderer_process = process::Command::new(current_exe)
            .env("RUN_TURTLE_CANVAS", "true")
            .stdin(process::Stdio::piped())
            .stdout(process::Stdio::piped())
            .stderr(process::Stdio::inherit())
            .spawn()
            .expect("renderer process failed to start");

        let renderer_stdout = renderer_process.stdout.take()
            .expect("renderer process was not opened with stdout");
        let (response_tx, response_rx) = mpsc::channel();
        let handle = thread::spawn(move || {
            // Continously read responses from the renderer process
            // This is in its own thread because it uses blocking IO and we don't want to block
            // the main thread waiting for the renderer process
            messenger::read_forever(
                renderer_stdout,
                "bug: unable to read data from renderer process",
                "bug: failed to read response from renderer process",
                |resp| response_tx.send(resp).map_err(|_| ()),
            );
        });

        Self {
            process: renderer_process,
            thread_handle: Some(handle),
            response_channel: response_rx,
        }
    }

    /// Sends a query and automatically decides whether or not to wait for a response.
    ///
    /// If a query does not require a response, this function will return immediately after
    /// sending the query
    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        messenger::send(
            match self.process.stdin {
                Some(ref mut stdin) => stdin,
                None => unreachable!("bug: renderer process was not opened with stdin"),
            },
            &query,
            "bug: unable to write final newline when sending query"
        ).unwrap_or_else(|_| {
            // Something went wrong while sending the query, check if the renderer process
            // panicked (exited with an error)
            match self.process.try_wait() {
                Ok(Some(status)) => {
                    if status.success() {
                        // The window/renderer process was closed normally
                        process::exit(0);
                    }
                    else {
                        // Something went wrong, likely the other thread panicked
                        process::exit(1);
                    }
                },
                Ok(None) => panic!("bug: failed to send query even though renderer process was still running"),
                Err(_) => panic!("bug: unable to check the exit status of the renderer process"),
            }
        });

        // Requests need responses
        if let Query::Request(_) = query {
            Some(self.wait_for_response())
        }
        else {
            None
        }
    }

    fn wait_for_response(&mut self) -> Response {
        self.response_channel.recv().unwrap_or_else(|_| {
            // The client thread has exited, that means that the renderer process has exited
            // and the window has closed
            self.exit_process()
        })
    }

    /// Exits the current process with the correct error code
    ///
    /// Panics if the thread handle has already been consumed
    #[inline]
    fn exit_process(&mut self) -> ! {
        let status = self.thread_handle.take().ok_or_else(|| {
            unreachable!("bug: the thread handle was used but the process did not end");
        }).and_then(|handle| {
            // First check if the other thread panicked before it quit
            handle.join().map_err(|_| ())
        }).and_then(|_| {
            // Then check if the renderer process ended normally
            self.process.wait()
                .map_err(|_| unreachable!("bug: renderer process never ran even though we exited"))
                .and_then(|status| if status.success() { Ok(()) } else { Err(()) })
        });

        match status {
            Ok(_) => process::exit(0),
            Err(_) => process::exit(1),
        }
    }
}

#[cfg(not(any(feature = "test", test)))]
impl Drop for RendererProcess {
    fn drop(&mut self) {
        // If the current thread is panicking, we want to abort right away
        // because otherwise there is code in the rendering thread that will call
        // process::exit(0) and then the exit code will be 0 instead of 1
        if thread::panicking() {
            process::exit(1);
        }

        // If this is just a normal ending of the main thread, we want to leave the renderer
        // running so that the user can see their drawing as long as they keep the window open
        if let Some(handle) = self.thread_handle.take() {
            handle.join().unwrap_or_else(|_| {
                // If this returns an error, the other thread panicked
                process::exit(1);
            });
        }

        // Now that the thread has completed, the process likely has as well (or is about to).
        // Check its exit status to see if we need to quit with an error
        match self.process.wait() {
            Ok(status) => if !status.success() {
                process::exit(1);
            },
            Err(_) => unreachable!("bug: renderer process never ran even though we exited"),
        }
    }
}

/// A special "renderer process" specifically for tests. Simulates the renderer process by
/// providing all of the same functionality and reusing internal parts of the
#[cfg(any(feature = "test", test))]
pub struct RendererProcess {
    app: ::app::TurtleApp,
    events: (mpsc::Sender<::Event>, mpsc::Receiver<::Event>),
    drawing: (mpsc::Sender<::query::DrawingCommand>, mpsc::Receiver<::query::DrawingCommand>),
}

#[cfg(any(feature = "test", test))]
impl RendererProcess {
    pub fn new() -> Self {
        Self {
            app: ::app::TurtleApp::new(),
            events: mpsc::channel(),
            drawing: mpsc::channel(),
        }
    }

    pub fn send_query(&mut self, query: Query) -> Option<Response> {
        ::server::handle_query_for_test_use_only(query, &mut self.app, &self.events.1, &self.drawing.0)
            .expect("test bug: a query failed to be successful")
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::Cursor;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };

    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, coverage: &[TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        let name = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("filename", name));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));

        writer.write_event(Event::Start(class));
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("line", "n"));
            line.push_attribute(("hits", "h"));
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(writer: &mut Writer<T>,
                          package: &str,
                          coverage: &[TracerData],
                          config: &Config) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("Cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<commit_msg>Implemented package and class exporting.<commit_after>use std::fs::File;
use std::ffi::OsStr;
use std::path::Path;
use std::io::prelude::*;
use std::io::Cursor;
use std::collections::HashSet;
use quick_xml::writer::Writer;
use quick_xml::events::{Event, BytesEnd, BytesStart};
use quick_xml::errors::Result;
use tracer::TracerData;
use config::Config;

fn write_header<T:Write>(writer: &mut Writer<T>, config: &Config) -> Result<usize> {

    writer.write_event(Event::Start(BytesStart::borrowed(b"sources", b"sources".len())))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"source", b"source".len())))?;

    let parent_folder = match config.manifest.parent() {
        Some(s) => s.to_str().unwrap_or_default(),
        None => "",
    };

    writer.write(parent_folder.as_bytes()).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"source")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"sources")))
}

/// Input only from single source file
fn write_class<T:Write>(writer: &mut Writer<T>, coverage: &[&TracerData]) ->Result<usize> {
    if !coverage.is_empty() {
        let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
        let covered = (covered as f32)/(coverage.len() as f32);
        let name = coverage[0].path.file_name().unwrap_or_default().to_str().unwrap_or_default();
        let mut class = BytesStart::owned(b"class".to_vec(), b"class".len());
        class.push_attribute(("filename", name));
        class.push_attribute(("line-rate", covered.to_string().as_ref()));

        writer.write_event(Event::Start(class))?;
        writer.write_event(Event::Start(BytesStart::borrowed(b"lines", b"lines".len())))?;
        for trace in coverage {
            let mut line = BytesStart::owned(b"line".to_vec(), b"line".len());
            line.push_attribute(("line", trace.line.to_string().as_ref()));
            line.push_attribute(("hits", trace.hits.to_string().as_ref()));
            writer.write_event(Event::Empty(line))?;
        }
        writer.write_event(Event::End(BytesEnd::borrowed(b"lines")))?;
        writer.write_event(Event::End(BytesEnd::borrowed(b"class")))
    } else {
        Ok(0)
    }
}

/// Input only tracer data from a single source folder
fn write_package<T:Write>(mut writer: &mut Writer<T>,
                          package: &str,
                          coverage: &[&TracerData]) -> Result<usize> {
    let covered = coverage.iter().filter(|&x| (x.hits > 0)).count();
    let covered = (covered as f32)/(coverage.len() as f32);

    let mut pack = BytesStart::owned(b"package".to_vec(), b"package".len());
    pack.push_attribute(("name", package));
    pack.push_attribute(("line-rate", covered.to_string().as_ref()));
    writer.write_event(Event::Start(pack))?;
    writer.write_event(Event::Start(BytesStart::borrowed(b"classes", b"classes".len())))?;
    let mut file_set: HashSet<&OsStr> = HashSet::new();

    for t in coverage.iter() {
        let filename = t.path.file_name();
        if !file_set.contains(filename.unwrap_or_default()) {
            file_set.insert(filename.unwrap_or_default());
            let class = coverage.iter()
                                .filter(|x| x.path.file_name() == filename)
                                .map(|x| *x)
                                .collect::<Vec<_>>();

            write_class(&mut writer, &class)?;
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"classes")))?;
    writer.write_event(Event::End(BytesEnd::borrowed(b"package")))
}

pub fn export(coverage_data: &[TracerData], config: &Config) {
    let mut file = File::create("cobertura.xml").unwrap();
    let mut writer = Writer::new(Cursor::new(Vec::new()));
    // Construct cobertura xml
    let covered = coverage_data.iter().filter(|&x| (x.hits > 0 )).count();
    let total = coverage_data.len();
    let line_rate = (covered as f32)/(total as f32);
    let mut cov = BytesStart::owned(b"coverage".to_vec(), b"coverage".len());
    cov.push_attribute(("line-rate", line_rate.to_string().as_ref()));
    writer.write_event(Event::Start(cov)).unwrap();
    let _ = write_header(&mut writer, &config);
    // other data
    writer.write_event(Event::Start(BytesStart::borrowed(b"packages", b"packages".len()))).unwrap();

    let mut folder_set: HashSet<&Path> = HashSet::new();
    for t in coverage_data.iter() {
        let parent = match t.path.parent() {
            Some(x) => x,
            None => continue,
        };
        if !folder_set.contains(&parent) {
            folder_set.insert(parent);
            let package_name = match parent.strip_prefix(config.manifest.as_path()) {
                Ok(p) => p,
                _ => parent,
            };
            let package = coverage_data.iter().filter(|x| x.path.parent() == Some(&parent)).collect::<Vec<_>>();
            let _ = write_package(&mut writer, package_name.to_str().unwrap_or_default(), &package);
        }
    }

    writer.write_event(Event::End(BytesEnd::borrowed(b"packages"))).unwrap();
    writer.write_event(Event::End(BytesEnd::borrowed(b"coverage"))).unwrap();
    let result = writer.into_inner().into_inner();
    file.write_all(&result).unwrap();
}
<|endoftext|>


<commit_before>use parser;
use runtime::Interpreter;
use runtime::StatementResult;
use ast_walk_interpreter::AstWalkInterpreter;
use value::Value;
use value::Number;

fn run_and_get_last_value(code: &str) -> Value {
    match run_and_get_last_result(code) {
        StatementResult::Value(ref v) => v.clone(),
        _ => panic!("Cannot unwrap value"),
    }
}

fn run_and_get_last_result(code: &str) -> StatementResult {
    let ast = parser::program(code);
    match ast {
        Ok(ast) => {
            let mut ast_walk_interpreter = AstWalkInterpreter::new();
            let reference_val = ast_walk_interpreter.run_ast_as_program(&ast)
                .unwrap()
                .unwrap();
            return reference_val;
            /*
             * Test plan forward once LLVM backend is written
            let mut llvm_interpreter = LLVMInterpreter::new()
            let llvm_val = llvm_interpreter.run_ast_as_program(&ast)
                .unwrap().unwrap();

            if (llvm_val == reference_val) {
                reference_val
            }
            else {
                panic!("LLVM value: {:?} does not agree with Reference Value {:?}"
            }
            */
        }
        Err(_) => panic!("{:?}", ast),
    }
}

#[test]
fn literal_value_int() {
    assert_eq!(run_and_get_last_value("7;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn literal_value_float() {
    assert_eq!(run_and_get_last_value("7.0;"),
               Value::Number(Number::Float(7.0)));
}

#[test]
fn literal_value_bool_true() {
    assert_eq!(run_and_get_last_value("true;"), Value::Bool(true));
}

#[test]
fn literal_value_bool_false() {
    assert_eq!(run_and_get_last_value("false;"), Value::Bool(false));
}

#[test]
fn variable_reassignment_with_type_change() {
    assert_eq!(run_and_get_last_value("var x = 10; x = false; x;"),
               Value::Bool(false));
}

#[test]
fn identifier_name() {
    assert_eq!(run_and_get_last_value("var _x_X123_ = false; _x_X123_;"),
               Value::Bool(false));
}

#[test]
fn arithmetic_1() {
    assert_eq!(run_and_get_last_value("(1 + 3 / (1 + 1)) * 12 - 1;"),
               Value::Number(Number::Float(29.0)));
}

#[test]
fn arithmetic_2() {
    assert_eq!(run_and_get_last_value("3 - 5 + 12 / 4 - 1;"),
               Value::Number(Number::Float(0.0)));
}

#[test]
fn arithmetic_3() {
    assert_eq!(run_and_get_last_value("10 // 3 + 5 - 12;"),
               Value::Number(Number::Integer(-4)));
}

#[test]
fn arithmetic_4() {
    assert_eq!(run_and_get_last_value("-10 / 4 + -5 - 12;"),
               Value::Number(Number::Float(-19.5)));
}

#[test]
fn i64_support() {
    assert_eq!(run_and_get_last_value("9223372036854775807;"),
               Value::Number(Number::Integer(9223372036854775807)));
}

#[test]
fn f64_support() {
    assert_eq!(run_and_get_last_value("1234567890.012345678;"),
               Value::Number(Number::Float(1234567890.012345678)));
}

#[test]
fn logical_and() {
    assert_eq!(run_and_get_last_value("true and true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false and true;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("true and false;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("false and false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 and 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 and 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 and 0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("5.6 and 0.0;"), Value::Bool(false));
}

#[test]
fn logical_or() {
    assert_eq!(run_and_get_last_value("true or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("true or false;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 or 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 or 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 or 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("0 or 0.0;"), Value::Bool(false));
}

#[test]
fn logical_not() {
    assert_eq!(run_and_get_last_value("not true;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not false;"), Value::Bool(true));

    assert_eq!(run_and_get_last_value("not 5;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("not 0.0;"), Value::Bool(true));
}

#[test]
fn comparisons() {
    assert_eq!(run_and_get_last_value("1 < 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1 > 2;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("1.0 < 2.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1.0 > 2.0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("4 <= 4;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("2 >= 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3 == 3;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.1;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("true == 1;"), Value::Bool(false));
}

#[test]
fn variable_declaration() {
    assert_eq!(run_and_get_last_value("var x = 5; x;"),
               Value::Number(Number::Integer(5)));
}

#[test]
fn variable_reassignment() {
    assert_eq!(run_and_get_last_value("var x = 10; x = 42; x;"),
               Value::Number(Number::Integer(42)));
}

#[test]
fn id_in_expr() {
    assert_eq!(run_and_get_last_value("var x = 5; x / 2 + x;"),
               Value::Number(Number::Float(7.5)));
}

#[test]
fn create_tuple() {
    assert_eq!(run_and_get_last_value("(1, 2, 3);"),
               Value::Tuple(vec![Value::Number(Number::Integer(1)),
                                 Value::Number(Number::Integer(2)),
                                 Value::Number(Number::Integer(3))]));
}

#[test]
fn add_tuples() {
    assert_eq!(run_and_get_last_value("(\"a\",) + (\"t\",);"),
               Value::Tuple(vec![Value::String("a".to_owned()), Value::String("t".to_owned())]));
}

#[test]
fn index_tuple() {
    assert_eq!(run_and_get_last_value("(1, 2, 3)[0];"),
               Value::Number(Number::Integer(1)));
}

#[test]
fn mix_call_and_index() {
    assert_eq!(run_and_get_last_value("((fn() { return false; },), 2, 3)[0][0]();"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("fn() { return fn() { return (1, 2, true); }; }()()[2];"),
               Value::Bool(true));
}

#[test]
fn if_expr_true() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 < 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(6)));
}

#[test]
fn if_expr_false() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 > 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn loop_and_break() {
    assert_eq!(run_and_get_last_value("var x = 5; loop { if x > 20 { break; } x = x + 5; } x;"),
               Value::Number(Number::Integer(25)));
}

#[test]
fn block_env() {
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; } x;"),
               Value::Number(Number::Integer(5)));
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; x; }"),
               Value::Number(Number::Integer(20)));
}

#[test]
fn test_println_runs() {
    assert_eq!(run_and_get_last_result("println();"), StatementResult::None);
    assert_eq!(run_and_get_last_result("println(5, true,);"),
               StatementResult::None);
}

#[test]
fn test_factorial() {
    assert_eq!(run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * \
                                       factorial(n - 1); } factorial(5); "),
               Value::Number(Number::Integer(120)));
    assert_eq!(run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * \
                                       factorial(n - 1); } factorial(8); "),
               Value::Number(Number::Integer(40320)));
}

#[test]
fn test_curried_add() {
    let code = "fn addX(x) {
    return fn(y) {
        return x + y;
    };
}

addX(10)(20);
";
    assert_eq!(run_and_get_last_value(code),
               Value::Number(Number::Integer(30)));
}

#[test]
fn test_y_combinator() {
    let code = "fn Y(f) {
    var lazywrapper = fn () { return Y (f); };
    return f(lazywrapper);
}

fn factorialwrap(lazywrapfact) {
    fn factorial(i) {
        if (i == 0) {
            return 1;
        } else {
            return i * lazywrapfact()(i - 1);
        }
    }
    return factorial;
}

var fact = Y(factorialwrap);

fact(4);
";
    assert_eq!(run_and_get_last_value(code),
               Value::Number(Number::Integer(24)));
}

#[test]
fn test_string_concat() {
    assert_eq!(run_and_get_last_value("\"abc\" + \"def\";"),
               Value::String("abcdef".to_owned()));
    assert_eq!(run_and_get_last_value("\"abc\" + 123;"),
               Value::String("abc123".to_owned()));
    assert_eq!(run_and_get_last_value("\"abc\" + true;"),
               Value::String("abctrue".to_owned()));
    assert_eq!(run_and_get_last_value("456 + \"abc\";"),
               Value::String("456abc".to_owned()));
    assert_eq!(run_and_get_last_value("false + \"abc\";"),
               Value::String("falseabc".to_owned()));
}

#[test]
fn test_len() {
    assert_eq!(run_and_get_last_value("len((1, 2, 3));"),
               Value::Number(Number::Integer(3)));
}
<commit_msg>Now that enums sizes are reduced, test factorial(15)<commit_after>use parser;
use runtime::Interpreter;
use runtime::StatementResult;
use ast_walk_interpreter::AstWalkInterpreter;
use value::Value;
use value::Number;

fn run_and_get_last_value(code: &str) -> Value {
    match run_and_get_last_result(code) {
        StatementResult::Value(ref v) => v.clone(),
        _ => panic!("Cannot unwrap value"),
    }
}

fn run_and_get_last_result(code: &str) -> StatementResult {
    let ast = parser::program(code);
    match ast {
        Ok(ast) => {
            let mut ast_walk_interpreter = AstWalkInterpreter::new();
            let reference_val = ast_walk_interpreter.run_ast_as_program(&ast)
                .unwrap()
                .unwrap();
            return reference_val;
            /*
             * Test plan forward once LLVM backend is written
            let mut llvm_interpreter = LLVMInterpreter::new()
            let llvm_val = llvm_interpreter.run_ast_as_program(&ast)
                .unwrap().unwrap();

            if (llvm_val == reference_val) {
                reference_val
            }
            else {
                panic!("LLVM value: {:?} does not agree with Reference Value {:?}"
            }
            */
        }
        Err(_) => panic!("{:?}", ast),
    }
}

#[test]
fn literal_value_int() {
    assert_eq!(run_and_get_last_value("7;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn literal_value_float() {
    assert_eq!(run_and_get_last_value("7.0;"),
               Value::Number(Number::Float(7.0)));
}

#[test]
fn literal_value_bool_true() {
    assert_eq!(run_and_get_last_value("true;"), Value::Bool(true));
}

#[test]
fn literal_value_bool_false() {
    assert_eq!(run_and_get_last_value("false;"), Value::Bool(false));
}

#[test]
fn variable_reassignment_with_type_change() {
    assert_eq!(run_and_get_last_value("var x = 10; x = false; x;"),
               Value::Bool(false));
}

#[test]
fn identifier_name() {
    assert_eq!(run_and_get_last_value("var _x_X123_ = false; _x_X123_;"),
               Value::Bool(false));
}

#[test]
fn arithmetic_1() {
    assert_eq!(run_and_get_last_value("(1 + 3 / (1 + 1)) * 12 - 1;"),
               Value::Number(Number::Float(29.0)));
}

#[test]
fn arithmetic_2() {
    assert_eq!(run_and_get_last_value("3 - 5 + 12 / 4 - 1;"),
               Value::Number(Number::Float(0.0)));
}

#[test]
fn arithmetic_3() {
    assert_eq!(run_and_get_last_value("10 // 3 + 5 - 12;"),
               Value::Number(Number::Integer(-4)));
}

#[test]
fn arithmetic_4() {
    assert_eq!(run_and_get_last_value("-10 / 4 + -5 - 12;"),
               Value::Number(Number::Float(-19.5)));
}

#[test]
fn i64_support() {
    assert_eq!(run_and_get_last_value("9223372036854775807;"),
               Value::Number(Number::Integer(9223372036854775807)));
}

#[test]
fn f64_support() {
    assert_eq!(run_and_get_last_value("1234567890.012345678;"),
               Value::Number(Number::Float(1234567890.012345678)));
}

#[test]
fn logical_and() {
    assert_eq!(run_and_get_last_value("true and true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false and true;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("true and false;"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("false and false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 and 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 and 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 and 0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("5.6 and 0.0;"), Value::Bool(false));
}

#[test]
fn logical_or() {
    assert_eq!(run_and_get_last_value("true or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or true;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("true or false;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("false or false;"),
               Value::Bool(false));

    assert_eq!(run_and_get_last_value("2 or 1;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3.0 or 22.5;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("5 or 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("0 or 0.0;"), Value::Bool(false));
}

#[test]
fn logical_not() {
    assert_eq!(run_and_get_last_value("not true;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not false;"), Value::Bool(true));

    assert_eq!(run_and_get_last_value("not 5;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("not 0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("not 0.0;"), Value::Bool(true));
}

#[test]
fn comparisons() {
    assert_eq!(run_and_get_last_value("1 < 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1 > 2;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("1.0 < 2.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("1.0 > 2.0;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("4 <= 4;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("2 >= 2;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("3 == 3;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.0;"), Value::Bool(true));
    assert_eq!(run_and_get_last_value("7 == 7.1;"), Value::Bool(false));
    assert_eq!(run_and_get_last_value("true == 1;"), Value::Bool(false));
}

#[test]
fn variable_declaration() {
    assert_eq!(run_and_get_last_value("var x = 5; x;"),
               Value::Number(Number::Integer(5)));
}

#[test]
fn variable_reassignment() {
    assert_eq!(run_and_get_last_value("var x = 10; x = 42; x;"),
               Value::Number(Number::Integer(42)));
}

#[test]
fn id_in_expr() {
    assert_eq!(run_and_get_last_value("var x = 5; x / 2 + x;"),
               Value::Number(Number::Float(7.5)));
}

#[test]
fn create_tuple() {
    assert_eq!(run_and_get_last_value("(1, 2, 3);"),
               Value::Tuple(vec![Value::Number(Number::Integer(1)),
                                 Value::Number(Number::Integer(2)),
                                 Value::Number(Number::Integer(3))]));
}

#[test]
fn add_tuples() {
    assert_eq!(run_and_get_last_value("(\"a\",) + (\"t\",);"),
               Value::Tuple(vec![Value::String("a".to_owned()), Value::String("t".to_owned())]));
}

#[test]
fn index_tuple() {
    assert_eq!(run_and_get_last_value("(1, 2, 3)[0];"),
               Value::Number(Number::Integer(1)));
}

#[test]
fn mix_call_and_index() {
    assert_eq!(run_and_get_last_value("((fn() { return false; },), 2, 3)[0][0]();"),
               Value::Bool(false));
    assert_eq!(run_and_get_last_value("fn() { return fn() { return (1, 2, true); }; }()()[2];"),
               Value::Bool(true));
}

#[test]
fn if_expr_true() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 < 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(6)));
}

#[test]
fn if_expr_false() {
    assert_eq!(run_and_get_last_value("var x = 5; if 1 > 2 { x = 6; } else { x = 7; } x;"),
               Value::Number(Number::Integer(7)));
}

#[test]
fn loop_and_break() {
    assert_eq!(run_and_get_last_value("var x = 5; loop { if x > 20 { break; } x = x + 5; } x;"),
               Value::Number(Number::Integer(25)));
}

#[test]
fn block_env() {
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; } x;"),
               Value::Number(Number::Integer(5)));
    assert_eq!(run_and_get_last_value("var x = 5; { var x = 10; x = 20; x; }"),
               Value::Number(Number::Integer(20)));
}

#[test]
fn test_println_runs() {
    assert_eq!(run_and_get_last_result("println();"), StatementResult::None);
    assert_eq!(run_and_get_last_result("println(5, true,);"),
               StatementResult::None);
}

#[test]
fn test_factorial() {
    assert_eq!(run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * \
                                       factorial(n - 1); } factorial(5); "),
               Value::Number(Number::Integer(120)));
    assert_eq!(run_and_get_last_value("fn factorial(n) { if n < 2 { return 1; } return n * \
                                       factorial(n - 1); } factorial(15); "),
               Value::Number(Number::Integer(1307674368000)));
}

#[test]
fn test_curried_add() {
    let code = "fn addX(x) {
    return fn(y) {
        return x + y;
    };
}

addX(10)(20);
";
    assert_eq!(run_and_get_last_value(code),
               Value::Number(Number::Integer(30)));
}

#[test]
fn test_y_combinator() {
    let code = "fn Y(f) {
    var lazywrapper = fn () { return Y (f); };
    return f(lazywrapper);
}

fn factorialwrap(lazywrapfact) {
    fn factorial(i) {
        if (i == 0) {
            return 1;
        } else {
            return i * lazywrapfact()(i - 1);
        }
    }
    return factorial;
}

var fact = Y(factorialwrap);

fact(4);
";
    assert_eq!(run_and_get_last_value(code),
               Value::Number(Number::Integer(24)));
}

#[test]
fn test_string_concat() {
    assert_eq!(run_and_get_last_value("\"abc\" + \"def\";"),
               Value::String("abcdef".to_owned()));
    assert_eq!(run_and_get_last_value("\"abc\" + 123;"),
               Value::String("abc123".to_owned()));
    assert_eq!(run_and_get_last_value("\"abc\" + true;"),
               Value::String("abctrue".to_owned()));
    assert_eq!(run_and_get_last_value("456 + \"abc\";"),
               Value::String("456abc".to_owned()));
    assert_eq!(run_and_get_last_value("false + \"abc\";"),
               Value::String("falseabc".to_owned()));
}

#[test]
fn test_len() {
    assert_eq!(run_and_get_last_value("len((1, 2, 3));"),
               Value::Number(Number::Integer(3)));
}
<|endoftext|>


<commit_before>mod cd;
mod ls;
mod echo;
mod set;
mod pwd;
mod exit;

use std::path::PathBuf;
use std::collections::HashMap;

use rsh::State;
use rsh::utils;

pub use self::cd::cd;
pub use self::ls::ls;
pub use self::echo::echo;
pub use self::set::{set, unset, get};
pub use self::pwd::pwd;
pub use self::exit::exit;

pub type Builtin = fn(&mut State) -> i32;

pub fn load() -> HashMap<String, Builtin> {
    let mut h: HashMap<String, Builtin> = HashMap::new();

    h.insert("cd".to_string(), cd);
    h.insert("ls".to_string(), ls);
    h.insert("echo".to_string(), echo);
    h.insert("set".to_string(), set);
    h.insert("unset".to_string(), unset);
    h.insert("get".to_string(), get);
    h.insert("pwd".to_string(), pwd);
    h.insert("exit".to_string(), exit);

    h
}

// TODO make these tests better once we can capture stdout
#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_cd() {
        let mut s = State::default();
        s.argv = vec!["cd".to_string(), "/".to_string()];

        let i = cd(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.cwd.to_str().unwrap(), "/");
    }

    #[test]
    #[should_panic]
    fn test_cd_bad_path() {
        let mut s = State::default();
        s.argv = vec!["cd".to_string(), "/123".to_string()];

        let i = cd(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_set() {
        let mut s = State::default();
        s.argv = vec!["set".to_string(), "test".to_string(), "hello world".to_string()];

        let i = set(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.variables.get("test").unwrap(), "hello world");
    }

    #[test]
    fn test_unset() {
        let mut s = State::default();
        s.variables.insert("test".to_string(), "hello world".to_string());
        s.argv = vec!["unset".to_string(), "test".to_string()];

        let i = unset(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.variables.get("test"), None);
    }

    #[test]
    fn test_get() {
        let mut s = State::default();
        s.variables.insert("test".to_string(), "hello world".to_string());
        s.argv = vec!["get".to_string(), "test".to_string()];

        let i = get(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_ls() {
        let mut s = State::default();
        s.argv = vec!["ls".to_string()];

        let i = ls(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_echo() {
        let mut s = State::default();
        s.argv = vec!["echo".to_string(), "\"Hello world!\"".to_string()];

        let i = get(&mut s);

        assert_eq!(i, 0);
    }
}
<commit_msg>Added a test for pwd<commit_after>mod cd;
mod ls;
mod echo;
mod set;
mod pwd;
mod exit;

use std::path::PathBuf;
use std::collections::HashMap;

use rsh::State;
use rsh::utils;

pub use self::cd::cd;
pub use self::ls::ls;
pub use self::echo::echo;
pub use self::set::{set, unset, get};
pub use self::pwd::pwd;
pub use self::exit::exit;

pub type Builtin = fn(&mut State) -> i32;

pub fn load() -> HashMap<String, Builtin> {
    let mut h: HashMap<String, Builtin> = HashMap::new();

    h.insert("cd".to_string(), cd);
    h.insert("ls".to_string(), ls);
    h.insert("echo".to_string(), echo);
    h.insert("set".to_string(), set);
    h.insert("unset".to_string(), unset);
    h.insert("get".to_string(), get);
    h.insert("pwd".to_string(), pwd);
    h.insert("exit".to_string(), exit);

    h
}

// TODO make these tests better once we can capture stdout
#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_cd() {
        let mut s = State::default();
        s.argv = vec!["cd".to_string(), "/".to_string()];

        let i = cd(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.cwd.to_str().unwrap(), "/");
    }

    #[test]
    #[should_panic]
    fn test_cd_bad_path() {
        let mut s = State::default();
        s.argv = vec!["cd".to_string(), "/123".to_string()];

        let i = cd(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_set() {
        let mut s = State::default();
        s.argv = vec!["set".to_string(), "test".to_string(), "hello world".to_string()];

        let i = set(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.variables.get("test").unwrap(), "hello world");
    }

    #[test]
    fn test_unset() {
        let mut s = State::default();
        s.variables.insert("test".to_string(), "hello world".to_string());
        s.argv = vec!["unset".to_string(), "test".to_string()];

        let i = unset(&mut s);

        assert_eq!(i, 0);
        assert_eq!(s.variables.get("test"), None);
    }

    #[test]
    fn test_get() {
        let mut s = State::default();
        s.variables.insert("test".to_string(), "hello world".to_string());
        s.argv = vec!["get".to_string(), "test".to_string()];

        let i = get(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_ls() {
        let mut s = State::default();
        s.argv = vec!["ls".to_string()];

        let i = ls(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_echo() {
        let mut s = State::default();
        s.argv = vec!["echo".to_string(), "\"Hello world!\"".to_string()];

        let i = get(&mut s);

        assert_eq!(i, 0);
    }

    #[test]
    fn test_pwd() {
        let mut s = State::default();

        s.argv = vec!["pwd".to_string()];

        let i = pwd(&mut s);

        assert_eq!(i, 0);
    }
}
<|endoftext|>


<commit_before>// vim: tw=80
use super::*;

/// An item that's ready to be mocked.
///
/// It should be functionally identical or near-identical to the original item,
/// but with minor alterations that make it suitable for mocking, such as
/// altered lifetimes.
pub(crate) enum MockableItem {
    Module(MockableModule),
    Struct(MockableStruct)
}

impl From<(Attrs, Item)> for MockableItem {
    fn from((attrs, item): (Attrs, Item)) -> MockableItem {
        match item {
            Item::Impl(item_impl) =>
                MockableItem::Struct(MockableStruct::from(item_impl)),
            Item::ForeignMod(item_foreign_mod) =>
                MockableItem::Module(
                    MockableModule::from((attrs, item_foreign_mod))
                ),
            Item::Mod(item_mod) =>
                MockableItem::Module(MockableModule::from(item_mod)),
            Item::Trait(trait_) =>
                MockableItem::Struct(MockableStruct::from((attrs, trait_))),
            _ => panic!("automock does not support this item type")
        }
    }
}

impl From<MockableStruct> for MockableItem {
    fn from(mock: MockableStruct) -> MockableItem {
        // TODO: stuff like deselfify
        MockableItem::Struct(mock)
    }
}

pub(crate) struct MockableModule {
    pub vis: Visibility,
    pub mock_ident: Ident,
    pub mod_token: token::Mod,
    /// Ident of the original module, if any
    pub orig_ident: Option<Ident>,
    pub content: Vec<Item>
}

impl From<(Attrs, ItemForeignMod)> for MockableModule {
    fn from((attrs, foreign): (Attrs, ItemForeignMod)) -> MockableModule {
        let orig_ident = None;
        let mock_ident = attrs.modname.expect(concat!(
            "module name is required when mocking foreign functions,",
            " like `#[automock(mod mock_ffi)]`"
        ));
        let mod_token = <Token![mod]>::default();
        let vis = Visibility::Public(VisPublic{
            pub_token: <Token![pub]>::default()
        });
        let mut content = vec![
            // When mocking extern blocks, we pretend that they're modules, so
            // we need a "use super::*;" to ensure that types can resolve
            Item::Use(ItemUse {
                attrs: Vec::new(),
                vis: Visibility::Inherited,
                use_token: token::Use::default(),
                leading_colon: None,
                tree: UseTree::Path(UsePath {
                    ident: Ident::new("super", Span::call_site()),
                    colon2_token: token::Colon2::default(),
                    tree: Box::new(UseTree::Glob(UseGlob {
                        star_token: token::Star::default()
                    }))
                }),
                semi_token: token::Semi::default()
            })
        ];
        content.extend(foreign.items.into_iter()
            .map(|foreign_item| {
                match foreign_item {
                    ForeignItem::Fn(f) => {
                        let span = f.sig.span();
                        let mut sig = f.sig;

                        // When mocking extern blocks, we pretend that they're
                        // modules.  So we must supersuperfy everything by one
                        // level.
                        let vis = expectation_visibility(&f.vis, 1);

                        for arg in sig.inputs.iter_mut() {
                            if let FnArg::Typed(pt) = arg {
                                *pt.ty = supersuperfy(&*pt.ty, 1);
                            }
                        }
                        if let ReturnType::Type(_, ty) = &mut sig.output {
                                **ty = supersuperfy(&*ty, 1);
                        }

                        // Foreign functions are always unsafe.  Mock foreign
                        // functions should be unsafe too, to prevent "warning:
                        // unused unsafe" messages.
                        sig.unsafety = Some(Token![unsafe](span));
                        let block = Box::new(Block {
                            brace_token: token::Brace::default(),
                            stmts: Vec::new()
                        });

                        Item::Fn(
                            ItemFn {
                                attrs: f.attrs,
                                vis,
                                sig,
                                block
                            }
                        )
                    },
                    _ => {
                        compile_error(foreign_item.span(),
                            "Unsupported foreign item type"
                        );
                        Item::Verbatim(TokenStream::default())
                    }
                }
            }));
        MockableModule { vis, mock_ident, mod_token, orig_ident, content }
    }
}

impl From<ItemMod> for MockableModule {
    fn from(mod_: ItemMod) -> MockableModule {
        let span = mod_.span();
        // TODO: in the future, consider mocking non-public modules
        let vis = Visibility::Public(VisPublic{
            pub_token: Token![pub](mod_.vis.span())
        });
        let mock_ident = format_ident!("mock_{}", mod_.ident);
        let orig_ident = Some(mod_.ident);
        let mod_token = mod_.mod_token;
        let content = if let Some((_, content)) = mod_.content {
            content
        } else {
            compile_error(span,
            "automock can only mock inline modules, not modules from another file");
            Vec::new()
        };
        // TODO: demutify funcs
        // TODO: deimplify funcs
        MockableModule { vis, mock_ident, mod_token, orig_ident, content }
    }
}
<commit_msg>Delete an obsolete comment.<commit_after>// vim: tw=80
use super::*;

/// An item that's ready to be mocked.
///
/// It should be functionally identical or near-identical to the original item,
/// but with minor alterations that make it suitable for mocking, such as
/// altered lifetimes.
pub(crate) enum MockableItem {
    Module(MockableModule),
    Struct(MockableStruct)
}

impl From<(Attrs, Item)> for MockableItem {
    fn from((attrs, item): (Attrs, Item)) -> MockableItem {
        match item {
            Item::Impl(item_impl) =>
                MockableItem::Struct(MockableStruct::from(item_impl)),
            Item::ForeignMod(item_foreign_mod) =>
                MockableItem::Module(
                    MockableModule::from((attrs, item_foreign_mod))
                ),
            Item::Mod(item_mod) =>
                MockableItem::Module(MockableModule::from(item_mod)),
            Item::Trait(trait_) =>
                MockableItem::Struct(MockableStruct::from((attrs, trait_))),
            _ => panic!("automock does not support this item type")
        }
    }
}

impl From<MockableStruct> for MockableItem {
    fn from(mock: MockableStruct) -> MockableItem {
        MockableItem::Struct(mock)
    }
}

pub(crate) struct MockableModule {
    pub vis: Visibility,
    pub mock_ident: Ident,
    pub mod_token: token::Mod,
    /// Ident of the original module, if any
    pub orig_ident: Option<Ident>,
    pub content: Vec<Item>
}

impl From<(Attrs, ItemForeignMod)> for MockableModule {
    fn from((attrs, foreign): (Attrs, ItemForeignMod)) -> MockableModule {
        let orig_ident = None;
        let mock_ident = attrs.modname.expect(concat!(
            "module name is required when mocking foreign functions,",
            " like `#[automock(mod mock_ffi)]`"
        ));
        let mod_token = <Token![mod]>::default();
        let vis = Visibility::Public(VisPublic{
            pub_token: <Token![pub]>::default()
        });
        let mut content = vec![
            // When mocking extern blocks, we pretend that they're modules, so
            // we need a "use super::*;" to ensure that types can resolve
            Item::Use(ItemUse {
                attrs: Vec::new(),
                vis: Visibility::Inherited,
                use_token: token::Use::default(),
                leading_colon: None,
                tree: UseTree::Path(UsePath {
                    ident: Ident::new("super", Span::call_site()),
                    colon2_token: token::Colon2::default(),
                    tree: Box::new(UseTree::Glob(UseGlob {
                        star_token: token::Star::default()
                    }))
                }),
                semi_token: token::Semi::default()
            })
        ];
        content.extend(foreign.items.into_iter()
            .map(|foreign_item| {
                match foreign_item {
                    ForeignItem::Fn(f) => {
                        let span = f.sig.span();
                        let mut sig = f.sig;

                        // When mocking extern blocks, we pretend that they're
                        // modules.  So we must supersuperfy everything by one
                        // level.
                        let vis = expectation_visibility(&f.vis, 1);

                        for arg in sig.inputs.iter_mut() {
                            if let FnArg::Typed(pt) = arg {
                                *pt.ty = supersuperfy(&*pt.ty, 1);
                            }
                        }
                        if let ReturnType::Type(_, ty) = &mut sig.output {
                                **ty = supersuperfy(&*ty, 1);
                        }

                        // Foreign functions are always unsafe.  Mock foreign
                        // functions should be unsafe too, to prevent "warning:
                        // unused unsafe" messages.
                        sig.unsafety = Some(Token![unsafe](span));
                        let block = Box::new(Block {
                            brace_token: token::Brace::default(),
                            stmts: Vec::new()
                        });

                        Item::Fn(
                            ItemFn {
                                attrs: f.attrs,
                                vis,
                                sig,
                                block
                            }
                        )
                    },
                    _ => {
                        compile_error(foreign_item.span(),
                            "Unsupported foreign item type"
                        );
                        Item::Verbatim(TokenStream::default())
                    }
                }
            }));
        MockableModule { vis, mock_ident, mod_token, orig_ident, content }
    }
}

impl From<ItemMod> for MockableModule {
    fn from(mod_: ItemMod) -> MockableModule {
        let span = mod_.span();
        // TODO: in the future, consider mocking non-public modules
        let vis = Visibility::Public(VisPublic{
            pub_token: Token![pub](mod_.vis.span())
        });
        let mock_ident = format_ident!("mock_{}", mod_.ident);
        let orig_ident = Some(mod_.ident);
        let mod_token = mod_.mod_token;
        let content = if let Some((_, content)) = mod_.content {
            content
        } else {
            compile_error(span,
            "automock can only mock inline modules, not modules from another file");
            Vec::new()
        };
        // TODO: demutify funcs
        // TODO: deimplify funcs
        MockableModule { vis, mock_ident, mod_token, orig_ident, content }
    }
}
<|endoftext|>