<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/. */

//! Common [values][values] used in CSS.
//!
//! [values]: https://drafts.csswg.org/css-values/

#![deny(missing_docs)]

use Atom;
pub use cssparser::{RGBA, Token, Parser, serialize_identifier, BasicParseError, CompactCowStr};
use parser::{Parse, ParserContext};
use selectors::parser::SelectorParseError;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::fmt::{self, Debug};
use std::hash;
use style_traits::{ToCss, ParseError, StyleParseError};

pub mod animated;
pub mod computed;
pub mod generics;
pub mod specified;

/// A CSS float value.
pub type CSSFloat = f32;

/// A CSS integer value.
pub type CSSInteger = i32;

/// The default font size.
pub const FONT_MEDIUM_PX: i32 = 16;

define_keyword_type!(None_, "none");
define_keyword_type!(Auto, "auto");
define_keyword_type!(Normal, "normal");

/// Convenience void type to disable some properties and values through types.
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
pub enum Impossible {}

impl Parse for Impossible {
    fn parse<'i, 't>(
        _context: &ParserContext,
        _input: &mut Parser<'i, 't>)
    -> Result<Self, ParseError<'i>> {
        Err(StyleParseError::UnspecifiedError.into())
    }
}

/// A struct representing one of two kinds of values.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToCss)]
pub enum Either<A, B> {
    /// The first value.
    First(A),
    /// The second kind of value.
    Second(B),
}

impl<A: Debug, B: Debug> Debug for Either<A, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Either::First(ref v) => v.fmt(f),
            Either::Second(ref v) => v.fmt(f),
        }
    }
}

impl<A: Parse, B: Parse> Parse for Either<A, B> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Either<A, B>, ParseError<'i>> {
        if let Ok(v) = input.try(|i| A::parse(context, i)) {
            Ok(Either::First(v))
        } else {
            B::parse(context, input).map(Either::Second)
        }
    }
}

use self::computed::{Context, ToComputedValue};

impl<A: ToComputedValue, B: ToComputedValue> ToComputedValue for Either<A, B> {
    type ComputedValue = Either<A::ComputedValue, B::ComputedValue>;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Either::First(ref a) => Either::First(a.to_computed_value(context)),
            Either::Second(ref a) => Either::Second(a.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Either::First(ref a) => Either::First(ToComputedValue::from_computed_value(a)),
            Either::Second(ref a) => Either::Second(ToComputedValue::from_computed_value(a)),
        }
    }
}

/// https://drafts.csswg.org/css-values-4/#custom-idents
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct CustomIdent(pub Atom);

impl CustomIdent {
    /// Parse an already-tokenizer identifier
    pub fn from_ident<'i>(ident: CompactCowStr<'i>, excluding: &[&str]) -> Result<Self, ParseError<'i>> {
        let valid = match_ignore_ascii_case! { &ident,
            "initial" | "inherit" | "unset" | "default" => false,
            _ => true
        };
        if !valid {
            return Err(SelectorParseError::UnexpectedIdent(ident).into());
        }
        if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
            Err(StyleParseError::UnspecifiedError.into())
        } else {
            Ok(CustomIdent(Atom::from(Cow::from(ident))))
        }
    }
}

impl ToCss for CustomIdent {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        serialize_identifier(&self.0.to_string(), dest)
    }
}

/// https://drafts.csswg.org/css-animations/#typedef-keyframes-name
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesName {
    /// <custom-ident>
    Ident(CustomIdent),
    /// <string>
    QuotedString(Atom),
}

impl KeyframesName {
    /// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
    pub fn from_ident(value: String) -> Self {
        let custom_ident = CustomIdent::from_ident((&*value).into(), &["none"]).ok();
        match custom_ident {
            Some(ident) => KeyframesName::Ident(ident),
            None => KeyframesName::QuotedString(value.into()),
        }
    }

    /// The name as an Atom
    pub fn as_atom(&self) -> &Atom {
        match *self {
            KeyframesName::Ident(ref ident) => &ident.0,
            KeyframesName::QuotedString(ref atom) => atom,
        }
    }
}

impl Eq for KeyframesName {}

impl PartialEq for KeyframesName {
    fn eq(&self, other: &Self) -> bool {
        self.as_atom() == other.as_atom()
    }
}

impl hash::Hash for KeyframesName {
    fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
        self.as_atom().hash(state)
    }
}

impl Parse for KeyframesName {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        match input.next() {
            Ok(Token::Ident(s)) => Ok(KeyframesName::Ident(CustomIdent::from_ident(s, &["none"])?)),
            Ok(Token::QuotedString(s)) => Ok(KeyframesName::QuotedString(Atom::from(Cow::from(s)))),
            Ok(t) => Err(BasicParseError::UnexpectedToken(t).into()),
            Err(e) => Err(e.into()),
        }
    }
}

impl ToCss for KeyframesName {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            KeyframesName::Ident(ref ident) => ident.to_css(dest),
            KeyframesName::QuotedString(ref atom) => atom.to_string().to_css(dest),
        }
    }
}

// A type for possible values for min- and max- flavors of width,
// height, block-size, and inline-size.
define_css_keyword_enum!(ExtremumLength:
                         "-moz-max-content" => MaxContent,
                         "-moz-min-content" => MinContent,
                         "-moz-fit-content" => FitContent,
                         "-moz-available" => FillAvailable);
no_viewport_percentage!(ExtremumLength);
<commit_msg>Derive ToComputedValue for Either<A, B><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/. */

//! Common [values][values] used in CSS.
//!
//! [values]: https://drafts.csswg.org/css-values/

#![deny(missing_docs)]

use Atom;
pub use cssparser::{RGBA, Token, Parser, serialize_identifier, BasicParseError, CompactCowStr};
use parser::{Parse, ParserContext};
use selectors::parser::SelectorParseError;
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::fmt::{self, Debug};
use std::hash;
use style_traits::{ToCss, ParseError, StyleParseError};

pub mod animated;
pub mod computed;
pub mod generics;
pub mod specified;

/// A CSS float value.
pub type CSSFloat = f32;

/// A CSS integer value.
pub type CSSInteger = i32;

/// The default font size.
pub const FONT_MEDIUM_PX: i32 = 16;

define_keyword_type!(None_, "none");
define_keyword_type!(Auto, "auto");
define_keyword_type!(Normal, "normal");

/// Convenience void type to disable some properties and values through types.
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
#[derive(Clone, Copy, Debug, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
pub enum Impossible {}

impl Parse for Impossible {
    fn parse<'i, 't>(
        _context: &ParserContext,
        _input: &mut Parser<'i, 't>)
    -> Result<Self, ParseError<'i>> {
        Err(StyleParseError::UnspecifiedError.into())
    }
}

/// A struct representing one of two kinds of values.
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
#[derive(Clone, Copy, HasViewportPercentage, PartialEq, ToComputedValue, ToCss)]
pub enum Either<A, B> {
    /// The first value.
    First(A),
    /// The second kind of value.
    Second(B),
}

impl<A: Debug, B: Debug> Debug for Either<A, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Either::First(ref v) => v.fmt(f),
            Either::Second(ref v) => v.fmt(f),
        }
    }
}

impl<A: Parse, B: Parse> Parse for Either<A, B> {
    fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>)
                     -> Result<Either<A, B>, ParseError<'i>> {
        if let Ok(v) = input.try(|i| A::parse(context, i)) {
            Ok(Either::First(v))
        } else {
            B::parse(context, input).map(Either::Second)
        }
    }
}

/// https://drafts.csswg.org/css-values-4/#custom-idents
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub struct CustomIdent(pub Atom);

impl CustomIdent {
    /// Parse an already-tokenizer identifier
    pub fn from_ident<'i>(ident: CompactCowStr<'i>, excluding: &[&str]) -> Result<Self, ParseError<'i>> {
        let valid = match_ignore_ascii_case! { &ident,
            "initial" | "inherit" | "unset" | "default" => false,
            _ => true
        };
        if !valid {
            return Err(SelectorParseError::UnexpectedIdent(ident).into());
        }
        if excluding.iter().any(|s| ident.eq_ignore_ascii_case(s)) {
            Err(StyleParseError::UnspecifiedError.into())
        } else {
            Ok(CustomIdent(Atom::from(Cow::from(ident))))
        }
    }
}

impl ToCss for CustomIdent {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        serialize_identifier(&self.0.to_string(), dest)
    }
}

/// https://drafts.csswg.org/css-animations/#typedef-keyframes-name
#[derive(Debug, Clone)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum KeyframesName {
    /// <custom-ident>
    Ident(CustomIdent),
    /// <string>
    QuotedString(Atom),
}

impl KeyframesName {
    /// https://drafts.csswg.org/css-animations/#dom-csskeyframesrule-name
    pub fn from_ident(value: String) -> Self {
        let custom_ident = CustomIdent::from_ident((&*value).into(), &["none"]).ok();
        match custom_ident {
            Some(ident) => KeyframesName::Ident(ident),
            None => KeyframesName::QuotedString(value.into()),
        }
    }

    /// The name as an Atom
    pub fn as_atom(&self) -> &Atom {
        match *self {
            KeyframesName::Ident(ref ident) => &ident.0,
            KeyframesName::QuotedString(ref atom) => atom,
        }
    }
}

impl Eq for KeyframesName {}

impl PartialEq for KeyframesName {
    fn eq(&self, other: &Self) -> bool {
        self.as_atom() == other.as_atom()
    }
}

impl hash::Hash for KeyframesName {
    fn hash<H>(&self, state: &mut H) where H: hash::Hasher {
        self.as_atom().hash(state)
    }
}

impl Parse for KeyframesName {
    fn parse<'i, 't>(_context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
        match input.next() {
            Ok(Token::Ident(s)) => Ok(KeyframesName::Ident(CustomIdent::from_ident(s, &["none"])?)),
            Ok(Token::QuotedString(s)) => Ok(KeyframesName::QuotedString(Atom::from(Cow::from(s)))),
            Ok(t) => Err(BasicParseError::UnexpectedToken(t).into()),
            Err(e) => Err(e.into()),
        }
    }
}

impl ToCss for KeyframesName {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            KeyframesName::Ident(ref ident) => ident.to_css(dest),
            KeyframesName::QuotedString(ref atom) => atom.to_string().to_css(dest),
        }
    }
}

// A type for possible values for min- and max- flavors of width,
// height, block-size, and inline-size.
define_css_keyword_enum!(ExtremumLength:
                         "-moz-max-content" => MaxContent,
                         "-moz-min-content" => MinContent,
                         "-moz-fit-content" => FitContent,
                         "-moz-available" => FillAvailable);
no_viewport_percentage!(ExtremumLength);
<|endoftext|>


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

//! Common [values][values] used in CSS.
//!
//! [values]: https://drafts.csswg.org/css-values/

pub use cssparser::{RGBA, Parser};

use parser::Parse;
use std::fmt::{self, Debug};
use style_traits::ToCss;

macro_rules! define_numbered_css_keyword_enum {
    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
        define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
    };
    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
        #[allow(non_camel_case_types)]
        #[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Copy, RustcEncodable, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
        pub enum $name {
            $( $variant = $value ),+
        }

        impl $name {
            pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                match_ignore_ascii_case! { try!(input.expect_ident()),
                    $( $css => Ok($name::$variant), )+
                    _ => Err(())
                }
            }
        }

        impl ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
            where W: ::std::fmt::Write {
                match *self {
                    $( $name::$variant => dest.write_str($css) ),+
                }
            }
        }
    }
}

pub mod computed;
pub mod specified;

pub type CSSFloat = f32;

pub const FONT_MEDIUM_PX: i32 = 16;

pub trait HasViewportPercentage {
    fn has_viewport_percentage(&self) -> bool;
}

pub trait NoViewportPercentage {}

impl<T> HasViewportPercentage for T where T: NoViewportPercentage {
    fn has_viewport_percentage(&self) -> bool {
        false
    }
}

#[derive(Clone, PartialEq, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Either<A, B> {
    First(A),
    Second(B),
}

impl<A: Debug, B: Debug> Debug for Either<A, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Either::First(ref v) => v.fmt(f),
            Either::Second(ref v) => v.fmt(f),
        }
    }
}

impl<A: ToCss, B: ToCss> ToCss for Either<A, B> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Either::First(ref v) => v.to_css(dest),
            Either::Second(ref v) => v.to_css(dest),
        }
    }
}

impl<A: HasViewportPercentage, B: HasViewportPercentage> HasViewportPercentage for Either<A, B> {
    fn has_viewport_percentage(&self) -> bool {
        match *self {
            Either::First(ref v) => v.has_viewport_percentage(),
            Either::Second(ref v) => v.has_viewport_percentage(),
        }
    }
}

impl<A: Parse, B: Parse> Parse for Either<A, B> {
    fn parse(input: &mut Parser) -> Result<Either<A, B>, ()> {
        if let Ok(v) = input.try(|i| A::parse(i)) {
            Ok(Either::First(v))
        } else {
            B::parse(input).map(Either::Second)
        }
    }
}

use self::computed::{Context, ToComputedValue};

impl<A: ToComputedValue, B: ToComputedValue> ToComputedValue for Either<A, B> {
    type ComputedValue = Either<A::ComputedValue, B::ComputedValue>;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Either::First(ref a) => Either::First(a.to_computed_value(context)),
            Either::Second(ref a) => Either::Second(a.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Either::First(ref a) => Either::First(ToComputedValue::from_computed_value(a)),
            Either::Second(ref a) => Either::Second(ToComputedValue::from_computed_value(a)),
        }
    }
}
<commit_msg>Add unit structs for representing keywords: auto, none, normal<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/. */

//! Common [values][values] used in CSS.
//!
//! [values]: https://drafts.csswg.org/css-values/

pub use cssparser::{RGBA, Parser};

use parser::Parse;
use std::fmt::{self, Debug};
use style_traits::ToCss;

macro_rules! define_numbered_css_keyword_enum {
    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+,) => {
        define_numbered_css_keyword_enum!($name: $( $css => $variant = $value ),+);
    };
    ($name: ident: $( $css: expr => $variant: ident = $value: expr ),+) => {
        #[allow(non_camel_case_types)]
        #[derive(Clone, Eq, PartialEq, PartialOrd, Ord, Copy, RustcEncodable, Debug)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf, Deserialize, Serialize))]
        pub enum $name {
            $( $variant = $value ),+
        }

        impl $name {
            pub fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                match_ignore_ascii_case! { try!(input.expect_ident()),
                    $( $css => Ok($name::$variant), )+
                    _ => Err(())
                }
            }
        }

        impl ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result
            where W: ::std::fmt::Write {
                match *self {
                    $( $name::$variant => dest.write_str($css) ),+
                }
            }
        }
    }
}

pub mod computed;
pub mod specified;

pub type CSSFloat = f32;

pub const FONT_MEDIUM_PX: i32 = 16;

pub trait HasViewportPercentage {
    fn has_viewport_percentage(&self) -> bool;
}

pub trait NoViewportPercentage {}

impl<T> HasViewportPercentage for T where T: NoViewportPercentage {
    fn has_viewport_percentage(&self) -> bool {
        false
    }
}

use self::computed::ComputedValueAsSpecified;

macro_rules! define_keyword_type {
    ($name: ident, $css: expr) => {
        #[derive(Clone, PartialEq, Copy)]
        #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
        pub struct $name;

        impl ::style_traits::ToCss for $name {
            fn to_css<W>(&self, dest: &mut W) -> ::std::fmt::Result where W: ::std::fmt::Write {
                write!(dest, $css)
            }
        }

        impl fmt::Debug for $name {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, $css)
            }
        }

        impl Parse for $name {
            fn parse(input: &mut ::cssparser::Parser) -> Result<$name, ()> {
                input.expect_ident_matching($css).map(|_| $name)
            }
        }

        impl ComputedValueAsSpecified for $name {}
        impl NoViewportPercentage for $name {}
    };
}

define_keyword_type!(None_, "none");
define_keyword_type!(Auto, "auto");
define_keyword_type!(Normal, "normal");

#[derive(Clone, PartialEq, Copy)]
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
pub enum Either<A, B> {
    First(A),
    Second(B),
}

impl<A: Debug, B: Debug> Debug for Either<A, B> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Either::First(ref v) => v.fmt(f),
            Either::Second(ref v) => v.fmt(f),
        }
    }
}

impl<A: ToCss, B: ToCss> ToCss for Either<A, B> {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Either::First(ref v) => v.to_css(dest),
            Either::Second(ref v) => v.to_css(dest),
        }
    }
}

impl<A: HasViewportPercentage, B: HasViewportPercentage> HasViewportPercentage for Either<A, B> {
    fn has_viewport_percentage(&self) -> bool {
        match *self {
            Either::First(ref v) => v.has_viewport_percentage(),
            Either::Second(ref v) => v.has_viewport_percentage(),
        }
    }
}

impl<A: Parse, B: Parse> Parse for Either<A, B> {
    fn parse(input: &mut Parser) -> Result<Either<A, B>, ()> {
        if let Ok(v) = input.try(|i| A::parse(i)) {
            Ok(Either::First(v))
        } else {
            B::parse(input).map(Either::Second)
        }
    }
}

use self::computed::{Context, ToComputedValue};

impl<A: ToComputedValue, B: ToComputedValue> ToComputedValue for Either<A, B> {
    type ComputedValue = Either<A::ComputedValue, B::ComputedValue>;

    fn to_computed_value(&self, context: &Context) -> Self::ComputedValue {
        match *self {
            Either::First(ref a) => Either::First(a.to_computed_value(context)),
            Either::Second(ref a) => Either::Second(a.to_computed_value(context)),
        }
    }

    #[inline]
    fn from_computed_value(computed: &Self::ComputedValue) -> Self {
        match *computed {
            Either::First(ref a) => Either::First(ToComputedValue::from_computed_value(a)),
            Either::Second(ref a) => Either::Second(ToComputedValue::from_computed_value(a)),
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![deny(unused_imports)]
#![deny(unused_variables)]

extern crate png;
extern crate test;
extern crate regex;
extern crate url;

use std::ascii::AsciiExt;
use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::io::fs::PathExtensions;
use std::os;
use std::path::Path;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
)

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => panic!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let mut render_mode = match render_mode_string.as_slice() {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, all_tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [Path, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    experimental: bool,
    fragment_identifier: Option<String>,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => panic!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => panic!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };
        let base = file.dir_path();
        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut experimental = false;
        let mut fragment_identifier = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                "experimental" => experimental = true,
                _ => (),
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string());
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
        };

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut command = Command::new("target/servo");
    command
        .args(reftest.servo_args.as_slice())
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"])
        .arg(png_filename.as_slice())
        .arg({
            let mut url = Url::from_file_path(&reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

    let image = png::load_png(&from_str::<Path>(png_filename.as_slice()).unwrap()).unwrap();
    let rgba8_bytes = match image.pixels {
        png::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left_width,
            height: left_height,
            pixels: png::RGBA8(pixels),
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, reftest.is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => panic!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == Same);
    }
}
<commit_msg>auto merge of #4053 : mttr/servo/reftests_with_relative_dir, r=SimonSapin<commit_after>// Copyright 2013 The Servo Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution.
//
// 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.

#![deny(unused_imports)]
#![deny(unused_variables)]

extern crate png;
extern crate test;
extern crate regex;
extern crate url;

use std::ascii::AsciiExt;
use std::io;
use std::io::{File, Reader, Command};
use std::io::process::ExitStatus;
use std::io::fs::PathExtensions;
use std::os;
use std::path::Path;
use test::{AutoColor, DynTestName, DynTestFn, TestDesc, TestOpts, TestDescAndFn};
use test::run_tests_console;
use regex::Regex;
use url::Url;

bitflags!(
    flags RenderMode: u32 {
        const CPU_RENDERING  = 0x00000001,
        const GPU_RENDERING  = 0x00000010,
        const LINUX_TARGET   = 0x00000100,
        const MACOS_TARGET   = 0x00001000,
        const ANDROID_TARGET = 0x00010000
    }
)

fn main() {
    let args = os::args();
    let mut parts = args.tail().split(|e| "--" == e.as_slice());

    let harness_args = parts.next().unwrap();  // .split() is never empty
    let servo_args = parts.next().unwrap_or(&[]);

    let (render_mode_string, base_path, testname) = match harness_args {
        [] | [_] => panic!("USAGE: cpu|gpu base_path [testname regex]"),
        [ref render_mode_string, ref base_path] => (render_mode_string, base_path, None),
        [ref render_mode_string, ref base_path, ref testname, ..] => (render_mode_string, base_path, Some(Regex::new(testname.as_slice()).unwrap())),
    };

    let mut render_mode = match render_mode_string.as_slice() {
        "cpu" => CPU_RENDERING,
        "gpu" => GPU_RENDERING,
        _ => panic!("First argument must specify cpu or gpu as rendering mode")
    };
    if cfg!(target_os = "linux") {
        render_mode.insert(LINUX_TARGET);
    }
    if cfg!(target_os = "macos") {
        render_mode.insert(MACOS_TARGET);
    }
    if cfg!(target_os = "android") {
        render_mode.insert(ANDROID_TARGET);
    }

    let mut all_tests = vec!();
    println!("Scanning {} for manifests\n", base_path);

    for file in io::fs::walk_dir(&Path::new(base_path.as_slice())).unwrap() {
        let maybe_extension = file.extension_str();
        match maybe_extension {
            Some(extension) => {
                if extension.to_ascii_lower().as_slice() == "list" && file.is_file() {
                    let tests = parse_lists(&file, servo_args, render_mode, all_tests.len());
                    println!("\t{} [{} tests]", file.display(), tests.len());
                    all_tests.extend(tests.into_iter());
                }
            }
            _ => {}
        }
    }

    let test_opts = TestOpts {
        filter: testname,
        run_ignored: false,
        logfile: None,
        run_tests: true,
        run_benchmarks: false,
        ratchet_noise_percent: None,
        ratchet_metrics: None,
        save_metrics: None,
        test_shard: None,
        nocapture: false,
        color: AutoColor
    };

    match run_tests_console(&test_opts, all_tests) {
        Ok(false) => os::set_exit_status(1), // tests failed
        Err(_) => os::set_exit_status(2),    // I/O-related failure
        _ => (),
    }
}

#[deriving(PartialEq)]
enum ReftestKind {
    Same,
    Different,
}

struct Reftest {
    name: String,
    kind: ReftestKind,
    files: [Path, ..2],
    id: uint,
    servo_args: Vec<String>,
    render_mode: RenderMode,
    is_flaky: bool,
    experimental: bool,
    fragment_identifier: Option<String>,
}

struct TestLine<'a> {
    conditions: &'a str,
    kind: &'a str,
    file_left: &'a str,
    file_right: &'a str,
}

fn parse_lists(file: &Path, servo_args: &[String], render_mode: RenderMode, id_offset: uint) -> Vec<TestDescAndFn> {
    let mut tests = Vec::new();
    let contents = File::open_mode(file, io::Open, io::Read)
                       .and_then(|mut f| f.read_to_string())
                       .ok().expect("Could not read file");

    for line in contents.as_slice().lines() {
        // ignore comments or empty lines
        if line.starts_with("#") || line.is_empty() {
            continue;
        }

        let parts: Vec<&str> = line.split(' ').filter(|p| !p.is_empty()).collect();

        let test_line = match parts.len() {
            3 => TestLine {
                conditions: "",
                kind: parts[0],
                file_left: parts[1],
                file_right: parts[2],
            },
            4 => TestLine {
                conditions: parts[0],
                kind: parts[1],
                file_left: parts[2],
                file_right: parts[3],
            },
            _ => panic!("reftest line: '{:s}' doesn't match '[CONDITIONS] KIND LEFT RIGHT'", line),
        };

        let kind = match test_line.kind {
            "==" => Same,
            "!=" => Different,
            part => panic!("reftest line: '{:s}' has invalid kind '{:s}'", line, part)
        };

        // If we're running this directly, file.dir_path() might be relative.
        // (see issue #3521)
        let base = match file.dir_path().is_relative() {
            true  => os::getcwd().join(file.dir_path()),
            false => file.dir_path()
        };

        let file_left =  base.join(test_line.file_left);
        let file_right = base.join(test_line.file_right);

        let mut conditions_list = test_line.conditions.split(',');
        let mut flakiness = RenderMode::empty();
        let mut experimental = false;
        let mut fragment_identifier = None;
        for condition in conditions_list {
            match condition {
                "flaky_cpu" => flakiness.insert(CPU_RENDERING),
                "flaky_gpu" => flakiness.insert(GPU_RENDERING),
                "flaky_linux" => flakiness.insert(LINUX_TARGET),
                "flaky_macos" => flakiness.insert(MACOS_TARGET),
                "experimental" => experimental = true,
                _ => (),
            }
            if condition.starts_with("fragment=") {
                fragment_identifier = Some(condition.slice_from("fragment=".len()).to_string());
            }
        }

        let reftest = Reftest {
            name: format!("{} {} {}", test_line.file_left, test_line.kind, test_line.file_right),
            kind: kind,
            files: [file_left, file_right],
            id: id_offset + tests.len(),
            render_mode: render_mode,
            servo_args: servo_args.iter().map(|x| x.clone()).collect(),
            is_flaky: render_mode.intersects(flakiness),
            experimental: experimental,
            fragment_identifier: fragment_identifier,
        };

        tests.push(make_test(reftest));
    }
    tests
}

fn make_test(reftest: Reftest) -> TestDescAndFn {
    let name = reftest.name.clone();
    TestDescAndFn {
        desc: TestDesc {
            name: DynTestName(name),
            ignore: false,
            should_fail: false,
        },
        testfn: DynTestFn(proc() {
            check_reftest(reftest);
        }),
    }
}

fn capture(reftest: &Reftest, side: uint) -> (u32, u32, Vec<u8>) {
    let png_filename = format!("/tmp/servo-reftest-{:06u}-{:u}.png", reftest.id, side);
    let mut command = Command::new("target/servo");
    command
        .args(reftest.servo_args.as_slice())
        // Allows pixel perfect rendering of Ahem font for reftests.
        .arg("-Z")
        .arg("disable-text-aa")
        .args(["-f", "-o"])
        .arg(png_filename.as_slice())
        .arg({
            let mut url = Url::from_file_path(&reftest.files[side]).unwrap();
            url.fragment = reftest.fragment_identifier.clone();
            url.to_string()
        });
    // CPU rendering is the default
    if reftest.render_mode.contains(CPU_RENDERING) {
        command.arg("-c");
    }
    if reftest.render_mode.contains(GPU_RENDERING) {
        command.arg("-g");
    }
    if reftest.experimental {
        command.arg("--experimental");
    }
    let retval = match command.status() {
        Ok(status) => status,
        Err(e) => panic!("failed to execute process: {}", e),
    };
    assert_eq!(retval, ExitStatus(0));

    let image = png::load_png(&from_str::<Path>(png_filename.as_slice()).unwrap()).unwrap();
    let rgba8_bytes = match image.pixels {
        png::RGBA8(pixels) => pixels,
        _ => panic!(),
    };
    (image.width, image.height, rgba8_bytes)
}

fn check_reftest(reftest: Reftest) {
    let (left_width, left_height, left_bytes) = capture(&reftest, 0);
    let (right_width, right_height, right_bytes) = capture(&reftest, 1);

    assert_eq!(left_width, right_width);
    assert_eq!(left_height, right_height);

    let left_all_white = left_bytes.iter().all(|&p| p == 255);
    let right_all_white = right_bytes.iter().all(|&p| p == 255);

    if left_all_white && right_all_white {
        panic!("Both renderings are empty")
    }

    let pixels = left_bytes.iter().zip(right_bytes.iter()).map(|(&a, &b)| {
        if a as i8 - b as i8 == 0 {
            // White for correct
            0xFF
        } else {
            // "1100" in the RGBA channel with an error for an incorrect value
            // This results in some number of C0 and FFs, which is much more
            // readable (and distinguishable) than the previous difference-wise
            // scaling but does not require reconstructing the actual RGBA pixel.
            0xC0
        }
    }).collect::<Vec<u8>>();

    if pixels.iter().any(|&a| a < 255) {
        let output_str = format!("/tmp/servo-reftest-{:06u}-diff.png", reftest.id);
        let output = from_str::<Path>(output_str.as_slice()).unwrap();

        let mut img = png::Image {
            width: left_width,
            height: left_height,
            pixels: png::RGBA8(pixels),
        };
        let res = png::store_png(&mut img, &output);
        assert!(res.is_ok());

        match (reftest.kind, reftest.is_flaky) {
            (Same, true) => println!("flaky test - rendering difference: {}", output_str),
            (Same, false) => panic!("rendering difference: {}", output_str),
            (Different, _) => {}   // Result was different and that's what was expected
        }
    } else {
        assert!(reftest.is_flaky || reftest.kind == Same);
    }
}
<|endoftext|>


<commit_before>use index::lsmtree::placement::sm::client::SMClient as PlacementClient;
use index::lsmtree::placement::sm::{Placement as PlacementMeta, QueryError};
use index::lsmtree::service::AsyncServiceClient;
use index::trees::{EntryKey, KEY_SIZE};
use linked_hash_map::LinkedHashMap;
use parking_lot::RwLock;
use ram::types::Id;
use server::NebServer;
use std::collections::btree_map::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use std::sync::Arc;
use futures::prelude::*;
use index::lsmtree::split::placement_client;
use index::lsmtree::placement;
use index::builder::Feature;
use byteorder::{BigEndian, WriteBytesExt};

pub struct IndexEntry {
    id: Id,
    val: u64,
}

pub struct Cursor {
    buffer: Vec<IndexEntry>,
    at_end: bool,
    pos: usize,
}

pub struct Placement {
    meta: PlacementMeta,
    client: Arc<AsyncServiceClient>,
}

pub struct LSMTreeClient {
    counter: AtomicUsize,
    placements: RwLock<BTreeMap<Vec<u8>, Placement>>,
    cursors: LinkedHashMap<usize, Cursor>,
    placement_client: PlacementClient,
    neb: Arc<NebServer>,
}

pub struct SubTree {
    client: Arc<AsyncServiceClient>,
    epoch: u64,
    tree_id: Id
}

impl SubTree {
    pub fn new(tree_id: Id, client: Arc<AsyncServiceClient>, epoch: u64) -> Self {
        Self {
            tree_id, client, epoch
        }
    }
}

impl LSMTreeClient {
    fn inspect_placement(&self, id: &Id) {
        match self.placement_client.get(id).wait().unwrap() {
            Ok(placement) => {
                let mut placements = self.placements.write();
                let rpc_client = placement_client(&placement.id, &self.neb).wait().unwrap();
                placements.insert(placement.starts.clone(), Placement {
                    meta: placement,
                    client: rpc_client
                });
            },
            Err(QueryError::PlacementNotFound) => panic!("cannot find a suitable placement"),
            Err(e) => panic!("{:?}", e)
        }
    }

    fn get_sub_tree(&self, key: &Vec<u8>) -> SubTree {
        // Stage one, early exit if placement founded. Read lock only
        {
            let placements = self.placements.read();
            let candidate = placements.range::<Vec<u8>, _>(key..).next();
            if let Some((_, candidate_placement)) = candidate {
                if &candidate_placement.meta.ends >= key {
                    // in range, return
                    return SubTree::new(
                        candidate_placement.meta.id,
                        candidate_placement.client.clone(),
                        candidate_placement.meta.epoch
                    );
                }
            }
        }

        // Stage two, write lock. Also check for placement availability. If not, insert it to the cache.
        {
            let mut placements = self.placements.write();
            let candidate = placements.range::<Vec<u8>, _>(key..).next();
            if let Some((_, candidate_placement)) = candidate {
                if &candidate_placement.meta.ends >= key {
                    // in range, return
                    return SubTree::new(
                        candidate_placement.meta.id,
                        candidate_placement.client.clone(),
                        candidate_placement.meta.epoch
                    );;
                }
            }
            let placement: PlacementMeta = self.placement_client.locate(&Vec::from(key.as_slice()))
                .wait().unwrap().unwrap();
            let rpc_client = placement_client(&placement.id, &self.neb)
                .wait().unwrap();
            let tree_id = placement.id;
            let epoch = placement.epoch;
            placements.insert(placement.starts.clone(), Placement {
                meta: placement,
                client: rpc_client.clone()
            });
            SubTree::new(tree_id, rpc_client, epoch)
        }
    }

    fn essential_key_components(schema_id: u32, field_id: &u64) -> Vec<u8> {
        let mut key = Vec::with_capacity(KEY_SIZE);
        let mut schema_id_fut = [0u8; 4];
        let mut field_id_fut = [0u8; 8];
        (&mut schema_id_fut as &mut [u8]).write_u32::<BigEndian>(schema_id).unwrap();
        (&mut field_id_fut as &mut [u8]).write_u64::<BigEndian>(*field_id).unwrap();
        key.extend_from_slice(&schema_id_fut);  // 4 bytes
        key.extend_from_slice(&field_id_fut);   // 8 bytes
        key
    }

    pub fn insert(&self,schema_id: u32, field_id: &u64, cell_id: &Id, feature: &Feature) {
        let mut key = Self::essential_key_components(schema_id, field_id);
        key.extend_from_slice(feature); // 8 bytes
        key.extend_from_slice(&cell_id.to_binary()); // ID SIZE
        let sub_tree = self.get_sub_tree(&key);

    }
}
<commit_msg>attempted distributed tree insertion<commit_after>use index::lsmtree::placement::sm::client::SMClient as PlacementClient;
use index::lsmtree::placement::sm::{Placement as PlacementMeta, QueryError};
use index::lsmtree::service::{AsyncServiceClient, LSMTreeSvrError};
use index::trees::{EntryKey, KEY_SIZE};
use linked_hash_map::LinkedHashMap;
use parking_lot::RwLock;
use ram::types::Id;
use server::NebServer;
use std::collections::btree_map::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering::Relaxed};
use std::sync::Arc;
use futures::prelude::*;
use index::lsmtree::split::placement_client;
use index::lsmtree::placement;
use index::builder::Feature;
use byteorder::{BigEndian, WriteBytesExt};
use index::lsmtree::tree::LSMTreeResult;

pub struct IndexEntry {
    id: Id,
    val: u64,
}

pub struct Cursor {
    buffer: Vec<IndexEntry>,
    at_end: bool,
    pos: usize,
}

pub struct Placement {
    meta: PlacementMeta,
    client: Arc<AsyncServiceClient>,
}

pub struct LSMTreeClient {
    counter: AtomicUsize,
    placements: RwLock<BTreeMap<Vec<u8>, Placement>>,
    cursors: LinkedHashMap<usize, Cursor>,
    placement_client: PlacementClient,
    neb: Arc<NebServer>,
}

pub struct SubTree {
    client: Arc<AsyncServiceClient>,
    epoch: u64,
    tree_id: Id,
    starts: Vec<u8>
}

impl SubTree {
    pub fn new(tree_id: Id, client: Arc<AsyncServiceClient>, epoch: u64, starts: &Vec<u8>) -> Self {
        Self {
            tree_id, client, epoch, starts: starts.clone()
        }
    }
}

impl LSMTreeClient {
    fn update_placement(&self, sub_tree: &SubTree) {
        match self.placement_client.get(&sub_tree.tree_id).wait().unwrap() {
            Ok(placement) => {
                let mut placements = self.placements.write();
                let rpc_client = placement_client(&placement.id, &self.neb).wait().unwrap();
                placements.remove(&sub_tree.starts);
                placements.insert(placement.starts.clone(), Placement {
                    meta: placement,
                    client: rpc_client
                });
            },
            Err(QueryError::PlacementNotFound) => panic!("cannot find a suitable placement"),
            Err(e) => panic!("{:?}", e)
        }
    }

    fn get_sub_tree(&self, key: &Vec<u8>) -> SubTree {
        // Stage one, early exit if placement founded. Read lock only
        {
            let placements = self.placements.read();
            let candidate = placements.range::<Vec<u8>, _>(key..).next();
            if let Some((_, candidate_placement)) = candidate {
                if &candidate_placement.meta.ends >= key {
                    // in range, return
                    return SubTree::new(
                        candidate_placement.meta.id,
                        candidate_placement.client.clone(),
                        candidate_placement.meta.epoch,
                        &candidate_placement.meta.starts
                    );
                }
            }
        }

        // Stage two, write lock. Also check for placement availability. If not, insert it to the cache.
        {
            let mut placements = self.placements.write();
            let candidate = placements.range::<Vec<u8>, _>(key..).next();
            if let Some((_, candidate_placement)) = candidate {
                if &candidate_placement.meta.ends >= key {
                    // in range, return
                    return SubTree::new(
                        candidate_placement.meta.id,
                        candidate_placement.client.clone(),
                        candidate_placement.meta.epoch,
                        &candidate_placement.meta.starts
                    );
                }
            }
            let placement: PlacementMeta = self.placement_client.locate(&Vec::from(key.as_slice()))
                .wait().unwrap().unwrap();
            let rpc_client = placement_client(&placement.id, &self.neb)
                .wait().unwrap();
            let sub_tree = SubTree::new(
                placement.id,
                rpc_client.clone(),
                placement.epoch,
                &placement.starts
            );
            placements.insert(placement.starts.clone(), Placement {
                meta: placement,
                client: rpc_client
            });
            sub_tree
        }
    }

    fn essential_key_components(schema_id: u32, field_id: &u64) -> Vec<u8> {
        let mut key = Vec::with_capacity(KEY_SIZE);
        let mut schema_id_fut = [0u8; 4];
        let mut field_id_fut = [0u8; 8];
        (&mut schema_id_fut as &mut [u8]).write_u32::<BigEndian>(schema_id).unwrap();
        (&mut field_id_fut as &mut [u8]).write_u64::<BigEndian>(*field_id).unwrap();
        key.extend_from_slice(&schema_id_fut);  // 4 bytes
        key.extend_from_slice(&field_id_fut);   // 8 bytes
        key
    }

    pub fn insert(&self,schema_id: u32, field_id: &u64, cell_id: &Id, feature: &Feature) -> bool {
        let mut key = Self::essential_key_components(schema_id, field_id);
        key.extend_from_slice(feature); // 8 bytes
        key.extend_from_slice(&cell_id.to_binary()); // ID SIZE
        loop {
            let sub_tree = self.get_sub_tree(&key);
            let tree_client = &sub_tree.client;
            let insertion_result = tree_client
                    .insert(sub_tree.tree_id, key.clone(), sub_tree.epoch)
                    .wait().unwrap();
            match insertion_result {
                Ok(LSMTreeResult::Ok(insert_res)) => {
                    return insert_res;
                },
                Ok(LSMTreeResult::EpochMismatch(_, _)) | Err(LSMTreeSvrError::TreeNotFound) => {
                    self.update_placement(&sub_tree);
                },
                Err(_) => {
                    panic!("Error occurred on distributed LSM-tree insertion");
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/* A very simple front end to test agb-core (https://github.com/aHeraud/agb)*/

extern crate agb_core;
extern crate sdl2;
extern crate image;
extern crate clap;

mod debugger;

use std::sync::mpsc::sync_channel;
use std::thread;
use std::time::Duration;
use std::thread::sleep;
use std::collections::HashMap;
use std::fs::File;
use std::io::{stdin, stdout, Read, Write, Error};
use std::path::Path;
use std::num::ParseIntError;
use std::rc::Rc;
use std::cell::RefCell;
use std::ops::DerefMut;
use std::net::{TcpListener, TcpStream, SocketAddr, IpAddr, Ipv4Addr};

use agb_core::gameboy::Gameboy;
use agb_core::gameboy::debugger::DebuggerInterface;

use sdl2::pixels::Color;
use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use clap::{Arg, App};

const DEFAULT_SCALE: usize = 2;

fn main() {
	/* Create and initialize gameboy */

	//Parse command line arguments
	let matches = App::new("agb")
		.version("0.1")
		.author("Achille Heraud <achille@heraud.xyz>")
		.about("A GameBoy Emulator")
		.arg(Arg::with_name("rom")
			.long("rom")
			.takes_value(true)
			.value_name("FILE")
			.required(true))
		.arg(Arg::with_name("ram")
			.long("ram")
			.takes_value(true)
			.value_name("FILE")
			.required(false))
		.arg(Arg::with_name("paused")
			.long("pause")
			.short("p")
			.required(false))
		.arg(Arg::with_name("print_serial")
			.help("print data written to the serial port")
			.long("print_serial")
			.conflicts_with_all(&["listen", "connect"])
			.required(false))
		.arg(Arg::with_name("listen")
			.help("listen for remote connections (for serial communication over tcp/ip)")
			.long("listen")
			.takes_value(true)
			.value_name("PORT")
			.conflicts_with("connect")
			.required(false))
		.arg(Arg::with_name("connect")
			.help("provide to ip:port to connect to a remote host (for serial communication over tcp/ip")
			.long("connect")
			.takes_value(true)
			.value_name("IP:PORT")
			.required(false))
		.get_matches();

	let rom = read_file(matches.value_of("rom").unwrap()).expect("Could not open rom file.");
	let ram: Option<Box<[u8]>> = if let Some(ram_path) = matches.value_of("ram") {
		Some(read_file(ram_path).expect("failed to read ram file"))
	}
	else {
		None
	};

	let start_paused: bool = matches.occurrences_of("paused") > 0;

	let mut gameboy = agb_core::init(rom, ram).expect("Failed to initialize gameboy");
	let paused: Rc<RefCell<bool>> = Rc::new(RefCell::new(start_paused));
	gameboy.debugger.enable();
	{
		let paused = paused.clone();
		gameboy.register_breakpoint_callback(move |breakpoint| {
			println!("triggered breakpoint access_type: {:?}, address: 0x{:x}", breakpoint.access_type, breakpoint.address);
			*paused.borrow_mut() = true;
		});
	}

	if let Some(ref port_str) = matches.value_of("listen") {
		// set up a tcp socket to accept incoming connections
		if let Ok(port) = u16::from_str_radix(port_str, 10) {
			let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), port);
			let listener = TcpListener::bind(addr).unwrap();
			let (local_sender, local_receiver) = gameboy.create_serial_channels();

			println!("waiting for connection...");
			let (mut stream, remote_addr) = listener.accept().unwrap();
			println!("connected to {}", remote_addr);

			stream.set_nonblocking(false).expect("failed to set tcp socket to nonblocking mode");
			stream.set_nodelay(true).expect("failed to set tcp socket nodelay mode");

			//create sender thread
			{
				let mut stream = stream.try_clone().unwrap();
				thread::spawn(move || {
					let mut buffer: [u8; 1] = [0;1];
					loop {
						let data = local_receiver.recv().unwrap();
						buffer[0] = data;
						if stream.write(&buffer[..]).is_err() {
							return;
						}
					}
				});
			}

			//create receiver thread
			thread::spawn(move || {
				let mut buffer: [u8; 1] = [0;1];
				loop {
					if stream.read(&mut buffer[..]).is_err() {
						return;
					}
					local_sender.send(buffer[0]).unwrap();
				}
			});
		}
		else {
			println!("Invalid port: port must be an integer in the range 0 to 65535.");
			return;
		}
	}
	else if let Some(ref addr) = matches.value_of("connect") {
		let (local_sender, local_receiver) = gameboy.create_serial_channels();
		let addr: SocketAddr = addr.parse().unwrap();
		println!("attempting to connect...");
		let mut stream = TcpStream::connect(addr).unwrap();
		println!("connected!");

		stream.set_nonblocking(false).expect("failed to set tcp socket to nonblocking mode");
		stream.set_nodelay(true).expect("failed to set tcp socket nodelay mode");

		//create sender thread
		{
			let mut stream = stream.try_clone().unwrap();
			thread::spawn(move || {
				let mut buffer: [u8; 1] = [0;1];
				loop {
					let data = local_receiver.recv().unwrap();
					buffer[0] = data;
					if stream.write(&buffer[..]).is_err() {
						return;
					}
				}
			});
		}

		//create receiver thread
		thread::spawn(move || {
			let mut buffer: [u8; 1] = [0;1];
			loop {
				if stream.read(&mut buffer[..]).is_err() {
					return;
				}
				local_sender.send(buffer[0]).unwrap();
			}
		});
	}
	else if matches.occurrences_of("print_serial") > 0 {
		let (sender, reciever) = gameboy.create_serial_channels();
		thread::spawn(move || {
			loop {
				let data = reciever.recv().unwrap();
				sender.send(0xFF).unwrap();
				print!("{}", data as char);
				stdout().flush().ok().expect("could not flush stdout");
			}
		});
	}

	//debugger text input
	let (tx, rx) = sync_channel(0);
	let main_handle = thread::current();
	thread::spawn(move || {
		println!("debugger enabled - you can type debugger commands here! Type 'help' for more info.");
		loop {
			let mut buf: String = String::new();
			stdin().read_line(&mut buf).unwrap();
			tx.send(buf).unwrap();
			main_handle.unpark();
		}
	});

	//Keys
	let mut keymap: HashMap<Keycode, agb_core::gameboy::Key> = HashMap::new();
	keymap.insert(Keycode::Up, agb_core::gameboy::Key::Up);
	keymap.insert(Keycode::Down, agb_core::gameboy::Key::Down);
	keymap.insert(Keycode::Left, agb_core::gameboy::Key::Left);
	keymap.insert(Keycode::Right, agb_core::gameboy::Key::Right);
	keymap.insert(Keycode::X, agb_core::gameboy::Key::A);
	keymap.insert(Keycode::Z, agb_core::gameboy::Key::B);
	keymap.insert(Keycode::C, agb_core::gameboy::Key::Select);
	keymap.insert(Keycode::V, agb_core::gameboy::Key::Start);

	/* Setup for sdl */
	let sdl_context = sdl2::init().expect("Failed to initialize sdl2");
	let video_subsystem = sdl_context.video().expect("Failed to initialize sdl2 video subsystem");
	let timer_subsystem = sdl_context.timer().expect("Failed to initialize sdl2 timer subsystem");

	//Set resolution
	let width: u32 = (agb_core::WIDTH * DEFAULT_SCALE) as u32;
	let height: u32 = (agb_core::HEIGHT * DEFAULT_SCALE) as u32;

	let window = video_subsystem.window("agb", width, height)
		.position_centered()
		.opengl()
		.build()
		.expect("Failed to create window");

	let mut renderer = window.renderer().build().unwrap();
	let mut event_pump = sdl_context.event_pump().unwrap();
	let mut draw = |gameboy: &mut Gameboy| {
		renderer.set_draw_color(Color::RGB(80, 120, 120));
		renderer.clear();
		const WIDTH: usize = agb_core::WIDTH;
		const HEIGHT: usize = agb_core::HEIGHT;

		let data: &mut[u8] = unsafe {
			//#[allow(mutable_transmutes)]
			//std::mem::transmute::<&[u32], &mut[u8]>(gbc.get_framebuffer())
			let temp: &mut [u32] = agb_core::get_framebuffer_mut(gameboy);
			std::mem::transmute::<&mut[u32], &mut[u8]>(temp)
		};

		let mut texture = renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, WIDTH as u32, HEIGHT as u32).unwrap();
		let _ = texture.update(None, data, WIDTH * 4);
		let rect: Rect = renderer.viewport();
		let _ = renderer.copy(&texture, None, Some(rect));
		renderer.present();
	};

	//Get timer frequency
	let frequency: u64 = timer_subsystem.performance_frequency();

	'running: loop {
		//wait for input from the debugger, but don't wait forever since
		//we don't want the block the gui thread forever
		if *paused.borrow_mut() {
			std::thread::park_timeout(Duration::new(0, 100000000));
		}

		let frame_start: u64 = timer_subsystem.performance_counter();

		//handle debugger input
		if let Ok(input) = rx.try_recv() {
			if input.trim() == "quit" || input.trim() == "exit" {
				break 'running;
			}
			else {
				debugger::debug(input, &mut gameboy, paused.borrow_mut().deref_mut());
			}
		}

		for event in event_pump.poll_iter() {
			match event {
				Event::KeyDown { keycode, .. } => {
					if keycode.is_some() {
						let key = keymap.get(&keycode.unwrap());
						if key.is_some() {
							agb_core::keydown(&mut gameboy, *key.unwrap());
						}
					}
				},
				Event::KeyUp { keycode, .. } => {
					if keycode.is_some() {
						let key = keymap.get(&keycode.unwrap());
						if key.is_some() {
							agb_core::keyup(&mut gameboy, *key.unwrap());
						}
					}
				},
				Event::Quit {..} => {
					break 'running;
				},
				_ => {},
			};
		}

		if !*paused.borrow_mut() {
			gameboy.emulate(Duration::from_millis(1000 / 60));
			draw(&mut gameboy);

			//60hz
			let frame_end: u64 = timer_subsystem.performance_counter();
			let frame_duration: u64 = frame_end - frame_start;
			let ms: u64 = (frame_duration * 1000) / frequency;
			if ms < 1000/60 {
				let duration = Duration::from_millis((1000/60) - ms);
				sleep(duration);
			}
		}
	}
}

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>, Error> {
	let mut file = try!(File::open(path));
	let mut buffer = Vec::new();
	try!(file.read_to_end(&mut buffer));
	Ok(buffer.into_boxed_slice())
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
///from_str_radix isn't part of a trait so it can't be generic
pub fn parse_u8(s: &str) -> Result<u8, ParseIntError> {
	if s.starts_with("0x") {
		u8::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		u8::from_str_radix(&s[2..], 2)
	}
	else {
		u8::from_str_radix(s, 10)
	}
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
pub fn parse_u16(s: &str) -> Result<u16, ParseIntError> {
	if s.starts_with("0x") {
		u16::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		u16::from_str_radix(&s[2..], 2)
	}
	else {
		u16::from_str_radix(s, 10)
	}
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
pub fn parse_usize(s: &str) -> Result<usize, ParseIntError> {
	if s.starts_with("0x") {
		usize::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		usize::from_str_radix(&s[2..], 2)
	}
	else {
		usize::from_str_radix(s, 10)
	}
}
<commit_msg>Made the debugger callback +Send<commit_after>/* A very simple front end to test agb-core (https://github.com/aHeraud/agb)*/

extern crate agb_core;
extern crate sdl2;
extern crate image;
extern crate clap;

mod debugger;

use std::sync::mpsc::sync_channel;
use std::thread;
use std::time::Duration;
use std::thread::sleep;
use std::collections::HashMap;
use std::fs::File;
use std::io::{stdin, stdout, Read, Write, Error};
use std::path::Path;
use std::num::ParseIntError;
use std::sync::{Arc, Mutex};
use std::ops::DerefMut;
use std::net::{TcpListener, TcpStream, SocketAddr, IpAddr, Ipv4Addr};

use agb_core::gameboy::Gameboy;
use agb_core::gameboy::debugger::DebuggerInterface;

use sdl2::pixels::Color;
use sdl2::pixels::PixelFormatEnum;
use sdl2::rect::Rect;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;

use clap::{Arg, App};

const DEFAULT_SCALE: usize = 2;

fn main() {
	/* Create and initialize gameboy */

	//Parse command line arguments
	let matches = App::new("agb")
		.version("0.1")
		.author("Achille Heraud <achille@heraud.xyz>")
		.about("A GameBoy Emulator")
		.arg(Arg::with_name("rom")
			.long("rom")
			.takes_value(true)
			.value_name("FILE")
			.required(true))
		.arg(Arg::with_name("ram")
			.long("ram")
			.takes_value(true)
			.value_name("FILE")
			.required(false))
		.arg(Arg::with_name("paused")
			.long("pause")
			.short("p")
			.required(false))
		.arg(Arg::with_name("print_serial")
			.help("print data written to the serial port")
			.long("print_serial")
			.conflicts_with_all(&["listen", "connect"])
			.required(false))
		.arg(Arg::with_name("listen")
			.help("listen for remote connections (for serial communication over tcp/ip)")
			.long("listen")
			.takes_value(true)
			.value_name("PORT")
			.conflicts_with("connect")
			.required(false))
		.arg(Arg::with_name("connect")
			.help("provide to ip:port to connect to a remote host (for serial communication over tcp/ip")
			.long("connect")
			.takes_value(true)
			.value_name("IP:PORT")
			.required(false))
		.get_matches();

	let rom = read_file(matches.value_of("rom").unwrap()).expect("Could not open rom file.");
	let ram: Option<Box<[u8]>> = if let Some(ram_path) = matches.value_of("ram") {
		Some(read_file(ram_path).expect("failed to read ram file"))
	}
	else {
		None
	};

	let start_paused: bool = matches.occurrences_of("paused") > 0;

	let mut gameboy = agb_core::init(rom, ram).expect("Failed to initialize gameboy");
	let paused: Arc<Mutex<bool>> = Arc::new(Mutex::new(start_paused));
	gameboy.debugger.enable();
	{
		let paused = paused.clone();
		gameboy.register_breakpoint_callback(move |breakpoint| {
			println!("triggered breakpoint access_type: {:?}, address: 0x{:x}", breakpoint.access_type, breakpoint.address);
			let mut paused = paused.lock().unwrap();
			*paused = true;
		});
	}

	if let Some(ref port_str) = matches.value_of("listen") {
		// set up a tcp socket to accept incoming connections
		if let Ok(port) = u16::from_str_radix(port_str, 10) {
			let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127,0,0,1)), port);
			let listener = TcpListener::bind(addr).unwrap();
			let (local_sender, local_receiver) = gameboy.create_serial_channels();

			println!("waiting for connection...");
			let (mut stream, remote_addr) = listener.accept().unwrap();
			println!("connected to {}", remote_addr);

			stream.set_nonblocking(false).expect("failed to set tcp socket to nonblocking mode");
			stream.set_nodelay(true).expect("failed to set tcp socket nodelay mode");

			//create sender thread
			{
				let mut stream = stream.try_clone().unwrap();
				thread::spawn(move || {
					let mut buffer: [u8; 1] = [0;1];
					loop {
						let data = local_receiver.recv().unwrap();
						buffer[0] = data;
						if stream.write(&buffer[..]).is_err() {
							return;
						}
					}
				});
			}

			//create receiver thread
			thread::spawn(move || {
				let mut buffer: [u8; 1] = [0;1];
				loop {
					if stream.read(&mut buffer[..]).is_err() {
						return;
					}
					local_sender.send(buffer[0]).unwrap();
				}
			});
		}
		else {
			println!("Invalid port: port must be an integer in the range 0 to 65535.");
			return;
		}
	}
	else if let Some(ref addr) = matches.value_of("connect") {
		let (local_sender, local_receiver) = gameboy.create_serial_channels();
		let addr: SocketAddr = addr.parse().unwrap();
		println!("attempting to connect...");
		let mut stream = TcpStream::connect(addr).unwrap();
		println!("connected!");

		stream.set_nonblocking(false).expect("failed to set tcp socket to nonblocking mode");
		stream.set_nodelay(true).expect("failed to set tcp socket nodelay mode");

		//create sender thread
		{
			let mut stream = stream.try_clone().unwrap();
			thread::spawn(move || {
				let mut buffer: [u8; 1] = [0;1];
				loop {
					let data = local_receiver.recv().unwrap();
					buffer[0] = data;
					if stream.write(&buffer[..]).is_err() {
						return;
					}
				}
			});
		}

		//create receiver thread
		thread::spawn(move || {
			let mut buffer: [u8; 1] = [0;1];
			loop {
				if stream.read(&mut buffer[..]).is_err() {
					return;
				}
				local_sender.send(buffer[0]).unwrap();
			}
		});
	}
	else if matches.occurrences_of("print_serial") > 0 {
		let (sender, reciever) = gameboy.create_serial_channels();
		thread::spawn(move || {
			loop {
				let data = reciever.recv().unwrap();
				sender.send(0xFF).unwrap();
				print!("{}", data as char);
				stdout().flush().ok().expect("could not flush stdout");
			}
		});
	}

	//debugger text input
	let (tx, rx) = sync_channel(0);
	let main_handle = thread::current();
	thread::spawn(move || {
		println!("debugger enabled - you can type debugger commands here! Type 'help' for more info.");
		loop {
			let mut buf: String = String::new();
			stdin().read_line(&mut buf).unwrap();
			tx.send(buf).unwrap();
			main_handle.unpark();
		}
	});

	//Keys
	let mut keymap: HashMap<Keycode, agb_core::gameboy::Key> = HashMap::new();
	keymap.insert(Keycode::Up, agb_core::gameboy::Key::Up);
	keymap.insert(Keycode::Down, agb_core::gameboy::Key::Down);
	keymap.insert(Keycode::Left, agb_core::gameboy::Key::Left);
	keymap.insert(Keycode::Right, agb_core::gameboy::Key::Right);
	keymap.insert(Keycode::X, agb_core::gameboy::Key::A);
	keymap.insert(Keycode::Z, agb_core::gameboy::Key::B);
	keymap.insert(Keycode::C, agb_core::gameboy::Key::Select);
	keymap.insert(Keycode::V, agb_core::gameboy::Key::Start);

	/* Setup for sdl */
	let sdl_context = sdl2::init().expect("Failed to initialize sdl2");
	let video_subsystem = sdl_context.video().expect("Failed to initialize sdl2 video subsystem");
	let timer_subsystem = sdl_context.timer().expect("Failed to initialize sdl2 timer subsystem");

	//Set resolution
	let width: u32 = (agb_core::WIDTH * DEFAULT_SCALE) as u32;
	let height: u32 = (agb_core::HEIGHT * DEFAULT_SCALE) as u32;

	let window = video_subsystem.window("agb", width, height)
		.position_centered()
		.opengl()
		.build()
		.expect("Failed to create window");

	let mut renderer = window.renderer().build().unwrap();
	let mut event_pump = sdl_context.event_pump().unwrap();
	let mut draw = |gameboy: &mut Gameboy| {
		renderer.set_draw_color(Color::RGB(80, 120, 120));
		renderer.clear();
		const WIDTH: usize = agb_core::WIDTH;
		const HEIGHT: usize = agb_core::HEIGHT;

		let data: &mut[u8] = unsafe {
			//#[allow(mutable_transmutes)]
			//std::mem::transmute::<&[u32], &mut[u8]>(gbc.get_framebuffer())
			let temp: &mut [u32] = agb_core::get_framebuffer_mut(gameboy);
			std::mem::transmute::<&mut[u32], &mut[u8]>(temp)
		};

		let mut texture = renderer.create_texture_streaming(PixelFormatEnum::RGBA8888, WIDTH as u32, HEIGHT as u32).unwrap();
		let _ = texture.update(None, data, WIDTH * 4);
		let rect: Rect = renderer.viewport();
		let _ = renderer.copy(&texture, None, Some(rect));
		renderer.present();
	};

	//Get timer frequency
	let frequency: u64 = timer_subsystem.performance_frequency();

	'running: loop {
		//wait for input from the debugger, but don't wait forever since
		//we don't want the block the gui thread forever
		{
			let paused = paused.lock().unwrap();
			if *paused {
				std::thread::park_timeout(Duration::new(0, 100000000));
			}
		}

		let frame_start: u64 = timer_subsystem.performance_counter();

		//handle debugger input
		if let Ok(input) = rx.try_recv() {
			if input.trim() == "quit" || input.trim() == "exit" {
				break 'running;
			}
			else {
				let mut paused = paused.lock().unwrap();
				debugger::debug(input, &mut gameboy, paused.deref_mut());
			}
		}

		for event in event_pump.poll_iter() {
			match event {
				Event::KeyDown { keycode, .. } => {
					if keycode.is_some() {
						let key = keymap.get(&keycode.unwrap());
						if key.is_some() {
							agb_core::keydown(&mut gameboy, *key.unwrap());
						}
					}
				},
				Event::KeyUp { keycode, .. } => {
					if keycode.is_some() {
						let key = keymap.get(&keycode.unwrap());
						if key.is_some() {
							agb_core::keyup(&mut gameboy, *key.unwrap());
						}
					}
				},
				Event::Quit {..} => {
					break 'running;
				},
				_ => {},
			};
		}

		{
			let paused = paused.lock().unwrap();
			if !*paused {
				gameboy.emulate(Duration::from_millis(1000 / 60));
				draw(&mut gameboy);

				//60hz
				let frame_end: u64 = timer_subsystem.performance_counter();
				let frame_duration: u64 = frame_end - frame_start;
				let ms: u64 = (frame_duration * 1000) / frequency;
				if ms < 1000/60 {
					let duration = Duration::from_millis((1000/60) - ms);
					sleep(duration);
				}
			}
		}
	}
}

pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Box<[u8]>, Error> {
	let mut file = try!(File::open(path));
	let mut buffer = Vec::new();
	try!(file.read_to_end(&mut buffer));
	Ok(buffer.into_boxed_slice())
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
///from_str_radix isn't part of a trait so it can't be generic
pub fn parse_u8(s: &str) -> Result<u8, ParseIntError> {
	if s.starts_with("0x") {
		u8::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		u8::from_str_radix(&s[2..], 2)
	}
	else {
		u8::from_str_radix(s, 10)
	}
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
pub fn parse_u16(s: &str) -> Result<u16, ParseIntError> {
	if s.starts_with("0x") {
		u16::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		u16::from_str_radix(&s[2..], 2)
	}
	else {
		u16::from_str_radix(s, 10)
	}
}

///accepts prefixes (none for base 10, 0x for hex, 0b for binary)
pub fn parse_usize(s: &str) -> Result<usize, ParseIntError> {
	if s.starts_with("0x") {
		usize::from_str_radix(&s[2..], 16)
	}
	else if s.starts_with("0b") {
		usize::from_str_radix(&s[2..], 2)
	}
	else {
		usize::from_str_radix(s, 10)
	}
}
<|endoftext|>


<commit_before>use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

/// The `terminal` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Terminal {
	/// Encoding used for unibyte strings. This is better left at default, as Rust uses UTF-8 for everything.
	///
	/// Default: `"utf8"`
	pub encoding: Option<String>, //TODO: use an enum/validated struct?
}

impl Terminal {
	/// Construct a new `terminal` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override
	/// segment
	///
	/// `None` values will not override current ones.
	pub fn new(encoding: Option<String>) -> Terminal {
		Terminal{
			encoding: encoding,
		}
	}
}

impl ConfigPart for Terminal {
	fn to_config_str(&self) -> String {
		match self.encoding {
			Some(ref encoding) => format!("terminal.encoding={};", escape_config_string(&encoding)),
			None               => "".to_string(),
		}
	}
}

/// The `window` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Window {
	/// Window size in cells.
	///
	/// Default: `80x25`
	pub size: Option<Size>,
	/// Size of all cells, in pixels.
	///
	/// Default: [`Cellsize::Auto`](enum.Cellsize.html#variant.Auto)
	pub cellsize: Option<Cellsize>,
	/// The terminal window's title.
	///
	/// Default: `"BearLibTerminal"`
	pub title: Option<String>,
	/// The path of the icon used for the terminal window.
	///
	/// Default: none
	pub icon: Option<String>,
	/// Whether the terminal window should be resizeable.
	///
	/// Default: `false`
	pub resizeable: Option<bool>,
	/// Whether to enforce fullscreen mode.
	///
	/// Default: `false`
	pub fullscreen: Option<bool>,
}

impl Window {
	/// Construct a new `window` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	///
	/// `None` values will not override current ones.
	pub fn new<T: AsRef<Path>>(size: Option<Size>, cellsize: Option<Cellsize>, title: Option<String>, icon: Option<T>, resizeable: Option<bool>,
	                           fullscreen: Option<bool>) -> Window {
		Window{
			size: size,
			cellsize: cellsize,
			title: title,
			icon: icon.map(|s| s.as_ref().to_str().unwrap().to_string()),
			resizeable: resizeable,
			fullscreen: fullscreen,
		}
	}
}

impl ConfigPart for Window {
	fn to_config_str(&self) -> String {
		if self.size.is_some() || self.cellsize.is_some() || self.title.is_some() || self.icon.is_some() || self.resizeable.is_some() ||
		   self.fullscreen.is_some() {
			format!("window: {}, {}, {}, {}, {}, {};",
				match self.size {
					Some(ref size) => format!("size={}", size),
					None           => "".to_string(),
				},
				match self.cellsize {
					Some(ref cellsize) =>
						match cellsize {
							&Cellsize::Sized(size) => format!("cellsize={}", size),
							&Cellsize::Auto        => "cellsize=auto".to_string(),
						},
					None               => "".to_string(),
				},
				match self.title {
					Some(ref title) => format!("title={}", escape_config_string(&title)),
					None            => "".to_string(),
				},
				match self.icon {
					Some(ref icon) => format!("icon={}", escape_config_string(&icon)),
					None           => "".to_string(),
				},
				match self.resizeable {
					Some(ref resizeable) => format!("resizeable={}", resizeable),
					None                 => "".to_string(),
				},
				match self.fullscreen {
					Some(ref fullscreen) => format!("fullscreen={}", fullscreen),
					None                 => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `input` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Input {
	/// Whether to generate a mouse-move event when a mouse moves from one pixel to another as opposed to from one cell to another.
	///
	/// Default: `false`
	pub precise_mouse: Option<bool>,
	/// Whether to show the cursor.
	///
	/// Default: `true`
	pub mouse_cursor: Option<bool>,
	/// The cursor symbol to blink in the read string function.
	///
	/// Default: `'_'` a.k.a. `0x5F`
	pub cursor_symbol: Option<char>,
	/// Amount of time in milliseconds to blink the cursor symbol for.
	///
	/// Default: `500`
	pub cursor_blink_rate: Option<i32>,
}

impl Input {
	/// Construct a new `input` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	///
	/// `None` values will not override current ones.
	pub fn new(precise_mouse: Option<bool>, mouse_cursor: Option<bool>, cursor_symbol: Option<char>, cursor_blink_rate: Option<i32>) -> Input {
		Input{
			precise_mouse: precise_mouse,
			mouse_cursor: mouse_cursor,
			cursor_symbol: cursor_symbol,
			cursor_blink_rate: cursor_blink_rate,
		}
	}
}

impl ConfigPart for Input {
	fn to_config_str(&self) -> String {
		if self.precise_mouse.is_some() || self.mouse_cursor.is_some() || self.cursor_symbol.is_some() || self.cursor_blink_rate.is_some() {
			format!("input: {}, {}, {}, {};",
				match self.precise_mouse {
					Some(ref precise_mouse) => format!("precise-mouse={}", precise_mouse),
					None                    => "".to_string(),
				},
				match self.mouse_cursor {
					Some(ref mouse_cursor) => format!("mouse-cursor={}", mouse_cursor),
					None                   => "".to_string(),
				},
				match self.cursor_symbol {
					Some(ref cursor_symbol) => format!("cursor-symbol=0x{:x}", *cursor_symbol as i8),
					None                    => "".to_string(),
				},
				match self.cursor_blink_rate {
					Some(ref cursor_blink_rate) => format!("cursor-blink-rate={}", cursor_blink_rate),
					None                        => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `output` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Output {
	/// Whether to process special tags in the [`print()`](../fn.print.html) function.
	///
	/// Default: `true`
	pub postformatting: Option<bool>,
	/// Toggle OpenGL VSync.
	///
	/// Default: `true`
	pub vsync: Option<bool>,
}

impl Output {
	/// Construct a new `output` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	///
	/// `None` values will not override current ones.
	pub fn new(postformatting: Option<bool>, vsync: Option<bool>) -> Output {
		Output{
			postformatting: postformatting,
			vsync: vsync,
		}
	}
}

impl ConfigPart for Output {
	fn to_config_str(&self) -> String {
		if self.postformatting.is_some() || self.vsync.is_some() {
			format!("output: {}, {};",
				match self.postformatting {
					Some(ref postformatting) => format!("postformatting={}", postformatting),
					None                     => "".to_string(),
				},
				match self.vsync {
					Some(ref vsync) => format!("vsync={}", vsync),
					None            => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `log` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Log {
	/// The file to write the log to. Note, that, IME, it didn't work.
	///
	/// Default: `"bearlibterminal.log"`
	pub file: Option<String>,
	/// The minimal log level to print at.
	///
	/// Default: [`LogLevel::Error`](enum.LogLevel.html#variant.Error)
	pub level: Option<LogLevel>,
	/// How to write to the log file if one laready exists.
	///
	/// Default: [`LogMode::Truncate`](enum.LogMode.html#variant.Truncate)
	pub mode: Option<LogMode>,
}

impl Log {
	/// Construct a new `log` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	///
	/// `None` values will not override current ones.
	pub fn new(file: Option<String>, level: Option<LogLevel>, mode: Option<LogMode>) -> Log {
		Log{
			file: file,
			level: level,
			mode: mode,
		}
	}
}

impl ConfigPart for Log {
	fn to_config_str(&self) -> String {
		if self.file.is_some() || self.level.is_some() || self.mode.is_some() {
			format!("log: {}, {}, {};",
				match self.file {
					Some(ref file) => format!("file={}", escape_config_string(&file)),
					None           => "".to_string(),
				},
				match self.level {
					Some(ref level) => format!("level={}", level),
					None            => "".to_string(),
				},
				match self.mode {
					Some(ref mode) => format!("mode={}", mode),
					None            => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// Possible cell size, `Auto` will make the size be selected based on the font.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Cellsize {
	Auto,
	Sized(Size),
}

/// Logging levels, as specified [here](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum LogLevel {
	None,
	Fatal,
	Error,
	Warning,
	Info,
	Debug,
	Trace,
}

impl fmt::Display for LogLevel {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		formatter.write_str(match self {
			&LogLevel::None    => "none",
			&LogLevel::Fatal   => "fatal",
			&LogLevel::Error   => "error",
			&LogLevel::Warning => "warning",
			&LogLevel::Info    => "info",
			&LogLevel::Debug   => "debug",
			&LogLevel::Trace   => "trace",
		})
	}
}

/// Log writing mode.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum LogMode {
	/// Restart the log each time.
	Truncate,
	/// Continue writing at the end.
	Append,
}

impl fmt::Display for LogMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		formatter.write_str(match self {
			&LogMode::Truncate => "truncate",
			&LogMode::Append   => "append",
		})
	}
}
<commit_msg>Use the builder pattern for the config segments (except for Terminal)<commit_after>use std::fmt;
use std::path::Path;
use geometry::Size;
use terminal::config::{ConfigPart, escape_config_string};

/// The `terminal` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Terminal {
	/// Encoding used for unibyte strings. This is better left at default, as Rust uses UTF-8 for everything.
	///
	/// Default: `"utf8"`
	pub encoding: Option<String>,  //TODO: use an enum/validated struct?
}

impl Terminal {
	/// Construct a new `terminal` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override
	/// segment
	///
	/// `None` values will not override current ones.
	pub fn new(encoding: Option<String>) -> Terminal {
		Terminal{
			encoding: encoding,
		}
	}
}

impl ConfigPart for Terminal {
	fn to_config_str(&self) -> String {
		match self.encoding {
			Some(ref encoding) => format!("terminal.encoding={};", escape_config_string(&encoding)),
			None               => "".to_string(),
		}
	}
}

/// The `window` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// `None` values will not override current ones.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Window {
	/// Window size in cells.
	///
	/// Default: `80x25`
	pub size: Option<Size>,
	/// Size of all cells, in pixels.
	///
	/// Default: [`Cellsize::Auto`](enum.Cellsize.html#variant.Auto)
	pub cellsize: Option<Cellsize>,
	/// The terminal window's title.
	///
	/// Default: `"BearLibTerminal"`
	pub title: Option<String>,
	/// The path of the icon used for the terminal window.
	///
	/// Default: none
	pub icon: Option<String>,
	/// Whether the terminal window should be resizeable.
	///
	/// Default: `false`
	pub resizeable: Option<bool>,
	/// Whether to enforce fullscreen mode.
	///
	/// Default: `false`
	pub fullscreen: Option<bool>,
}

impl Window {
	/// Construct a `window` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	/// with everything being equal to `None`.
	pub fn empty() -> Window {
		Window{
			size: None,
			cellsize: None,
			title: None,
			icon: None,
			resizeable: None,
			fullscreen: None,
		}
	}

	pub fn size(mut self, size: Size) -> Window {
		self.size = Some(size);
		self
	}

	pub fn cellsize(mut self, cellsize: Cellsize) -> Window {
		self.cellsize = Some(cellsize);
		self
	}

	pub fn title(mut self, title: String) -> Window {
		self.title = Some(title);
		self
	}

	pub fn icon<T: AsRef<Path>>(mut self, icon: T) -> Window {
		self.icon = Some(icon.as_ref().to_str().unwrap().to_string());
		self
	}

	pub fn resizeable(mut self, resizeable: bool) -> Window {
		self.resizeable = Some(resizeable);
		self
	}

	pub fn fullscreen(mut self, fullscreen: bool) -> Window {
		self.fullscreen = Some(fullscreen);
		self
	}
}

impl ConfigPart for Window {
	fn to_config_str(&self) -> String {
		if self.size.is_some() || self.cellsize.is_some() || self.title.is_some() || self.icon.is_some() || self.resizeable.is_some() ||
		   self.fullscreen.is_some() {
			format!("window: {}, {}, {}, {}, {}, {};",
				match self.size {
					Some(ref size) => format!("size={}", size),
					None           => "".to_string(),
				},
				match self.cellsize {
					Some(ref cellsize) =>
						match cellsize {
							&Cellsize::Sized(size) => format!("cellsize={}", size),
							&Cellsize::Auto        => "cellsize=auto".to_string(),
						},
					None               => "".to_string(),
				},
				match self.title {
					Some(ref title) => format!("title={}", escape_config_string(&title)),
					None            => "".to_string(),
				},
				match self.icon {
					Some(ref icon) => format!("icon={}", escape_config_string(&icon)),
					None           => "".to_string(),
				},
				match self.resizeable {
					Some(ref resizeable) => format!("resizeable={}", resizeable),
					None                 => "".to_string(),
				},
				match self.fullscreen {
					Some(ref fullscreen) => format!("fullscreen={}", fullscreen),
					None                 => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `input` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// `None` values will not override current ones.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Input {
	/// Whether to generate a mouse-move event when a mouse moves from one pixel to another as opposed to from one cell to another.
	///
	/// Default: `false`
	pub precise_mouse: Option<bool>,
	/// Whether to show the cursor.
	///
	/// Default: `true`
	pub mouse_cursor: Option<bool>,
	/// The cursor symbol to blink in the read string function.
	///
	/// Default: `'_'` a.k.a. `0x5F`
	pub cursor_symbol: Option<char>,
	/// Amount of time in milliseconds to blink the cursor symbol for.
	///
	/// Default: `500`
	pub cursor_blink_rate: Option<i32>,
}

impl Input {
	/// Construct an `input` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	/// with all elements equal to `None`.
	pub fn empty() -> Input {
		Input{
			precise_mouse: None,
			mouse_cursor: None,
			cursor_symbol: None,
			cursor_blink_rate: None,
		}
	}

	pub fn precise_mouse(mut self, precise_mouse: bool) -> Input {
		self.precise_mouse = Some(precise_mouse);
		self
	}

	pub fn mouse_cursor(mut self, mouse_cursor: bool) -> Input {
		self.mouse_cursor = Some(mouse_cursor);
		self
	}

	pub fn cursor_symbol(mut self, cursor_symbol: char) -> Input {
		self.cursor_symbol = Some(cursor_symbol);
		self
	}

	pub fn cursor_blink_rate(mut self, cursor_blink_rate: i32) -> Input {
		self.cursor_blink_rate = Some(cursor_blink_rate);
		self
	}
}

impl ConfigPart for Input {
	fn to_config_str(&self) -> String {
		if self.precise_mouse.is_some() || self.mouse_cursor.is_some() || self.cursor_symbol.is_some() || self.cursor_blink_rate.is_some() {
			format!("input: {}, {}, {}, {};",
				match self.precise_mouse {
					Some(ref precise_mouse) => format!("precise-mouse={}", precise_mouse),
					None                    => "".to_string(),
				},
				match self.mouse_cursor {
					Some(ref mouse_cursor) => format!("mouse-cursor={}", mouse_cursor),
					None                   => "".to_string(),
				},
				match self.cursor_symbol {
					Some(ref cursor_symbol) => format!("cursor-symbol=0x{:x}", *cursor_symbol as i8),
					None                    => "".to_string(),
				},
				match self.cursor_blink_rate {
					Some(ref cursor_blink_rate) => format!("cursor-blink-rate={}", cursor_blink_rate),
					None                        => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `output` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// `None` values will not override current ones.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Output {
	/// Whether to process special tags in the [`print()`](../fn.print.html) function.
	///
	/// Default: `true`
	pub postformatting: Option<bool>,
	/// Toggle OpenGL VSync.
	///
	/// Default: `true`
	pub vsync: Option<bool>,
}

impl Output {
	/// Construct a new `output` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	pub fn clean() -> Output {
		Output{
			postformatting: None,
			vsync: None,
		}
	}

	pub fn postformatting(mut self, postformatting: bool) -> Output {
		self.postformatting = Some(postformatting);
		self
	}

	pub fn vsync(mut self, vsync: bool) -> Output {
		self.vsync = Some(vsync);
		self
	}
}

impl ConfigPart for Output {
	fn to_config_str(&self) -> String {
		if self.postformatting.is_some() || self.vsync.is_some() {
			format!("output: {}, {};",
				match self.postformatting {
					Some(ref postformatting) => format!("postformatting={}", postformatting),
					None                     => "".to_string(),
				},
				match self.vsync {
					Some(ref vsync) => format!("vsync={}", vsync),
					None            => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// The `log` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section repr.
///
/// `None` values will not override current ones.
///
/// See [`terminal::configure()`](../fn.configure.html).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub struct Log {
	/// The file to write the log to. Note, that, IME, it didn't work.
	///
	/// Default: `"bearlibterminal.log"`
	pub file: Option<String>,
	/// The minimal log level to print at.
	///
	/// Default: [`LogLevel::Error`](enum.LogLevel.html#variant.Error)
	pub level: Option<LogLevel>,
	/// How to write to the log file if one laready exists.
	///
	/// Default: [`LogMode::Truncate`](enum.LogMode.html#variant.Truncate)
	pub mode: Option<LogMode>,
}

impl Log {
	/// Construct an `log` [configuration](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration) section override segment
	/// with everything set to `None`
	pub fn empty() -> Log {
		Log{
			file: None,
			level: None,
			mode: None,
		}
	}

	pub fn file(mut self, file: String) -> Log {
		self.file = Some(file);
		self
	}

	pub fn level(mut self, level: LogLevel) -> Log {
		self.level = Some(level);
		self
	}

	pub fn mode(mut self, mode: LogMode) -> Log {
		self.mode = Some(mode);
		self
	}
}

impl ConfigPart for Log {
	fn to_config_str(&self) -> String {
		if self.file.is_some() || self.level.is_some() || self.mode.is_some() {
			format!("log: {}, {}, {};",
				match self.file {
					Some(ref file) => format!("file={}", escape_config_string(&file)),
					None           => "".to_string(),
				},
				match self.level {
					Some(ref level) => format!("level={}", level),
					None            => "".to_string(),
				},
				match self.mode {
					Some(ref mode) => format!("mode={}", mode),
					None            => "".to_string(),
				},
			)
		} else {
			"".to_string()
		}
	}
}

/// Possible cell size, `Auto` will make the size be selected based on the font.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum Cellsize {
	Auto,
	Sized(Size),
}

/// Logging levels, as specified [here](http://foo.wyrd.name/en:bearlibterminal:reference:configuration#library_configuration).
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum LogLevel {
	None,
	Fatal,
	Error,
	Warning,
	Info,
	Debug,
	Trace,
}

impl fmt::Display for LogLevel {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		formatter.write_str(match self {
			&LogLevel::None    => "none",
			&LogLevel::Fatal   => "fatal",
			&LogLevel::Error   => "error",
			&LogLevel::Warning => "warning",
			&LogLevel::Info    => "info",
			&LogLevel::Debug   => "debug",
			&LogLevel::Trace   => "trace",
		})
	}
}

/// Log writing mode.
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
pub enum LogMode {
	/// Restart the log each time.
	Truncate,
	/// Continue writing at the end.
	Append,
}

impl fmt::Display for LogMode {
	fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
		formatter.write_str(match self {
			&LogMode::Truncate => "truncate",
			&LogMode::Append   => "append",
		})
	}
}
<|endoftext|>