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

use geometry::Au;

use cssparser::{mod, RGBA, Color};
use std::ascii::AsciiExt;
use std::iter::Filter;
use std::num::Int;
use std::str::{CharEq, CharSplits, FromStr};
use unicode::char::to_lowercase;

pub type DOMString = String;
pub type StaticCharVec = &'static [char];
pub type StaticStringVec = &'static [&'static str];

pub fn null_str_as_empty(s: &Option<DOMString>) -> DOMString {
    // We don't use map_default because it would allocate "".into_string() even
    // for Some.
    match *s {
        Some(ref s) => s.clone(),
        None => "".into_string()
    }
}

pub fn null_str_as_empty_ref<'a>(s: &'a Option<DOMString>) -> &'a str {
    match *s {
        Some(ref s) => s.as_slice(),
        None => ""
    }
}

/// Whitespace as defined by HTML5 § 2.4.1.
struct Whitespace;

impl CharEq for Whitespace {
    #[inline]
    fn matches(&mut self, ch: char) -> bool {
        match ch {
            ' ' | '\t' | '\x0a' | '\x0c' | '\x0d' => true,
            _ => false,
        }
    }

    #[inline]
    fn only_ascii(&self) -> bool {
        true
    }
}

pub fn is_whitespace(s: &str) -> bool {
    s.chars().all(|c| Whitespace.matches(c))
}

/// A "space character" according to:
///
/// http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[
    '\u{0020}',
    '\u{0009}',
    '\u{000a}',
    '\u{000c}',
    '\u{000d}',
];

pub fn split_html_space_chars<'a>(s: &'a str)
                                  -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> {
    s.split(HTML_SPACE_CHARACTERS).filter(|&split| !split.is_empty())
}

/// Shared implementation to parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-integers> or
/// <http://www.whatwg.org/html/#rules-for-parsing-non-negative-integers>.
fn do_parse_integer<T: Iterator<char>>(input: T) -> Option<i64> {
    fn is_ascii_digit(c: &char) -> bool {
        match *c {
            '0'...'9' => true,
            _ => false,
        }
    }

    let mut input = input.skip_while(|c| {
        HTML_SPACE_CHARACTERS.iter().any(|s| s == c)
    }).peekable();

    let sign = match input.peek() {
        None => return None,
        Some(&'-') => {
            input.next();
            -1
        },
        Some(&'+') => {
            input.next();
            1
        },
        Some(_) => 1,
    };

    match input.peek() {
        Some(c) if is_ascii_digit(c) => (),
        _ => return None,
    }

    let value = input.take_while(is_ascii_digit).map(|d| {
        d as i64 - '0' as i64
    }).fold(Some(0i64), |accumulator, d| {
        accumulator.and_then(|accumulator| {
            accumulator.checked_mul(10)
        }).and_then(|accumulator| {
            accumulator.checked_add(d)
        })
    });

    return value.and_then(|value| value.checked_mul(sign));
}

/// Parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-integers>.
pub fn parse_integer<T: Iterator<char>>(input: T) -> Option<i32> {
    do_parse_integer(input).and_then(|result| {
        result.to_i32()
    })
}

/// Parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-non-negative-integers>.
pub fn parse_unsigned_integer<T: Iterator<char>>(input: T) -> Option<u32> {
    do_parse_integer(input).and_then(|result| {
        result.to_u32()
    })
}

#[deriving(Copy)]
pub enum LengthOrPercentageOrAuto {
    Auto,
    Percentage(f64),
    Length(Au),
}

/// Parses a length per HTML5 § 2.4.4.4. If unparseable, `Auto` is returned.
pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
    value = value.trim_left_chars(Whitespace);
    if value.len() == 0 {
        return LengthOrPercentageOrAuto::Auto
    }
    if value.starts_with("+") {
        value = value.slice_from(1)
    }
    value = value.trim_left_chars('0');
    if value.len() == 0 {
        return LengthOrPercentageOrAuto::Auto
    }

    let mut end_index = value.len();
    let (mut found_full_stop, mut found_percent) = (false, false);
    for (i, ch) in value.chars().enumerate() {
        match ch {
            '0'...'9' => continue,
            '%' => {
                found_percent = true;
                end_index = i;
                break
            }
            '.' if !found_full_stop => {
                found_full_stop = true;
                continue
            }
            _ => {
                end_index = i;
                break
            }
        }
    }
    value = value.slice_to(end_index);

    if found_percent {
        let result: Option<f64> = FromStr::from_str(value);
        match result {
            Some(number) => return LengthOrPercentageOrAuto::Percentage((number as f64) / 100.0),
            None => return LengthOrPercentageOrAuto::Auto,
        }
    }

    match FromStr::from_str(value) {
        Some(number) => LengthOrPercentageOrAuto::Length(Au::from_px(number)),
        None => LengthOrPercentageOrAuto::Auto,
    }
}

/// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned.
pub fn parse_legacy_color(mut input: &str) -> Result<RGBA,()> {
    // Steps 1 and 2.
    if input.len() == 0 {
        return Err(())
    }

    // Step 3.
    input = input.trim_left_chars(Whitespace).trim_right_chars(Whitespace);

    // Step 4.
    if input.eq_ignore_ascii_case("transparent") {
        return Err(())
    }

    // Step 5.
    match cssparser::parse_color_keyword(input) {
        Ok(Color::RGBA(rgba)) => return Ok(rgba),
        _ => {}
    }

    // Step 6.
    if input.len() == 4 {
        match (input.as_bytes()[0],
               hex(input.as_bytes()[1] as char),
               hex(input.as_bytes()[2] as char),
               hex(input.as_bytes()[3] as char)) {
            (b'#', Ok(r), Ok(g), Ok(b)) => {
                return Ok(RGBA {
                    red: (r as f32) * 17.0 / 255.0,
                    green: (g as f32) * 17.0 / 255.0,
                    blue: (b as f32) * 17.0 / 255.0,
                    alpha: 1.0,
                })
            }
            _ => {}
        }
    }

    // Step 7.
    let mut new_input = String::new();
    for ch in input.chars() {
        if ch as u32 > 0xffff {
            new_input.push_str("00")
        } else {
            new_input.push(ch)
        }
    }
    let mut input = new_input.as_slice();

    // Step 8.
    for (char_count, (index, _)) in input.char_indices().enumerate() {
        if char_count == 128 {
            input = input.slice_to(index);
            break
        }
    }

    // Step 9.
    if input.char_at(0) == '#' {
        input = input.slice_from(1)
    }

    // Step 10.
    let mut new_input = Vec::new();
    for ch in input.chars() {
        if hex(ch).is_ok() {
            new_input.push(ch as u8)
        } else {
            new_input.push(b'0')
        }
    }
    let mut input = new_input;

    // Step 11.
    while input.len() == 0 || (input.len() % 3) != 0 {
        input.push(b'0')
    }

    // Step 12.
    let mut length = input.len() / 3;
    let (mut red, mut green, mut blue) = (input.slice_to(length),
                                          input.slice(length, length * 2),
                                          input.slice_from(length * 2));

    // Step 13.
    if length > 8 {
        red = red.slice_from(length - 8);
        green = green.slice_from(length - 8);
        blue = blue.slice_from(length - 8);
        length = 8
    }

    // Step 14.
    while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {
        red = red.slice_from(1);
        green = green.slice_from(1);
        blue = blue.slice_from(1);
        length -= 1
    }

    // Steps 15-20.
    return Ok(RGBA {
        red: hex_string(red).unwrap() as f32 / 255.0,
        green: hex_string(green).unwrap() as f32 / 255.0,
        blue: hex_string(blue).unwrap() as f32 / 255.0,
        alpha: 1.0,
    });

    fn hex(ch: char) -> Result<u8,()> {
        match ch {
            '0'...'9' => Ok((ch as u8) - b'0'),
            'a'...'f' => Ok((ch as u8) - b'a' + 10),
            'A'...'F' => Ok((ch as u8) - b'A' + 10),
            _ => Err(()),
        }
    }

    fn hex_string(string: &[u8]) -> Result<u8,()> {
        match string.len() {
            0 => Err(()),
            1 => hex(string[0] as char),
            _ => {
                let upper = try!(hex(string[0] as char));
                let lower = try!(hex(string[1] as char));
                Ok((upper << 4) | lower)
            }
        }
    }
}

#[deriving(Clone, Eq, PartialEq, Hash, Show)]
pub struct LowercaseString {
    inner: String,
}

impl LowercaseString {
    pub fn new(s: &str) -> LowercaseString {
        LowercaseString {
            inner: s.chars().map(to_lowercase).collect(),
        }
    }
}

impl Str for LowercaseString {
    #[inline]
    fn as_slice(&self) -> &str {
        self.inner.as_slice()
    }
}
<commit_msg>Fix `use UnicodeChar::to_lowercase` warning<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use geometry::Au;

use cssparser::{mod, RGBA, Color};
use std::ascii::AsciiExt;
use std::iter::Filter;
use std::num::Int;
use std::str::{CharEq, CharSplits, FromStr};
use unicode::char::UnicodeChar;

pub type DOMString = String;
pub type StaticCharVec = &'static [char];
pub type StaticStringVec = &'static [&'static str];

pub fn null_str_as_empty(s: &Option<DOMString>) -> DOMString {
    // We don't use map_default because it would allocate "".into_string() even
    // for Some.
    match *s {
        Some(ref s) => s.clone(),
        None => "".into_string()
    }
}

pub fn null_str_as_empty_ref<'a>(s: &'a Option<DOMString>) -> &'a str {
    match *s {
        Some(ref s) => s.as_slice(),
        None => ""
    }
}

/// Whitespace as defined by HTML5 § 2.4.1.
struct Whitespace;

impl CharEq for Whitespace {
    #[inline]
    fn matches(&mut self, ch: char) -> bool {
        match ch {
            ' ' | '\t' | '\x0a' | '\x0c' | '\x0d' => true,
            _ => false,
        }
    }

    #[inline]
    fn only_ascii(&self) -> bool {
        true
    }
}

pub fn is_whitespace(s: &str) -> bool {
    s.chars().all(|c| Whitespace.matches(c))
}

/// A "space character" according to:
///
/// http://www.whatwg.org/specs/web-apps/current-work/multipage/common-microsyntaxes.html#space-character
pub static HTML_SPACE_CHARACTERS: StaticCharVec = &[
    '\u{0020}',
    '\u{0009}',
    '\u{000a}',
    '\u{000c}',
    '\u{000d}',
];

pub fn split_html_space_chars<'a>(s: &'a str)
                                  -> Filter<'a, &'a str, CharSplits<'a, StaticCharVec>> {
    s.split(HTML_SPACE_CHARACTERS).filter(|&split| !split.is_empty())
}

/// Shared implementation to parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-integers> or
/// <http://www.whatwg.org/html/#rules-for-parsing-non-negative-integers>.
fn do_parse_integer<T: Iterator<char>>(input: T) -> Option<i64> {
    fn is_ascii_digit(c: &char) -> bool {
        match *c {
            '0'...'9' => true,
            _ => false,
        }
    }

    let mut input = input.skip_while(|c| {
        HTML_SPACE_CHARACTERS.iter().any(|s| s == c)
    }).peekable();

    let sign = match input.peek() {
        None => return None,
        Some(&'-') => {
            input.next();
            -1
        },
        Some(&'+') => {
            input.next();
            1
        },
        Some(_) => 1,
    };

    match input.peek() {
        Some(c) if is_ascii_digit(c) => (),
        _ => return None,
    }

    let value = input.take_while(is_ascii_digit).map(|d| {
        d as i64 - '0' as i64
    }).fold(Some(0i64), |accumulator, d| {
        accumulator.and_then(|accumulator| {
            accumulator.checked_mul(10)
        }).and_then(|accumulator| {
            accumulator.checked_add(d)
        })
    });

    return value.and_then(|value| value.checked_mul(sign));
}

/// Parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-integers>.
pub fn parse_integer<T: Iterator<char>>(input: T) -> Option<i32> {
    do_parse_integer(input).and_then(|result| {
        result.to_i32()
    })
}

/// Parse an integer according to
/// <http://www.whatwg.org/html/#rules-for-parsing-non-negative-integers>.
pub fn parse_unsigned_integer<T: Iterator<char>>(input: T) -> Option<u32> {
    do_parse_integer(input).and_then(|result| {
        result.to_u32()
    })
}

#[deriving(Copy)]
pub enum LengthOrPercentageOrAuto {
    Auto,
    Percentage(f64),
    Length(Au),
}

/// Parses a length per HTML5 § 2.4.4.4. If unparseable, `Auto` is returned.
pub fn parse_length(mut value: &str) -> LengthOrPercentageOrAuto {
    value = value.trim_left_chars(Whitespace);
    if value.len() == 0 {
        return LengthOrPercentageOrAuto::Auto
    }
    if value.starts_with("+") {
        value = value.slice_from(1)
    }
    value = value.trim_left_chars('0');
    if value.len() == 0 {
        return LengthOrPercentageOrAuto::Auto
    }

    let mut end_index = value.len();
    let (mut found_full_stop, mut found_percent) = (false, false);
    for (i, ch) in value.chars().enumerate() {
        match ch {
            '0'...'9' => continue,
            '%' => {
                found_percent = true;
                end_index = i;
                break
            }
            '.' if !found_full_stop => {
                found_full_stop = true;
                continue
            }
            _ => {
                end_index = i;
                break
            }
        }
    }
    value = value.slice_to(end_index);

    if found_percent {
        let result: Option<f64> = FromStr::from_str(value);
        match result {
            Some(number) => return LengthOrPercentageOrAuto::Percentage((number as f64) / 100.0),
            None => return LengthOrPercentageOrAuto::Auto,
        }
    }

    match FromStr::from_str(value) {
        Some(number) => LengthOrPercentageOrAuto::Length(Au::from_px(number)),
        None => LengthOrPercentageOrAuto::Auto,
    }
}

/// Parses a legacy color per HTML5 § 2.4.6. If unparseable, `Err` is returned.
pub fn parse_legacy_color(mut input: &str) -> Result<RGBA,()> {
    // Steps 1 and 2.
    if input.len() == 0 {
        return Err(())
    }

    // Step 3.
    input = input.trim_left_chars(Whitespace).trim_right_chars(Whitespace);

    // Step 4.
    if input.eq_ignore_ascii_case("transparent") {
        return Err(())
    }

    // Step 5.
    match cssparser::parse_color_keyword(input) {
        Ok(Color::RGBA(rgba)) => return Ok(rgba),
        _ => {}
    }

    // Step 6.
    if input.len() == 4 {
        match (input.as_bytes()[0],
               hex(input.as_bytes()[1] as char),
               hex(input.as_bytes()[2] as char),
               hex(input.as_bytes()[3] as char)) {
            (b'#', Ok(r), Ok(g), Ok(b)) => {
                return Ok(RGBA {
                    red: (r as f32) * 17.0 / 255.0,
                    green: (g as f32) * 17.0 / 255.0,
                    blue: (b as f32) * 17.0 / 255.0,
                    alpha: 1.0,
                })
            }
            _ => {}
        }
    }

    // Step 7.
    let mut new_input = String::new();
    for ch in input.chars() {
        if ch as u32 > 0xffff {
            new_input.push_str("00")
        } else {
            new_input.push(ch)
        }
    }
    let mut input = new_input.as_slice();

    // Step 8.
    for (char_count, (index, _)) in input.char_indices().enumerate() {
        if char_count == 128 {
            input = input.slice_to(index);
            break
        }
    }

    // Step 9.
    if input.char_at(0) == '#' {
        input = input.slice_from(1)
    }

    // Step 10.
    let mut new_input = Vec::new();
    for ch in input.chars() {
        if hex(ch).is_ok() {
            new_input.push(ch as u8)
        } else {
            new_input.push(b'0')
        }
    }
    let mut input = new_input;

    // Step 11.
    while input.len() == 0 || (input.len() % 3) != 0 {
        input.push(b'0')
    }

    // Step 12.
    let mut length = input.len() / 3;
    let (mut red, mut green, mut blue) = (input.slice_to(length),
                                          input.slice(length, length * 2),
                                          input.slice_from(length * 2));

    // Step 13.
    if length > 8 {
        red = red.slice_from(length - 8);
        green = green.slice_from(length - 8);
        blue = blue.slice_from(length - 8);
        length = 8
    }

    // Step 14.
    while length > 2 && red[0] == b'0' && green[0] == b'0' && blue[0] == b'0' {
        red = red.slice_from(1);
        green = green.slice_from(1);
        blue = blue.slice_from(1);
        length -= 1
    }

    // Steps 15-20.
    return Ok(RGBA {
        red: hex_string(red).unwrap() as f32 / 255.0,
        green: hex_string(green).unwrap() as f32 / 255.0,
        blue: hex_string(blue).unwrap() as f32 / 255.0,
        alpha: 1.0,
    });

    fn hex(ch: char) -> Result<u8,()> {
        match ch {
            '0'...'9' => Ok((ch as u8) - b'0'),
            'a'...'f' => Ok((ch as u8) - b'a' + 10),
            'A'...'F' => Ok((ch as u8) - b'A' + 10),
            _ => Err(()),
        }
    }

    fn hex_string(string: &[u8]) -> Result<u8,()> {
        match string.len() {
            0 => Err(()),
            1 => hex(string[0] as char),
            _ => {
                let upper = try!(hex(string[0] as char));
                let lower = try!(hex(string[1] as char));
                Ok((upper << 4) | lower)
            }
        }
    }
}

#[deriving(Clone, Eq, PartialEq, Hash, Show)]
pub struct LowercaseString {
    inner: String,
}

impl LowercaseString {
    pub fn new(s: &str) -> LowercaseString {
        LowercaseString {
            inner: s.chars().map(|c| c.to_lowercase()).collect(),
        }
    }
}

impl Str for LowercaseString {
    #[inline]
    fn as_slice(&self) -> &str {
        self.inner.as_slice()
    }
}
<|endoftext|>


<commit_before>#![feature(io)] // For io::ErrorKind

#[macro_use]
extern crate log;
extern crate fern;

use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::fs;
use std::io;
use std::env;
use std::collections;

const PLUGIN_CARGO_TEMPLATE: &'static str = include_str!("../templates/cargo.toml");
const TOP_CARGO_TEMPLATE: &'static str = include_str!("../templates/plugins-crate/cargo.toml");
const TOP_LIB_TEMPLATE: &'static str = include_str!("../templates/plugins-crate/lib.rs");

fn main() {
    if let Err(e) = main_possibly_errors() {
        panic!("Build error! Error: {} ({:?})", e, e);
    }
}

fn setup_logger(out_dir: &Path) -> Result<(), fern::InitError> {
    fern::init_global_logger(
        fern::OutputConfig::File(out_dir.join("output.log")),
        log::LogLevelFilter::Trace
    )
}

fn main_possibly_errors() -> io::Result<()> {
    let zaldinar_runtime_dir_str = env::var_os("CARGO_MANIFEST_DIR")
        .expect("Expected CARGO_MANIFEST_DIR, found None");

    let source_plugin_directory = Path::new(&zaldinar_runtime_dir_str).parent()
        .expect("CARGO_MANIFEST_DIR does not have a parent path.")
        .join("plugins");

    let output_dir = Path::new(&zaldinar_runtime_dir_str).parent().unwrap()
        .join("build-out");

    setup_logger(&output_dir).unwrap();

    let generated_plugin_directory = output_dir.join("plugin-crates");

    let zaldinar_core_path = Path::new(&zaldinar_runtime_dir_str).parent().unwrap()
        .join("zaldinar-core");

    let core_path_str = zaldinar_core_path.to_str()
        .expect("Expected valid UTF8 zaldinar-core path (for embedding in Cargo.toml), found None");

    debug!("Creating generated plugin directory: {}", generated_plugin_directory.display());
    try!(fs::create_dir_all(&generated_plugin_directory));

    let mut created_plugins = Vec::new();

    debug!("Walking {}", source_plugin_directory.display());
    for dir_entry in try!(fs::read_dir(&source_plugin_directory)) {
        let plugin_path = try!(dir_entry).path();
        if plugin_path.file_name().unwrap() == "template.rs" {
            continue; // ignore template.rs!
        }
        let created = try!(create_plugin_crate(&plugin_path, &generated_plugin_directory,
            &core_path_str));
        created_plugins.push(created);
    }

    debug!("Creating main dependency crate.");

    {

        let cumulative_crate_dir = output_dir.join("cumulative-crate");

        // Create directory
        debug!("Creating cumulative plugin crate directory: {}", cumulative_crate_dir.display());
        try!(fs::create_dir_all(&cumulative_crate_dir));

        // Set of names which have already been used for importing
        // Because we take the last string in a plugin name split by `-`, this is needed
        // to prevent names like `thing1-cmds` and `thing2-cmds` to conflict, just as an example.
        let mut taken_import_names = collections::HashSet::new();
        // List of dependency lines to insert into Cargo.toml
        let mut dependency_lines = Vec::new();
        // List of `extern crate ...` lines
        let mut extern_crate_lines = Vec::new();
        // List of `xxx::register(register);` lines
        let mut register_lines = Vec::new();

        // We use the zaldinar name for zaldinar-core
        taken_import_names.insert("zaldinar".to_string());

        for (crate_name, path) in created_plugins {
            dependency_lines.push(format!("[dependencies.{}]", crate_name));
            dependency_lines.push(format!("path = \"{}\"", path.display()));

            let imported_name = { // This calculation could probably be done more efficiently.
                // any string.split() will at least have one element
                let name_without_additions = crate_name.split("-").next().unwrap();
                let mut temp_name = name_without_additions.to_string();
                // TODO: this could possibly overflow, some check for that?
                let mut addition = 0u32;
                while !taken_import_names.insert(temp_name.clone()) {
                    // Continue incrementing an integer added to the end of the name until the
                    // name is no longer taken.
                    temp_name = format!("{}{}", name_without_additions, addition);
                    addition = addition + 1;
                }
                temp_name
            };

            extern_crate_lines.push(format!("extern crate \"{}\" as {};",
                                            crate_name, imported_name));
            register_lines.push(format!("{}::register(register);", imported_name));
        }

        {
            let cargo_toml_contents = TOP_CARGO_TEMPLATE
                    .replace("{{dependency_lines}}", &dependency_lines.connect("\n"))
                    .replace("{{zaldinar_core_path}}", &core_path_str);

            let cargo_toml_path = cumulative_crate_dir.join("Cargo.toml");
            try!(write_file(&cargo_toml_path, &cargo_toml_contents));
        }

        {
            let lib_rs_contents = TOP_LIB_TEMPLATE
                    .replace("{{extern_crate_lines}}", &extern_crate_lines.connect("\n"))
                    .replace("{{register_lines}}", &register_lines.connect("\n"));

            let src_path = cumulative_crate_dir.join("src");
            debug!("Creating source directory {}", src_path.display());
            try!(fs::create_dir_all(&src_path));

            try!(write_file(&src_path.join("lib.rs"), &lib_rs_contents));
        }
    }

    return Ok(());
}

fn create_plugin_crate(path: &Path, output_dir: &Path, core_path_str: &str)
        -> io::Result<(String, PathBuf)> {
    debug!("Reading plugin source file: {}", path.display());

    let contents = {
        let mut stream = try!(fs::File::open(path));
        let mut buf = String::new();
        try!(stream.read_to_string(&mut buf));
        buf
    };

    let mut dependency_lines = Vec::new();

    for line in contents.lines_any() {
        let split_iter = line.split("//! depends: ");
        // If the line has `//! depends: `, .skip(1) will remove all content before the
        // `//! depends: `. If it doesn't, then .skip(1) will leave an empty iterator, and this if
        // let statement won't run due to .next() returning None.
        if let Some(dependency_line) = split_iter.skip(1).next() {
            dependency_lines.push(dependency_line);
        }
    }

    let name = {
        let name_os = path.file_stem().unwrap();
        let temp_name = name_os.to_str().expect("Expected UTF8 file name for plugin, found None");
        format!("zaldinar-plugin-{}", temp_name)
    };
    let output_directory = output_dir.join(&name);

    debug!("Creating plugin crate from {{source: {:?}, name: {:?}, dependencies: {:?}}} at {:?}",
        path.display(), name, dependency_lines, output_directory.display());

    // Create directory
    debug!("Creating crate directory: {}", output_directory.display());
    try!(fs::create_dir_all(&output_directory));

    { // Scope for writing Cargo.toml

        // TODO: This could probably be done more efficiently
        let dependencies_contents = dependency_lines.connect("\n");
        let cargo_contents = PLUGIN_CARGO_TEMPLATE.replace("{{name}}", &name)
                                .replace("{{dependencies}}", &dependencies_contents)
                                .replace("{{zaldinar_core_path}}", core_path_str);

        // Write to the file
        try!(write_file(&output_directory.join("Cargo.toml"), &cargo_contents));
    }

    { // Scope for writing lib.rs
        let src_path = output_directory.join("src");

        debug!("Creating source directory {}", src_path.display());
        try!(fs::create_dir_all(&src_path));

        // This is just writing the contents which were originally read from the plugin.rs file.
        try!(write_file(&src_path.join("lib.rs"), &contents));
    }

    return Ok((name, output_directory));
}

fn write_file(path: &Path, contents: &str) -> io::Result<()> {
    if try!(is_same(path, contents)) {
        debug!("Skipping writing to {}, as file contents are same", path.display());
        return Ok(());
    }

    // create a parent directory, if it has a parent
    if let Some(parent) = path.parent() {
        debug!("Creating directory {}", parent.display());
        try!(fs::create_dir_all(parent));
    }

    debug!("Writing to {}", path.display());
    let mut stream = try!(fs::File::create(path));
    try!(stream.write(contents.as_bytes()));

    return Ok(());
}

fn is_same(path: &Path, expected_contents: &str) -> io::Result<bool> {
    debug!("Checking differences for {}", path.display());
    let metadata = match fs::metadata(path) {
        Ok(v) => v,
        Err(e) => if e.kind() == io::ErrorKind::FileNotFound {
            return Ok(false); // File does not exist
        } else {
            return Err(e);
        }
    };
    if metadata.len() != expected_contents.len() as u64 {
        return Ok(false);
    }
    let file_stream = try!(fs::File::open(path));
    // this gets if any bytes in the file are different from bytes in the expected contents,
    // without holding the file in memory.
    for (read_byte, expected_byte) in file_stream.bytes().zip(expected_contents.bytes()) {
        if try!(read_byte) != expected_byte {
            return Ok(false);
        }
    }
    return Ok(true);
}
<commit_msg>Fix plugin-generator erroring if build-out doesn't exist<commit_after>#![feature(io)] // For io::ErrorKind

#[macro_use]
extern crate log;
extern crate fern;

use std::io::prelude::*;
use std::path::{Path, PathBuf};
use std::fs;
use std::io;
use std::env;
use std::collections;

const PLUGIN_CARGO_TEMPLATE: &'static str = include_str!("../templates/cargo.toml");
const TOP_CARGO_TEMPLATE: &'static str = include_str!("../templates/plugins-crate/cargo.toml");
const TOP_LIB_TEMPLATE: &'static str = include_str!("../templates/plugins-crate/lib.rs");

fn main() {
    if let Err(e) = main_possibly_errors() {
        panic!("Build error! Error: {} ({:?})", e, e);
    }
}

fn setup_logger(out_dir: &Path) -> Result<(), fern::InitError> {
    try!(fs::create_dir_all(out_dir));
    fern::init_global_logger(
        fern::OutputConfig::File(out_dir.join("output.log")),
        log::LogLevelFilter::Trace
    )
}

fn main_possibly_errors() -> io::Result<()> {
    let zaldinar_runtime_dir_str = env::var_os("CARGO_MANIFEST_DIR")
        .expect("Expected CARGO_MANIFEST_DIR, found None");

    let source_plugin_directory = Path::new(&zaldinar_runtime_dir_str).parent()
        .expect("CARGO_MANIFEST_DIR does not have a parent path.")
        .join("plugins");

    let output_dir = Path::new(&zaldinar_runtime_dir_str).parent().unwrap()
        .join("build-out");

    if let Err(e) = setup_logger(&output_dir) {
        panic!("Error setting up logging: {}", e);
    }

    let generated_plugin_directory = output_dir.join("plugin-crates");

    let zaldinar_core_path = Path::new(&zaldinar_runtime_dir_str).parent().unwrap()
        .join("zaldinar-core");

    let core_path_str = zaldinar_core_path.to_str()
        .expect("Expected valid UTF8 zaldinar-core path (for embedding in Cargo.toml), found None");

    debug!("Creating generated plugin directory: {}", generated_plugin_directory.display());
    try!(fs::create_dir_all(&generated_plugin_directory));

    let mut created_plugins = Vec::new();

    debug!("Walking {}", source_plugin_directory.display());
    for dir_entry in try!(fs::read_dir(&source_plugin_directory)) {
        let plugin_path = try!(dir_entry).path();
        if plugin_path.file_name().unwrap() == "template.rs" {
            continue; // ignore template.rs!
        }
        let created = try!(create_plugin_crate(&plugin_path, &generated_plugin_directory,
            &core_path_str));
        created_plugins.push(created);
    }

    debug!("Creating main dependency crate.");

    {

        let cumulative_crate_dir = output_dir.join("cumulative-crate");

        // Create directory
        debug!("Creating cumulative plugin crate directory: {}", cumulative_crate_dir.display());
        try!(fs::create_dir_all(&cumulative_crate_dir));

        // Set of names which have already been used for importing
        // Because we take the last string in a plugin name split by `-`, this is needed
        // to prevent names like `thing1-cmds` and `thing2-cmds` to conflict, just as an example.
        let mut taken_import_names = collections::HashSet::new();
        // List of dependency lines to insert into Cargo.toml
        let mut dependency_lines = Vec::new();
        // List of `extern crate ...` lines
        let mut extern_crate_lines = Vec::new();
        // List of `xxx::register(register);` lines
        let mut register_lines = Vec::new();

        // We use the zaldinar name for zaldinar-core
        taken_import_names.insert("zaldinar".to_string());

        for (crate_name, path) in created_plugins {
            dependency_lines.push(format!("[dependencies.{}]", crate_name));
            dependency_lines.push(format!("path = \"{}\"", path.display()));

            let imported_name = { // This calculation could probably be done more efficiently.
                // any string.split() will at least have one element
                let name_without_additions = crate_name.split("-").next().unwrap();
                let mut temp_name = name_without_additions.to_string();
                // TODO: this could possibly overflow, some check for that?
                let mut addition = 0u32;
                while !taken_import_names.insert(temp_name.clone()) {
                    // Continue incrementing an integer added to the end of the name until the
                    // name is no longer taken.
                    temp_name = format!("{}{}", name_without_additions, addition);
                    addition = addition + 1;
                }
                temp_name
            };

            extern_crate_lines.push(format!("extern crate \"{}\" as {};",
                                            crate_name, imported_name));
            register_lines.push(format!("{}::register(register);", imported_name));
        }

        {
            let cargo_toml_contents = TOP_CARGO_TEMPLATE
                    .replace("{{dependency_lines}}", &dependency_lines.connect("\n"))
                    .replace("{{zaldinar_core_path}}", &core_path_str);

            let cargo_toml_path = cumulative_crate_dir.join("Cargo.toml");
            try!(write_file(&cargo_toml_path, &cargo_toml_contents));
        }

        {
            let lib_rs_contents = TOP_LIB_TEMPLATE
                    .replace("{{extern_crate_lines}}", &extern_crate_lines.connect("\n"))
                    .replace("{{register_lines}}", &register_lines.connect("\n"));

            let src_path = cumulative_crate_dir.join("src");
            debug!("Creating source directory {}", src_path.display());
            try!(fs::create_dir_all(&src_path));

            try!(write_file(&src_path.join("lib.rs"), &lib_rs_contents));
        }
    }

    return Ok(());
}

fn create_plugin_crate(path: &Path, output_dir: &Path, core_path_str: &str)
        -> io::Result<(String, PathBuf)> {
    debug!("Reading plugin source file: {}", path.display());

    let contents = {
        let mut stream = try!(fs::File::open(path));
        let mut buf = String::new();
        try!(stream.read_to_string(&mut buf));
        buf
    };

    let mut dependency_lines = Vec::new();

    for line in contents.lines_any() {
        let split_iter = line.split("//! depends: ");
        // If the line has `//! depends: `, .skip(1) will remove all content before the
        // `//! depends: `. If it doesn't, then .skip(1) will leave an empty iterator, and this if
        // let statement won't run due to .next() returning None.
        if let Some(dependency_line) = split_iter.skip(1).next() {
            dependency_lines.push(dependency_line);
        }
    }

    let name = {
        let name_os = path.file_stem().unwrap();
        let temp_name = name_os.to_str().expect("Expected UTF8 file name for plugin, found None");
        format!("zaldinar-plugin-{}", temp_name)
    };
    let output_directory = output_dir.join(&name);

    debug!("Creating plugin crate from {{source: {:?}, name: {:?}, dependencies: {:?}}} at {:?}",
        path.display(), name, dependency_lines, output_directory.display());

    // Create directory
    debug!("Creating crate directory: {}", output_directory.display());
    try!(fs::create_dir_all(&output_directory));

    { // Scope for writing Cargo.toml

        // TODO: This could probably be done more efficiently
        let dependencies_contents = dependency_lines.connect("\n");
        let cargo_contents = PLUGIN_CARGO_TEMPLATE.replace("{{name}}", &name)
                                .replace("{{dependencies}}", &dependencies_contents)
                                .replace("{{zaldinar_core_path}}", core_path_str);

        // Write to the file
        try!(write_file(&output_directory.join("Cargo.toml"), &cargo_contents));
    }

    { // Scope for writing lib.rs
        let src_path = output_directory.join("src");

        debug!("Creating source directory {}", src_path.display());
        try!(fs::create_dir_all(&src_path));

        // This is just writing the contents which were originally read from the plugin.rs file.
        try!(write_file(&src_path.join("lib.rs"), &contents));
    }

    return Ok((name, output_directory));
}

fn write_file(path: &Path, contents: &str) -> io::Result<()> {
    if try!(is_same(path, contents)) {
        debug!("Skipping writing to {}, as file contents are same", path.display());
        return Ok(());
    }

    // create a parent directory, if it has a parent
    if let Some(parent) = path.parent() {
        debug!("Creating directory {}", parent.display());
        try!(fs::create_dir_all(parent));
    }

    debug!("Writing to {}", path.display());
    let mut stream = try!(fs::File::create(path));
    try!(stream.write(contents.as_bytes()));

    return Ok(());
}

fn is_same(path: &Path, expected_contents: &str) -> io::Result<bool> {
    debug!("Checking differences for {}", path.display());
    let metadata = match fs::metadata(path) {
        Ok(v) => v,
        Err(e) => if e.kind() == io::ErrorKind::FileNotFound {
            return Ok(false); // File does not exist
        } else {
            return Err(e);
        }
    };
    if metadata.len() != expected_contents.len() as u64 {
        return Ok(false);
    }
    let file_stream = try!(fs::File::open(path));
    // this gets if any bytes in the file are different from bytes in the expected contents,
    // without holding the file in memory.
    for (read_byte, expected_byte) in file_stream.bytes().zip(expected_contents.bytes()) {
        if try!(read_byte) != expected_byte {
            return Ok(false);
        }
    }
    return Ok(true);
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ops::Add;
use libnum::{self, Zero, Float};

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
    aview0,
};

impl<A, S, D> ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    /// Return sum along `axis`.
    ///
    /// ```
    /// use ndarray::{aview0, aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.sum(Axis(0)) == aview1(&[4., 6.]) &&
    ///     a.sum(Axis(1)) == aview1(&[3., 7.]) &&
    ///
    ///     a.sum(Axis(0)).sum(Axis(0)) == aview0(&10.)
    /// );
    /// ```
    ///
    /// **Panics** if `axis` is out of bounds.
    pub fn sum(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: Clone + Zero + Add<Output=A>,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let mut res = self.subview(axis, 0).to_owned();
        for i in 1..n {
            let view = self.subview(axis, i);
            res = res + &view;
        }
        res
    }

    /// Return the sum of all elements in the array.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert_eq!(a.scalar_sum(), 10.);
    /// ```
    pub fn scalar_sum(&self) -> A
        where A: Clone + Add<Output=A> + libnum::Zero,
    {
        if let Some(slc) = self.as_slice_memory_order() {
            return numeric_util::unrolled_sum(slc);
        }
        let mut sum = A::zero();
        for row in self.inner_iter() {
            if let Some(slc) = row.as_slice() {
                sum = sum + numeric_util::unrolled_sum(slc);
            } else {
                sum = sum + row.iter().fold(A::zero(), |acc, elt| acc + elt.clone());
            }
        }
        sum
    }

    /// Return mean along `axis`.
    ///
    /// **Panics** if `axis` is out of bounds.
    ///
    /// ```
    /// use ndarray::{aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.mean(Axis(0)) == aview1(&[2.0, 3.0]) &&
    ///     a.mean(Axis(1)) == aview1(&[1.5, 3.5])
    /// );
    /// ```
    pub fn mean(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: LinalgScalar,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let sum = self.sum(axis);
        let mut cnt = A::one();
        for _ in 1..n {
            cnt = cnt + A::one();
        }
        sum / &aview0(&cnt)
    }

    /// Return `true` if the arrays' elementwise differences are all within
    /// the given absolute tolerance, `false` otherwise.
    ///
    /// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
    ///
    /// **Panics** if broadcasting to the same shape isn’t possible.
    pub fn all_close<S2, E>(&self, rhs: &ArrayBase<S2, E>, tol: A) -> bool
        where A: Float,
              S2: Data<Elem=A>,
              E: Dimension,
    {
        let rhs_broadcast = rhs.broadcast_unwrap(self.dim());
        self.iter().zip(rhs_broadcast.iter()).all(|(x, y)| (*x - *y).abs() <= tol)
    }

    #[cfg_attr(has_deprecated, deprecated(note=
      "Replaced by .all_close() which has clearer error cases"))]
    /// ***Deprecated: Replaced by .all_close()***
    ///
    /// Return `true` if the arrays' elementwise differences are all within
    /// the given absolute tolerance.<br>
    /// Return `false` otherwise, or if the shapes disagree.
    pub fn allclose<S2>(&self, rhs: &ArrayBase<S2, D>, tol: A) -> bool
        where A: Float,
              S2: Data<Elem=A>,
    {
        self.shape() == rhs.shape() &&
        self.iter().zip(rhs.iter()).all(|(x, y)| (*x - *y).abs() <= tol)
    }
}

<commit_msg>Remove deprecated .allclose (replaced by .all_close())<commit_after>// Copyright 2014-2016 bluss and ndarray developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ops::Add;
use libnum::{self, Zero, Float};

use imp_prelude::*;
use numeric_util;

use {
    LinalgScalar,
    aview0,
};

impl<A, S, D> ArrayBase<S, D>
    where S: Data<Elem=A>,
          D: Dimension,
{
    /// Return sum along `axis`.
    ///
    /// ```
    /// use ndarray::{aview0, aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.sum(Axis(0)) == aview1(&[4., 6.]) &&
    ///     a.sum(Axis(1)) == aview1(&[3., 7.]) &&
    ///
    ///     a.sum(Axis(0)).sum(Axis(0)) == aview0(&10.)
    /// );
    /// ```
    ///
    /// **Panics** if `axis` is out of bounds.
    pub fn sum(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: Clone + Zero + Add<Output=A>,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let mut res = self.subview(axis, 0).to_owned();
        for i in 1..n {
            let view = self.subview(axis, i);
            res = res + &view;
        }
        res
    }

    /// Return the sum of all elements in the array.
    ///
    /// ```
    /// use ndarray::arr2;
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert_eq!(a.scalar_sum(), 10.);
    /// ```
    pub fn scalar_sum(&self) -> A
        where A: Clone + Add<Output=A> + libnum::Zero,
    {
        if let Some(slc) = self.as_slice_memory_order() {
            return numeric_util::unrolled_sum(slc);
        }
        let mut sum = A::zero();
        for row in self.inner_iter() {
            if let Some(slc) = row.as_slice() {
                sum = sum + numeric_util::unrolled_sum(slc);
            } else {
                sum = sum + row.iter().fold(A::zero(), |acc, elt| acc + elt.clone());
            }
        }
        sum
    }

    /// Return mean along `axis`.
    ///
    /// **Panics** if `axis` is out of bounds.
    ///
    /// ```
    /// use ndarray::{aview1, arr2, Axis};
    ///
    /// let a = arr2(&[[1., 2.],
    ///                [3., 4.]]);
    /// assert!(
    ///     a.mean(Axis(0)) == aview1(&[2.0, 3.0]) &&
    ///     a.mean(Axis(1)) == aview1(&[1.5, 3.5])
    /// );
    /// ```
    pub fn mean(&self, axis: Axis) -> OwnedArray<A, <D as RemoveAxis>::Smaller>
        where A: LinalgScalar,
              D: RemoveAxis,
    {
        let n = self.shape().axis(axis);
        let sum = self.sum(axis);
        let mut cnt = A::one();
        for _ in 1..n {
            cnt = cnt + A::one();
        }
        sum / &aview0(&cnt)
    }

    /// Return `true` if the arrays' elementwise differences are all within
    /// the given absolute tolerance, `false` otherwise.
    ///
    /// If their shapes disagree, `rhs` is broadcast to the shape of `self`.
    ///
    /// **Panics** if broadcasting to the same shape isn’t possible.
    pub fn all_close<S2, E>(&self, rhs: &ArrayBase<S2, E>, tol: A) -> bool
        where A: Float,
              S2: Data<Elem=A>,
              E: Dimension,
    {
        let rhs_broadcast = rhs.broadcast_unwrap(self.dim());
        self.iter().zip(rhs_broadcast.iter()).all(|(x, y)| (*x - *y).abs() <= tol)
    }
}

<|endoftext|>


<commit_before>extern crate syscall;

use syscall::scheme::Scheme;

use std::{env, fs,thread};
use std::io::{stderr, Write};
use std::os::unix::process::CommandExt;
use std::path::Path;
use std::process::{self, Command};

use self::chroot::ChrootScheme;

mod chroot;

fn usage() -> ! {
    write!(stderr(), "contain root cmd args..\n").unwrap();
    process::exit(1);
}

fn enter(root: &Path, cmd: &str, args: &[String]) {
    let names = [
        "rand",
        "tcp",
        "udp"
    ];

    let mut name_ptrs = Vec::new();
    for name in names.iter() {
        name_ptrs.push([name.as_ptr() as usize, name.len()]);
    }

    let new_ns = syscall::mkns(&name_ptrs).unwrap();

    let root_canon = fs::canonicalize(root).unwrap();
    let root_thread = thread::spawn(move || {
        syscall::setrens(-1isize as usize, new_ns).unwrap();
        let scheme_fd = syscall::open(":file", syscall::O_CREAT | syscall::O_RDWR | syscall::O_CLOEXEC).unwrap();
        syscall::setrens(-1isize as usize, syscall::getns().unwrap()).unwrap();

        let chroot_scheme = ChrootScheme::new(root_canon);
        loop {
            let mut packet = syscall::Packet::default();
            if syscall::read(scheme_fd, &mut packet).unwrap() == 0 {
                break;
            }
            chroot_scheme.handle(&mut packet);
            syscall::write(scheme_fd, &packet).unwrap();
        }

        let _ = syscall::close(scheme_fd);
    });

    let pid = unsafe { syscall::clone(0).unwrap() };
    if pid == 0 {
        syscall::setrens(new_ns, new_ns).unwrap();

        println!("Container {}: enter: {}", new_ns, cmd);

        let mut command = Command::new(&cmd);
        for arg in args {
            command.arg(&arg);
        }
        command.current_dir("/");

        let err = command.exec();

        panic!("contain: failed to launch {}: {}", cmd, err);
    } else {
        let mut status = 0;
        syscall::waitpid(pid, &mut status, 0).unwrap();

        loop {
            let mut c_status = 0;
            let c_pid = syscall::waitpid(0, &mut c_status, syscall::WNOHANG).unwrap();
            if c_pid == 0 {
                break;
            } else {
                println!("Container zombie {}: {:X}", c_pid, c_status);
            }
        }

        println!("Container {}: exit: {:X}", new_ns, status);
    }
}

pub fn main() {
    let mut args = env::args().skip(1);

    if let Some(root) = args.next() {
        let cmd = args.next().unwrap_or(env::var("SHELL").unwrap_or("sh".to_string()));
        let args: Vec<String> = args.collect();
        enter(Path::new(&root), &cmd, &args);
    } else {
        usage();
    }
}
<commit_msg>Pass through more schemes - display and pty<commit_after>extern crate syscall;

use syscall::scheme::Scheme;

use std::{env, fs,thread};
use std::io::{stderr, Write};
use std::os::unix::process::CommandExt;
use std::path::Path;
use std::process::{self, Command};

use self::chroot::ChrootScheme;

mod chroot;

fn usage() -> ! {
    write!(stderr(), "contain root cmd args..\n").unwrap();
    process::exit(1);
}

fn enter(root: &Path, cmd: &str, args: &[String]) {
    let names = [
        "display",
        "pty",
        "rand",
        "tcp",
        "udp"
    ];

    let mut name_ptrs = Vec::new();
    for name in names.iter() {
        name_ptrs.push([name.as_ptr() as usize, name.len()]);
    }

    let new_ns = syscall::mkns(&name_ptrs).unwrap();

    let root_canon = fs::canonicalize(root).unwrap();
    let root_thread = thread::spawn(move || {
        syscall::setrens(-1isize as usize, new_ns).unwrap();
        let scheme_fd = syscall::open(":file", syscall::O_CREAT | syscall::O_RDWR | syscall::O_CLOEXEC).unwrap();
        syscall::setrens(-1isize as usize, syscall::getns().unwrap()).unwrap();

        let chroot_scheme = ChrootScheme::new(root_canon);
        loop {
            let mut packet = syscall::Packet::default();
            if syscall::read(scheme_fd, &mut packet).unwrap() == 0 {
                break;
            }
            chroot_scheme.handle(&mut packet);
            syscall::write(scheme_fd, &packet).unwrap();
        }

        let _ = syscall::close(scheme_fd);
    });

    let pid = unsafe { syscall::clone(0).unwrap() };
    if pid == 0 {
        syscall::setrens(new_ns, new_ns).unwrap();

        println!("Container {}: enter: {}", new_ns, cmd);

        let mut command = Command::new(&cmd);
        for arg in args {
            command.arg(&arg);
        }
        command.current_dir("/");

        let err = command.exec();

        panic!("contain: failed to launch {}: {}", cmd, err);
    } else {
        let mut status = 0;
        syscall::waitpid(pid, &mut status, 0).unwrap();

        loop {
            let mut c_status = 0;
            let c_pid = syscall::waitpid(0, &mut c_status, syscall::WNOHANG).unwrap();
            if c_pid == 0 {
                break;
            } else {
                println!("Container zombie {}: {:X}", c_pid, c_status);
            }
        }

        println!("Container {}: exit: {:X}", new_ns, status);
    }
}

pub fn main() {
    let mut args = env::args().skip(1);

    if let Some(root) = args.next() {
        let cmd = args.next().unwrap_or(env::var("SHELL").unwrap_or("sh".to_string()));
        let args: Vec<String> = args.collect();
        enter(Path::new(&root), &cmd, &args);
    } else {
        usage();
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::borrow::Cow;

use escape::Escaped;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<'a> {
    pub name: Cow<'a, str>,
    pub value: Cow<'a, str>,
}

impl<'a> Attribute<'a> {
    pub fn new<N, V>(name: N, value: V) -> Attribute<'a>
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        Attribute {
            name: name.into(),
            value: value.into(),
        }
    }
}

impl<'a> fmt::Display for Attribute<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}=\"{}\"", self.name.as_ref(), Escaped(&self.value))
    }
}

#[derive(Clone, Debug)]
/// Use `attr_set!` macro instead of manual construction
pub struct AttributeSet<'a>(pub Cow<'a, [Attribute<'a>]>);

impl<'a> fmt::Display for AttributeSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for attr in self.0.iter() {
            try!(write!(f, " {}", attr));
        }
        Ok(())
    }
}
<commit_msg>AttributeSet: get and set attributes<commit_after>use std::fmt;
use std::borrow::Cow;

use escape::Escaped;

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Attribute<'a> {
    pub name: Cow<'a, str>,
    pub value: Cow<'a, str>,
}

impl<'a> Attribute<'a> {
    pub fn new<N, V>(name: N, value: V) -> Attribute<'a>
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        Attribute {
            name: name.into(),
            value: value.into(),
        }
    }
}

impl<'a> fmt::Display for Attribute<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}=\"{}\"", self.name.as_ref(), Escaped(&self.value))
    }
}

#[derive(Clone, Debug)]
/// Prefer `attr_set!` macro over manual construction
pub struct AttributeSet<'a>(pub Cow<'a, [Attribute<'a>]>);

impl<'a> AttributeSet<'a> {
    fn search_name<S>(&self, name: S) -> Option<usize>
        where S: AsRef<str>
    {
        for (i, attr) in self.0.iter().enumerate() {
            if attr.name == name.as_ref() {
                return Some(i);
            }
        }
        None
    }

    pub fn get_attr<S>(&self, name: S) -> Option<&Attribute<'a>>
        where S: AsRef<str>
    {
        self.search_name(name).map(|index| &self.0.as_ref()[index])
    }

    pub fn get_attr_mut<S>(&mut self, name: S) -> Option<&mut Attribute<'a>>
        where S: AsRef<str>
    {
        self.search_name(name).map(move |index| &mut self.0.to_mut()[index])
    }

    pub fn set_attr<N, V>(&mut self, name: N, value: V)
        where N: Into<Cow<'a, str>>,
              V: Into<Cow<'a, str>>
    {
        let name = name.into();
        if let Some(index) = self.search_name(name.as_ref()) {
            self.0.to_mut()[index].value = value.into();
        } else {
            self.0.to_mut().push(Attribute::new(name, value));
        }
    }
}

impl<'a> fmt::Display for AttributeSet<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        for attr in self.0.iter() {
            try!(write!(f, " {}", attr));
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::fmt::Display;
use std::os::raw::c_int;

use ffi;
use stream::OutStream;

pub type SioResult<T> = Result<T, ffi::SioError>;

pub struct SoundIo {
    context: *mut ffi::SoundIo,
}
impl SoundIo {
    pub fn new() -> Self {
        SoundIo { context: unsafe { ffi::soundio_create() } }
    }

    pub fn channel_layout_builtin_count() -> i32 {
        let cnt = unsafe { ffi::soundio_channel_layout_builtin_count() };
        if cnt < 0 {
            panic!("Negative # of builtin channel layouts!")
        } else {
            cnt as i32
        }
    }

    pub fn connect(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_connect(self.context) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn connect_backend(&self, backend: ffi::SioBackend) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_connect_backend(self.context, backend) } {
            ffi::SioError::None => None,
            err @ _ => {
                println!("{:?}", err);
                Some(err)
            }
        }
    }

    pub fn backend_count(&self) -> i32 {
        let cnt = unsafe { ffi::soundio_backend_count(self.context) } as i32;
        if cnt < 0 {
            panic!("Negative backend count!");
        } else {
            cnt
        }
    }

    pub fn get_backend(&self, idx: i32) -> Option<ffi::SioBackend> {
        match unsafe { ffi::soundio_get_backend(self.context, idx) } {
            ffi::SioBackend::None => None,
            backend @ _ => Some(backend),
        }
    }

    pub fn current_backend(&self) -> Option<ffi::SioBackend> {
        match unsafe { (*self.context).current_backend } {
            ffi::SioBackend::None => None,
            backend @ _ => Some(backend),
        }
    }

    pub fn have_backend(&self, backend: ffi::SioBackend) -> bool {
        unsafe { ffi::soundio_have_backend(backend) == 1u8 }
    }

    pub fn flush_events(&self) {
        unsafe { ffi::soundio_flush_events(self.context) }
    }

    pub fn wait_events(&self) {
        unsafe { ffi::soundio_wait_events(self.context) }
    }

    pub fn wakeup(&self) {
        unsafe { ffi::soundio_wakeup(self.context) }
    }

    pub fn force_device_scan(&self) {
        unsafe { ffi::soundio_force_device_scan(self.context) }
    }

    pub fn disconnect(&self) {
        unsafe { ffi::soundio_disconnect(self.context) }
    }

    pub fn input_device_count(&self) -> Option<i32> {
        let cnt = unsafe { ffi::soundio_input_device_count(self.context) };
        if cnt < 0 {
            None
        } else {
            Some(cnt as i32)
        }
    }

    pub fn output_device_count(&self) -> Option<i32> {
        let cnt = unsafe { ffi::soundio_output_device_count(self.context) };
        if cnt < 0 {
            None
        } else {
            Some(cnt as i32)
        }
    }

    pub fn get_input_device(&self, idx: i32) -> Option<Device> {
        let dev_ptr = unsafe { ffi::soundio_get_input_device(self.context, idx) };
        if dev_ptr.is_null() {
            None
        } else {
            Some(Device::new(dev_ptr))
        }
    }

    pub fn get_output_device(&self, idx: i32) -> Option<Device> {
        let dev_ptr = unsafe { ffi::soundio_get_output_device(self.context, idx) };
        if dev_ptr.is_null() {
            None
        } else {
            Some(Device::new(dev_ptr))
        }
    }

    pub fn default_input_device_index(&self) -> Option<i32> {
        match unsafe { ffi::soundio_default_input_device_index(self.context) } {
            -1 => None,
            idx @ _ => Some(idx as i32),
        }
    }

    pub fn default_output_device_index(&self) -> Option<i32> {
        match unsafe { ffi::soundio_default_output_device_index(self.context) } {
            -1 => None,
            idx @ _ => Some(idx as i32),
        }
    }

    /// Returns the default output `Device` of the backend.
    /// `None` if you aren't connected to a backend.
    pub fn default_output_device(&self) -> Option<Device> {
        self.default_output_device_index().and_then(|idx| self.get_output_device(idx))
    }

    /// Returns the default input `Device` of the backend.
    /// `None` if you aren't connected to a backend.
    pub fn default_input_device(&self) -> Option<Device> {
        self.default_input_device_index().and_then(|idx| self.get_input_device(idx))
    }

    /// Sets the application name that is shown in the
    /// system audio mixer and returns `true` if it could
    /// be set.
    /// The `name` must **not** contain a `:` character.
    ///
    /// TODO: Return a `Result` instead of `bool`?
    pub fn set_app_name<T: Into<String>>(&self, name: T) -> bool {
        let s = name.into();
        if s.contains(":") {
            return false;
        } else {
            match CString::new(s) {
                Ok(cstr) => {
                    unsafe { (*self.context).app_name = cstr.as_ptr() };
                    true
                }
                Err(_) => false,
            }
        }
    }
}
impl Drop for SoundIo {
    fn drop(&mut self) {
        unsafe {
            self.disconnect();
            ffi::soundio_destroy(self.context)
        }
    }
}

#[derive(Debug)]
pub struct ChannelLayout {
    layout: *const ffi::SoundIoChannelLayout,
}
impl ChannelLayout {
    pub fn new(raw_layout: *const ffi::SoundIoChannelLayout) -> Self {
        ChannelLayout { layout: raw_layout }
    }

    pub fn get_builtin(idx: i32) -> Option<Self> {
        if 0 <= idx && idx < SoundIo::channel_layout_builtin_count() {
            Some(ChannelLayout::new(unsafe {
                ffi::soundio_channel_layout_get_builtin(idx as c_int)
            }))
        } else {
            None
        }
    }

    pub fn get_default(channel_count: i32) -> Option<Self> {
        if channel_count < 0 {
            None
        } else {
            Some(ChannelLayout::new(unsafe {
                ffi::soundio_channel_layout_get_default(channel_count as i32)
            }))
        }
    }

    pub fn find_channel(&self, channel: ffi::SioChannelId) -> Option<i32> {
        match unsafe { ffi::soundio_channel_layout_find_channel(self.layout, channel) } {
            -1 => None,
            idx @ _ => Some(idx),
        }
    }

    pub fn detect_builtin(&mut self) -> bool {
        // This is a hack because of the transmute.
        unsafe {
            let mut_layout: *mut ffi::SoundIoChannelLayout = ::std::mem::transmute(self.layout);
            ffi::soundio_channel_layout_detect_builtin(mut_layout) == 1
        }
    }

    pub fn best_matching_channel_layout(preferred_layouts: &[ChannelLayout],
                                        available_layouts: &[ChannelLayout])
                                        -> Option<ChannelLayout> {
        // do some magic with the slices
        let raw_preferred_layouts: Vec<_> = preferred_layouts.iter()
                                                             .map(|l| unsafe { (*l.layout) })
                                                             .collect();
        let raw_available_layouts: Vec<_> = available_layouts.iter()
                                                             .map(|l| unsafe { (*l.layout) })
                                                             .collect();
        let layout_ptr = unsafe {
            ffi::soundio_best_matching_channel_layout(raw_preferred_layouts.as_ptr(),
                                                      preferred_layouts.len() as c_int,
                                                      raw_available_layouts.as_ptr(),
                                                      available_layouts.len() as c_int)
        };
        if layout_ptr.is_null() {
            None
        } else {
            Some(ChannelLayout::new(layout_ptr))
        }
    }

    pub fn channel_count(&self) -> i32 {
        unsafe { (*self.layout).channel_count as i32 }
    }
}
impl PartialEq for ChannelLayout {
    fn eq(&self, other: &Self) -> bool {
        unsafe { ffi::soundio_channel_layout_equal(self.layout, other.layout) == 1u8 }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}
impl Display for ChannelLayout {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let str_ptr = unsafe { (*self.layout).name };
        write!(f, "{}", ffi::ptr_to_string(str_ptr).unwrap())
    }
}

#[derive(Debug)]
pub struct Device {
    device: *mut ffi::SoundIoDevice,
}
impl Device {
    pub fn new(dev_ptr: *mut ffi::SoundIoDevice) -> Self {
        Device { device: dev_ptr }
    }

    // ref is a keyword
    pub fn inc_ref(&self) {
        unsafe { ffi::soundio_device_ref(self.device) }
    }

    pub fn dec_ref(&self) {
        unsafe { ffi::soundio_device_unref(self.device) }
    }

    pub fn sort_channel_layouts(&self) {
        unsafe { ffi::soundio_device_sort_channel_layouts(self.device) }
    }

    pub fn supports_format(&self, format: ffi::SioFormat) -> bool {
        unsafe { ffi::soundio_device_supports_format(self.device, format) == 1u8 }
    }

    pub fn supports_layout(&self, layout: &ChannelLayout) -> bool {
        unsafe { ffi::soundio_device_supports_layout(self.device, layout.layout) == 1u8 }
    }

    pub fn supports_sample_rate(&self, sample_rate: i32) -> bool {
        unsafe {
            ffi::soundio_device_supports_sample_rate(self.device, sample_rate as c_int) == 1u8
        }
    }

    pub fn nearest_sample_rate(&self, sample_rate: i32) -> i32 {
        unsafe { ffi::soundio_device_nearest_sample_rate(self.device, sample_rate) as i32 }
    }

    pub fn create_outstream(&self) -> Option<OutStream> {
        let stream_ptr = unsafe { ffi::soundio_outstream_create(self.device) };
        if stream_ptr.is_null() {
            None
        } else {
            Some(OutStream::new(stream_ptr))
        }
    }

    pub fn ref_count(&self) -> i32 {
        unsafe { (*self.device).ref_count as i32 }
    }
}
impl Display for Device {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let str_ptr = unsafe { (*self.device).name };
        write!(f, "{}", ffi::ptr_to_string(str_ptr).unwrap())
    }
}
impl Drop for Device {
    fn drop(&mut self) {
        self.dec_ref()
    }
}
impl PartialEq for Device {
    fn eq(&self, other: &Self) -> bool {
        unsafe { ffi::soundio_device_equal(self.device, other.device) == 1u8 }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}
<commit_msg>Let `set_app_name` return a `Result`<commit_after>use std::fmt::Display;
use std::os::raw::c_int;
use std::ffi::{CString, NulError};

use ffi;
use stream::OutStream;

pub type SioResult<T> = Result<T, ffi::SioError>;

pub struct SoundIo {
    context: *mut ffi::SoundIo,
}
impl SoundIo {
    pub fn new() -> Self {
        SoundIo { context: unsafe { ffi::soundio_create() } }
    }

    pub fn channel_layout_builtin_count() -> i32 {
        let cnt = unsafe { ffi::soundio_channel_layout_builtin_count() };
        if cnt < 0 {
            panic!("Negative # of builtin channel layouts!")
        } else {
            cnt as i32
        }
    }

    pub fn connect(&self) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_connect(self.context) } {
            ffi::SioError::None => None,
            err @ _ => Some(err),
        }
    }

    pub fn connect_backend(&self, backend: ffi::SioBackend) -> Option<ffi::SioError> {
        match unsafe { ffi::soundio_connect_backend(self.context, backend) } {
            ffi::SioError::None => None,
            err @ _ => {
                println!("{:?}", err);
                Some(err)
            }
        }
    }

    pub fn backend_count(&self) -> i32 {
        let cnt = unsafe { ffi::soundio_backend_count(self.context) } as i32;
        if cnt < 0 {
            panic!("Negative backend count!");
        } else {
            cnt
        }
    }

    pub fn get_backend(&self, idx: i32) -> Option<ffi::SioBackend> {
        match unsafe { ffi::soundio_get_backend(self.context, idx) } {
            ffi::SioBackend::None => None,
            backend @ _ => Some(backend),
        }
    }

    pub fn current_backend(&self) -> Option<ffi::SioBackend> {
        match unsafe { (*self.context).current_backend } {
            ffi::SioBackend::None => None,
            backend @ _ => Some(backend),
        }
    }

    pub fn have_backend(&self, backend: ffi::SioBackend) -> bool {
        unsafe { ffi::soundio_have_backend(backend) == 1u8 }
    }

    pub fn flush_events(&self) {
        unsafe { ffi::soundio_flush_events(self.context) }
    }

    pub fn wait_events(&self) {
        unsafe { ffi::soundio_wait_events(self.context) }
    }

    pub fn wakeup(&self) {
        unsafe { ffi::soundio_wakeup(self.context) }
    }

    pub fn force_device_scan(&self) {
        unsafe { ffi::soundio_force_device_scan(self.context) }
    }

    pub fn disconnect(&self) {
        unsafe { ffi::soundio_disconnect(self.context) }
    }

    pub fn input_device_count(&self) -> Option<i32> {
        let cnt = unsafe { ffi::soundio_input_device_count(self.context) };
        if cnt < 0 {
            None
        } else {
            Some(cnt as i32)
        }
    }

    pub fn output_device_count(&self) -> Option<i32> {
        let cnt = unsafe { ffi::soundio_output_device_count(self.context) };
        if cnt < 0 {
            None
        } else {
            Some(cnt as i32)
        }
    }

    pub fn get_input_device(&self, idx: i32) -> Option<Device> {
        let dev_ptr = unsafe { ffi::soundio_get_input_device(self.context, idx) };
        if dev_ptr.is_null() {
            None
        } else {
            Some(Device::new(dev_ptr))
        }
    }

    pub fn get_output_device(&self, idx: i32) -> Option<Device> {
        let dev_ptr = unsafe { ffi::soundio_get_output_device(self.context, idx) };
        if dev_ptr.is_null() {
            None
        } else {
            Some(Device::new(dev_ptr))
        }
    }

    pub fn default_input_device_index(&self) -> Option<i32> {
        match unsafe { ffi::soundio_default_input_device_index(self.context) } {
            -1 => None,
            idx @ _ => Some(idx as i32),
        }
    }

    pub fn default_output_device_index(&self) -> Option<i32> {
        match unsafe { ffi::soundio_default_output_device_index(self.context) } {
            -1 => None,
            idx @ _ => Some(idx as i32),
        }
    }

    /// Returns the default output `Device` of the backend.
    /// `None` if you aren't connected to a backend.
    pub fn default_output_device(&self) -> Option<Device> {
        self.default_output_device_index().and_then(|idx| self.get_output_device(idx))
    }

    /// Returns the default input `Device` of the backend.
    /// `None` if you aren't connected to a backend.
    pub fn default_input_device(&self) -> Option<Device> {
        self.default_input_device_index().and_then(|idx| self.get_input_device(idx))
    }

    /// Sets the application name that is shown in the
    /// system audio mixer.
    /// If the `name` contains a null byte, a `NulError` is returned.
    /// The `:` characters in the `name` will be replaced by `_`.
    pub fn set_app_name<T: Into<String>>(&self, name: T) -> Result<(), NulError>{
        let s = name.into().s.replace(":", "_");
        let c_str = try!(CString::new(s));
        unsafe { (*self.context).app_name = c_str.as_ptr() };
        Ok(())
    }
}
impl Drop for SoundIo {
    fn drop(&mut self) {
        unsafe {
            self.disconnect();
            ffi::soundio_destroy(self.context)
        }
    }
}

#[derive(Debug)]
pub struct ChannelLayout {
    layout: *const ffi::SoundIoChannelLayout,
}
impl ChannelLayout {
    pub fn new(raw_layout: *const ffi::SoundIoChannelLayout) -> Self {
        ChannelLayout { layout: raw_layout }
    }

    pub fn get_builtin(idx: i32) -> Option<Self> {
        if 0 <= idx && idx < SoundIo::channel_layout_builtin_count() {
            Some(ChannelLayout::new(unsafe {
                ffi::soundio_channel_layout_get_builtin(idx as c_int)
            }))
        } else {
            None
        }
    }

    pub fn get_default(channel_count: i32) -> Option<Self> {
        if channel_count < 0 {
            None
        } else {
            Some(ChannelLayout::new(unsafe {
                ffi::soundio_channel_layout_get_default(channel_count as i32)
            }))
        }
    }

    pub fn find_channel(&self, channel: ffi::SioChannelId) -> Option<i32> {
        match unsafe { ffi::soundio_channel_layout_find_channel(self.layout, channel) } {
            -1 => None,
            idx @ _ => Some(idx),
        }
    }

    pub fn detect_builtin(&mut self) -> bool {
        // This is a hack because of the transmute.
        unsafe {
            let mut_layout: *mut ffi::SoundIoChannelLayout = ::std::mem::transmute(self.layout);
            ffi::soundio_channel_layout_detect_builtin(mut_layout) == 1
        }
    }

    pub fn best_matching_channel_layout(preferred_layouts: &[ChannelLayout],
                                        available_layouts: &[ChannelLayout])
                                        -> Option<ChannelLayout> {
        // do some magic with the slices
        let raw_preferred_layouts: Vec<_> = preferred_layouts.iter()
                                                             .map(|l| unsafe { (*l.layout) })
                                                             .collect();
        let raw_available_layouts: Vec<_> = available_layouts.iter()
                                                             .map(|l| unsafe { (*l.layout) })
                                                             .collect();
        let layout_ptr = unsafe {
            ffi::soundio_best_matching_channel_layout(raw_preferred_layouts.as_ptr(),
                                                      preferred_layouts.len() as c_int,
                                                      raw_available_layouts.as_ptr(),
                                                      available_layouts.len() as c_int)
        };
        if layout_ptr.is_null() {
            None
        } else {
            Some(ChannelLayout::new(layout_ptr))
        }
    }

    pub fn channel_count(&self) -> i32 {
        unsafe { (*self.layout).channel_count as i32 }
    }
}
impl PartialEq for ChannelLayout {
    fn eq(&self, other: &Self) -> bool {
        unsafe { ffi::soundio_channel_layout_equal(self.layout, other.layout) == 1u8 }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}
impl Display for ChannelLayout {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let str_ptr = unsafe { (*self.layout).name };
        write!(f, "{}", ffi::ptr_to_string(str_ptr).unwrap())
    }
}

#[derive(Debug)]
pub struct Device {
    device: *mut ffi::SoundIoDevice,
}
impl Device {
    pub fn new(dev_ptr: *mut ffi::SoundIoDevice) -> Self {
        Device { device: dev_ptr }
    }

    // ref is a keyword
    pub fn inc_ref(&self) {
        unsafe { ffi::soundio_device_ref(self.device) }
    }

    pub fn dec_ref(&self) {
        unsafe { ffi::soundio_device_unref(self.device) }
    }

    pub fn sort_channel_layouts(&self) {
        unsafe { ffi::soundio_device_sort_channel_layouts(self.device) }
    }

    pub fn supports_format(&self, format: ffi::SioFormat) -> bool {
        unsafe { ffi::soundio_device_supports_format(self.device, format) == 1u8 }
    }

    pub fn supports_layout(&self, layout: &ChannelLayout) -> bool {
        unsafe { ffi::soundio_device_supports_layout(self.device, layout.layout) == 1u8 }
    }

    pub fn supports_sample_rate(&self, sample_rate: i32) -> bool {
        unsafe {
            ffi::soundio_device_supports_sample_rate(self.device, sample_rate as c_int) == 1u8
        }
    }

    pub fn nearest_sample_rate(&self, sample_rate: i32) -> i32 {
        unsafe { ffi::soundio_device_nearest_sample_rate(self.device, sample_rate) as i32 }
    }

    pub fn create_outstream(&self) -> Option<OutStream> {
        let stream_ptr = unsafe { ffi::soundio_outstream_create(self.device) };
        if stream_ptr.is_null() {
            None
        } else {
            Some(OutStream::new(stream_ptr))
        }
    }

    pub fn ref_count(&self) -> i32 {
        unsafe { (*self.device).ref_count as i32 }
    }
}
impl Display for Device {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        let str_ptr = unsafe { (*self.device).name };
        write!(f, "{}", ffi::ptr_to_string(str_ptr).unwrap())
    }
}
impl Drop for Device {
    fn drop(&mut self) {
        self.dec_ref()
    }
}
impl PartialEq for Device {
    fn eq(&self, other: &Self) -> bool {
        unsafe { ffi::soundio_device_equal(self.device, other.device) == 1u8 }
    }

    fn ne(&self, other: &Self) -> bool {
        !self.eq(other)
    }
}
<|endoftext|>


<commit_before>use std::fmt;

pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

pub enum Value {
    None    =   0,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "D",
            Suit::Clubs     =>  "C",
            Suit::Hearts    =>  "H",
            Suit::Spades    =>  "S",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<commit_msg>Added debug and partialEq dervies for Value and Suit enums<commit_after>use std::fmt;

pub struct Card {
    pub value: Value,
    pub suit: Suit
}

impl fmt::Display for Card {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.value, self.suit)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Value {
    None    =   0,
    Two     =   2,
    Three   =   3,
    Four    =   4,
    Five    =   5,
    Six     =   6,
    Seven   =   7,
    Eight   =   8,
    Nine    =   9,
    Ten     =   10,
    Jack    =   11,
    Queen   =   12,
    King    =   13,
    Ace     =   14
}

impl fmt::Display for Value {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let value = match *self {
            Value::Two      =>  "2",
            Value::Three    =>  "3",
            Value::Four     =>  "4",
            Value::Five     =>  "5",
            Value::Six      =>  "6",
            Value::Seven    =>  "7",
            Value::Eight    =>  "8",
            Value::Nine     =>  "9",
            Value::Ten      =>  "T",
            Value::Jack     =>  "J",
            Value::Queen    =>  "Q",
            Value::King     =>  "K",
            Value::Ace      =>  "A",
            Value::None     =>  "-"
        };
        write!(f, "{}", value)
    }
}

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Suit {
    None,
    Diamonds,
    Clubs,
    Hearts,
    Spades
}

impl fmt::Display for Suit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let suit = match *self {
            Suit::Diamonds  =>  "D",
            Suit::Clubs     =>  "C",
            Suit::Hearts    =>  "H",
            Suit::Spades    =>  "S",
            Suit::None      =>  "-"
        };
        write!(f, "{}", suit)
    }
}
<|endoftext|>


<commit_before>//! Thread-safe connections on IrcStreams.
#![experimental]
use std::sync::{Mutex, MutexGuard};
use std::io::{BufferedStream, IoError, IoErrorKind, IoResult, MemWriter, TcpStream};
use encoding::{DecoderTrap, EncoderTrap, Encoding};
use encoding::label::encoding_from_whatwg_label;
use data::kinds::{IrcReader, IrcStream, IrcWriter};
use data::message::Message;
#[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslStream, Tlsv1};
#[cfg(feature = "ssl")] use openssl::ssl::error::SslError;

/// A thread-safe connection.
#[experimental]
pub struct Connection<T> where T: IrcStream {
    stream: Mutex<T>
}

impl Connection<BufferedStream<TcpStream>> {
    /// Creates a thread-safe TCP connection to the specified server.
    #[experimental]
    pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_internal(host, port, None)
    }

    /// Creates a thread-safe TCP connection to the specified server with a given timeout in
    /// milliseconds.
    #[experimental]
    pub fn connect_with_timeout(host: &str, port: u16, timeout_ms: u64)
        -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_internal(host, port, Some(timeout_ms))
    }

    /// Creates a thread-safe TCP connection with an optional timeout.
    #[experimental]
    fn connect_internal(host: &str, port: u16, timeout_ms: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        let mut socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
        socket.set_timeout(timeout_ms);
        Ok(Connection::new(BufferedStream::new(NetStream::UnsecuredTcpStream(socket))))
    }

    /// Creates a thread-safe TCP connection to the specified server over SSL.
    /// If the library is compiled without SSL support, this method panics.
    #[experimental]
    pub fn connect_ssl(host: &str, port: u16) -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_ssl_internal(host, port, None)
    }

    /// Creates a thread-safe TCP connection to the specificed server over SSL with a given timeout
    /// in milliseconds. If the library is compiled without SSL support, this method panics.
    #[experimental]
    pub fn connect_ssl_with_timeout(host: &str, port: u16, timeout_ms: u64)
        -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_ssl_internal(host, port, Some(timeout_ms))
    }

    /// Panics because SSL support was not included at compilation.
    #[experimental]
    #[cfg(not(feature = "ssl"))]
    fn connect_ssl_internal(host: &str, port: u16, _: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        panic!("Cannot connect to {}:{} over SSL without compiling with SSL support.", host, port)
    }

    /// Creates a thread-safe TCP connection over SSL with an optional timeout.
    #[experimental]
    #[cfg(feature = "ssl")]
    fn connect_ssl_internal(host: &str, port: u16, timeout_ms: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        let mut socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
        socket.set_timeout(timeout_ms);
        let ssl = try!(ssl_to_io(SslContext::new(Tlsv1)));
        let ssl_socket = try!(ssl_to_io(SslStream::new(&ssl, socket)));
        Ok(Connection::new(BufferedStream::new(NetStream::SslTcpStream(ssl_socket))))
    }
}

impl<T: IrcStream> Connection<T> {
    /// Creates a new connection from any arbitrary IrcStream.
    #[experimental]
    pub fn new(stream: T) -> Connection<T> {
        Connection {
            stream: Mutex::new(stream),
        }
    }

    /// Sends a Message over this connection.
    #[experimental]
    pub fn send(&self, message: Message, encoding: &str) -> IoResult<()> {
        let encoding = match encoding_from_whatwg_label(encoding) {
            Some(enc) => enc,
            None => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to find decoder.",
                detail: Some(format!("Invalid decoder: {}", encoding))
            })
        };
        let data = match encoding.encode(message.into_string()[], EncoderTrap::Strict) {
            Ok(data) => data,
            Err(data) => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to decode message.",
                detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
            })
        };
        let mut stream = self.stream.lock();
        try!(stream.write(data[]));
        stream.flush()
    }

    /// Receives a single line from this connection.
    #[experimental]
    pub fn recv(&self, encoding: &str) -> IoResult<String> {
        let encoding = match encoding_from_whatwg_label(encoding) {
            Some(enc) => enc,
            None => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to find decoder.",
                detail: Some(format!("Invalid decoder: {}", encoding))
            })
        };
        self.stream.lock().read_until(b'\n').and_then(|line|
            match encoding.decode(line[], DecoderTrap::Strict) {
                Ok(data) => Ok(data),
                Err(data) => Err(IoError {
                    kind: IoErrorKind::InvalidInput,
                    desc: "Failed to decode message.",
                    detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
                })
            }
        )
    }

    /// Acquires the Stream lock.
    #[experimental]
    pub fn stream<'a>(&'a self) -> MutexGuard<'a, T> {
        self.stream.lock()
    }
}

/// Converts a Result<T, SslError> into an IoResult<T>.
#[cfg(feature = "ssl")]
fn ssl_to_io<T>(res: Result<T, SslError>) -> IoResult<T> {
    match res {
        Ok(x) => Ok(x),
        Err(e) => Err(IoError {
            kind: OtherIoError,
            desc: "An SSL error occurred.",
            detail: Some(format!("{}", e)),
        }),
    }
}

/// An abstraction over different networked streams.
#[experimental]
pub enum NetStream {
    /// An unsecured TcpStream.
    UnsecuredTcpStream(TcpStream),
    /// An SSL-secured TcpStream.
    /// This is only available when compiled with SSL support.
    #[cfg(feature = "ssl")]
    SslTcpStream(SslStream<TcpStream>),
}

impl Reader for NetStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match self {
            &NetStream::UnsecuredTcpStream(ref mut stream) => stream.read(buf),
            #[cfg(feature = "ssl")]
            &NetStream::SslTcpStream(ref mut stream) => stream.read(buf),
        }
    }
}

impl Writer for NetStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self {
            &NetStream::UnsecuredTcpStream(ref mut stream) => stream.write(buf),
            #[cfg(feature = "ssl")]
            &NetStream::SslTcpStream(ref mut stream) => stream.write(buf),
        }
    }
}

/// An IrcStream built from an IrcWriter and an IrcReader.
#[experimental]
pub struct IoStream<T: IrcWriter, U: IrcReader> {
    writer: T,
    reader: U,
}

impl<T: IrcWriter, U: IrcReader> IoStream<T, U> {
    /// Creates a new IoStream from the given IrcWriter and IrcReader.
    #[experimental]
    pub fn new(writer: T, reader: U) -> IoStream<T, U> {
        IoStream { writer: writer, reader: reader }
    }
}

impl<U: IrcReader> IoStream<MemWriter, U> {
    pub fn value(&self) -> Vec<u8> {
        self.writer.get_ref().to_vec()
    }
}

impl<T: IrcWriter, U: IrcReader> Buffer for IoStream<T, U> {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        self.reader.fill_buf()
    }

    fn consume(&mut self, amt: uint) {
        self.reader.consume(amt)
    }
}

impl<T: IrcWriter, U: IrcReader> Reader for IoStream<T, U> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.reader.read(buf)
    }
}

impl<T: IrcWriter, U: IrcReader> Writer for IoStream<T, U> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.writer.write(buf)
    }
}

#[cfg(test)]
mod test {
    use super::{Connection, IoStream};
    use std::io::{MemReader, MemWriter};
    use std::io::util::{NullReader, NullWriter};
    use data::message::Message;
    use encoding::{DecoderTrap, Encoding};
    use encoding::all::ISO_8859_15;

    #[test]
    fn send_utf8() {
        let conn = Connection::new(IoStream::new(MemWriter::new(), NullReader));
        assert!(conn.send(
            Message::new(None, "PRIVMSG", Some(vec!["test"]), Some("€ŠšŽžŒœŸ")), "l9"
        ).is_ok());
        let data = ISO_8859_15.decode(conn.stream().value()[], DecoderTrap::Strict).unwrap();
        assert_eq!(data[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
    }

    #[test]
    fn send_iso885915() {

    }

    #[test]
    fn recv_utf8() {
        let conn = Connection::new(IoStream::new(
            NullWriter, MemReader::new(b"PRIVMSG test :Testing!\r\n".to_vec())
        ));
        assert_eq!(conn.recv("UTF-8").unwrap()[], "PRIVMSG test :Testing!\r\n");
    }

    #[test]
    fn recv_iso885915() {
        let conn = Connection::new(IoStream::new(
            NullWriter, MemReader::new({
                let mut vec = Vec::new();
                vec.push_all(b"PRIVMSG test :");
                vec.push_all(&[0xA4, 0xA6, 0xA8, 0xB4, 0xB8, 0xBC, 0xBD, 0xBE]);
                vec.push_all(b"\r\n");
                vec
            })
        ));
        assert_eq!(conn.recv("l9").unwrap()[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
    }
}
<commit_msg>Fixed SSL feature compilation.<commit_after>//! Thread-safe connections on IrcStreams.
#![experimental]
use std::sync::{Mutex, MutexGuard};
use std::io::{BufferedStream, IoError, IoErrorKind, IoResult, MemWriter, TcpStream};
use encoding::{DecoderTrap, EncoderTrap, Encoding};
use encoding::label::encoding_from_whatwg_label;
use data::kinds::{IrcReader, IrcStream, IrcWriter};
use data::message::Message;
#[cfg(feature = "ssl")] use openssl::ssl::{SslContext, SslStream, Tlsv1};
#[cfg(feature = "ssl")] use openssl::ssl::error::SslError;

/// A thread-safe connection.
#[experimental]
pub struct Connection<T> where T: IrcStream {
    stream: Mutex<T>
}

impl Connection<BufferedStream<TcpStream>> {
    /// Creates a thread-safe TCP connection to the specified server.
    #[experimental]
    pub fn connect(host: &str, port: u16) -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_internal(host, port, None)
    }

    /// Creates a thread-safe TCP connection to the specified server with a given timeout in
    /// milliseconds.
    #[experimental]
    pub fn connect_with_timeout(host: &str, port: u16, timeout_ms: u64)
        -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_internal(host, port, Some(timeout_ms))
    }

    /// Creates a thread-safe TCP connection with an optional timeout.
    #[experimental]
    fn connect_internal(host: &str, port: u16, timeout_ms: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        let mut socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
        socket.set_timeout(timeout_ms);
        Ok(Connection::new(BufferedStream::new(NetStream::UnsecuredTcpStream(socket))))
    }

    /// Creates a thread-safe TCP connection to the specified server over SSL.
    /// If the library is compiled without SSL support, this method panics.
    #[experimental]
    pub fn connect_ssl(host: &str, port: u16) -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_ssl_internal(host, port, None)
    }

    /// Creates a thread-safe TCP connection to the specificed server over SSL with a given timeout
    /// in milliseconds. If the library is compiled without SSL support, this method panics.
    #[experimental]
    pub fn connect_ssl_with_timeout(host: &str, port: u16, timeout_ms: u64)
        -> IoResult<Connection<BufferedStream<NetStream>>> {
        Connection::connect_ssl_internal(host, port, Some(timeout_ms))
    }

    /// Panics because SSL support was not included at compilation.
    #[experimental]
    #[cfg(not(feature = "ssl"))]
    fn connect_ssl_internal(host: &str, port: u16, _: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        panic!("Cannot connect to {}:{} over SSL without compiling with SSL support.", host, port)
    }

    /// Creates a thread-safe TCP connection over SSL with an optional timeout.
    #[experimental]
    #[cfg(feature = "ssl")]
    fn connect_ssl_internal(host: &str, port: u16, timeout_ms: Option<u64>)
    -> IoResult<Connection<BufferedStream<NetStream>>> {
        let mut socket = try!(TcpStream::connect(format!("{}:{}", host, port)[]));
        socket.set_timeout(timeout_ms);
        let ssl = try!(ssl_to_io(SslContext::new(Tlsv1)));
        let ssl_socket = try!(ssl_to_io(SslStream::new(&ssl, socket)));
        Ok(Connection::new(BufferedStream::new(NetStream::SslTcpStream(ssl_socket))))
    }
}

impl<T: IrcStream> Connection<T> {
    /// Creates a new connection from any arbitrary IrcStream.
    #[experimental]
    pub fn new(stream: T) -> Connection<T> {
        Connection {
            stream: Mutex::new(stream),
        }
    }

    /// Sends a Message over this connection.
    #[experimental]
    pub fn send(&self, message: Message, encoding: &str) -> IoResult<()> {
        let encoding = match encoding_from_whatwg_label(encoding) {
            Some(enc) => enc,
            None => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to find decoder.",
                detail: Some(format!("Invalid decoder: {}", encoding))
            })
        };
        let data = match encoding.encode(message.into_string()[], EncoderTrap::Strict) {
            Ok(data) => data,
            Err(data) => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to decode message.",
                detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
            })
        };
        let mut stream = self.stream.lock();
        try!(stream.write(data[]));
        stream.flush()
    }

    /// Receives a single line from this connection.
    #[experimental]
    pub fn recv(&self, encoding: &str) -> IoResult<String> {
        let encoding = match encoding_from_whatwg_label(encoding) {
            Some(enc) => enc,
            None => return Err(IoError {
                kind: IoErrorKind::InvalidInput,
                desc: "Failed to find decoder.",
                detail: Some(format!("Invalid decoder: {}", encoding))
            })
        };
        self.stream.lock().read_until(b'\n').and_then(|line|
            match encoding.decode(line[], DecoderTrap::Strict) {
                Ok(data) => Ok(data),
                Err(data) => Err(IoError {
                    kind: IoErrorKind::InvalidInput,
                    desc: "Failed to decode message.",
                    detail: Some(format!("Failed to decode {} as {}.", data, encoding.name())),
                })
            }
        )
    }

    /// Acquires the Stream lock.
    #[experimental]
    pub fn stream<'a>(&'a self) -> MutexGuard<'a, T> {
        self.stream.lock()
    }
}

/// Converts a Result<T, SslError> into an IoResult<T>.
#[cfg(feature = "ssl")]
fn ssl_to_io<T>(res: Result<T, SslError>) -> IoResult<T> {
    match res {
        Ok(x) => Ok(x),
        Err(e) => Err(IoError {
            kind: IoErrorKind::OtherIoError,
            desc: "An SSL error occurred.",
            detail: Some(format!("{}", e)),
        }),
    }
}

/// An abstraction over different networked streams.
#[experimental]
pub enum NetStream {
    /// An unsecured TcpStream.
    UnsecuredTcpStream(TcpStream),
    /// An SSL-secured TcpStream.
    /// This is only available when compiled with SSL support.
    #[cfg(feature = "ssl")]
    SslTcpStream(SslStream<TcpStream>),
}

impl Reader for NetStream {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match self {
            &NetStream::UnsecuredTcpStream(ref mut stream) => stream.read(buf),
            #[cfg(feature = "ssl")]
            &NetStream::SslTcpStream(ref mut stream) => stream.read(buf),
        }
    }
}

impl Writer for NetStream {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self {
            &NetStream::UnsecuredTcpStream(ref mut stream) => stream.write(buf),
            #[cfg(feature = "ssl")]
            &NetStream::SslTcpStream(ref mut stream) => stream.write(buf),
        }
    }
}

/// An IrcStream built from an IrcWriter and an IrcReader.
#[experimental]
pub struct IoStream<T: IrcWriter, U: IrcReader> {
    writer: T,
    reader: U,
}

impl<T: IrcWriter, U: IrcReader> IoStream<T, U> {
    /// Creates a new IoStream from the given IrcWriter and IrcReader.
    #[experimental]
    pub fn new(writer: T, reader: U) -> IoStream<T, U> {
        IoStream { writer: writer, reader: reader }
    }
}

impl<U: IrcReader> IoStream<MemWriter, U> {
    pub fn value(&self) -> Vec<u8> {
        self.writer.get_ref().to_vec()
    }
}

impl<T: IrcWriter, U: IrcReader> Buffer for IoStream<T, U> {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        self.reader.fill_buf()
    }

    fn consume(&mut self, amt: uint) {
        self.reader.consume(amt)
    }
}

impl<T: IrcWriter, U: IrcReader> Reader for IoStream<T, U> {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        self.reader.read(buf)
    }
}

impl<T: IrcWriter, U: IrcReader> Writer for IoStream<T, U> {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        self.writer.write(buf)
    }
}

#[cfg(test)]
mod test {
    use super::{Connection, IoStream};
    use std::io::{MemReader, MemWriter};
    use std::io::util::{NullReader, NullWriter};
    use data::message::Message;
    use encoding::{DecoderTrap, Encoding};
    use encoding::all::ISO_8859_15;

    #[test]
    fn send_utf8() {
        let conn = Connection::new(IoStream::new(MemWriter::new(), NullReader));
        assert!(conn.send(
            Message::new(None, "PRIVMSG", Some(vec!["test"]), Some("€ŠšŽžŒœŸ")), "l9"
        ).is_ok());
        let data = ISO_8859_15.decode(conn.stream().value()[], DecoderTrap::Strict).unwrap();
        assert_eq!(data[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
    }

    #[test]
    fn send_iso885915() {

    }

    #[test]
    fn recv_utf8() {
        let conn = Connection::new(IoStream::new(
            NullWriter, MemReader::new(b"PRIVMSG test :Testing!\r\n".to_vec())
        ));
        assert_eq!(conn.recv("UTF-8").unwrap()[], "PRIVMSG test :Testing!\r\n");
    }

    #[test]
    fn recv_iso885915() {
        let conn = Connection::new(IoStream::new(
            NullWriter, MemReader::new({
                let mut vec = Vec::new();
                vec.push_all(b"PRIVMSG test :");
                vec.push_all(&[0xA4, 0xA6, 0xA8, 0xB4, 0xB8, 0xBC, 0xBD, 0xBE]);
                vec.push_all(b"\r\n");
                vec
            })
        ));
        assert_eq!(conn.recv("l9").unwrap()[], "PRIVMSG test :€ŠšŽžŒœŸ\r\n");
    }
}
<|endoftext|>


<commit_before>use super::apu::SAMPLE_RATE;

// TODO: This should really be a generic parameter on RingBuffer,
// but rust does not currently have any facilities for this.
const BUFFER_SIZE: usize = SAMPLE_RATE * 2;

struct RingBuffer {
    left_buffer: [i16; BUFFER_SIZE],
    right_buffer: [i16; BUFFER_SIZE],
    write_pos: i32,
    read_pos: i32,
    sample_count: i32
}

impl RingBuffer {
    pub fn new() -> RingBuffer {
        RingBuffer {
            left_buffer: [0; BUFFER_SIZE],
            right_buffer: [0; BUFFER_SIZE],
            write_pos: 0,
            read_pos: 0,
            sample_count: 0
        }
    }
}
<commit_msg>that should cover RingBuffer<commit_after>use super::apu::SAMPLE_RATE;

// TODO: This should really be a generic parameter on RingBuffer,
// but rust does not currently have any facilities for this.
const BUFFER_SIZE: usize = SAMPLE_RATE * 2;

struct RingBuffer {
    left_buffer: [i16; BUFFER_SIZE],
    right_buffer: [i16; BUFFER_SIZE],
    write_pos: i32,
    read_pos: i32,
    sample_count: i32
}

impl RingBuffer {
    pub fn new() -> RingBuffer {
        RingBuffer {
            left_buffer: [0; BUFFER_SIZE],
            right_buffer: [0; BUFFER_SIZE],
            write_pos: 0,
            read_pos: 0,
            sample_count: 0
        }
    }

    pub fn set_write_pos(&mut self, pos: i32) {
        self.write_pos = pos % (BUFFER_SIZE as i32);
    }

    pub fn set_read_pos(&mut self, pos: i32) {
        self.read_pos = pos % (BUFFER_SIZE as i32);
    }

    pub fn write(&mut self, left: &[i16], right: &[i16], num_samples: i32) {
        for i in 0..num_samples {
            self.left_buffer[self.write_pos as usize] = left[i as usize];
            self.right_buffer[self.write_pos as usize] = right[i as usize];
            self.write_pos = (self.write_pos + 1) % (BUFFER_SIZE as i32);
        }
        self.sample_count += num_samples;
    }

    pub fn read(&mut self, left: &mut [i16], right: &mut [i16], num_samples: i32) {
        for i in 0..num_samples {
            left[i as usize] = self.left_buffer[self.read_pos as usize];
            right[i as usize] = self.right_buffer[self.read_pos as usize];
            self.read_pos = (self.read_pos + 1) % (BUFFER_SIZE as i32);
        }
        self.sample_count -= num_samples;
    }

    pub fn get_sample_count(&self) -> i32 {
        self.sample_count
    }
}
<|endoftext|>