<commit_before>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

fn help_text(cmds: Vec<String>) -> String {
    let text = format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#, imagbins = cmds.into_iter()
        .map(|cmd| format!("\t{}\n", cmd))
        .fold(String::new(), |s, c| {
            let s = s + c.as_str();
            s
        }));
    text
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands);
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            debug!("Calling with subcommand: {}", subcommand);
            let subcommand_args : Vec<&str> = match scmd.values_of("") {
                Some(values) => values.collect(),
                None => Vec::new()
            };
            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(1));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1);
                        }
                    }
                }
            }
        },
        // clap ensures we have valid input by exiting if not.
        // The above case is a catch-all for subcommands,
        // so nothing else needs to be expexted.
        _ => unreachable!(),
    }
}
<commit_msg>Add check if given subcommand is supported<commit_after>extern crate crossbeam;
extern crate clap;
#[macro_use] extern crate version;
#[macro_use] extern crate log;
extern crate walkdir;

extern crate libimagrt;
extern crate libimagerror;

use std::env;
use std::process::exit;
use std::process::Command;
use std::process::Stdio;
use std::io::ErrorKind;

use walkdir::WalkDir;
use crossbeam::*;
use clap::{Arg, AppSettings, SubCommand};

use libimagrt::runtime::Runtime;
use libimagerror::trace::trace_error;

fn help_text(cmds: Vec<String>) -> String {
    let text = format!(r#"

     _
    (_)_ __ ___   __ _  __ _
    | | '_ \` _ \/ _\`|/ _\`|
    | | | | | | | (_| | (_| |
    |_|_| |_| |_|\__,_|\__, |
                       |___/
    -------------------------

    Usage: imag [--version | --versions | -h | --help] <command> <args...>

    imag - the personal information management suite for the commandline

    imag is a PIM suite for the commandline. It consists of several commands,
    called "modules". Each module implements one PIM aspect and all of these
    modules can be used independently.

    Available commands:

    {imagbins}

    Call a command with 'imag <command> <args>'
    Each command can be called with "--help" to get the respective helptext.

    Please visit https://github.com/matthiasbeyer/imag to view the source code,
    follow the development of imag or maybe even contribute to imag.

    imag is free software. It is released under the terms of LGPLv2.1

    (c) 2016 Matthias Beyer and contributors"#, imagbins = cmds.into_iter()
        .map(|cmd| format!("\t{}\n", cmd))
        .fold(String::new(), |s, c| {
            let s = s + c.as_str();
            s
        }));
    text
}

fn get_commands() -> Vec<String> {
    let path = env::var("PATH");
    if path.is_err() {
        println!("PATH error: {:?}", path);
        exit(1);
    }
    let pathelements = path.unwrap();
    let pathelements = pathelements.split(":");

    let joinhandles : Vec<ScopedJoinHandle<Vec<String>>> = pathelements
        .map(|elem| {
            crossbeam::scope(|scope| {
                scope.spawn(|| {
                    WalkDir::new(elem)
                        .max_depth(1)
                        .into_iter()
                        .filter(|path| {
                            match path {
                                &Ok(ref p) => p.file_name()
                                    .to_str()
                                    .map_or(false, |filename| filename.starts_with("imag-")),
                                &Err(_)   => false,
                            }
                        })
                        .filter_map(|x| x.ok())
                        .filter_map(|path| {
                           path.file_name()
                               .to_str()
                               .and_then(|s| s.splitn(2, "-").nth(1).map(String::from))
                        })
                        .collect()
                })
            })
        })
        .collect();

    let mut execs = vec![];
    for joinhandle in joinhandles.into_iter() {
        let mut v = joinhandle.join();
        execs.append(&mut v);
    }

    execs
}

fn main() {
    let appname  = "imag";
    let version  = &version!();
    let about    = "imag - the PIM suite for the commandline";
    let commands = get_commands();
    let helptext = help_text(commands);
    let app      = Runtime::get_default_cli_builder(appname, version, about)
        .settings(&[AppSettings::AllowExternalSubcommands])
        .arg(Arg::with_name("version")
             .long("version")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the version of imag"))
        .arg(Arg::with_name("versions")
             .long("versions")
             .takes_value(false)
             .required(false)
             .multiple(false)
             .help("Get the versions of the imag commands"))
        .subcommand(SubCommand::with_name("help").help("Show help"))
        .help(helptext.as_str());
    let rt = Runtime::new(app)
        .unwrap_or_else(|e| {
            println!("Runtime couldn't be setup. Exiting");
            trace_error(&e);
            exit(1);
        });
    let matches = rt.cli();

    debug!("matches: {:?}", matches);

    if matches.is_present("version") {
        debug!("Showing version");
        println!("imag {}", &version!()[..]);
        exit(0);
    }

    if matches.is_present("versions") {
        debug!("Showing versions");
        let mut result = vec![];
        for command in get_commands().iter() {
            result.push(crossbeam::scope(|scope| {
                scope.spawn(|| {
                    let v = Command::new(command).arg("--version").output();
                    match v {
                        Ok(v) => match String::from_utf8(v.stdout) {
                            Ok(s) => format!("{} -> {}", command, s),
                            Err(e) => format!("Failed calling {} -> {:?}", command, e),
                        },
                        Err(e) => format!("Failed calling {} -> {:?}", command, e),
                    }
                })
            }))
        }

        for versionstring in result.into_iter().map(|handle| handle.join()) {
            println!("{}", versionstring);
        }
    }

    match matches.subcommand() {
        (subcommand, Some(scmd)) => {
            debug!("Calling with subcommand: {}", subcommand);
            let subcommand_args : Vec<&str> = match scmd.values_of("") {
                Some(values) => values.collect(),
                None => Vec::new()
            };

            if !get_commands().contains(&String::from(subcommand)) {
                println!("No such command: 'imag-{}'", subcommand);
                println!("See 'imag --help' for available subcommands");
                exit(2);
            }

            debug!("Calling 'imag-{}' with args: {:?}", subcommand, subcommand_args);

            match Command::new(format!("imag-{}", subcommand))
                .stdin(Stdio::inherit())
                .stdout(Stdio::inherit())
                .stderr(Stdio::inherit())
                .args(&subcommand_args[..])
                .spawn()
                .and_then(|mut handle| handle.wait())
            {
                Ok(exit_status) => {
                    if !exit_status.success() {
                        debug!("{} exited with non-zero exit code: {:?}", subcommand, exit_status);
                        println!("{} exited with non-zero exit code", subcommand);
                        exit(exit_status.code().unwrap_or(1));
                    }
                    debug!("Successful exit!");
                },

                Err(e) => {
                    debug!("Error calling the subcommand");
                    match e.kind() {
                        ErrorKind::NotFound => {
                            // With the check above, this absolutely should not happen.
                            // Keeping it to be safe
                            println!("No such command: 'imag-{}'", subcommand);
                            println!("See 'imag --help' for available subcommands");
                            exit(2);
                        },
                        ErrorKind::PermissionDenied => {
                            println!("No permission to execute: 'imag-{}'", subcommand);
                            exit(1);
                        },
                        _ => {
                            println!("Error spawning: {:?}", e);
                            exit(1);
                        }
                    }
                }
            }
        },
        // clap ensures we have valid input by exiting if not.
        // The above case is a catch-all for subcommands,
        // so nothing else needs to be expexted.
        _ => unreachable!(),
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![crate_name = "macros"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]

#![feature(macro_rules, plugin_registrar, quote)]

//! Exports macros for use in other Servo crates.

#[cfg(test)]
extern crate sync;

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::parse::token;
use syntax::util::small_vector::SmallVector;
use rustc::plugin::Registry;
use std::gc::{Gc, GC};

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("bit_struct"),
        base::IdentTT(box base::BasicIdentMacroExpander {
            expander: expand_bit_struct,
            span: None,
        }, None));
}

fn expand_bit_struct(cx: &mut ExtCtxt, sp: Span, name: ast::Ident, tts: Vec<ast::TokenTree>)
                     -> Box<base::MacResult> {
    let mut fields = Vec::new();
    for (i, e) in tts.iter().enumerate() {
        if i & 1 == 1 {
            match *e {
                ast::TTTok(_, token::COMMA) => (),
                _ => {
                    cx.span_err(sp, "bit_struct! expecting comma.");
                    return base::DummyResult::any(sp);
                }
            }
        } else {
            match *e {
                ast::TTTok(_, token::IDENT(ident, _)) => {
                    fields.push(ident)
                }
                _ => {
                    cx.span_err(sp, "bit_struct! requires ident args.");
                    return base::DummyResult::any(sp);
                }
            }
        }
    }
    let bits_per_word =
        if cfg!(target_word_size = "64") { 64 }
        else if cfg!(target_word_size = "32") { 32 }
        else { fail!("Unexpected target word size") };
    let nb_words = (fields.len() - 1 + bits_per_word) / bits_per_word;

    let struct_def = quote_item!(&*cx,
        pub struct $name {
            storage: [uint, ..$nb_words]
        }
    ).unwrap();
    let impl_def = quote_item!(&*cx,
        impl $name {
            #[inline]
            pub fn new() -> $name {
                $name { storage: [0, ..$nb_words] }
            }
        }
    ).unwrap();

    // Unwrap from Gc<T>, which does not implement DerefMut
    let mut impl_def = (*impl_def).clone();
    match impl_def.node {
        ast::ItemImpl(_, _, _, ref mut methods) => {
            for (i, field) in fields.iter().enumerate() {
                let setter_name = "set_".to_string() + field.as_str();
                let setter = token::str_to_ident(setter_name.as_slice());
                let word = i / bits_per_word;
                let bit = i % bits_per_word;
                let additional_impl = quote_item!(&*cx,
                    impl $name {
                        #[allow(non_snake_case_functions)]
                        #[inline]
                        pub fn $field(&self) -> bool {
                            (self.storage[$word] & (1 << $bit)) != 0
                        }
                        #[allow(non_snake_case_functions)]
                        #[inline]
                        pub fn $setter(&mut self, new_value: bool) {
                            if new_value {
                                self.storage[$word] |= 1 << $bit
                            } else {
                                self.storage[$word] &= !(1 << $bit)
                            }
                        }
                    }
                ).unwrap();
                match additional_impl.node {
                    ast::ItemImpl(_, _, _, ref additional_methods) => {
                        methods.push_all(additional_methods.as_slice());
                    }
                    _ => fail!()
                }
            }
        }
        _ => fail!()
    }
    // Re-wrap.
    let impl_def = box(GC) impl_def;

    // FIXME(SimonSapin) replace this with something from libsyntax
    // if/when https://github.com/rust-lang/rust/issues/16723 is fixed
    struct MacItems {
        items: Vec<Gc<ast::Item>>
    }

    impl MacResult for MacItems {
        fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
            Some(SmallVector::many(self.items.clone()))
        }
    }

    box MacItems { items: vec![struct_def, impl_def] } as Box<MacResult>
}

#[macro_export]
macro_rules! bitfield(
    ($bitfieldname:ident, $getter:ident, $setter:ident, $value:expr) => (
        impl $bitfieldname {
            #[inline]
            pub fn $getter(self) -> bool {
                let $bitfieldname(this) = self;
                (this & $value) != 0
            }

            #[inline]
            pub fn $setter(&mut self, value: bool) {
                let $bitfieldname(this) = *self;
                *self = $bitfieldname((this & !$value) | (if value { $value } else { 0 }))
            }
        }
    )
)

#[macro_export]
macro_rules! lazy_init(
    ($(static ref $N:ident : $T:ty = $e:expr;)*) => (
        $(
            #[allow(non_camel_case_types)]
            struct $N {__unit__: ()}
            static $N: $N = $N {__unit__: ()};
            impl Deref<$T> for $N {
                fn deref<'a>(&'a self) -> &'a $T {
                    unsafe {
                        static mut s: *const $T = 0 as *const $T;
                        static mut ONCE: ::sync::one::Once = ::sync::one::ONCE_INIT;
                        ONCE.doit(|| {
                            s = ::std::mem::transmute::<Box<$T>, *const $T>(box () ($e));
                        });
                        &*s
                    }
                }
            }

        )*
    )
)

#[cfg(test)]
mod tests {
    use std::collections::hashmap::HashMap;
    lazy_init! {
        static ref NUMBER: uint = times_two(3);
        static ref VEC: [Box<uint>, ..3] = [box 1, box 2, box 3];
        static ref OWNED_STRING: String = "hello".to_string();
        static ref HASHMAP: HashMap<uint, &'static str> = {
            let mut m = HashMap::new();
            m.insert(0u, "abc");
            m.insert(1, "def");
            m.insert(2, "ghi");
            m
        };
    }

    fn times_two(n: uint) -> uint {
        n * 2
    }

    #[test]
    fn test_basic() {
        assert_eq!(*OWNED_STRING, "hello".to_string());
        assert_eq!(*NUMBER, 6);
        assert!(HASHMAP.find(&1).is_some());
        assert!(HASHMAP.find(&3).is_none());
        assert_eq!(VEC.as_slice(), &[box 1, box 2, box 3]);
    }

    #[test]
    fn test_repeat() {
        assert_eq!(*NUMBER, 6);
        assert_eq!(*NUMBER, 6);
        assert_eq!(*NUMBER, 6);
    }
}
<commit_msg>Revert "fixup! Move PropertyBitField to a syntax extension."<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#![crate_name = "macros"]
#![crate_type = "rlib"]
#![crate_type = "dylib"]

#![feature(macro_rules, plugin_registrar, quote)]

//! Exports macros for use in other Servo crates.

#[cfg(test)]
extern crate sync;

extern crate rustc;
extern crate syntax;

use syntax::ast;
use syntax::codemap::Span;
use syntax::ext::base;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::parse::token;
use syntax::util::small_vector::SmallVector;
use rustc::plugin::Registry;
use std::gc::{Gc, GC};

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_syntax_extension(
        token::intern("bit_struct"),
        base::IdentTT(box base::BasicIdentMacroExpander {
            expander: expand_bit_struct,
            span: None,
        }, None));
}

fn expand_bit_struct(cx: &mut ExtCtxt, sp: Span, name: ast::Ident, tts: Vec<ast::TokenTree>)
                     -> Box<base::MacResult> {
    let mut fields = Vec::new();
    for (i, e) in tts.iter().enumerate() {
        if i & 1 == 1 {
            match *e {
                ast::TTTok(_, token::COMMA) => (),
                _ => {
                    cx.span_err(sp, "bit_struct! expecting comma.");
                    return base::DummyResult::any(sp);
                }
            }
        } else {
            match *e {
                ast::TTTok(_, token::IDENT(ident, _)) => {
                    fields.push(ident)
                }
                _ => {
                    cx.span_err(sp, "bit_struct! requires ident args.");
                    return base::DummyResult::any(sp);
                }
            }
        }
    }
    let bits_per_word =
        if cfg!(target_word_size = "64") { 64 }
        else if cfg!(target_word_size = "32") { 32 }
        else { fail!("Unexpected target word size") };
    let nb_words = (fields.len() - 1 + bits_per_word) / bits_per_word;

    let struct_def = quote_item!(&*cx,
        pub struct $name {
            storage: [uint, ..$nb_words]
        }
    ).unwrap();
    let impl_def = quote_item!(&*cx,
        impl $name {
            #[inline]
            pub fn new() -> $name {
                $name { storage: [0, ..$nb_words] }
            }
        }
    ).unwrap();

    // Unwrap from Gc<T>, which does not implement DerefMut
    let mut impl_def = (*impl_def).clone();
    match impl_def.node {
        ast::ItemImpl(_, _, _, ref mut methods) => {
            for (i, field) in fields.iter().enumerate() {
                let setter_name = "set_".to_string() + field.as_str();
                let setter = token::str_to_ident(setter_name.as_slice());
                let word = i / bits_per_word;
                let bit = i % bits_per_word;
                let additional_impl = quote_item!(&*cx,
                    impl $name {
                        #[allow(non_snake_case_functions)]
                        pub fn $field(&self) -> bool {
                            (self.storage[$word] & (1 << $bit)) != 0
                        }
                        #[allow(non_snake_case_functions)]
                        pub fn $setter(&mut self, new_value: bool) {
                            if new_value {
                                self.storage[$word] |= 1 << $bit
                            } else {
                                self.storage[$word] &= !(1 << $bit)
                            }
                        }
                    }
                ).unwrap();
                match additional_impl.node {
                    ast::ItemImpl(_, _, _, ref additional_methods) => {
                        methods.push_all(additional_methods.as_slice());
                    }
                    _ => fail!()
                }
            }
        }
        _ => fail!()
    }
    // Re-wrap.
    let impl_def = box(GC) impl_def;

    struct MacItems {
        items: Vec<Gc<ast::Item>>
    }

    impl MacResult for MacItems {
        fn make_items(&self) -> Option<SmallVector<Gc<ast::Item>>> {
            Some(SmallVector::many(self.items.clone()))
        }
    }

    box MacItems { items: vec![struct_def, impl_def] } as Box<MacResult>
}

#[macro_export]
macro_rules! bitfield(
    ($bitfieldname:ident, $getter:ident, $setter:ident, $value:expr) => (
        impl $bitfieldname {
            #[inline]
            pub fn $getter(self) -> bool {
                let $bitfieldname(this) = self;
                (this & $value) != 0
            }

            #[inline]
            pub fn $setter(&mut self, value: bool) {
                let $bitfieldname(this) = *self;
                *self = $bitfieldname((this & !$value) | (if value { $value } else { 0 }))
            }
        }
    )
)

#[macro_export]
macro_rules! lazy_init(
    ($(static ref $N:ident : $T:ty = $e:expr;)*) => (
        $(
            #[allow(non_camel_case_types)]
            struct $N {__unit__: ()}
            static $N: $N = $N {__unit__: ()};
            impl Deref<$T> for $N {
                fn deref<'a>(&'a self) -> &'a $T {
                    unsafe {
                        static mut s: *const $T = 0 as *const $T;
                        static mut ONCE: ::sync::one::Once = ::sync::one::ONCE_INIT;
                        ONCE.doit(|| {
                            s = ::std::mem::transmute::<Box<$T>, *const $T>(box () ($e));
                        });
                        &*s
                    }
                }
            }

        )*
    )
)

#[cfg(test)]
mod tests {
    use std::collections::hashmap::HashMap;
    lazy_init! {
        static ref NUMBER: uint = times_two(3);
        static ref VEC: [Box<uint>, ..3] = [box 1, box 2, box 3];
        static ref OWNED_STRING: String = "hello".to_string();
        static ref HASHMAP: HashMap<uint, &'static str> = {
            let mut m = HashMap::new();
            m.insert(0u, "abc");
            m.insert(1, "def");
            m.insert(2, "ghi");
            m
        };
    }

    fn times_two(n: uint) -> uint {
        n * 2
    }

    #[test]
    fn test_basic() {
        assert_eq!(*OWNED_STRING, "hello".to_string());
        assert_eq!(*NUMBER, 6);
        assert!(HASHMAP.find(&1).is_some());
        assert!(HASHMAP.find(&3).is_none());
        assert_eq!(VEC.as_slice(), &[box 1, box 2, box 3]);
    }

    #[test]
    fn test_repeat() {
        assert_eq!(*NUMBER, 6);
        assert_eq!(*NUMBER, 6);
        assert_eq!(*NUMBER, 6);
    }
}
<|endoftext|>


<commit_before>//! Stream-based consumer implementation.
use futures::{Future, Poll, Sink, Stream};
use futures::sync::mpsc;
use rdsys::types::*;
use rdsys;

use config::{FromClientConfig, FromClientConfigAndContext, ClientConfig};
use consumer::base_consumer::BaseConsumer;
use consumer::{Consumer, ConsumerContext, EmptyConsumerContext};
use error::{KafkaError, KafkaResult};
use message::Message;
use util::duration_to_millis;

use std::cell::Cell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};
use std::time::Duration;

/// A small wrapper for a message pointer. This wrapper is only used to
/// pass a message between the polling thread and the thread consuming the stream,
/// and transform it from pointer to `Message` with a lifetime that derives from the
/// lifetime of the stream consumer. In general is not safe to pass a struct with an internal
/// reference across threads. However the `StreamConsumer` guarantees that the polling thread
/// will be terminated before the consumer is actually dropped, ensuring that the messages
/// are safe to be used for their entire lifetime.
struct PolledMessagePtr {
    message_ptr: Option<*mut RDKafkaMessage>,
}

impl PolledMessagePtr {
    /// Creates a new PolledPtr from a message pointer.
    fn new(message_ptr: *mut RDKafkaMessage) -> PolledMessagePtr {
        trace!("New polled ptr {:?}", message_ptr);
        PolledMessagePtr {
            message_ptr: Some(message_ptr)
        }
    }

    /// Transforms the `PolledMessagePtr` into a message, that will be bound to the lifetime
    /// of the provided consumer.
    fn into_message_of<'a, C: ConsumerContext>(mut self, consumer: &'a StreamConsumer<C>) -> Message<'a> {
        Message::new(self.message_ptr.take().unwrap(), consumer)
    }
}

impl Drop for PolledMessagePtr {
    /// If the `PolledMessagePtr` is hasn't been transformed into a message and the pointer is
    /// still available, it will free the underlying resources.
    fn drop(&mut self) {
        if let Some(ptr) = self.message_ptr {
            trace!("Destroy PolledPtr {:?}", ptr);
            unsafe { rdsys::rd_kafka_message_destroy(ptr) };
        }
    }
}

/// Allow message pointer to be moved across threads.
unsafe impl Send for PolledMessagePtr {}

/// A Stream of Kafka messages. It can be used to receive messages as they are consumed from Kafka.
/// Note: there might be buffering between the actual Kafka consumer and the receiving end of this
/// stream, so it is not advised to use automatic commit, as some messages might have been consumed
/// by the internal Kafka consumer but not processed.
pub struct MessageStream<'a, C: ConsumerContext + 'static> {
    consumer: &'a StreamConsumer<C>,
    receiver: mpsc::Receiver<KafkaResult<PolledMessagePtr>>,
}

impl<'a, C: ConsumerContext + 'static> MessageStream<'a, C> {
    fn new(consumer: &'a StreamConsumer<C>, receiver: mpsc::Receiver<KafkaResult<PolledMessagePtr>>) -> MessageStream<'a, C> {
        MessageStream {
            consumer: consumer,
            receiver: receiver,
        }
    }
}

impl<'a, C: ConsumerContext + 'a> Stream for MessageStream<'a, C> {
    type Item = KafkaResult<Message<'a>>;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.receiver.poll()  // There has to be a better way
            .map(|ready|
                ready.map(|option|
                    option.map(|result|
                        result.map(|polled_ptr| polled_ptr.into_message_of(self.consumer)))))
    }
}

/// Internal consumer loop. This is the main body of the thread that will drive the
/// stream consumer.
fn poll_loop<C: ConsumerContext>(
    consumer: Arc<BaseConsumer<C>>,
    sender: mpsc::Sender<KafkaResult<PolledMessagePtr>>,
    should_stop: Arc<AtomicBool>,
    poll_interval: Duration,
    no_message_error: bool,
) {
    trace!("Polling thread loop started");
    let mut curr_sender = sender;
    let poll_interval_ms = duration_to_millis(poll_interval) as i32;
    while !should_stop.load(Ordering::Relaxed) {
        trace!("Polling base consumer");
        let future_sender = match consumer.poll_raw(poll_interval_ms) {
            Ok(None) => {
                if no_message_error {
                    curr_sender.send(Err(KafkaError::NoMessageReceived))
                } else {
                    continue // TODO: check stream closed
                }
            },
            Ok(Some(m_ptr)) => curr_sender.send(Ok(PolledMessagePtr::new(m_ptr))),
            Err(e) => curr_sender.send(Err(e)),
        };
        match future_sender.wait() {
            Ok(new_sender) => curr_sender = new_sender,
            Err(e) => {
                debug!("Sender not available: {:?}", e);
                break;
            }
        };
    }
    trace!("Polling thread loop terminated");
}

/// A Consumer with an associated polling thread. This consumer doesn't need to
/// be polled and it will return all consumed messages as a `Stream`.
/// Due to the asynchronous nature of the stream, some messages might be consumed by the consumer
/// without being processed on the other end of the stream. If auto commit is used, it might cause
/// message loss after consumer restart. Manual offset storing should be used, see the `store_offset`
/// function on `Consumer`.
#[must_use = "Consumer polling thread will stop immediately if unused"]
pub struct StreamConsumer<C: ConsumerContext + 'static> {
    consumer: Arc<BaseConsumer<C>>,
    should_stop: Arc<AtomicBool>,
    handle: Cell<Option<JoinHandle<()>>>,
}

impl<C: ConsumerContext> Consumer<C> for StreamConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<C> {
        Arc::as_ref(&self.consumer)
    }
}

impl FromClientConfig for StreamConsumer<EmptyConsumerContext> {
    fn from_config(config: &ClientConfig) -> KafkaResult<StreamConsumer<EmptyConsumerContext>> {
        StreamConsumer::from_config_and_context(config, EmptyConsumerContext)
    }
}

/// Creates a new `Consumer` starting from a `ClientConfig`.
impl<C: ConsumerContext> FromClientConfigAndContext<C> for StreamConsumer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<StreamConsumer<C>> {
        let stream_consumer = StreamConsumer {
            consumer: Arc::new(BaseConsumer::from_config_and_context(config, context)?),
            should_stop: Arc::new(AtomicBool::new(false)),
            handle: Cell::new(None),
        };
        Ok(stream_consumer)
    }
}

impl<C: ConsumerContext> StreamConsumer<C> {
    /// Starts the StreamConsumer with default configuration (100ms polling interval and no
    /// `NoMessageReceived` notifications).
    pub fn start(&self) -> MessageStream<C> {
        self.start_with(Duration::from_millis(100), false)
    }

    /// Starts the StreamConsumer with the specified poll interval. Additionally, if
    /// `no_message_error` is set to true, it will return an error of type
    /// `KafkaError::NoMessageReceived` every time the poll interval is reached and no message
    /// has been received.
    pub fn start_with(&self, poll_interval: Duration, no_message_error: bool) -> MessageStream<C> {
        let (sender, receiver) = mpsc::channel(0);
        let consumer = self.consumer.clone();
        let should_stop = self.should_stop.clone();
        let handle = thread::Builder::new()
            .name("poll".to_string())
            .spawn(move || {
                poll_loop(consumer, sender, should_stop, poll_interval, no_message_error);
            })
            .expect("Failed to start polling thread");
        self.handle.set(Some(handle));
        MessageStream::new(self, receiver)
    }

    /// Stops the StreamConsumer, blocking the caller until the internal consumer
    /// has been stopped.
    pub fn stop(&mut self) {
        if let Some(handle) = self.handle.take() {
            trace!("Stopping polling");
            self.should_stop.store(true, Ordering::Relaxed);
            match handle.join() {
                Ok(()) => trace!("Polling stopped"),
                Err(e) => warn!("Failure while terminating thread: {:?}", e),
            };
        }
    }
}

impl<C: ConsumerContext> Drop for StreamConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroy StreamConsumer");
        // The polling thread must be fully stopped before we can proceed with the actual drop,
        // otherwise it might consume from a destroyed consumer.
        self.stop();
    }
}

<commit_msg>Consumer stop doesn't have to be mut<commit_after>//! Stream-based consumer implementation.
use futures::{Future, Poll, Sink, Stream};
use futures::sync::mpsc;
use rdsys::types::*;
use rdsys;

use config::{FromClientConfig, FromClientConfigAndContext, ClientConfig};
use consumer::base_consumer::BaseConsumer;
use consumer::{Consumer, ConsumerContext, EmptyConsumerContext};
use error::{KafkaError, KafkaResult};
use message::Message;
use util::duration_to_millis;

use std::cell::Cell;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use std::thread::{self, JoinHandle};
use std::time::Duration;

/// A small wrapper for a message pointer. This wrapper is only used to
/// pass a message between the polling thread and the thread consuming the stream,
/// and transform it from pointer to `Message` with a lifetime that derives from the
/// lifetime of the stream consumer. In general is not safe to pass a struct with an internal
/// reference across threads. However the `StreamConsumer` guarantees that the polling thread
/// will be terminated before the consumer is actually dropped, ensuring that the messages
/// are safe to be used for their entire lifetime.
struct PolledMessagePtr {
    message_ptr: Option<*mut RDKafkaMessage>,
}

impl PolledMessagePtr {
    /// Creates a new PolledPtr from a message pointer.
    fn new(message_ptr: *mut RDKafkaMessage) -> PolledMessagePtr {
        trace!("New polled ptr {:?}", message_ptr);
        PolledMessagePtr {
            message_ptr: Some(message_ptr)
        }
    }

    /// Transforms the `PolledMessagePtr` into a message, that will be bound to the lifetime
    /// of the provided consumer.
    fn into_message_of<'a, C: ConsumerContext>(mut self, consumer: &'a StreamConsumer<C>) -> Message<'a> {
        Message::new(self.message_ptr.take().unwrap(), consumer)
    }
}

impl Drop for PolledMessagePtr {
    /// If the `PolledMessagePtr` is hasn't been transformed into a message and the pointer is
    /// still available, it will free the underlying resources.
    fn drop(&mut self) {
        if let Some(ptr) = self.message_ptr {
            trace!("Destroy PolledPtr {:?}", ptr);
            unsafe { rdsys::rd_kafka_message_destroy(ptr) };
        }
    }
}

/// Allow message pointer to be moved across threads.
unsafe impl Send for PolledMessagePtr {}

/// A Stream of Kafka messages. It can be used to receive messages as they are consumed from Kafka.
/// Note: there might be buffering between the actual Kafka consumer and the receiving end of this
/// stream, so it is not advised to use automatic commit, as some messages might have been consumed
/// by the internal Kafka consumer but not processed.
pub struct MessageStream<'a, C: ConsumerContext + 'static> {
    consumer: &'a StreamConsumer<C>,
    receiver: mpsc::Receiver<KafkaResult<PolledMessagePtr>>,
}

impl<'a, C: ConsumerContext + 'static> MessageStream<'a, C> {
    fn new(consumer: &'a StreamConsumer<C>, receiver: mpsc::Receiver<KafkaResult<PolledMessagePtr>>) -> MessageStream<'a, C> {
        MessageStream {
            consumer: consumer,
            receiver: receiver,
        }
    }
}

impl<'a, C: ConsumerContext + 'a> Stream for MessageStream<'a, C> {
    type Item = KafkaResult<Message<'a>>;
    type Error = ();

    fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
        self.receiver.poll()  // There has to be a better way
            .map(|ready|
                ready.map(|option|
                    option.map(|result|
                        result.map(|polled_ptr| polled_ptr.into_message_of(self.consumer)))))
    }
}

/// Internal consumer loop. This is the main body of the thread that will drive the
/// stream consumer.
fn poll_loop<C: ConsumerContext>(
    consumer: Arc<BaseConsumer<C>>,
    sender: mpsc::Sender<KafkaResult<PolledMessagePtr>>,
    should_stop: Arc<AtomicBool>,
    poll_interval: Duration,
    no_message_error: bool,
) {
    trace!("Polling thread loop started");
    let mut curr_sender = sender;
    let poll_interval_ms = duration_to_millis(poll_interval) as i32;
    while !should_stop.load(Ordering::Relaxed) {
        trace!("Polling base consumer");
        let future_sender = match consumer.poll_raw(poll_interval_ms) {
            Ok(None) => {
                if no_message_error {
                    curr_sender.send(Err(KafkaError::NoMessageReceived))
                } else {
                    continue // TODO: check stream closed
                }
            },
            Ok(Some(m_ptr)) => curr_sender.send(Ok(PolledMessagePtr::new(m_ptr))),
            Err(e) => curr_sender.send(Err(e)),
        };
        match future_sender.wait() {
            Ok(new_sender) => curr_sender = new_sender,
            Err(e) => {
                debug!("Sender not available: {:?}", e);
                break;
            }
        };
    }
    trace!("Polling thread loop terminated");
}

/// A Consumer with an associated polling thread. This consumer doesn't need to
/// be polled and it will return all consumed messages as a `Stream`.
/// Due to the asynchronous nature of the stream, some messages might be consumed by the consumer
/// without being processed on the other end of the stream. If auto commit is used, it might cause
/// message loss after consumer restart. Manual offset storing should be used, see the `store_offset`
/// function on `Consumer`.
#[must_use = "Consumer polling thread will stop immediately if unused"]
pub struct StreamConsumer<C: ConsumerContext + 'static> {
    consumer: Arc<BaseConsumer<C>>,
    should_stop: Arc<AtomicBool>,
    handle: Cell<Option<JoinHandle<()>>>,
}

impl<C: ConsumerContext> Consumer<C> for StreamConsumer<C> {
    fn get_base_consumer(&self) -> &BaseConsumer<C> {
        Arc::as_ref(&self.consumer)
    }
}

impl FromClientConfig for StreamConsumer<EmptyConsumerContext> {
    fn from_config(config: &ClientConfig) -> KafkaResult<StreamConsumer<EmptyConsumerContext>> {
        StreamConsumer::from_config_and_context(config, EmptyConsumerContext)
    }
}

/// Creates a new `Consumer` starting from a `ClientConfig`.
impl<C: ConsumerContext> FromClientConfigAndContext<C> for StreamConsumer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<StreamConsumer<C>> {
        let stream_consumer = StreamConsumer {
            consumer: Arc::new(BaseConsumer::from_config_and_context(config, context)?),
            should_stop: Arc::new(AtomicBool::new(false)),
            handle: Cell::new(None),
        };
        Ok(stream_consumer)
    }
}

impl<C: ConsumerContext> StreamConsumer<C> {
    /// Starts the StreamConsumer with default configuration (100ms polling interval and no
    /// `NoMessageReceived` notifications).
    pub fn start(&self) -> MessageStream<C> {
        self.start_with(Duration::from_millis(100), false)
    }

    /// Starts the StreamConsumer with the specified poll interval. Additionally, if
    /// `no_message_error` is set to true, it will return an error of type
    /// `KafkaError::NoMessageReceived` every time the poll interval is reached and no message
    /// has been received.
    pub fn start_with(&self, poll_interval: Duration, no_message_error: bool) -> MessageStream<C> {
        let (sender, receiver) = mpsc::channel(0);
        let consumer = self.consumer.clone();
        let should_stop = self.should_stop.clone();
        let handle = thread::Builder::new()
            .name("poll".to_string())
            .spawn(move || {
                poll_loop(consumer, sender, should_stop, poll_interval, no_message_error);
            })
            .expect("Failed to start polling thread");
        self.handle.set(Some(handle));
        MessageStream::new(self, receiver)
    }

    /// Stops the StreamConsumer, blocking the caller until the internal consumer
    /// has been stopped.
    pub fn stop(&self) {
        if let Some(handle) = self.handle.take() {
            trace!("Stopping polling");
            self.should_stop.store(true, Ordering::Relaxed);
            match handle.join() {
                Ok(()) => trace!("Polling stopped"),
                Err(e) => warn!("Failure while terminating thread: {:?}", e),
            };
        }
    }
}

impl<C: ConsumerContext> Drop for StreamConsumer<C> {
    fn drop(&mut self) {
        trace!("Destroy StreamConsumer");
        // The polling thread must be fully stopped before we can proceed with the actual drop,
        // otherwise it might consume from a destroyed consumer.
        self.stop();
    }
}

<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// The visitors in this module collect sizes and counts of the most important
// pieces of AST and HIR. The resulting numbers are good approximations but not
// completely accurate (some things might be counted twice, others missed).

use rustc::hir;
use rustc::hir::intravisit as hir_visit;
use rustc::util::common::to_readable_str;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use syntax::ast::{self, NodeId, AttrId};
use syntax::visit as ast_visit;
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum Id {
    Node(NodeId),
    Attr(AttrId),
    None,
}

struct NodeData {
    count: usize,
    size: usize,
}

struct StatCollector<'k> {
    krate: Option<&'k hir::Crate>,
    data: FxHashMap<&'static str, NodeData>,
    seen: FxHashSet<Id>,
}

pub fn print_hir_stats(krate: &hir::Crate) {
    let mut collector = StatCollector {
        krate: Some(krate),
        data: FxHashMap(),
        seen: FxHashSet(),
    };
    hir_visit::walk_crate(&mut collector, krate);
    collector.print("HIR STATS");
}

pub fn print_ast_stats<'v>(krate: &'v ast::Crate, title: &str) {
    let mut collector = StatCollector {
        krate: None,
        data: FxHashMap(),
        seen: FxHashSet(),
    };
    ast_visit::walk_crate(&mut collector, krate);
    collector.print(title);
}

impl<'k> StatCollector<'k> {

    fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
        if id != Id::None {
            if !self.seen.insert(id) {
                return
            }
        }

        let entry = self.data.entry(label).or_insert(NodeData {
            count: 0,
            size: 0,
        });

        entry.count += 1;
        entry.size = ::std::mem::size_of_val(node);
    }

    fn print(&self, title: &str) {
        let mut stats: Vec<_> = self.data.iter().collect();

        stats.sort_by_key(|&(_, ref d)| d.count * d.size);

        let mut total_size = 0;

        println!("\n{}\n", title);

        println!("{:<18}{:>18}{:>14}{:>14}",
            "Name", "Accumulated Size", "Count", "Item Size");
        println!("----------------------------------------------------------------");

        for (label, data) in stats {
            println!("{:<18}{:>18}{:>14}{:>14}",
                label,
                to_readable_str(data.count * data.size),
                to_readable_str(data.count),
                to_readable_str(data.size));

            total_size += data.count * data.size;
        }
        println!("----------------------------------------------------------------");
        println!("{:<18}{:>18}\n",
                "Total",
                to_readable_str(total_size));
    }
}

impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
    fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
        panic!("visit_nested_xxx must be manually implemented in this visitor")
    }

    fn visit_nested_item(&mut self, id: hir::ItemId) {
        let nested_item = self.krate.unwrap().item(id.id);
        self.visit_item(nested_item)
    }

    fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
        let nested_trait_item = self.krate.unwrap().trait_item(trait_item_id);
        self.visit_trait_item(nested_trait_item)
    }

    fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
        let nested_impl_item = self.krate.unwrap().impl_item(impl_item_id);
        self.visit_impl_item(nested_impl_item)
    }

    fn visit_item(&mut self, i: &'v hir::Item) {
        self.record("Item", Id::Node(i.id), i);
        hir_visit::walk_item(self, i)
    }

    ///////////////////////////////////////////////////////////////////////////

    fn visit_mod(&mut self, m: &'v hir::Mod, _s: Span, n: NodeId) {
        self.record("Mod", Id::None, m);
        hir_visit::walk_mod(self, m, n)
    }
    fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem) {
        self.record("ForeignItem", Id::Node(i.id), i);
        hir_visit::walk_foreign_item(self, i)
    }
    fn visit_local(&mut self, l: &'v hir::Local) {
        self.record("Local", Id::Node(l.id), l);
        hir_visit::walk_local(self, l)
    }
    fn visit_block(&mut self, b: &'v hir::Block) {
        self.record("Block", Id::Node(b.id), b);
        hir_visit::walk_block(self, b)
    }
    fn visit_stmt(&mut self, s: &'v hir::Stmt) {
        self.record("Stmt", Id::Node(s.node.id()), s);
        hir_visit::walk_stmt(self, s)
    }
    fn visit_arm(&mut self, a: &'v hir::Arm) {
        self.record("Arm", Id::None, a);
        hir_visit::walk_arm(self, a)
    }
    fn visit_pat(&mut self, p: &'v hir::Pat) {
        self.record("Pat", Id::Node(p.id), p);
        hir_visit::walk_pat(self, p)
    }
    fn visit_decl(&mut self, d: &'v hir::Decl) {
        self.record("Decl", Id::None, d);
        hir_visit::walk_decl(self, d)
    }
    fn visit_expr(&mut self, ex: &'v hir::Expr) {
        self.record("Expr", Id::Node(ex.id), ex);
        hir_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v hir::Ty) {
        self.record("Ty", Id::Node(t.id), t);
        hir_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self,
                fk: hir_visit::FnKind<'v>,
                fd: &'v hir::FnDecl,
                b: hir::BodyId,
                s: Span,
                id: NodeId) {
        self.record("FnDecl", Id::None, fd);
        hir_visit::walk_fn(self, fk, fd, b, s, id)
    }

    fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate) {
        self.record("WherePredicate", Id::None, predicate);
        hir_visit::walk_where_predicate(self, predicate)
    }

    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem) {
        self.record("TraitItem", Id::Node(ti.id), ti);
        hir_visit::walk_trait_item(self, ti)
    }
    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) {
        self.record("ImplItem", Id::Node(ii.id), ii);
        hir_visit::walk_impl_item(self, ii)
    }

    fn visit_ty_param_bound(&mut self, bounds: &'v hir::TyParamBound) {
        self.record("TyParamBound", Id::None, bounds);
        hir_visit::walk_ty_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v hir::StructField) {
        self.record("StructField", Id::Node(s.id), s);
        hir_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self,
                     v: &'v hir::Variant,
                     g: &'v hir::Generics,
                     item_id: NodeId) {
        self.record("Variant", Id::None, v);
        hir_visit::walk_variant(self, v, g, item_id)
    }
    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
        self.record("Lifetime", Id::Node(lifetime.id), lifetime);
        hir_visit::walk_lifetime(self, lifetime)
    }
    fn visit_lifetime_def(&mut self, lifetime: &'v hir::LifetimeDef) {
        self.record("LifetimeDef", Id::None, lifetime);
        hir_visit::walk_lifetime_def(self, lifetime)
    }
    fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) {
        self.record("QPath", Id::None, qpath);
        hir_visit::walk_qpath(self, qpath, id, span)
    }
    fn visit_path(&mut self, path: &'v hir::Path, _id: NodeId) {
        self.record("Path", Id::None, path);
        hir_visit::walk_path(self, path)
    }
    fn visit_path_segment(&mut self,
                          path_span: Span,
                          path_segment: &'v hir::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        hir_visit::walk_path_segment(self, path_span, path_segment)
    }
    fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding) {
        self.record("TypeBinding", Id::Node(type_binding.id), type_binding);
        hir_visit::walk_assoc_type_binding(self, type_binding)
    }
    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::Attr(attr.id), attr);
    }
    fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef) {
        self.record("MacroDef", Id::Node(macro_def.id), macro_def);
        hir_visit::walk_macro_def(self, macro_def)
    }
}

impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {

    fn visit_mod(&mut self, m: &'v ast::Mod, _s: Span, _a: &[ast::Attribute], _n: NodeId) {
        self.record("Mod", Id::None, m);
        ast_visit::walk_mod(self, m)
    }

    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
        self.record("ForeignItem", Id::None, i);
        ast_visit::walk_foreign_item(self, i)
    }

    fn visit_item(&mut self, i: &'v ast::Item) {
        self.record("Item", Id::None, i);
        ast_visit::walk_item(self, i)
    }

    fn visit_local(&mut self, l: &'v ast::Local) {
        self.record("Local", Id::None, l);
        ast_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        self.record("Block", Id::None, b);
        ast_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
        self.record("Stmt", Id::None, s);
        ast_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v ast::Arm) {
        self.record("Arm", Id::None, a);
        ast_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v ast::Pat) {
        self.record("Pat", Id::None, p);
        ast_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        self.record("Expr", Id::None, ex);
        ast_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v ast::Ty) {
        self.record("Ty", Id::None, t);
        ast_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self,
                fk: ast_visit::FnKind<'v>,
                fd: &'v ast::FnDecl,
                s: Span,
                _: NodeId) {
        self.record("FnDecl", Id::None, fd);
        ast_visit::walk_fn(self, fk, fd, s)
    }

    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
        self.record("TraitItem", Id::None, ti);
        ast_visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
        self.record("ImplItem", Id::None, ii);
        ast_visit::walk_impl_item(self, ii)
    }

    fn visit_ty_param_bound(&mut self, bounds: &'v ast::TyParamBound) {
        self.record("TyParamBound", Id::None, bounds);
        ast_visit::walk_ty_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
        self.record("StructField", Id::None, s);
        ast_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self,
                     v: &'v ast::Variant,
                     g: &'v ast::Generics,
                     item_id: NodeId) {
        self.record("Variant", Id::None, v);
        ast_visit::walk_variant(self, v, g, item_id)
    }

    fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) {
        self.record("Lifetime", Id::None, lifetime);
        ast_visit::walk_lifetime(self, lifetime)
    }

    fn visit_lifetime_def(&mut self, lifetime: &'v ast::LifetimeDef) {
        self.record("LifetimeDef", Id::None, lifetime);
        ast_visit::walk_lifetime_def(self, lifetime)
    }

    fn visit_mac(&mut self, mac: &'v ast::Mac) {
        self.record("Mac", Id::None, mac);
    }

    fn visit_path_list_item(&mut self,
                            prefix: &'v ast::Path,
                            item: &'v ast::PathListItem) {
        self.record("PathListItem", Id::None, item);
        ast_visit::walk_path_list_item(self, prefix, item)
    }

    fn visit_path_segment(&mut self,
                          path_span: Span,
                          path_segment: &'v ast::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        ast_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_type_binding(&mut self, type_binding: &'v ast::TypeBinding) {
        self.record("TypeBinding", Id::None, type_binding);
        ast_visit::walk_assoc_type_binding(self, type_binding)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::None, attr);
    }
}
<commit_msg>Rollup merge of #43824 - kennytm:fix-43232-hir-stats, r=petrochenkov<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// The visitors in this module collect sizes and counts of the most important
// pieces of AST and HIR. The resulting numbers are good approximations but not
// completely accurate (some things might be counted twice, others missed).

use rustc::hir;
use rustc::hir::intravisit as hir_visit;
use rustc::util::common::to_readable_str;
use rustc::util::nodemap::{FxHashMap, FxHashSet};
use syntax::ast::{self, NodeId, AttrId};
use syntax::visit as ast_visit;
use syntax_pos::Span;

#[derive(Copy, Clone, PartialEq, Eq, Hash)]
enum Id {
    Node(NodeId),
    Attr(AttrId),
    None,
}

struct NodeData {
    count: usize,
    size: usize,
}

struct StatCollector<'k> {
    krate: Option<&'k hir::Crate>,
    data: FxHashMap<&'static str, NodeData>,
    seen: FxHashSet<Id>,
}

pub fn print_hir_stats(krate: &hir::Crate) {
    let mut collector = StatCollector {
        krate: Some(krate),
        data: FxHashMap(),
        seen: FxHashSet(),
    };
    hir_visit::walk_crate(&mut collector, krate);
    collector.print("HIR STATS");
}

pub fn print_ast_stats<'v>(krate: &'v ast::Crate, title: &str) {
    let mut collector = StatCollector {
        krate: None,
        data: FxHashMap(),
        seen: FxHashSet(),
    };
    ast_visit::walk_crate(&mut collector, krate);
    collector.print(title);
}

impl<'k> StatCollector<'k> {

    fn record<T>(&mut self, label: &'static str, id: Id, node: &T) {
        if id != Id::None {
            if !self.seen.insert(id) {
                return
            }
        }

        let entry = self.data.entry(label).or_insert(NodeData {
            count: 0,
            size: 0,
        });

        entry.count += 1;
        entry.size = ::std::mem::size_of_val(node);
    }

    fn print(&self, title: &str) {
        let mut stats: Vec<_> = self.data.iter().collect();

        stats.sort_by_key(|&(_, ref d)| d.count * d.size);

        let mut total_size = 0;

        println!("\n{}\n", title);

        println!("{:<18}{:>18}{:>14}{:>14}",
            "Name", "Accumulated Size", "Count", "Item Size");
        println!("----------------------------------------------------------------");

        for (label, data) in stats {
            println!("{:<18}{:>18}{:>14}{:>14}",
                label,
                to_readable_str(data.count * data.size),
                to_readable_str(data.count),
                to_readable_str(data.size));

            total_size += data.count * data.size;
        }
        println!("----------------------------------------------------------------");
        println!("{:<18}{:>18}\n",
                "Total",
                to_readable_str(total_size));
    }
}

impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
    fn nested_visit_map<'this>(&'this mut self) -> hir_visit::NestedVisitorMap<'this, 'v> {
        panic!("visit_nested_xxx must be manually implemented in this visitor")
    }

    fn visit_nested_item(&mut self, id: hir::ItemId) {
        let nested_item = self.krate.unwrap().item(id.id);
        self.visit_item(nested_item)
    }

    fn visit_nested_trait_item(&mut self, trait_item_id: hir::TraitItemId) {
        let nested_trait_item = self.krate.unwrap().trait_item(trait_item_id);
        self.visit_trait_item(nested_trait_item)
    }

    fn visit_nested_impl_item(&mut self, impl_item_id: hir::ImplItemId) {
        let nested_impl_item = self.krate.unwrap().impl_item(impl_item_id);
        self.visit_impl_item(nested_impl_item)
    }

    fn visit_nested_body(&mut self, body_id: hir::BodyId) {
        let nested_body = self.krate.unwrap().body(body_id);
        self.visit_body(nested_body)
    }

    fn visit_item(&mut self, i: &'v hir::Item) {
        self.record("Item", Id::Node(i.id), i);
        hir_visit::walk_item(self, i)
    }

    ///////////////////////////////////////////////////////////////////////////

    fn visit_mod(&mut self, m: &'v hir::Mod, _s: Span, n: NodeId) {
        self.record("Mod", Id::None, m);
        hir_visit::walk_mod(self, m, n)
    }
    fn visit_foreign_item(&mut self, i: &'v hir::ForeignItem) {
        self.record("ForeignItem", Id::Node(i.id), i);
        hir_visit::walk_foreign_item(self, i)
    }
    fn visit_local(&mut self, l: &'v hir::Local) {
        self.record("Local", Id::Node(l.id), l);
        hir_visit::walk_local(self, l)
    }
    fn visit_block(&mut self, b: &'v hir::Block) {
        self.record("Block", Id::Node(b.id), b);
        hir_visit::walk_block(self, b)
    }
    fn visit_stmt(&mut self, s: &'v hir::Stmt) {
        self.record("Stmt", Id::Node(s.node.id()), s);
        hir_visit::walk_stmt(self, s)
    }
    fn visit_arm(&mut self, a: &'v hir::Arm) {
        self.record("Arm", Id::None, a);
        hir_visit::walk_arm(self, a)
    }
    fn visit_pat(&mut self, p: &'v hir::Pat) {
        self.record("Pat", Id::Node(p.id), p);
        hir_visit::walk_pat(self, p)
    }
    fn visit_decl(&mut self, d: &'v hir::Decl) {
        self.record("Decl", Id::None, d);
        hir_visit::walk_decl(self, d)
    }
    fn visit_expr(&mut self, ex: &'v hir::Expr) {
        self.record("Expr", Id::Node(ex.id), ex);
        hir_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v hir::Ty) {
        self.record("Ty", Id::Node(t.id), t);
        hir_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self,
                fk: hir_visit::FnKind<'v>,
                fd: &'v hir::FnDecl,
                b: hir::BodyId,
                s: Span,
                id: NodeId) {
        self.record("FnDecl", Id::None, fd);
        hir_visit::walk_fn(self, fk, fd, b, s, id)
    }

    fn visit_where_predicate(&mut self, predicate: &'v hir::WherePredicate) {
        self.record("WherePredicate", Id::None, predicate);
        hir_visit::walk_where_predicate(self, predicate)
    }

    fn visit_trait_item(&mut self, ti: &'v hir::TraitItem) {
        self.record("TraitItem", Id::Node(ti.id), ti);
        hir_visit::walk_trait_item(self, ti)
    }
    fn visit_impl_item(&mut self, ii: &'v hir::ImplItem) {
        self.record("ImplItem", Id::Node(ii.id), ii);
        hir_visit::walk_impl_item(self, ii)
    }

    fn visit_ty_param_bound(&mut self, bounds: &'v hir::TyParamBound) {
        self.record("TyParamBound", Id::None, bounds);
        hir_visit::walk_ty_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v hir::StructField) {
        self.record("StructField", Id::Node(s.id), s);
        hir_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self,
                     v: &'v hir::Variant,
                     g: &'v hir::Generics,
                     item_id: NodeId) {
        self.record("Variant", Id::None, v);
        hir_visit::walk_variant(self, v, g, item_id)
    }
    fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
        self.record("Lifetime", Id::Node(lifetime.id), lifetime);
        hir_visit::walk_lifetime(self, lifetime)
    }
    fn visit_lifetime_def(&mut self, lifetime: &'v hir::LifetimeDef) {
        self.record("LifetimeDef", Id::None, lifetime);
        hir_visit::walk_lifetime_def(self, lifetime)
    }
    fn visit_qpath(&mut self, qpath: &'v hir::QPath, id: NodeId, span: Span) {
        self.record("QPath", Id::None, qpath);
        hir_visit::walk_qpath(self, qpath, id, span)
    }
    fn visit_path(&mut self, path: &'v hir::Path, _id: NodeId) {
        self.record("Path", Id::None, path);
        hir_visit::walk_path(self, path)
    }
    fn visit_path_segment(&mut self,
                          path_span: Span,
                          path_segment: &'v hir::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        hir_visit::walk_path_segment(self, path_span, path_segment)
    }
    fn visit_assoc_type_binding(&mut self, type_binding: &'v hir::TypeBinding) {
        self.record("TypeBinding", Id::Node(type_binding.id), type_binding);
        hir_visit::walk_assoc_type_binding(self, type_binding)
    }
    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::Attr(attr.id), attr);
    }
    fn visit_macro_def(&mut self, macro_def: &'v hir::MacroDef) {
        self.record("MacroDef", Id::Node(macro_def.id), macro_def);
        hir_visit::walk_macro_def(self, macro_def)
    }
}

impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {

    fn visit_mod(&mut self, m: &'v ast::Mod, _s: Span, _a: &[ast::Attribute], _n: NodeId) {
        self.record("Mod", Id::None, m);
        ast_visit::walk_mod(self, m)
    }

    fn visit_foreign_item(&mut self, i: &'v ast::ForeignItem) {
        self.record("ForeignItem", Id::None, i);
        ast_visit::walk_foreign_item(self, i)
    }

    fn visit_item(&mut self, i: &'v ast::Item) {
        self.record("Item", Id::None, i);
        ast_visit::walk_item(self, i)
    }

    fn visit_local(&mut self, l: &'v ast::Local) {
        self.record("Local", Id::None, l);
        ast_visit::walk_local(self, l)
    }

    fn visit_block(&mut self, b: &'v ast::Block) {
        self.record("Block", Id::None, b);
        ast_visit::walk_block(self, b)
    }

    fn visit_stmt(&mut self, s: &'v ast::Stmt) {
        self.record("Stmt", Id::None, s);
        ast_visit::walk_stmt(self, s)
    }

    fn visit_arm(&mut self, a: &'v ast::Arm) {
        self.record("Arm", Id::None, a);
        ast_visit::walk_arm(self, a)
    }

    fn visit_pat(&mut self, p: &'v ast::Pat) {
        self.record("Pat", Id::None, p);
        ast_visit::walk_pat(self, p)
    }

    fn visit_expr(&mut self, ex: &'v ast::Expr) {
        self.record("Expr", Id::None, ex);
        ast_visit::walk_expr(self, ex)
    }

    fn visit_ty(&mut self, t: &'v ast::Ty) {
        self.record("Ty", Id::None, t);
        ast_visit::walk_ty(self, t)
    }

    fn visit_fn(&mut self,
                fk: ast_visit::FnKind<'v>,
                fd: &'v ast::FnDecl,
                s: Span,
                _: NodeId) {
        self.record("FnDecl", Id::None, fd);
        ast_visit::walk_fn(self, fk, fd, s)
    }

    fn visit_trait_item(&mut self, ti: &'v ast::TraitItem) {
        self.record("TraitItem", Id::None, ti);
        ast_visit::walk_trait_item(self, ti)
    }

    fn visit_impl_item(&mut self, ii: &'v ast::ImplItem) {
        self.record("ImplItem", Id::None, ii);
        ast_visit::walk_impl_item(self, ii)
    }

    fn visit_ty_param_bound(&mut self, bounds: &'v ast::TyParamBound) {
        self.record("TyParamBound", Id::None, bounds);
        ast_visit::walk_ty_param_bound(self, bounds)
    }

    fn visit_struct_field(&mut self, s: &'v ast::StructField) {
        self.record("StructField", Id::None, s);
        ast_visit::walk_struct_field(self, s)
    }

    fn visit_variant(&mut self,
                     v: &'v ast::Variant,
                     g: &'v ast::Generics,
                     item_id: NodeId) {
        self.record("Variant", Id::None, v);
        ast_visit::walk_variant(self, v, g, item_id)
    }

    fn visit_lifetime(&mut self, lifetime: &'v ast::Lifetime) {
        self.record("Lifetime", Id::None, lifetime);
        ast_visit::walk_lifetime(self, lifetime)
    }

    fn visit_lifetime_def(&mut self, lifetime: &'v ast::LifetimeDef) {
        self.record("LifetimeDef", Id::None, lifetime);
        ast_visit::walk_lifetime_def(self, lifetime)
    }

    fn visit_mac(&mut self, mac: &'v ast::Mac) {
        self.record("Mac", Id::None, mac);
    }

    fn visit_path_list_item(&mut self,
                            prefix: &'v ast::Path,
                            item: &'v ast::PathListItem) {
        self.record("PathListItem", Id::None, item);
        ast_visit::walk_path_list_item(self, prefix, item)
    }

    fn visit_path_segment(&mut self,
                          path_span: Span,
                          path_segment: &'v ast::PathSegment) {
        self.record("PathSegment", Id::None, path_segment);
        ast_visit::walk_path_segment(self, path_span, path_segment)
    }

    fn visit_assoc_type_binding(&mut self, type_binding: &'v ast::TypeBinding) {
        self.record("TypeBinding", Id::None, type_binding);
        ast_visit::walk_assoc_type_binding(self, type_binding)
    }

    fn visit_attribute(&mut self, attr: &'v ast::Attribute) {
        self.record("Attribute", Id::None, attr);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate pretty_assertions;

extern crate rexpect;

use rexpect::spawn;
use rexpect::errors::*;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};

#[test]
fn fmt_repl() {
    let source = "src/repl.glu";

    let mut before = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut before)
        .unwrap();

    let status = Command::new("../target/debug/gluon")
        .args(&["fmt", source])
        .spawn()
        .expect("Could not find gluon executable")
        .wait()
        .unwrap();
    assert!(status.success());

    let mut after = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut after)
        .unwrap();

    assert_eq!(before, after);
}

#[test]
fn issue_365_run_io_from_command_line() {
    if ::std::env::var("GLUON_PATH").is_err() {
        ::std::env::set_var("GLUON_PATH", "..");
    }

    let path = env::args().next().unwrap();
    let gluon_path = Path::new(&path[..])
        .parent()
        .and_then(|p| p.parent())
        .expect("folder")
        .join("gluon");
    let output = Command::new(&*gluon_path)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .arg("tests/print.glu")
        .output()
        .unwrap_or_else(|err| panic!("{}\nWhen opening `{}`", err, gluon_path.display()));

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr != "" {
        panic!("{}", stderr);
    }
    assert_eq!(String::from_utf8_lossy(&output.stdout), "123\n");
}

static COMMAND: &str = "../target/debug/gluon -i";
static TIMEOUT: u64 = 10_000;
static PROMPT: &str = "> ";

macro_rules! test {
    ($b: block) => {
        if ::std::env::var("GLUON_PATH").is_err() {
            ::std::env::set_var("GLUON_PATH", "..");
        }

        || -> Result<()> {
            $b
        }().unwrap_or_else(|err| panic!("{}", err));
    };
}

#[test]
fn prompt() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        Ok(())
    });
}

#[test]
fn exit() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":q")?;
        repl.exp_eof()?;

        Ok(())
    });
}

#[test]
fn hello_world() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let io = import! std.io")?;

        repl.send_line("io.println \"Hello world\"")?;
        repl.exp_string("Hello world")?;

        Ok(())
    });
}

#[test]
fn expression_types() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":t 5")?;
        repl.exp_string("Int")?;

        repl.send_line(":t 5 + 5")?;
        repl.exp_string("Int -> Int")?;

        repl.send_line(":t \"gluon\"")?;
        repl.exp_string("String")?;

        Ok(())
    });
}

#[test]
fn names() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":i std.prelude.show")?;
        repl.exp_string("std.prelude.show: forall a . [std.prelude.Show a] -> a -> String")?;

        Ok(())
    });
}

#[test]
fn comments() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("1 + 2 // Calls the + function on 1 and 2")?;
        repl.exp_string("3")?;

        repl.send_line("1 + 2 /* Calls the + function on 1 and 2 */")?;
        repl.exp_string("3")?;

        Ok(())
    });
}

#[test]
fn if_expressions() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("if True then 1 else 0")?;
        repl.exp_string("1")?;

        repl.send_line("if False then 1 else 0")?;
        repl.exp_string("0")?;

        Ok(())
    });
}

#[test]
fn records() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let record = { pi = 3.14, add1 = (+) 1.0 }")?;

        repl.send_line("record.pi")?;
        repl.exp_string("3.14")?;

        repl.send_line("let record_2 = {x = 1 .. record }")?;

        repl.send_line("record_2.x")?;
        repl.exp_string("1")?;

        repl.send_line("record_2.pi")?;
        repl.exp_string("3.14")?;

        Ok(())
    });
}

#[test]
fn arrays() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let array = import! std.array")?;

        repl.send_line("array.len [1, 2, 3]")?;
        repl.exp_string("3")?;

        Ok(())
    });
}
<commit_msg>Add implicit Ok(()) to the test macro.<commit_after>#[macro_use]
extern crate pretty_assertions;

extern crate rexpect;

use rexpect::spawn;
use rexpect::errors::*;

use std::env;
use std::fs::File;
use std::io::Read;
use std::path::Path;
use std::process::{Command, Stdio};

#[test]
fn fmt_repl() {
    let source = "src/repl.glu";

    let mut before = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut before)
        .unwrap();

    let status = Command::new("../target/debug/gluon")
        .args(&["fmt", source])
        .spawn()
        .expect("Could not find gluon executable")
        .wait()
        .unwrap();
    assert!(status.success());

    let mut after = String::new();
    File::open(source)
        .unwrap()
        .read_to_string(&mut after)
        .unwrap();

    assert_eq!(before, after);
}

#[test]
fn issue_365_run_io_from_command_line() {
    if ::std::env::var("GLUON_PATH").is_err() {
        ::std::env::set_var("GLUON_PATH", "..");
    }

    let path = env::args().next().unwrap();
    let gluon_path = Path::new(&path[..])
        .parent()
        .and_then(|p| p.parent())
        .expect("folder")
        .join("gluon");
    let output = Command::new(&*gluon_path)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
        .arg("tests/print.glu")
        .output()
        .unwrap_or_else(|err| panic!("{}\nWhen opening `{}`", err, gluon_path.display()));

    let stderr = String::from_utf8_lossy(&output.stderr);
    if stderr != "" {
        panic!("{}", stderr);
    }
    assert_eq!(String::from_utf8_lossy(&output.stdout), "123\n");
}

static COMMAND: &str = "../target/debug/gluon -i";
static TIMEOUT: u64 = 10_000;
static PROMPT: &str = "> ";

macro_rules! test {
    ($b: block) => {
        if ::std::env::var("GLUON_PATH").is_err() {
            ::std::env::set_var("GLUON_PATH", "..");
        }

        || -> Result<()> {
            $b
            Ok(())
        }().unwrap_or_else(|err| panic!("{}", err));
    };
}

#[test]
fn prompt() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;
    });
}

#[test]
fn exit() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":q")?;
        repl.exp_eof()?;
    });
}

#[test]
fn hello_world() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let io = import! std.io")?;

        repl.send_line("io.println \"Hello world\"")?;
        repl.exp_string("Hello world")?;
    });
}

#[test]
fn expression_types() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":t 5")?;
        repl.exp_string("Int")?;

        repl.send_line(":t 5 + 5")?;
        repl.exp_string("Int -> Int")?;

        repl.send_line(":t \"gluon\"")?;
        repl.exp_string("String")?;
    });
}

#[test]
fn names() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line(":i std.prelude.show")?;
        repl.exp_string("std.prelude.show: forall a . [std.prelude.Show a] -> a -> String")?;
    });
}

#[test]
fn comments() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("1 + 2 // Calls the + function on 1 and 2")?;
        repl.exp_string("3")?;

        repl.send_line("1 + 2 /* Calls the + function on 1 and 2 */")?;
        repl.exp_string("3")?;
    });
}

#[test]
fn if_expressions() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("if True then 1 else 0")?;
        repl.exp_string("1")?;

        repl.send_line("if False then 1 else 0")?;
        repl.exp_string("0")?;
    });
}

#[test]
fn records() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let record = { pi = 3.14, add1 = (+) 1.0 }")?;

        repl.send_line("record.pi")?;
        repl.exp_string("3.14")?;

        repl.send_line("let record_2 = {x = 1 .. record }")?;

        repl.send_line("record_2.x")?;
        repl.exp_string("1")?;

        repl.send_line("record_2.pi")?;
        repl.exp_string("3.14")?;
    });
}

#[test]
fn arrays() {
    test!({
        let mut repl = spawn(COMMAND, Some(TIMEOUT))?;
        repl.exp_string(PROMPT)?;

        repl.send_line("let array = import! std.array")?;

        repl.send_line("array.len [1, 2, 3]")?;
        repl.exp_string("3")?;
    });
}
<|endoftext|>


<commit_before>use super::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord;
use super::crosslink_record::CrosslinkRecord;
use super::fork_data::ForkData;
use super::pending_attestation_record::PendingAttestationRecord;
use super::shard_and_committee::ShardAndCommittee;
use super::shard_reassignment_record::ShardReassignmentRecord;
use super::validator_record::ValidatorRecord;
use super::Hash256;

#[derive(Debug, PartialEq)]
pub struct BeaconState {
    validator_registry: Vec<ValidatorRecord>,
    validator_registry_latest_change_slot: u64,
    validator_registry_exit_count: u64,
    validator_registry_delta_chain_tip: Hash256,
    randao_mix: Hash256,
    next_seed: Hash256,
    shard_and_committee_for_slots: Vec<Vec<ShardAndCommittee>>,
    persistent_committees: Vec<Vec<u32>>,
    persistent_committee_reassignments: Vec<ShardReassignmentRecord>,
    previous_justified_slot: u64,
    justified_slot: u64,
    justified_slot_bitfield: u64,
    finalized_slot: u64,
    latest_crosslinks: Vec<CrosslinkRecord>,
    latest_state_recalculation_slot: u64,
    latest_block_hashes: Vec<Hash256>,
    latest_penalized_exit_balances: Vec<u64>,
    latest_attestations: Vec<PendingAttestationRecord>,
    processed_pow_receipt_root: Hash256,
    candidate_pow_receipt_roots: Vec<CandidatePoWReceiptRootRecord>,
    genesis_time: u64,
    fork_data: ForkData,
}
<commit_msg>Make all BeaconState fields public (doh!)<commit_after>use super::candidate_pow_receipt_root_record::CandidatePoWReceiptRootRecord;
use super::crosslink_record::CrosslinkRecord;
use super::fork_data::ForkData;
use super::pending_attestation_record::PendingAttestationRecord;
use super::shard_and_committee::ShardAndCommittee;
use super::shard_reassignment_record::ShardReassignmentRecord;
use super::validator_record::ValidatorRecord;
use super::Hash256;

#[derive(Debug, PartialEq)]
pub struct BeaconState {
    pub validator_registry: Vec<ValidatorRecord>,
    pub validator_registry_latest_change_slot: u64,
    pub validator_registry_exit_count: u64,
    pub validator_registry_delta_chain_tip: Hash256,
    pub randao_mix: Hash256,
    pub next_seed: Hash256,
    pub shard_and_committee_for_slots: Vec<Vec<ShardAndCommittee>>,
    pub persistent_committees: Vec<Vec<u32>>,
    pub persistent_committee_reassignments: Vec<ShardReassignmentRecord>,
    pub previous_justified_slot: u64,
    pub justified_slot: u64,
    pub justified_slot_bitfield: u64,
    pub finalized_slot: u64,
    pub latest_crosslinks: Vec<CrosslinkRecord>,
    pub latest_state_recalculation_slot: u64,
    pub latest_block_hashes: Vec<Hash256>,
    pub latest_penalized_exit_balances: Vec<u64>,
    pub latest_attestations: Vec<PendingAttestationRecord>,
    pub processed_pow_receipt_root: Hash256,
    pub candidate_pow_receipt_roots: Vec<CandidatePoWReceiptRootRecord>,
    pub genesis_time: u64,
    pub fork_data: ForkData,
}
<|endoftext|>


<commit_before>extern crate clap;

use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::fs::File;
use std::collections::hash_map::HashMap;
use std::ascii::AsciiExt;
use std::cmp::Ordering;

#[derive(Debug)]
struct Room {
    name: String,
    id: u64,
    checksum: String,
}

impl Room {
    fn is_valid(&self) -> bool {
        self.checksum == self.calculate_checksum()
    }
    fn calculate_checksum(&self) -> String {
        let mut letter_freqs = HashMap::new();
        for letter in self.name.chars() {
            if letter.is_ascii() && letter.is_lowercase() {
                *letter_freqs.entry(letter).or_insert(0) += 1;
            }
        }
        let mut letters: Vec<char> = letter_freqs.keys().cloned().collect();
        letters.sort_by(
            // want descending order in frequency
            |a, b| match letter_freqs[b].cmp(&letter_freqs[a]) {
                Ordering::Equal => a.cmp(b),
                other => other,
            }
        );
        letters.into_iter().take(5).collect()
    }
}

fn invalid_data(msg: &str) -> Error {
    Error::new(ErrorKind::InvalidData, msg)
}

fn parse_args() -> std::io::Result<Box<Read>> {
    let source = clap::App::new("Day 04")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("rooms")
             .index(1)
             .short("f")
             .long("rooms")
             .help("file to read rooms from. Reads from stdin otherwise")
             .takes_value(true)
        )
        .get_matches()
        .value_of_os("rooms")
        .map(|str| str.to_owned());
    match source {
        Some(filename) => Ok(Box::new(File::open(filename)?)),
        None => Ok(Box::new(std::io::stdin())),
    }
}

fn read_rooms<R: Read>(source: &mut R) -> std::io::Result<Vec<Room>> {
    let mut contents = String::new();
    source.read_to_string(&mut contents)?;
    contents.lines().map(
        |line| {
            let line = line.trim();
            let (name, rest) = line.split_at(
                line.find(|ch: char| !(ch.is_ascii() && ch.is_lowercase() || ch == '-'))
                    .ok_or(invalid_data(&format!("Unnamed room: {}", line)))?
            );
            if !name.ends_with('-') {
                return Err(invalid_data(&format!("Malformed line: {}", line)));
            }
            let name = &name[0..name.len()-1];
            let (id, rest) = rest.split_at(
                rest.find(|ch: char| !ch.is_digit(10))
                    .ok_or(invalid_data(&format!("No-id room: {}", line)))?
            );
            if rest.len() != 7
                || !rest.starts_with('[')
                || !rest.ends_with(']')
                || !rest[1..6].chars().all(|ch| ch.is_ascii() && ch.is_lowercase())
            {
                return Err(invalid_data(&format!("Malformed checksum: {}", rest)));
            }
            let checksum = &rest[1..6];
            Ok(Room{
                name: name.to_owned(),
                id: id.parse().expect(&format!("somehow got non-numeric id: {}", id)),
                checksum: checksum.to_owned(),
            })
        }
    ).collect()
}

fn main() {
    let mut source = parse_args()
        .unwrap_or_else(|err| panic!("Error reading file: {}", err));
    let rooms = read_rooms(&mut source)
        .unwrap_or_else(|err| panic!("Error reading rooms: {}", err));
    let valid_room_sum: u64 = rooms.iter()
        .filter(|room| room.is_valid())
        .map(|room| room.id)
        .sum();
    println!("Sum of valid room numbers: {}", valid_room_sum);
}
<commit_msg>Day 4 part 2<commit_after>extern crate clap;

use std::io::prelude::*;
use std::io::{Error, ErrorKind};
use std::fs::File;
use std::collections::hash_map::HashMap;
use std::ascii::AsciiExt;
use std::cmp::Ordering;

#[derive(Debug)]
struct Room {
    name: String,
    id: u64,
    checksum: String,
}

impl Room {
    fn is_valid(&self) -> bool {
        self.checksum == self.calculate_checksum()
    }
    fn calculate_checksum(&self) -> String {
        let mut letter_freqs = HashMap::new();
        for letter in self.name.chars() {
            if letter.is_ascii() && letter.is_lowercase() {
                *letter_freqs.entry(letter).or_insert(0) += 1;
            }
        }
        let mut letters: Vec<char> = letter_freqs.keys().cloned().collect();
        letters.sort_by(
            // want descending order in frequency
            |a, b| match letter_freqs[b].cmp(&letter_freqs[a]) {
                Ordering::Equal => a.cmp(b),
                other => other,
            }
        );
        letters.into_iter().take(5).collect()
    }
    fn decrypt(&self) -> String {
        fn rotate(ch: char, dist: u64) -> char {
            match ch {
                '-' => ' ',
                'a'...'z' =>
                    ((dist + ch as u64 - 'a' as u64) % 26 + 'a' as u64) as u8 as char,
                _ => ch,
            }
        }
        self.name.chars().map(|ch| rotate(ch, self.id)).collect()
    }
}

fn invalid_data(msg: &str) -> Error {
    Error::new(ErrorKind::InvalidData, msg)
}

fn parse_args() -> std::io::Result<Box<Read>> {
    let source = clap::App::new("Day 04")
        .author("Devon Hollowood")
        .arg(clap::Arg::with_name("rooms")
             .index(1)
             .short("f")
             .long("rooms")
             .help("file to read rooms from. Reads from stdin otherwise")
             .takes_value(true)
        )
        .get_matches()
        .value_of_os("rooms")
        .map(|str| str.to_owned());
    match source {
        Some(filename) => Ok(Box::new(File::open(filename)?)),
        None => Ok(Box::new(std::io::stdin())),
    }
}

fn read_rooms<R: Read>(source: &mut R) -> std::io::Result<Vec<Room>> {
    let mut contents = String::new();
    source.read_to_string(&mut contents)?;
    contents.lines().map(
        |line| {
            let line = line.trim();
            let (name, rest) = line.split_at(
                line.find(|ch: char| !(ch.is_ascii() && ch.is_lowercase() || ch == '-'))
                    .ok_or(invalid_data(&format!("Unnamed room: {}", line)))?
            );
            if !name.ends_with('-') {
                return Err(invalid_data(&format!("Malformed line: {}", line)));
            }
            let name = &name[0..name.len()-1];
            let (id, rest) = rest.split_at(
                rest.find(|ch: char| !ch.is_digit(10))
                    .ok_or(invalid_data(&format!("No-id room: {}", line)))?
            );
            if rest.len() != 7
                || !rest.starts_with('[')
                || !rest.ends_with(']')
                || !rest[1..6].chars().all(|ch| ch.is_ascii() && ch.is_lowercase())
            {
                return Err(invalid_data(&format!("Malformed checksum: {}", rest)));
            }
            let checksum = &rest[1..6];
            Ok(Room{
                name: name.to_owned(),
                id: id.parse().expect(&format!("somehow got non-numeric id: {}", id)),
                checksum: checksum.to_owned(),
            })
        }
    ).collect()
}

fn main() {
    let mut source = parse_args()
        .unwrap_or_else(|err| panic!("Error reading file: {}", err));
    let rooms = read_rooms(&mut source)
        .unwrap_or_else(|err| panic!("Error reading rooms: {}", err));
    let valid_room_sum: u64 = rooms.iter()
        .filter(|room| room.is_valid())
        .map(|room| room.id)
        .sum();
    println!("Sum of valid room numbers: {}", valid_room_sum);
    for room in rooms.iter().filter(|room| room.is_valid()) {
        println!("{}: {}", room.id, room.decrypt());
    }
}
<|endoftext|>