<commit_before>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// 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/.
//

extern crate libc;

use logger;
use self::libc::getenv;

use std::ffi::CString;

pub struct Env {
    device: String,
    dir:    String,
    script: String
}

impl Env {
    pub fn new() -> Env {
        return Env{device: "".to_string(),
                   dir:    "".to_string(),
                   script: "".to_string()}
    }

    pub fn load(&mut self, l: &logger::Logger) {
        self.load_device(l);
        self.load_dir(l);
        self.load_script(l);
    }

    fn load_device(&mut self, l: &logger::Logger) {
        self.device = self.load_("AMAE_DEVICE", l);
    }

    fn load_dir(&mut self, l: &logger::Logger) {
        self.dir = self.load_("AMAE_MOUNT_DIR", l);
    }

    fn load_script(&mut self, l: &logger::Logger) {
        self.script = self.load_("AMAE_SCRIPT", l);
    }

    fn load_(&self, name: &str, l: &logger::Logger) -> String {
        let cname = CString::new(name).unwrap();
        let cname_ptr = cname.as_ptr();
        unsafe {
            let out = getenv(cname_ptr);
            if out.is_null() { self.error_(name, l); }
            let cstring = CString::from_raw(out);
            let res = cstring.into_string();
            match res {
                Ok(v)  => return v,
                Err(e) => {
                    let mut ss = self.ef(name);
                    ss += " ";
                    ss += &(format!("{:?}", e));
                    self.error__(ss, l);
                    return "".to_string();
                }
            }
        }
    }

    fn error_(&self, name: &str, l: &logger::Logger) {
        self.error__(self.ef(name), l);
    }

    fn ef(&self, name: &str) -> String {
        let em = "Can't get env: ".to_string() +
            name + "!";
        return em;
    }

    fn error__(&self, s: String, l: &logger::Logger) {
        l.error(s.as_str());
        panic!(s);
    }
}
/*
#[test]
fn hello() {
    let x = Err("test");
    assert_eq!("test", x.unwrap());
}
 */
<commit_msg>add Env:: getDir, getScript<commit_after>//
// Copyright (c) 2016, Boris Popov <popov@whitekefir.ru>
//
// 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/.
//

extern crate libc;

use logger;
use self::libc::getenv;

use std::ffi::CString;

pub struct Env {
    device: String,
    dir:    String,
    script: String
}

impl Env {
    pub fn new() -> Env {
        return Env{device: "".to_string(),
                   dir:    "".to_string(),
                   script: "".to_string()}
    }

    pub fn load(&mut self, l: &logger::Logger) {
        self.load_device(l);
        self.load_dir(l);
        self.load_script(l);
    }

    pub fn getDir(&self) -> &str {
        return &self.dir;
    }

    pub fn getScript(&self) -> &str {
        return &self.script;
    }

    fn load_device(&mut self, l: &logger::Logger) {
        self.device = self.load_("AMAE_DEVICE", l);
    }

    fn load_dir(&mut self, l: &logger::Logger) {
        self.dir = self.load_("AMAE_MOUNT_DIR", l);
    }

    fn load_script(&mut self, l: &logger::Logger) {
        self.script = self.load_("AMAE_SCRIPT", l);
    }

    fn load_(&self, name: &str, l: &logger::Logger) -> String {
        let cname = CString::new(name).unwrap();
        let cname_ptr = cname.as_ptr();
        unsafe {
            let out = getenv(cname_ptr);
            if out.is_null() { self.error_(name, l); }
            let cstring = CString::from_raw(out);
            let res = cstring.into_string();
            match res {
                Ok(v)  => return v,
                Err(e) => {
                    let mut ss = self.ef(name);
                    ss += " ";
                    ss += &(format!("{}", e));
                    self.error__(ss, l);
                    return "".to_string();
                }
            }
        }
    }

    fn error_(&self, name: &str, l: &logger::Logger) {
        self.error__(self.ef(name), l);
    }

    fn ef(&self, name: &str) -> String {
        let em = "Can't get env: ".to_string() +
            name + "!";
        return em;
    }

    fn error__(&self, s: String, l: &logger::Logger) {
        l.error(s.as_str());
        panic!(s);
    }
}
/*
#[test]
fn hello() {
    let x = Err("test");
    assert_eq!("test", x.unwrap());
}
 */
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod bridge_converter;
mod cpp_postprocessor;
mod preprocessor_parse_callbacks;

#[cfg(test)]
mod integration_tests;

use proc_macro2::TokenStream as TokenStream2;
use std::path::PathBuf;

use cxx_gen::GeneratedCode;
use indoc::indoc;
use quote::ToTokens;
use syn::parse::{Parse, ParseStream, Result as ParseResult};
use syn::{parse_quote, ItemMod, Macro};

use cpp_postprocessor::EncounteredType;
use log::{debug, info, warn};
use osstrtools::OsStrTools;
use preprocessor_parse_callbacks::{PreprocessorDefinitions, PreprocessorParseCallbacks};
use std::rc::Rc;
use std::sync::Mutex;

/// Turn off and remove this constant when
/// https://github.com/rust-lang/rust/pull/75857 is fixed.
/// This should allow removal of the entire cpp_postprocessor module.
const TEMPORARY_HACK_TO_AVOID_REDEFINITIONS: bool = true;

const BINDGEN_BLOCKLIST: &[&str] = &[
    "std.*",
    ".*mbstate_t.*",
];

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Bindgen(()),
    CxxGen(cxx_gen::Error),
    Parsing(syn::Error),
    NoAutoCxxInc,
    CouldNotCanoncalizeIncludeDir(PathBuf),
    Conversion(bridge_converter::ConvertError),
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

pub enum CppInclusion {
    Define(String),
    Header(String),
}

/// Core of the autocxx engine.
/// TODO - consider whether this 'engine' crate should actually be a
/// directory of source symlinked from all the other sub-crates, so that
/// we avoid exposing an external interface from this code.
pub struct IncludeCpp {
    inclusions: Vec<CppInclusion>,
    allowlist: Vec<String>,
    preconfigured_inc_dirs: Option<std::ffi::OsString>,
    parse_only: bool,
    preprocessor_definitions: Rc<Mutex<PreprocessorDefinitions>>,
}

impl Parse for IncludeCpp {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Self::new_from_parse_stream(input)
    }
}

fn dump_generated_code(gen: GeneratedCode) -> Result<GeneratedCode> {
    info!(
        "CXX:\n{}",
        String::from_utf8(gen.implementation.clone()).unwrap()
    );
    info!(
        "header:\n{}",
        String::from_utf8(gen.header.clone()).unwrap()
    );
    Ok(gen)
}

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
static PRELUDE: &str = indoc! {"
    /**
    *

    */
    template<typename T> class UniquePtr {
        T* ptr;
    };

    /**
    *

    */
    class CxxString {
        char* str_data;
    };
    \n"};

impl IncludeCpp {
    fn new_from_parse_stream(input: ParseStream) -> syn::Result<Self> {
        // Takes as inputs:
        // 1. List of headers to include
        // 2. List of #defines to include
        // 3. Allowlist

        let mut inclusions = Vec::new();
        let mut allowlist = Vec::new();
        let mut parse_only = false;

        while !input.is_empty() {
            let ident: syn::Ident = input.parse()?;
            if ident == "Header" {
                let args;
                syn::parenthesized!(args in input);
                let hdr: syn::LitStr = args.parse()?;
                inclusions.push(CppInclusion::Header(hdr.value()));
            } else if ident == "Allow" {
                let args;
                syn::parenthesized!(args in input);
                let allow: syn::LitStr = args.parse()?;
                allowlist.push(allow.value());
            } else if ident == "ParseOnly" {
                parse_only = true;
            } else {
                return Err(syn::Error::new(ident.span(), "expected Header or Allow"));
            }
            if input.is_empty() {
                break;
            }
            input.parse::<syn::Token![,]>()?;
        }

        Ok(IncludeCpp {
            inclusions,
            allowlist,
            preconfigured_inc_dirs: None,
            parse_only,
            preprocessor_definitions: Rc::new(Mutex::new(PreprocessorDefinitions::new())),
        })
    }

    pub fn new_from_syn(mac: Macro) -> Result<Self> {
        mac.parse_body::<IncludeCpp>().map_err(Error::Parsing)
    }

    pub fn set_include_dirs<P: AsRef<std::ffi::OsStr>>(&mut self, include_dirs: P) {
        self.preconfigured_inc_dirs = Some(include_dirs.as_ref().into());
    }

    fn build_header(&self) -> String {
        let mut s = PRELUDE.to_string();
        for incl in &self.inclusions {
            let text = match incl {
                CppInclusion::Define(symbol) => format!("#define {}\n", symbol),
                CppInclusion::Header(path) => format!("#include \"{}\"\n", path),
            };
            s.push_str(&text);
        }
        s
    }

    fn determine_incdirs(&self) -> Result<Vec<PathBuf>> {
        let inc_dirs = match &self.preconfigured_inc_dirs {
            Some(d) => d.clone(),
            None => std::env::var_os("AUTOCXX_INC").ok_or(Error::NoAutoCxxInc)?,
        };
        // TODO consider if we can or should look up the include path automatically
        // instead of requiring callers always to set AUTOCXX_INC.
        let multi_path_separator = if std::path::MAIN_SEPARATOR == '/' {
            b':'
        } else {
            b';'
        }; // there's probably a crate for this
        let splitter = [multi_path_separator];
        let inc_dirs = inc_dirs.split(&splitter[0..1]);
        let mut inc_dir_paths = Vec::new();
        for inc_dir in inc_dirs {
            let p: PathBuf = inc_dir.into();
            let p = p
                .canonicalize()
                .map_err(|_| Error::CouldNotCanoncalizeIncludeDir(p))?;
            inc_dir_paths.push(p);
        }
        Ok(inc_dir_paths)
    }

    fn make_bindgen_builder(&self) -> Result<bindgen::Builder> {
        let inc_dirs = self.determine_incdirs()?;

        let full_header = self.build_header();
        debug!("Full header: {}", full_header);
        debug!("Inc dir: {:?}", inc_dirs);

        // TODO work with OsStrs here to avoid the .display()
        let mut builder = bindgen::builder()
            .clang_args(&["-x", "c++", "-std=c++14"])
            .derive_copy(false)
            .derive_debug(false)
            .parse_callbacks(Box::new(PreprocessorParseCallbacks::new(
                self.preprocessor_definitions.clone(),
            )))
            .default_enum_style(bindgen::EnumVariation::Rust {
                non_exhaustive: false,
            })
            .layout_tests(false); // TODO revisit later
        for item in BINDGEN_BLOCKLIST.iter() {
            builder = builder.blacklist_item(*item);
        }

        for inc_dir in inc_dirs {
            builder = builder.clang_arg(format!("-I{}", inc_dir.display()));
        }
        builder = builder.header_contents("example.hpp", &full_header);
        // 3. Passes allowlist and other options to the bindgen::Builder equivalent
        //    to --output-style=cxx --allowlist=<as passed in>
        for a in &self.allowlist {
            // TODO - allowlist type/functions/separately
            builder = builder
                .whitelist_type(a)
                .whitelist_function(a)
                .whitelist_var(a);
        }
        Ok(builder)
    }

    pub fn generate_rs(&self) -> Result<TokenStream2> {
        let (itemmod, _) = self.do_generation()?;
        Ok(itemmod.to_token_stream())
    }

    fn get_preprocessor_defs_mod(&self) -> Option<ItemMod> {
        let another_ref = self.preprocessor_definitions.clone();
        let m = another_ref.try_lock().unwrap().to_mod();
        m
    }

    fn do_generation(&self) -> Result<(Option<ItemMod>, Vec<EncounteredType>)> {
        // If we are in parse only mode, do nothing. This is used for
        // doc tests to ensure the parsing is valid, but we can't expect
        // valid C++ header files or linkers to allow a complete build.
        if self.parse_only {
            return Ok((None, Vec::new()));
        }
        // 4. (also respects environment variables to pick up more headers,
        //     include paths and #defines)
        // Then:
        // 1. Builds an overall C++ header with all those #defines and #includes
        // 2. Passes it to bindgen::Builder::header
        let bindings = self
            .make_bindgen_builder()?
            .generate()
            .map_err(Error::Bindgen)?;
        // This bindings object is actually a TokenStream internally and we're wasting
        // effort converting to and from string. We could enhance the bindgen API
        // in future.
        let bindings = bindings.to_string();
        // Manually add the mod ffi {} so that we can ask syn to parse
        // into a single construct.
        let bindings = format!("mod ffi {{ {} }}", bindings);
        info!("Bindings: {}", bindings);
        let bindings = syn::parse_str::<ItemMod>(&bindings).map_err(Error::Parsing)?;

        let mut include_list = Vec::new();
        for incl in &self.inclusions {
            match incl {
                CppInclusion::Header(ref hdr) => {
                    include_list.push(hdr.clone());
                }
                CppInclusion::Define(_) => warn!("Currently no way to define! within cxx"),
            }
        }

        let mut converter = bridge_converter::BridgeConverter::new(
            include_list,
            TEMPORARY_HACK_TO_AVOID_REDEFINITIONS,
        );
        let results = converter.convert(bindings).map_err(Error::Conversion)?;
        let mut items = results.items;
        let types_encountered = results.types_encountered;
        if let Some(itemmod) = self.get_preprocessor_defs_mod() {
            items.push(syn::Item::Mod(itemmod));
        }
        let new_bindings: ItemMod = parse_quote! {
            mod ffi {
                #(#items)*
            }
        };
        info!(
            "New bindings: {}",
            new_bindings.to_token_stream().to_string()
        );
        Ok((Some(new_bindings), types_encountered))
    }

    pub fn generate_h_and_cxx(self) -> Result<GeneratedCode> {
        let (rs, types_encountered) = self.do_generation()?;
        let rs = match rs {
            Some(itemmod) => itemmod.into_token_stream(),
            None => TokenStream2::new(),
        };
        let opt = cxx_gen::Opt::default();
        cxx_gen::generate_header_and_cc(rs, &opt)
            .map_err(Error::CxxGen)
            .and_then(dump_generated_code)
            .and_then(|gen| {
                if TEMPORARY_HACK_TO_AVOID_REDEFINITIONS {
                    Ok(GeneratedCode {
                        header: cpp_postprocessor::disable_types(gen.header, &types_encountered),
                        implementation: cpp_postprocessor::disable_types(
                            gen.implementation,
                            &types_encountered,
                        ),
                    })
                } else {
                    Ok(gen)
                }
            })
            .and_then(dump_generated_code)
    }

    pub fn include_dirs(&self) -> Result<Vec<PathBuf>> {
        self.determine_incdirs()
    }
}
<commit_msg>Update comments.<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod bridge_converter;
mod cpp_postprocessor;
mod preprocessor_parse_callbacks;

#[cfg(test)]
mod integration_tests;

use proc_macro2::TokenStream as TokenStream2;
use std::path::PathBuf;

use cxx_gen::GeneratedCode;
use indoc::indoc;
use quote::ToTokens;
use syn::parse::{Parse, ParseStream, Result as ParseResult};
use syn::{parse_quote, ItemMod, Macro};

use cpp_postprocessor::EncounteredType;
use log::{debug, info, warn};
use osstrtools::OsStrTools;
use preprocessor_parse_callbacks::{PreprocessorDefinitions, PreprocessorParseCallbacks};
use std::rc::Rc;
use std::sync::Mutex;

/// Turn off and remove this constant when
/// https://github.com/rust-lang/rust/pull/75857 is fixed.
/// This should allow removal of the entire cpp_postprocessor module.
const TEMPORARY_HACK_TO_AVOID_REDEFINITIONS: bool = true;

const BINDGEN_BLOCKLIST: &[&str] = &[
    "std.*",
    ".*mbstate_t.*",
];

#[derive(Debug)]
pub enum Error {
    Io(std::io::Error),
    Bindgen(()),
    CxxGen(cxx_gen::Error),
    Parsing(syn::Error),
    NoAutoCxxInc,
    CouldNotCanoncalizeIncludeDir(PathBuf),
    Conversion(bridge_converter::ConvertError),
}

pub type Result<T, E = Error> = std::result::Result<T, E>;

pub enum CppInclusion {
    Define(String),
    Header(String),
}

/// Core of the autocxx engine.
/// TODO - consider whether this 'engine' crate should actually be a
/// directory of source symlinked from all the other sub-crates, so that
/// we avoid exposing an external interface from this code.
pub struct IncludeCpp {
    inclusions: Vec<CppInclusion>,
    allowlist: Vec<String>,
    preconfigured_inc_dirs: Option<std::ffi::OsString>,
    parse_only: bool,
    preprocessor_definitions: Rc<Mutex<PreprocessorDefinitions>>,
}

impl Parse for IncludeCpp {
    fn parse(input: ParseStream) -> ParseResult<Self> {
        Self::new_from_parse_stream(input)
    }
}

fn dump_generated_code(gen: GeneratedCode) -> Result<GeneratedCode> {
    info!(
        "CXX:\n{}",
        String::from_utf8(gen.implementation.clone()).unwrap()
    );
    info!(
        "header:\n{}",
        String::from_utf8(gen.header.clone()).unwrap()
    );
    Ok(gen)
}

/// Prelude of C++ for squirting into bindgen. This configures
/// bindgen to output simpler types to replace some STL types
/// that bindgen just can't cope with. Although we then replace
/// those types with cxx types (e.g. UniquePtr), this intermediate
/// step is still necessary because bindgen can't otherwise
/// give us the templated types (e.g. when faced with the STL
/// unique_ptr, bindgen would normally give us std_unique_ptr
/// as opposed to std_unique_ptr<T>.)
static PRELUDE: &str = indoc! {"
    /**
    *

    */
    template<typename T> class UniquePtr {
        T* ptr;
    };

    /**
    *

    */
    class CxxString {
        char* str_data;
    };
    \n"};

impl IncludeCpp {
    fn new_from_parse_stream(input: ParseStream) -> syn::Result<Self> {
        // Takes as inputs:
        // 1. List of headers to include
        // 2. List of #defines to include
        // 3. Allowlist

        let mut inclusions = Vec::new();
        let mut allowlist = Vec::new();
        let mut parse_only = false;

        while !input.is_empty() {
            let ident: syn::Ident = input.parse()?;
            if ident == "Header" {
                let args;
                syn::parenthesized!(args in input);
                let hdr: syn::LitStr = args.parse()?;
                inclusions.push(CppInclusion::Header(hdr.value()));
            } else if ident == "Allow" {
                let args;
                syn::parenthesized!(args in input);
                let allow: syn::LitStr = args.parse()?;
                allowlist.push(allow.value());
            } else if ident == "ParseOnly" {
                parse_only = true;
            } else {
                return Err(syn::Error::new(ident.span(), "expected Header or Allow"));
            }
            if input.is_empty() {
                break;
            }
            input.parse::<syn::Token![,]>()?;
        }

        Ok(IncludeCpp {
            inclusions,
            allowlist,
            preconfigured_inc_dirs: None,
            parse_only,
            preprocessor_definitions: Rc::new(Mutex::new(PreprocessorDefinitions::new())),
        })
    }

    pub fn new_from_syn(mac: Macro) -> Result<Self> {
        mac.parse_body::<IncludeCpp>().map_err(Error::Parsing)
    }

    pub fn set_include_dirs<P: AsRef<std::ffi::OsStr>>(&mut self, include_dirs: P) {
        self.preconfigured_inc_dirs = Some(include_dirs.as_ref().into());
    }

    fn build_header(&self) -> String {
        let mut s = PRELUDE.to_string();
        for incl in &self.inclusions {
            let text = match incl {
                CppInclusion::Define(symbol) => format!("#define {}\n", symbol),
                CppInclusion::Header(path) => format!("#include \"{}\"\n", path),
            };
            s.push_str(&text);
        }
        s
    }

    fn determine_incdirs(&self) -> Result<Vec<PathBuf>> {
        let inc_dirs = match &self.preconfigured_inc_dirs {
            Some(d) => d.clone(),
            None => std::env::var_os("AUTOCXX_INC").ok_or(Error::NoAutoCxxInc)?,
        };
        // TODO consider if we can or should look up the include path automatically
        // instead of requiring callers always to set AUTOCXX_INC.
        let multi_path_separator = if std::path::MAIN_SEPARATOR == '/' {
            b':'
        } else {
            b';'
        }; // there's probably a crate for this
        let splitter = [multi_path_separator];
        let inc_dirs = inc_dirs.split(&splitter[0..1]);
        let mut inc_dir_paths = Vec::new();
        for inc_dir in inc_dirs {
            let p: PathBuf = inc_dir.into();
            let p = p
                .canonicalize()
                .map_err(|_| Error::CouldNotCanoncalizeIncludeDir(p))?;
            inc_dir_paths.push(p);
        }
        Ok(inc_dir_paths)
    }

    fn make_bindgen_builder(&self) -> Result<bindgen::Builder> {
        let inc_dirs = self.determine_incdirs()?;

        let full_header = self.build_header();
        debug!("Full header: {}", full_header);
        debug!("Inc dir: {:?}", inc_dirs);

        // TODO support different C++ versions
        let mut builder = bindgen::builder()
            .clang_args(&["-x", "c++", "-std=c++14"])
            .derive_copy(false)
            .derive_debug(false)
            .parse_callbacks(Box::new(PreprocessorParseCallbacks::new(
                self.preprocessor_definitions.clone(),
            )))
            .default_enum_style(bindgen::EnumVariation::Rust {
                non_exhaustive: false,
            })
            .layout_tests(false); // TODO revisit later
        for item in BINDGEN_BLOCKLIST.iter() {
            builder = builder.blacklist_item(*item);
        }

        for inc_dir in inc_dirs {
            // TODO work with OsStrs here to avoid the .display()
            builder = builder.clang_arg(format!("-I{}", inc_dir.display()));
        }
        builder = builder.header_contents("example.hpp", &full_header);
        // 3. Passes allowlist and other options to the bindgen::Builder equivalent
        //    to --output-style=cxx --allowlist=<as passed in>
        for a in &self.allowlist {
            // TODO - allowlist type/functions/separately
            builder = builder
                .whitelist_type(a)
                .whitelist_function(a)
                .whitelist_var(a);
        }
        Ok(builder)
    }

    pub fn generate_rs(&self) -> Result<TokenStream2> {
        let (itemmod, _) = self.do_generation()?;
        Ok(itemmod.to_token_stream())
    }

    fn get_preprocessor_defs_mod(&self) -> Option<ItemMod> {
        let another_ref = self.preprocessor_definitions.clone();
        let m = another_ref.try_lock().unwrap().to_mod();
        m
    }

    fn do_generation(&self) -> Result<(Option<ItemMod>, Vec<EncounteredType>)> {
        // If we are in parse only mode, do nothing. This is used for
        // doc tests to ensure the parsing is valid, but we can't expect
        // valid C++ header files or linkers to allow a complete build.
        if self.parse_only {
            return Ok((None, Vec::new()));
        }
        // 4. (also respects environment variables to pick up more headers,
        //     include paths and #defines)
        // Then:
        // 1. Builds an overall C++ header with all those #defines and #includes
        // 2. Passes it to bindgen::Builder::header
        let bindings = self
            .make_bindgen_builder()?
            .generate()
            .map_err(Error::Bindgen)?;
        // This bindings object is actually a TokenStream internally and we're wasting
        // effort converting to and from string. We could enhance the bindgen API
        // in future.
        let bindings = bindings.to_string();
        // Manually add the mod ffi {} so that we can ask syn to parse
        // into a single construct.
        let bindings = format!("mod ffi {{ {} }}", bindings);
        info!("Bindings: {}", bindings);
        let bindings = syn::parse_str::<ItemMod>(&bindings).map_err(Error::Parsing)?;

        let mut include_list = Vec::new();
        for incl in &self.inclusions {
            match incl {
                CppInclusion::Header(ref hdr) => {
                    include_list.push(hdr.clone());
                }
                CppInclusion::Define(_) => warn!("Currently no way to define! within cxx"),
            }
        }

        let mut converter = bridge_converter::BridgeConverter::new(
            include_list,
            TEMPORARY_HACK_TO_AVOID_REDEFINITIONS,
        );
        let results = converter.convert(bindings).map_err(Error::Conversion)?;
        let mut items = results.items;
        let types_encountered = results.types_encountered;
        if let Some(itemmod) = self.get_preprocessor_defs_mod() {
            items.push(syn::Item::Mod(itemmod));
        }
        let new_bindings: ItemMod = parse_quote! {
            mod ffi {
                #(#items)*
            }
        };
        info!(
            "New bindings: {}",
            new_bindings.to_token_stream().to_string()
        );
        Ok((Some(new_bindings), types_encountered))
    }

    pub fn generate_h_and_cxx(self) -> Result<GeneratedCode> {
        let (rs, types_encountered) = self.do_generation()?;
        let rs = match rs {
            Some(itemmod) => itemmod.into_token_stream(),
            None => TokenStream2::new(),
        };
        let opt = cxx_gen::Opt::default();
        cxx_gen::generate_header_and_cc(rs, &opt)
            .map_err(Error::CxxGen)
            .and_then(dump_generated_code)
            .and_then(|gen| {
                if TEMPORARY_HACK_TO_AVOID_REDEFINITIONS {
                    Ok(GeneratedCode {
                        header: cpp_postprocessor::disable_types(gen.header, &types_encountered),
                        implementation: cpp_postprocessor::disable_types(
                            gen.implementation,
                            &types_encountered,
                        ),
                    })
                } else {
                    Ok(gen)
                }
            })
            .and_then(dump_generated_code)
    }

    pub fn include_dirs(&self) -> Result<Vec<PathBuf>> {
        self.determine_incdirs()
    }
}
<|endoftext|>


<commit_before>//! GPIO wired to the LEDs of the Beaglebone

use just;

/// Root folder (modulo number) that contains the files that control the GPIO
static ROOT: &'static str = "/sys/class/leds/beaglebone:green:usr";

/// LED identifier
pub enum Number {
    /// First LED, by default is in heartbeat mode
    Zero = 0,
    /// Second LED, unused and powered off by default
    One = 1,
    /// Third LED, tracks CPU usage by default
    Three = 3,
    /// Fourth LED, tracks disk I/O by default
    Two = 2,
}

/// LED trigger modes
pub enum Trigger {
    Heartbeat,
    NoTrigger,
    Timer,
}

impl Trigger {
    fn from_str(s: &str) -> Option<Trigger> {
        match s {
            "heartbeat" => Some(Heartbeat),
            "none" => Some(NoTrigger),
            "timer" => Some(Timer),
            _ => None,
        }
    }

    fn to_str(&self) -> &'static str {
        match *self {
            Heartbeat => "heartbeat",
            NoTrigger => "none",
            Timer => "timer",
        }
    }
}

/// An LED controller
pub struct Led {
    root: Path,
}

impl Led {
    /// Create access to an LED
    pub fn new(number: Number) -> Led {
        Led {
            root: Path::new(format!("{}{}", ROOT, number as u8)),
        }
    }

    /// Makes the LED blink
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Zero};
    ///
    /// // On for one second, off for half a second
    /// Led::new(Zero).blink(1000, 500);
    /// ```
    // XXX Not sure about how big can `on_ms` and `off_ms` be
    pub fn blink(&self, on_ms: u16, off_ms: u16) {
        self.set_trigger(Timer);
        just::write(&self.root.join("delay_on"), format!("{}", on_ms)[]);
        just::write(&self.root.join("delay_off"), format!("{}", off_ms)[]);
    }

    /// Changes the brightness of the LED
    // XXX Is `u8` enough?
    pub fn set_brightness(&self, brightness: u8) {
        just::write(&self.root.join("brightness"), format!("{}", brightness)[])
    }

    /// Turns on the LED
    pub fn set_high(&self) {
        self.set_trigger(NoTrigger);
        self.set_brightness(1);
    }

    /// Turns off the LED
    pub fn set_low(&self) {
        self.set_trigger(NoTrigger);
        self.set_brightness(0);
    }

    /// Changes the trigger mode of the LED
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Heartbeat, Zero};
    ///
    /// Led::new(Zero).set_trigger(Heartbeat);
    /// println!("I'm alive!");
    /// ```
    pub fn set_trigger(&self, trigger: Trigger) {
        just::write(&self.root.join("trigger"), trigger.to_str());
    }

    /// Returns the current trigger mode the LED is using
    pub fn trigger(&self) -> Trigger {
        let s = just::read(&self.root.join("trigger"));
        match s[].split('[').skip(1).next().and_then(|s| s.split(']').next()) {
            Some(s) => match Trigger::from_str(s) {
                Some(trigger) => trigger,
                None => panic!("Unknown trigger mode: {}", s),
            },
            None => panic!("Failed to parse: {}", s),
        }
    }
}
<commit_msg>fix(lib): fix fallout of namespaced enums<commit_after>//! GPIO wired to the LEDs of the Beaglebone

use just;

/// Root folder (modulo number) that contains the files that control the GPIO
static ROOT: &'static str = "/sys/class/leds/beaglebone:green:usr";

/// LED identifier
pub enum Number {
    /// First LED, by default is in heartbeat mode
    Zero = 0,
    /// Second LED, unused and powered off by default
    One = 1,
    /// Third LED, tracks CPU usage by default
    Three = 3,
    /// Fourth LED, tracks disk I/O by default
    Two = 2,
}

/// LED trigger modes
pub enum Trigger {
    Heartbeat,
    None,
    Timer,
}

impl Trigger {
    fn from_str(s: &str) -> Option<Trigger> {
        match s {
            "heartbeat" => Some(Trigger::Heartbeat),
            "none" => Some(Trigger::None),
            "timer" => Some(Trigger::Timer),
            _ => None,
        }
    }

    fn to_str(&self) -> &'static str {
        match *self {
            Trigger::Heartbeat => "heartbeat",
            Trigger::None => "none",
            Trigger::Timer => "timer",
        }
    }
}

/// An LED controller
pub struct Led {
    root: Path,
}

impl Led {
    /// Create access to an LED
    pub fn new(number: Number) -> Led {
        Led {
            root: Path::new(format!("{}{}", ROOT, number as u8)),
        }
    }

    /// Makes the LED blink
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Zero};
    ///
    /// // On for one second, off for half a second
    /// Led::new(Zero).blink(1000, 500);
    /// ```
    // XXX Not sure about how big can `on_ms` and `off_ms` be
    pub fn blink(&self, on_ms: u16, off_ms: u16) {
        self.set_trigger(Trigger::Timer);
        just::write(&self.root.join("delay_on"), format!("{}", on_ms)[]);
        just::write(&self.root.join("delay_off"), format!("{}", off_ms)[]);
    }

    /// Changes the brightness of the LED
    // XXX Is `u8` enough?
    pub fn set_brightness(&self, brightness: u8) {
        just::write(&self.root.join("brightness"), format!("{}", brightness)[])
    }

    /// Turns on the LED
    pub fn set_high(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(1);
    }

    /// Turns off the LED
    pub fn set_low(&self) {
        self.set_trigger(Trigger::None);
        self.set_brightness(0);
    }

    /// Changes the trigger mode of the LED
    ///
    /// # Example
    ///
    /// ``` ignore
    /// use bb::led::{Led, Heartbeat, Zero};
    ///
    /// Led::new(Zero).set_trigger(Heartbeat);
    /// println!("I'm alive!");
    /// ```
    pub fn set_trigger(&self, trigger: Trigger) {
        just::write(&self.root.join("trigger"), trigger.to_str());
    }

    /// Returns the current trigger mode the LED is using
    pub fn trigger(&self) -> Trigger {
        let s = just::read(&self.root.join("trigger"));
        match s[].split('[').skip(1).next().and_then(|s| s.split(']').next()) {
            Some(s) => match Trigger::from_str(s) {
                Some(trigger) => trigger,
                None => panic!("Unknown trigger mode: {}", s),
            },
            None => panic!("Failed to parse: {}", s),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
#![feature(globs)]

extern crate semver;
extern crate conduit;

use std::iter;
use std::io::net::ip::IpAddr;
use std::collections::hash_map::{HashMap, Entries};

use conduit::{Method, Scheme, Host, Extensions, Headers, Request};

pub trait RequestDelegator {
    fn request<'a>(&'a self) -> &'a Request;
    fn mut_request<'a>(&'a mut self) -> &'a mut Request;

    fn http_version(&self) -> semver::Version {
        self.request().http_version()
    }

    fn conduit_version(&self) -> semver::Version {
        self.request().conduit_version()
    }

    fn method(&self) -> Method {
        self.request().method()
    }

    fn scheme(&self) -> Scheme {
        self.request().scheme()
    }

    fn host<'a>(&'a self) -> Host<'a> {
        self.request().host()
    }

    fn virtual_root<'a>(&'a self) -> Option<&'a str> {
        self.request().virtual_root()
    }

    fn path<'a>(&'a self) -> &'a str {
        self.request().path()
    }

    fn query_string<'a>(&'a self) -> Option<&'a str> {
        self.request().query_string()
    }

    fn remote_ip(&self) -> IpAddr {
        self.request().remote_ip()
    }

    fn content_length(&self) -> Option<uint> {
        self.request().content_length()
    }

    fn headers<'a>(&'a self) -> &'a Headers {
        self.request().headers()
    }

    fn body<'a>(&'a mut self) -> &'a mut Reader {
        self.mut_request().body()
    }

    fn extensions<'a>(&'a self) -> &'a Extensions {
        self.request().extensions()
    }

    fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
        self.mut_request().mut_extensions()
    }
}

impl<'a> Request for &'a mut (RequestDelegator + 'a) {
    fn http_version(&self) -> semver::Version {
        self.http_version()
    }

    fn conduit_version(&self) -> semver::Version {
        self.conduit_version()
    }

    fn method(&self) -> Method {
        self.method()
    }

    fn scheme(&self) -> Scheme {
        self.scheme()
    }

    fn host<'a>(&'a self) -> Host<'a> {
        self.host()
    }

    fn virtual_root<'a>(&'a self) -> Option<&'a str> {
        self.virtual_root()
    }

    fn path<'a>(&'a self) -> &'a str {
        self.path()
    }

    fn query_string<'a>(&'a self) -> Option<&'a str> {
        self.query_string()
    }

    fn remote_ip(&self) -> IpAddr {
        self.remote_ip()
    }

    fn content_length(&self) -> Option<uint> {
        self.content_length()
    }

    fn headers<'a>(&'a self) -> &'a Headers {
        self.headers()
    }

    fn body<'a>(&'a mut self) -> &'a mut Reader {
        self.body()
    }

    fn extensions<'a>(&'a self) -> &'a Extensions {
        self.extensions()
    }

    fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
        self.mut_extensions()
    }
}

type RawHeaders = HashMap<String, Vec<String>>;
type InHeader<'a> = (&'a String, &'a Vec<String>);
type OutHeader<'a> = (String, &'a Vec<String>);

#[deriving(PartialEq, Clone, Show)]
pub struct HeaderMap(HashMap<String, Vec<String>>);

impl HeaderMap {
    fn normalize(headers: HashMap<String, Vec<String>>) -> HeaderMap {
        let headers = headers.into_iter().map(|(k,v)| (to_lower(&k), v)).collect();
        HeaderMap(headers)
    }

    fn iter<'a>(&'a self) -> iter::Map<'a, InHeader<'a>, OutHeader<'a>, Entries<'a, String, Vec<String>>> {
        self.as_ref().iter().map(|(k,v)| (to_lower(k), v))
    }

    fn as_ref<'a>(&'a self) -> &'a HashMap<String, Vec<String>> {
        match *self {
            HeaderMap(ref map) => map
        }
    }

    fn as_mut<'a>(&'a mut self) -> &'a mut HashMap<String, Vec<String>> {
        match *self {
            HeaderMap(ref mut map) => map
        }
    }

    pub fn len(&self) -> uint {
        self.as_ref().len()
    }
    pub fn clear(&mut self) {
        self.as_mut().clear()
    }
    pub fn find<'a, S: Str>(&'a self, key: &S) -> Option<&'a Vec<String>> {
        self.as_ref().get(&to_lower(key))
    }
    pub fn insert<S: Str>(&mut self, k: S, v: Vec<String>) -> Option<Vec<String>> {
        self.as_mut().insert(to_lower(&k), v)
    }
    pub fn remove<S: Str>(&mut self, k: &S) -> Option<Vec<String>> {
        self.as_mut().remove(&to_lower(k))
    }

    pub fn find_mut<'a, S: Str>(&'a mut self, k: &S) -> Option<&'a mut Vec<String>> {
        self.as_mut().get_mut(&to_lower(k))
    }
}

fn to_lower<S: Str>(string: &S) -> String {
    string.as_slice().chars().map(|c| c.to_lowercase()).collect()
}

#[cfg(test)]
mod tests {
    extern crate "conduit-test" as test;

    use {RequestDelegator, HeaderMap};

    use std::collections::HashMap;
    use conduit::{Request, Method};

    struct OverrideRequest<'a> {
        request: &'a mut (Request + 'a),
    }

    impl<'a> RequestDelegator for OverrideRequest<'a> {
        fn request<'a>(&'a self) -> &'a Request {
            let req: &Request = self.request; req
        }

        fn mut_request<'a>(&'a mut self) -> &'a mut Request {
            let req: &mut Request = self.request; req
        }

        fn method(&self) -> Method {
            Method::Get
        }
    }

    #[test]
    fn test_delegate() {
        let request = &mut test::MockRequest::new(Method::Head, "/hello") as &mut Request;
        let new = OverrideRequest { request: request };

        assert_eq!(new.method(), Method::Get);
        assert_eq!(new.path(), "/hello");
    }

    #[test]
    fn test_header_map() {
        let mut map = HeaderMap(HashMap::new());
        map.insert("Content-Type".to_string(), vec!("text/html".to_string()));
        map.insert("location".to_string(), vec!("http://example.com".to_string()));

        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())))
        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())))
    }

    #[test]
    fn test_header_map_with_static_inserts() {
        let mut map = HeaderMap(HashMap::new());
        map.insert("Content-Type", vec!("text/html".to_string()));
        map.insert("location", vec!("http://example.com".to_string()));

        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())))
        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())))
    }

    #[test]
    fn test_normalize() {
        let mut map = HashMap::new();
        map.insert("Content-Type".to_string(), vec!("text/html".to_string()));

        let headers = HeaderMap::normalize(map);
        assert_eq!(headers.find(&"Content-Type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"Content-Type"), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"content-type"), Some(&vec!("text/html".to_string())))
    }

    #[test]
    fn test_iterate() {
        let mut headers = HeaderMap(HashMap::new());
        headers.insert("Content-Type", vec!("text/html".to_string()));
        headers.insert("location", vec!("http://example.com".to_string()));

        assert!(headers.iter().any(|t| {
            t.0.as_slice() == "content-type" &&
            t.1.as_slice() == &["text/html".to_string()]
        }));
        assert!(headers.iter().any(|t| {
            t.0.as_slice() == "location" &&
            t.1.as_slice() == &["http://example.com".to_string()]
        }));
        assert!(headers.iter().count() == 2);
    }
}
<commit_msg>Update to rust master<commit_after>#![feature(macro_rules)]
#![feature(globs)]
#![cfg_attr(test, deny(warnings))]

extern crate semver;
extern crate conduit;

use std::iter;
use std::io::net::ip::IpAddr;
use std::collections::hash_map::{HashMap, Entries};

use conduit::{Method, Scheme, Host, Extensions, Headers, Request};

pub trait RequestDelegator {
    fn request<'a>(&'a self) -> &'a Request;
    fn mut_request<'a>(&'a mut self) -> &'a mut Request;

    fn http_version(&self) -> semver::Version {
        self.request().http_version()
    }

    fn conduit_version(&self) -> semver::Version {
        self.request().conduit_version()
    }

    fn method(&self) -> Method {
        self.request().method()
    }

    fn scheme(&self) -> Scheme {
        self.request().scheme()
    }

    fn host<'a>(&'a self) -> Host<'a> {
        self.request().host()
    }

    fn virtual_root<'a>(&'a self) -> Option<&'a str> {
        self.request().virtual_root()
    }

    fn path<'a>(&'a self) -> &'a str {
        self.request().path()
    }

    fn query_string<'a>(&'a self) -> Option<&'a str> {
        self.request().query_string()
    }

    fn remote_ip(&self) -> IpAddr {
        self.request().remote_ip()
    }

    fn content_length(&self) -> Option<uint> {
        self.request().content_length()
    }

    fn headers<'a>(&'a self) -> &'a Headers {
        self.request().headers()
    }

    fn body<'a>(&'a mut self) -> &'a mut Reader {
        self.mut_request().body()
    }

    fn extensions<'a>(&'a self) -> &'a Extensions {
        self.request().extensions()
    }

    fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
        self.mut_request().mut_extensions()
    }
}

impl<'a> Request for &'a mut (RequestDelegator + 'a) {
    fn http_version(&self) -> semver::Version {
        self.http_version()
    }

    fn conduit_version(&self) -> semver::Version {
        self.conduit_version()
    }

    fn method(&self) -> Method {
        self.method()
    }

    fn scheme(&self) -> Scheme {
        self.scheme()
    }

    fn host<'a>(&'a self) -> Host<'a> {
        self.host()
    }

    fn virtual_root<'a>(&'a self) -> Option<&'a str> {
        self.virtual_root()
    }

    fn path<'a>(&'a self) -> &'a str {
        self.path()
    }

    fn query_string<'a>(&'a self) -> Option<&'a str> {
        self.query_string()
    }

    fn remote_ip(&self) -> IpAddr {
        self.remote_ip()
    }

    fn content_length(&self) -> Option<uint> {
        self.content_length()
    }

    fn headers<'a>(&'a self) -> &'a Headers {
        self.headers()
    }

    fn body<'a>(&'a mut self) -> &'a mut Reader {
        self.body()
    }

    fn extensions<'a>(&'a self) -> &'a Extensions {
        self.extensions()
    }

    fn mut_extensions<'a>(&'a mut self) -> &'a mut Extensions {
        self.mut_extensions()
    }
}

type RawHeaders = HashMap<String, Vec<String>>;
type InHeader<'a> = (&'a String, &'a Vec<String>);
type OutHeader<'a> = (String, &'a Vec<String>);

#[deriving(PartialEq, Clone, Show)]
pub struct HeaderMap(HashMap<String, Vec<String>>);

impl HeaderMap {
    fn normalize(headers: HashMap<String, Vec<String>>) -> HeaderMap {
        let headers = headers.into_iter().map(|(k,v)| (to_lower(&k), v)).collect();
        HeaderMap(headers)
    }

    fn iter<'a>(&'a self) -> iter::Map<InHeader<'a>, OutHeader<'a>,
                                       Entries<String, Vec<String>>,
                                       for<'a> fn(InHeader<'a>) -> OutHeader<'a>> {
        fn foo<'a>((k, v): InHeader<'a>) -> OutHeader<'a> { (to_lower(k), v) }
        self.as_ref().iter().map(foo)
    }

    fn as_ref<'a>(&'a self) -> &'a HashMap<String, Vec<String>> {
        match *self {
            HeaderMap(ref map) => map
        }
    }

    fn as_mut<'a>(&'a mut self) -> &'a mut HashMap<String, Vec<String>> {
        match *self {
            HeaderMap(ref mut map) => map
        }
    }

    pub fn len(&self) -> uint {
        self.as_ref().len()
    }
    pub fn clear(&mut self) {
        self.as_mut().clear()
    }
    pub fn find<'a, S: Str>(&'a self, key: &S) -> Option<&'a Vec<String>> {
        self.as_ref().get(&to_lower(key))
    }
    pub fn insert<S: Str>(&mut self, k: S, v: Vec<String>) -> Option<Vec<String>> {
        self.as_mut().insert(to_lower(&k), v)
    }
    pub fn remove<S: Str>(&mut self, k: &S) -> Option<Vec<String>> {
        self.as_mut().remove(&to_lower(k))
    }

    pub fn find_mut<'a, S: Str>(&'a mut self, k: &S) -> Option<&'a mut Vec<String>> {
        self.as_mut().get_mut(&to_lower(k))
    }
}

fn to_lower<S: Str>(string: &S) -> String {
    string.as_slice().chars().map(|c| c.to_lowercase()).collect()
}

#[cfg(test)]
mod tests {
    extern crate "conduit-test" as test;

    use {RequestDelegator, HeaderMap};

    use std::collections::HashMap;
    use conduit::{Request, Method};

    struct OverrideRequest<'a> {
        request: &'a mut (Request + 'a),
    }

    impl<'a> RequestDelegator for OverrideRequest<'a> {
        fn request<'a>(&'a self) -> &'a Request {
            let req: &Request = self.request; req
        }

        fn mut_request<'a>(&'a mut self) -> &'a mut Request {
            let req: &mut Request = self.request; req
        }

        fn method(&self) -> Method {
            Method::Get
        }
    }

    #[test]
    fn test_delegate() {
        let request = &mut test::MockRequest::new(Method::Head, "/hello") as &mut Request;
        let new = OverrideRequest { request: request };

        assert_eq!(new.method(), Method::Get);
        assert_eq!(new.path(), "/hello");
    }

    #[test]
    fn test_header_map() {
        let mut map = HeaderMap(HashMap::new());
        map.insert("Content-Type".to_string(), vec!("text/html".to_string()));
        map.insert("location".to_string(), vec!("http://example.com".to_string()));

        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())))
        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())))
    }

    #[test]
    fn test_header_map_with_static_inserts() {
        let mut map = HeaderMap(HashMap::new());
        map.insert("Content-Type", vec!("text/html".to_string()));
        map.insert("location", vec!("http://example.com".to_string()));

        assert_eq!(map.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location".to_string()), Some(&vec!("http://example.com".to_string())))
        assert_eq!(map.find(&"content-type"), Some(&vec!("text/html".to_string())))
        assert_eq!(map.find(&"Location"), Some(&vec!("http://example.com".to_string())))
    }

    #[test]
    fn test_normalize() {
        let mut map = HashMap::new();
        map.insert("Content-Type".to_string(), vec!("text/html".to_string()));

        let headers = HeaderMap::normalize(map);
        assert_eq!(headers.find(&"Content-Type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"Content-Type"), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"content-type".to_string()), Some(&vec!("text/html".to_string())))
        assert_eq!(headers.find(&"content-type"), Some(&vec!("text/html".to_string())))
    }

    #[test]
    fn test_iterate() {
        let mut headers = HeaderMap(HashMap::new());
        headers.insert("Content-Type", vec!("text/html".to_string()));
        headers.insert("location", vec!("http://example.com".to_string()));

        assert!(headers.iter().any(|t| {
            t.0.as_slice() == "content-type" &&
            t.1.as_slice() == ["text/html".to_string()]
        }));
        assert!(headers.iter().any(|t| {
            t.0.as_slice() == "location" &&
            t.1.as_slice() == ["http://example.com".to_string()]
        }));
        assert!(headers.iter().count() == 2);
    }
}
<|endoftext|>


<commit_before>#![cfg(any(target_os = "macos", target_os = "ios"))]

#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types)]

extern crate core_foundation_sys;

use core_foundation_sys::string::*;
use core_foundation_sys::data::*;
use core_foundation_sys::dictionary::*;
use core_foundation_sys::propertylist::*;

use std::ptr;

include!("generated.rs");

#[inline]
pub unsafe fn MIDIPacketNext(pkt: *const MIDIPacket) -> *const MIDIPacket {
    // Get pointer to potentially unaligned data without triggering undefined behavior
    // addr_of does not require creating an intermediate reference to unaligned data.
    let ptr = ptr::addr_of!((*pkt).data);
    let offset = (*pkt).length as isize;
    if cfg!(any(target_arch = "arm", target_arch = "aarch64")) {
        // MIDIPacket must be 4-byte aligned on ARM
        ((ptr.offset(offset + 3) as usize) & !(3usize)) as *const MIDIPacket
    } else {
        ptr.offset(offset) as *const MIDIPacket
    }
}

#[allow(dead_code)]
mod static_test {
    /// Statically assert the correct size of `MIDIPacket` and `MIDIPacketList`,
    /// which require non-default alignment.
    unsafe fn assert_sizes() {
        use super::{MIDIPacket, MIDIPacketList};
        use std::mem::{transmute, zeroed};

        let p: MIDIPacket = zeroed();
        transmute::<_, [u8; 268]>(p);

        let p: MIDIPacketList = zeroed();
        transmute::<_, [u8; 272]>(p);
    }
}
<commit_msg>Fix MIDIPacketNext failing to add proper offset to data<commit_after>#![cfg(any(target_os = "macos", target_os = "ios"))]

#![allow(non_snake_case, non_upper_case_globals, non_camel_case_types)]

extern crate core_foundation_sys;

use core_foundation_sys::string::*;
use core_foundation_sys::data::*;
use core_foundation_sys::dictionary::*;
use core_foundation_sys::propertylist::*;

use std::ptr;

include!("generated.rs");

#[inline]
pub unsafe fn MIDIPacketNext(pkt: *const MIDIPacket) -> *const MIDIPacket {
    // Get pointer to potentially unaligned data without triggering undefined behavior
    // addr_of does not require creating an intermediate reference to unaligned data.
    let ptr = ptr::addr_of!((*pkt).data) as *const u8;
    let offset = (*pkt).length as isize;
    if cfg!(any(target_arch = "arm", target_arch = "aarch64")) {
        // MIDIPacket must be 4-byte aligned on ARM
        ((ptr.offset(offset + 3) as usize) & !(3usize)) as *const MIDIPacket
    } else {
        ptr.offset(offset) as *const MIDIPacket
    }
}

#[allow(dead_code)]
mod static_test {
    /// Statically assert the correct size of `MIDIPacket` and `MIDIPacketList`,
    /// which require non-default alignment.
    unsafe fn assert_sizes() {
        use super::{MIDIPacket, MIDIPacketList};
        use std::mem::{transmute, zeroed};

        let p: MIDIPacket = zeroed();
        transmute::<_, [u8; 268]>(p);

        let p: MIDIPacketList = zeroed();
        transmute::<_, [u8; 272]>(p);
    }
}
<|endoftext|>


<commit_before>/*!
Component Graph System prototype.

Froggy is all about the smart component storage, unambiguously called `Storage`.
Components inside it are automatically reference-counted, and could be referenced by a `Pointer`.
The components are stored linearly, allowing for the efficient bulk data processing.
`Storage` has to be locked temporarily for either read or write before any usage.

You can find more information about Component Graph System concept on the [wiki](https://github.com/kvark/froggy/wiki/Component-Graph-System).
Comparing to Entity-Component Systems (ECS), CGS doesn't have the backwards relation of components to entities.
Thus, it can't process all "entities" by just selecting a subsect of compoments to work on, besides not having the whole "entity" concept.
However, CGS has a number of advantages:

  - you can share components naturally
  - you don't need to care about the component lifetime, it is managed automatically
  - you can have deeper hierarchies of components, with one component referencing the others
  - you can have user structures referencing components freely
  - there are no restrictions on the component types, and no need to implement any traits

*/
#![warn(missing_docs)]

use std::ops;
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

/// Reference counter type. It doesn't make sense to allocate too much bit for it in regular applications.
// TODO: control by a cargo feature
type RefCount = u16;

/// Inner storage data that is locked by `RwLock`.
struct StorageInner<T> {
    data: Vec<T>,
    meta: Vec<RefCount>,
    free_list: Vec<usize>,
}

/// Pending reference counts updates.
struct Pending {
    add_ref: Vec<usize>,
    sub_ref: Vec<usize>,
}

/// Shared pointer to the inner storage.
type StorageRef<T> = Arc<RwLock<StorageInner<T>>>;
/// Shared pointer to the pending updates.
type PendingRef = Arc<Mutex<Pending>>;
/// Component storage type.
/// Manages the components and allows for efficient processing.
pub struct Storage<T>(StorageRef<T>, PendingRef);

/// A pointer to a component of type `T`.
/// The component is guaranteed to be accessible for as long as this pointer is alive.
/// You'd need a locked storage to access the data.
/// The pointer also holds the storage alive and knows the index of the element to look up.
pub struct Pointer<T> {
    index: usize,
    target: StorageRef<T>,
    pending: PendingRef,
}

/// Read lock on the storage, allows multiple clients to read from the same storage simultaneously.
pub struct ReadLock<'a, T: 'a> {
    guard: RwLockReadGuard<'a, StorageInner<T>>,
    storage: StorageRef<T>,
    pending: PendingRef,
}

/// Write lock on the storage allows exclusive access.
pub struct WriteLock<'a, T: 'a> {
    guard: RwLockWriteGuard<'a, StorageInner<T>>,
    storage: StorageRef<T>,
    pending: PendingRef,
}

impl<T> Storage<T> {
    fn from_inner(inner: StorageInner<T>) -> Storage<T> {
        let pending = Pending {
            add_ref: Vec::new(),
            sub_ref: Vec::new(),
        };
        Storage(Arc::new(RwLock::new(inner)),
                Arc::new(Mutex::new(pending)))
    }

    /// Create a new empty storage.
    pub fn new() -> Storage<T> {
        Self::from_inner(StorageInner {
            data: Vec::new(),
            meta: Vec::new(),
            free_list: Vec::new(),
        })
    }

    /// Create a new empty storage with specified capacity.
    pub fn with_capacity(capacity: usize) -> Storage<T> {
        Self::from_inner(StorageInner {
            data: Vec::with_capacity(capacity),
            meta: Vec::with_capacity(capacity),
            free_list: Vec::new(),
        })
    }

    /// Lock the storage for reading. This operation will block until the write locks are done.
    pub fn read(&self) -> ReadLock<T> {
        ReadLock {
            guard: self.0.read().unwrap(),
            storage: self.0.clone(),
            pending: self.1.clone(),
        }
    }

    /// Lock the storage for writing. This operation will block untill all the locks are done.
    pub fn write(&self) -> WriteLock<T> {
        let mut s = self.0.write().unwrap();
        // process the pending refcount changes
        let mut pending = self.1.lock().unwrap();
        for index in pending.add_ref.drain(..) {
            s.meta[index] += 1;
        }
        for index in pending.sub_ref.drain(..) {
            s.meta[index] -= 1;
            if s.meta[index] == 0 {
                s.free_list.push(index);
            }
        }
        // return the lock
        WriteLock {
            guard: s,
            storage: self.0.clone(),
            pending: self.1.clone(),
        }
    }
}

impl<T> Clone for Pointer<T> {
    fn clone(&self) -> Pointer<T> {
        self.pending.lock().unwrap().add_ref.push(self.index);
        Pointer {
            index: self.index,
            target: self.target.clone(),
            pending: self.pending.clone(),
        }
    }
}

impl<T> PartialEq for Pointer<T> {
    fn eq(&self, other: &Pointer<T>) -> bool {
        self.index == other.index &&
        &*self.target as *const _ == &*other.target as *const _
    }
}

impl<T> Drop for Pointer<T> {
    fn drop(&mut self) {
        self.pending.lock().unwrap().sub_ref.push(self.index);
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::Deref for ReadLock<'a, T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        &self.guard.data
    }
}

impl<'a, T> ReadLock<'a, T> {
    /// Borrow a pointed component for reading.
    pub fn access(&self, ptr: &Pointer<T>) -> &T {
        debug_assert_eq!(&*self.storage as *const _, &*ptr.target as *const _);
        &self.guard.data[ptr.index]
    }

    /// Pin a specific component index with a newly created `Pointer`.
    /// Returns `false` if the element is dead or out of bounds.
    pub fn pin(&self, index: usize) -> Option<Pointer<T>> {
        if index < self.guard.data.len() && self.guard.meta[index] != 0 {
            self.pending.lock().unwrap().add_ref.push(index);
            Some(Pointer {
                index: index,
                target: self.storage.clone(),
                pending: self.pending.clone(),
            })
        } else {
            None
        }
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::Deref for WriteLock<'a, T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        &self.guard.data
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::DerefMut for WriteLock<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.guard.data
    }
}

impl<'a, T> WriteLock<'a, T> {
    /// Borrow a pointed component for writing.
    pub fn access(&mut self, ptr: &Pointer<T>) -> &mut T {
        debug_assert_eq!(&*self.storage as *const _, &*ptr.target as *const _);
        &mut self.guard.data[ptr.index]
    }

    /// Add a new component to the storage, returning the `Pointer` to it.
    pub fn create(&mut self, value: T) -> Pointer<T> {
        let index = match self.guard.free_list.pop() {
            Some(i) => {
                debug_assert_eq!(self.guard.meta[i], 0);
                self.guard.data[i] = value;
                self.guard.meta[i] = 1;
                i
            },
            None => {
                debug_assert_eq!(self.guard.data.len(), self.guard.meta.len());
                self.guard.data.push(value);
                self.guard.meta.push(1);
                self.guard.meta.len() - 1
            },
        };
        Pointer {
            index: index,
            target: self.storage.clone(),
            pending: self.pending.clone(),
        }
    }
}
<commit_msg>Added access_index<commit_after>/*!
Component Graph System prototype.

Froggy is all about the smart component storage, unambiguously called `Storage`.
Components inside it are automatically reference-counted, and could be referenced by a `Pointer`.
The components are stored linearly, allowing for the efficient bulk data processing.
`Storage` has to be locked temporarily for either read or write before any usage.

You can find more information about Component Graph System concept on the [wiki](https://github.com/kvark/froggy/wiki/Component-Graph-System).
Comparing to Entity-Component Systems (ECS), CGS doesn't have the backwards relation of components to entities.
Thus, it can't process all "entities" by just selecting a subsect of compoments to work on, besides not having the whole "entity" concept.
However, CGS has a number of advantages:

  - you can share components naturally
  - you don't need to care about the component lifetime, it is managed automatically
  - you can have deeper hierarchies of components, with one component referencing the others
  - you can have user structures referencing components freely
  - there are no restrictions on the component types, and no need to implement any traits

*/
#![warn(missing_docs)]

use std::ops;
use std::sync::{Arc, Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard};

/// Reference counter type. It doesn't make sense to allocate too much bit for it in regular applications.
// TODO: control by a cargo feature
type RefCount = u16;

/// Inner storage data that is locked by `RwLock`.
struct StorageInner<T> {
    data: Vec<T>,
    meta: Vec<RefCount>,
    free_list: Vec<usize>,
}

/// Pending reference counts updates.
struct Pending {
    add_ref: Vec<usize>,
    sub_ref: Vec<usize>,
}

/// Shared pointer to the inner storage.
type StorageRef<T> = Arc<RwLock<StorageInner<T>>>;
/// Shared pointer to the pending updates.
type PendingRef = Arc<Mutex<Pending>>;
/// Component storage type.
/// Manages the components and allows for efficient processing.
pub struct Storage<T>(StorageRef<T>, PendingRef);

/// A pointer to a component of type `T`.
/// The component is guaranteed to be accessible for as long as this pointer is alive.
/// You'd need a locked storage to access the data.
/// The pointer also holds the storage alive and knows the index of the element to look up.
pub struct Pointer<T> {
    index: usize,
    target: StorageRef<T>,
    pending: PendingRef,
}

/// Read lock on the storage, allows multiple clients to read from the same storage simultaneously.
pub struct ReadLock<'a, T: 'a> {
    guard: RwLockReadGuard<'a, StorageInner<T>>,
    storage: StorageRef<T>,
    pending: PendingRef,
}

/// Write lock on the storage allows exclusive access.
pub struct WriteLock<'a, T: 'a> {
    guard: RwLockWriteGuard<'a, StorageInner<T>>,
    storage: StorageRef<T>,
    pending: PendingRef,
}

impl<T> Storage<T> {
    fn from_inner(inner: StorageInner<T>) -> Storage<T> {
        let pending = Pending {
            add_ref: Vec::new(),
            sub_ref: Vec::new(),
        };
        Storage(Arc::new(RwLock::new(inner)),
                Arc::new(Mutex::new(pending)))
    }

    /// Create a new empty storage.
    pub fn new() -> Storage<T> {
        Self::from_inner(StorageInner {
            data: Vec::new(),
            meta: Vec::new(),
            free_list: Vec::new(),
        })
    }

    /// Create a new empty storage with specified capacity.
    pub fn with_capacity(capacity: usize) -> Storage<T> {
        Self::from_inner(StorageInner {
            data: Vec::with_capacity(capacity),
            meta: Vec::with_capacity(capacity),
            free_list: Vec::new(),
        })
    }

    /// Lock the storage for reading. This operation will block until the write locks are done.
    pub fn read(&self) -> ReadLock<T> {
        ReadLock {
            guard: self.0.read().unwrap(),
            storage: self.0.clone(),
            pending: self.1.clone(),
        }
    }

    /// Lock the storage for writing. This operation will block untill all the locks are done.
    pub fn write(&self) -> WriteLock<T> {
        let mut s = self.0.write().unwrap();
        // process the pending refcount changes
        let mut pending = self.1.lock().unwrap();
        for index in pending.add_ref.drain(..) {
            s.meta[index] += 1;
        }
        for index in pending.sub_ref.drain(..) {
            s.meta[index] -= 1;
            if s.meta[index] == 0 {
                s.free_list.push(index);
            }
        }
        // return the lock
        WriteLock {
            guard: s,
            storage: self.0.clone(),
            pending: self.1.clone(),
        }
    }
}

impl<T> Clone for Pointer<T> {
    fn clone(&self) -> Pointer<T> {
        self.pending.lock().unwrap().add_ref.push(self.index);
        Pointer {
            index: self.index,
            target: self.target.clone(),
            pending: self.pending.clone(),
        }
    }
}

impl<T> PartialEq for Pointer<T> {
    fn eq(&self, other: &Pointer<T>) -> bool {
        self.index == other.index &&
        &*self.target as *const _ == &*other.target as *const _
    }
}

impl<T> Drop for Pointer<T> {
    fn drop(&mut self) {
        self.pending.lock().unwrap().sub_ref.push(self.index);
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::Deref for ReadLock<'a, T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        &self.guard.data
    }
}

impl<'a, T> ReadLock<'a, T> {
    /// Borrow a pointed component for reading.
    pub fn access(&self, ptr: &Pointer<T>) -> &T {
        debug_assert_eq!(&*self.storage as *const _, &*ptr.target as *const _);
        &self.guard.data[ptr.index]
    }

    /// Borrow an existing component by index, not affecting it's reference count.
    pub fn access_index(&self, index: usize) -> Option<&T> {
        if index < self.guard.data.len() && self.guard.meta[index] != 0 {
            Some(&self.guard.data[index])
        } else {
            None
        }
    }

    /// Pin a specific component index with a newly created `Pointer`.
    /// Returns `false` if the element is dead or out of bounds.
    pub fn pin(&self, index: usize) -> Option<Pointer<T>> {
        if index < self.guard.data.len() && self.guard.meta[index] != 0 {
            self.pending.lock().unwrap().add_ref.push(index);
            Some(Pointer {
                index: index,
                target: self.storage.clone(),
                pending: self.pending.clone(),
            })
        } else {
            None
        }
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::Deref for WriteLock<'a, T> {
    type Target = [T];
    fn deref(&self) -> &Self::Target {
        &self.guard.data
    }
}

// Warning: this exposes deleted entries
impl<'a, T> ops::DerefMut for WriteLock<'a, T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.guard.data
    }
}

impl<'a, T> WriteLock<'a, T> {
    /// Borrow a pointed component for writing.
    pub fn access(&mut self, ptr: &Pointer<T>) -> &mut T {
        debug_assert_eq!(&*self.storage as *const _, &*ptr.target as *const _);
        &mut self.guard.data[ptr.index]
    }

    /// Borrow an existing component by index, not affecting it's reference count.
    pub fn access_index(&mut self, index: usize) -> Option<&mut T> {
        if index < self.guard.data.len() && self.guard.meta[index] != 0 {
            Some(&mut self.guard.data[index])
        } else {
            None
        }
    }

    /// Add a new component to the storage, returning the `Pointer` to it.
    pub fn create(&mut self, value: T) -> Pointer<T> {
        let index = match self.guard.free_list.pop() {
            Some(i) => {
                debug_assert_eq!(self.guard.meta[i], 0);
                self.guard.data[i] = value;
                self.guard.meta[i] = 1;
                i
            },
            None => {
                debug_assert_eq!(self.guard.data.len(), self.guard.meta.len());
                self.guard.data.push(value);
                self.guard.meta.push(1);
                self.guard.meta.len() - 1
            },
        };
        Pointer {
            index: index,
            target: self.storage.clone(),
            pending: self.pending.clone(),
        }
    }
}
<|endoftext|>


<commit_before>#![feature(semaphore)]
#![feature(read_exact)]
#![feature(plugin)]
#![plugin(clippy)]

extern crate irc;
extern crate rustc_serialize;
extern crate toml;
extern crate byteorder;

mod playpen;
mod cfg;
mod util;
mod eval;

use irc::client::prelude::*;
use std::sync::{Arc, Mutex, Semaphore};
use std::thread;
use std::collections::{VecDeque, HashMap};

use cfg::{EvalbotCfg, LangCfg};
use eval::{script, persistent};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

#[derive(Debug)]
struct Req {
    pub is_channel: bool,
    pub sender: String,
    pub target: String,
    pub code: String,
    pub language: Arc<LangCfg>
}

#[derive(Debug)]
enum CommandResult {
    Req(Req),
    Rehash,
    RestartEvaluator(String)
}

#[derive(Debug)]
enum WorkerMessage {
    Req(Req),
    Terminate
}

#[derive(Clone)]
struct State {
    requests: Arc<Mutex<VecDeque<WorkerMessage>>>,
    has_work: Arc<Semaphore>,
    cfg: Arc<EvalbotCfg>,
    languages: Arc<HashMap<String, Arc<LangCfg>>>,
    evaluators: Arc<HashMap<String, eval::persistent::Evaluator>>
}

impl State {
    fn send_message(&self, x: WorkerMessage) {
        self.requests.lock().unwrap().push_back(x); // if mutex is poisoned, just bail
        self.has_work.release();
    }
}

fn evaluate_loop<'a, S, T, U>(conn: Arc<S>, state: State)
    where T: IrcRead,
          U: IrcWrite,
          S: ServerExt<'a, T, U> + Sized {
    let has_work = state.has_work;
    let cfg = state.cfg;
    let requests = state.requests;
    let playpen_args = cfg.playpen_args.iter().map(|s| &**s).collect::<Vec<&str>>();
    loop {
        let work;
        {
            has_work.acquire();
            let mut rvec = requests.lock().unwrap();
            work = rvec.pop_front();
            std::mem::drop(rvec);
        }

        if let Some(WorkerMessage::Req(work)) = work {
            let result = if work.language.persistent {
                state.evaluators
                     .get(&work.language.long_name)
                     .unwrap()
                     .eval(&work.code, cfg.playpen_timeout)
            } else {
                script::eval(&work.code,
                             &*work.language,
                             &playpen_args[..],
                             &cfg.sandbox_dir,
                             cfg.playpen_timeout)
            };

            let (result, err) = match result {
                Ok(x) => (x, false),
                Err(x) => (x, true),
            };

            let dest = if work.is_channel { &work.target } else { &work.sender };

            if result.is_empty() {
                send_msg!(conn, dest, "ok (no output)");
                continue;
            }

            let result = util::wrap_output(&result,
                                           if work.is_channel {
                                               cfg.max_channel_line_len
                                           } else {
                                               425
                                           });

            let max_lines = if work.is_channel {
                cfg.max_channel_lines
            } else {
                cfg.max_priv_lines
            };
            let (truncated, result) = util::truncate_output(result, max_lines);
            let iter = result.iter().take(if truncated {
            // truncated: we take up to the 2nd last line, as we add "... (output truncated)"
            // to the last line
                result.len() - 1
            } else {
                result.len()
            });
            let prefix = if work.is_channel && !err {
                &cfg.chan_output_prefix as &str
            } else {
                ""
            };
            for line in iter {
                let line = format!("{}{}", prefix, line);
                send_msg!(conn, dest, &line);
            }
            if truncated {
                send_msg!(conn,
                          dest,
                          &format!("{}{}... (output truncated)", prefix, result.last().unwrap()));
            }
        } else if let Some(WorkerMessage::Terminate) = work {
            println!("got rehashing message; terminating");
            break;
        }
    }
}

fn parse_command(message: &str, prefix: &str) -> Option<CommandResult> {
    let tok: Vec<&str> = match message.splitn(2, prefix)
                                      .nth(1)
                                      .map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };
    if tok.is_empty() {
        return None;
    }
    match tok[0] {
        "rehash" => Some(CommandResult::Rehash),
        "restart" => {
            if tok.len() < 2 {
                None
            } else {
                Some(CommandResult::RestartEvaluator(tok[1].to_owned()))
            }
        }
        _ => None,
    }
}

fn parse_msg(message: &Message, state: &State) -> Option<(CommandResult, String)> {
    let sender = message.get_source_nickname().unwrap_or("");
    if let Ok(Command::PRIVMSG(target, message)) = message.into() {
        let in_channel = target.starts_with('#');
        if !in_channel && message.contains('\x01') {
            // we don't accept code via CTCP
            return None;
        }

        if state.cfg.owners.iter().any(|x| *x == sender) {
            if let Some(cmd) = parse_command(&message, &state.cfg.command_prefix) {
                return Some((cmd, sender.to_owned()));
            }
        }

        let tok: Vec<&str> = message.trim().splitn(2, '>').collect();
        match tok.len() {
            2 => (),
            _ => return None,
        };

        let language = state.languages.get(tok[0]);
        if let Some(language) = language {
            Some((CommandResult::Req(Req {
                is_channel: in_channel,
                sender: sender.to_owned(),
                target: target.to_owned(),
                code: tok[1].to_owned(),
                language: language.clone()
            }),
                  sender.to_owned()))
        } else {
            None
        }
    } else {
        None
    }
}

fn main() {
    let irc_config = match cfg::read("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => panic!("could not read irc config; {}", x),
    };
    println!("read irc config: {:?}", irc_config);

    let conn = Arc::new(IrcServer::from_config(irc_config).unwrap());
    loop {
        conn.identify().unwrap();
        'connection: loop {
            let config = Arc::new(match cfg::read::<EvalbotCfg>("evalbot.toml") {
                Ok(x) => x,
                Err(x) => panic!("could not read config; {}", x),
            });
            println!("read config: {:?}", config);
            let evalreqs = Arc::new(Mutex::new(VecDeque::new()));
            let has_work = Arc::new(Semaphore::new(0));

            let mut languages = HashMap::new();
            let mut evaluators = HashMap::new();
            for lang in config.languages.clone() {
                if lang.persistent {
                    let sandbox = config.sandbox_dir.clone();
                    let binary_path = lang.binary_path.clone();
                    let syscalls_path = lang.syscalls_path.clone();
                    let binary_args = lang.binary_args.clone();
                    let playpen_args = config.playpen_args.clone();
                    let childfn = move || {
                        playpen::spawn(&sandbox,
                                       &binary_path,
                                       &syscalls_path,
                                       &playpen_args.iter().map(|s| &**s).collect::<Vec<&str>>()[..],
                                       &binary_args.iter().map(|s| &**s).collect::<Vec<&str>>()[..],
                                       None,
                                       false)
                            .unwrap()
                    };
                    evaluators.insert(lang.long_name.clone(), persistent::new(childfn));
                }
                let lang = Arc::new(lang);
                languages.insert(lang.short_name.clone(), lang.clone());
                languages.insert(lang.long_name.clone(), lang);
            }
            let languages = Arc::new(languages);
            let evaluators = Arc::new(evaluators);

            let state = State {
                cfg: config,
                requests: evalreqs,
                has_work: has_work,
                languages: languages,
                evaluators: evaluators
            };

            for _ in 0..state.cfg.eval_threads {
                let conn = conn.clone();
                let state = state.clone();
                thread::spawn(move || {
                    evaluate_loop(conn, state);
                });
            }

            for maybe_msg in conn.iter() {
                let msg = match maybe_msg {
                    Ok(x) => x,
                    Err(x) => {
                        println!("{}, reconnecting", x);
                        break 'connection;
                    }
                };

                let req = parse_msg(&msg, &state);
                if let Some((x, sender)) = req {
                    println!("{:?}", (&x, &sender));
                    match x {
                        CommandResult::Req(req) => state.send_message(WorkerMessage::Req(req)),
                        CommandResult::Rehash => {
                            for _ in 0..state.cfg.eval_threads {
                                state.send_message(WorkerMessage::Terminate);
                            }
                            for evaluator in state.evaluators.values() {
                                evaluator.terminate();
                            }
                            send_notice!(conn, &sender, "rehashing");
                            break;
                        }
                        CommandResult::RestartEvaluator(lang) => {
                            if let Some(lang) = state.languages.get(&lang) {
                                send_notice!(conn,
                                             &sender,
                                             match state.evaluators.get(&lang.long_name) {
                                                 Some(x) => {
                                                     x.restart();
                                                     "restarting evaluator"
                                                 }
                                                 None => "this language's not persistent",
                                             });
                            } else {
                                send_notice!(conn, &sender, "invalid language name");
                            }
                        }
                    };
                }
            }
        }
        conn.reconnect().unwrap();
    }
}
<commit_msg>Check config before actually rehashing, to prevent panic<commit_after>#![feature(semaphore)]
#![feature(read_exact)]
#![feature(plugin)]
#![plugin(clippy)]

extern crate irc;
extern crate rustc_serialize;
extern crate toml;
extern crate byteorder;

mod playpen;
mod cfg;
mod util;
mod eval;

use irc::client::prelude::*;
use std::sync::{Arc, Mutex, Semaphore};
use std::thread;
use std::collections::{VecDeque, HashMap};

use cfg::{EvalbotCfg, LangCfg};
use eval::{script, persistent};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

#[derive(Debug)]
struct Req {
    pub is_channel: bool,
    pub sender: String,
    pub target: String,
    pub code: String,
    pub language: Arc<LangCfg>
}

#[derive(Debug)]
enum CommandResult {
    Req(Req),
    Rehash,
    RestartEvaluator(String)
}

#[derive(Debug)]
enum WorkerMessage {
    Req(Req),
    Terminate
}

#[derive(Clone)]
struct State {
    requests: Arc<Mutex<VecDeque<WorkerMessage>>>,
    has_work: Arc<Semaphore>,
    cfg: Arc<EvalbotCfg>,
    languages: Arc<HashMap<String, Arc<LangCfg>>>,
    evaluators: Arc<HashMap<String, eval::persistent::Evaluator>>
}

impl State {
    fn send_message(&self, x: WorkerMessage) {
        self.requests.lock().unwrap().push_back(x); // if mutex is poisoned, just bail
        self.has_work.release();
    }
}

fn evaluate_loop<'a, S, T, U>(conn: Arc<S>, state: State)
    where T: IrcRead,
          U: IrcWrite,
          S: ServerExt<'a, T, U> + Sized {
    let has_work = state.has_work;
    let cfg = state.cfg;
    let requests = state.requests;
    let playpen_args = cfg.playpen_args.iter().map(|s| &**s).collect::<Vec<&str>>();
    loop {
        let work;
        {
            has_work.acquire();
            let mut rvec = requests.lock().unwrap();
            work = rvec.pop_front();
            std::mem::drop(rvec);
        }

        if let Some(WorkerMessage::Req(work)) = work {
            let result = if work.language.persistent {
                state.evaluators
                     .get(&work.language.long_name)
                     .unwrap()
                     .eval(&work.code, cfg.playpen_timeout)
            } else {
                script::eval(&work.code,
                             &*work.language,
                             &playpen_args[..],
                             &cfg.sandbox_dir,
                             cfg.playpen_timeout)
            };

            let (result, err) = match result {
                Ok(x) => (x, false),
                Err(x) => (x, true),
            };

            let dest = if work.is_channel { &work.target } else { &work.sender };

            if result.is_empty() {
                send_msg!(conn, dest, "ok (no output)");
                continue;
            }

            let result = util::wrap_output(&result,
                                           if work.is_channel {
                                               cfg.max_channel_line_len
                                           } else {
                                               425
                                           });

            let max_lines = if work.is_channel {
                cfg.max_channel_lines
            } else {
                cfg.max_priv_lines
            };
            let (truncated, result) = util::truncate_output(result, max_lines);
            let iter = result.iter().take(if truncated {
            // truncated: we take up to the 2nd last line, as we add "... (output truncated)"
            // to the last line
                result.len() - 1
            } else {
                result.len()
            });
            let prefix = if work.is_channel && !err {
                &cfg.chan_output_prefix as &str
            } else {
                ""
            };
            for line in iter {
                let line = format!("{}{}", prefix, line);
                send_msg!(conn, dest, &line);
            }
            if truncated {
                send_msg!(conn,
                          dest,
                          &format!("{}{}... (output truncated)", prefix, result.last().unwrap()));
            }
        } else if let Some(WorkerMessage::Terminate) = work {
            println!("got rehashing message; terminating");
            break;
        }
    }
}

fn parse_command(message: &str, prefix: &str) -> Option<CommandResult> {
    let tok: Vec<&str> = match message.splitn(2, prefix)
                                      .nth(1)
                                      .map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };
    if tok.is_empty() {
        return None;
    }
    match tok[0] {
        "rehash" => Some(CommandResult::Rehash),
        "restart" => {
            if tok.len() < 2 {
                None
            } else {
                Some(CommandResult::RestartEvaluator(tok[1].to_owned()))
            }
        }
        _ => None,
    }
}

fn parse_msg(message: &Message, state: &State) -> Option<(CommandResult, String)> {
    let sender = message.get_source_nickname().unwrap_or("");
    if let Ok(Command::PRIVMSG(target, message)) = message.into() {
        let in_channel = target.starts_with('#');
        if !in_channel && message.contains('\x01') {
            // we don't accept code via CTCP
            return None;
        }

        if state.cfg.owners.iter().any(|x| *x == sender) {
            if let Some(cmd) = parse_command(&message, &state.cfg.command_prefix) {
                return Some((cmd, sender.to_owned()));
            }
        }

        let tok: Vec<&str> = message.trim().splitn(2, '>').collect();
        match tok.len() {
            2 => (),
            _ => return None,
        };

        let language = state.languages.get(tok[0]);
        if let Some(language) = language {
            Some((CommandResult::Req(Req {
                is_channel: in_channel,
                sender: sender.to_owned(),
                target: target.to_owned(),
                code: tok[1].to_owned(),
                language: language.clone()
            }),
                  sender.to_owned()))
        } else {
            None
        }
    } else {
        None
    }
}

fn main() {
    let irc_config = match cfg::read("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => panic!("could not read irc config; {}", x),
    };
    println!("read irc config: {:?}", irc_config);

    let conn = Arc::new(IrcServer::from_config(irc_config).unwrap());
    let mut rehash_cfg = None;
    loop {
        conn.identify().unwrap();
        'connection: loop {
            let config = {
                if let Some(cfg) = rehash_cfg.take() {
                    Arc::new(cfg)
                } else {
                    Arc::new(match cfg::read::<EvalbotCfg>("evalbot.toml") {
                        Ok(x) => x,
                        Err(x) => panic!("could not read config; {}", x),
                    })
                }
            };
            println!("read config: {:?}", config);
            let evalreqs = Arc::new(Mutex::new(VecDeque::new()));
            let has_work = Arc::new(Semaphore::new(0));

            let mut languages = HashMap::new();
            let mut evaluators = HashMap::new();
            for lang in config.languages.clone() {
                if lang.persistent {
                    let sandbox = config.sandbox_dir.clone();
                    let binary_path = lang.binary_path.clone();
                    let syscalls_path = lang.syscalls_path.clone();
                    let binary_args = lang.binary_args.clone();
                    let playpen_args = config.playpen_args.clone();
                    let childfn = move || {
                        playpen::spawn(&sandbox,
                                       &binary_path,
                                       &syscalls_path,
                                       &playpen_args.iter().map(|s| &**s).collect::<Vec<&str>>()[..],
                                       &binary_args.iter().map(|s| &**s).collect::<Vec<&str>>()[..],
                                       None,
                                       false)
                            .unwrap()
                    };
                    evaluators.insert(lang.long_name.clone(), persistent::new(childfn));
                }
                let lang = Arc::new(lang);
                languages.insert(lang.short_name.clone(), lang.clone());
                languages.insert(lang.long_name.clone(), lang);
            }
            let languages = Arc::new(languages);
            let evaluators = Arc::new(evaluators);

            let state = State {
                cfg: config,
                requests: evalreqs,
                has_work: has_work,
                languages: languages,
                evaluators: evaluators
            };

            for _ in 0..state.cfg.eval_threads {
                let conn = conn.clone();
                let state = state.clone();
                thread::spawn(move || {
                    evaluate_loop(conn, state);
                });
            }

            for maybe_msg in conn.iter() {
                let msg = match maybe_msg {
                    Ok(x) => x,
                    Err(x) => {
                        println!("{}, reconnecting", x);
                        break 'connection;
                    }
                };

                let req = parse_msg(&msg, &state);
                if let Some((x, sender)) = req {
                    println!("{:?}", (&x, &sender));
                    match x {
                        CommandResult::Req(req) => state.send_message(WorkerMessage::Req(req)),
                        CommandResult::Rehash => {
                            for _ in 0..state.cfg.eval_threads {
                                state.send_message(WorkerMessage::Terminate);
                            }
                            for evaluator in state.evaluators.values() {
                                evaluator.terminate();
                            }
                            let maybe_rehash_cfg = cfg::read::<EvalbotCfg>("evalbot.toml");
                            match maybe_rehash_cfg {
                                Ok(cfg) => {
                                    rehash_cfg = Some(cfg);
                                    send_notice!(conn, &sender, "read configuration file OK, rehashing");
                                    break;
                                },
                                Err(x) => {
                                    send_notice!(conn, &sender, "error reading configuration, check console");
                                    println!("error while reading config for rehash: {}", x);
                                }
                            };
                        }
                        CommandResult::RestartEvaluator(lang) => {
                            if let Some(lang) = state.languages.get(&lang) {
                                send_notice!(conn,
                                             &sender,
                                             match state.evaluators.get(&lang.long_name) {
                                                 Some(x) => {
                                                     x.restart();
                                                     "restarting evaluator"
                                                 }
                                                 None => "this language's not persistent",
                                             });
                            } else {
                                send_notice!(conn, &sender, "invalid language name");
                            }
                        }
                    };
                }
            }
        }
        conn.reconnect().unwrap();
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate pbr;
extern crate ansi_term;

use std::sync::{Arc, Mutex};

#[macro_use]
pub mod logs;

pub mod authorization;
pub mod bench;
pub mod cargo_helper;
pub mod client;
pub mod contentlength;
pub mod download;
pub mod filesize;
pub mod http_version;
pub mod response;
pub mod util;
pub mod write;

/// Represents a number of bytes, as `u64`.
pub type Bytes = u64;
/// Represents a 'chunk', which is just a piece of bytes.
type Chunk = Vec<u8>;
/// Represents a list of chunks
pub type Chunks = Vec<Chunk>;
/// Represents a shared mutable reference of chunks
pub type SChunks = Arc<Mutex<Chunks>>;
/// Represents an URL
pub type URL<'a> = &'a str;
/// MirrorsList is an alias that contain fast URLs to download the file
type MirrorsList<'a> = Vec<URL<'a>>;
<commit_msg>Use Rayon crate<commit_after>extern crate ansi_term;
extern crate hyper;
extern crate pbr;
extern crate rayon;

use std::sync::{Arc, Mutex};

#[macro_use]
pub mod logs;

pub mod authorization;
pub mod bench;
pub mod cargo_helper;
pub mod client;
pub mod contentlength;
pub mod download;
pub mod filesize;
pub mod http_version;
pub mod response;
pub mod util;
pub mod write;

/// Represents a number of bytes, as `u64`.
pub type Bytes = u64;
/// Represents a 'chunk', which is just a piece of bytes.
type Chunk = Vec<u8>;
/// Represents a list of chunks
pub type Chunks = Vec<Chunk>;
/// Represents a shared mutable reference of chunks
pub type SChunks = Arc<Mutex<Chunks>>;
/// Represents an URL
pub type URL<'a> = &'a str;
/// MirrorsList is an alias that contain fast URLs to download the file
type MirrorsList<'a> = Vec<URL<'a>>;
<|endoftext|>