<commit_before>// Copyright 2014 The Rustdown 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.

use std::cmp::PartialEq;

use html::ToHtml;
use html::Html;
use text;

macro_rules! parse (
    ($e:expr) => (match $e { Some(e) => return Some(e), None => () })
)

/// A markdown text heading. `depth` signifies the level of the heading, e.g.,
/// `# head` is depth 1, `## head` is depth 2, and so on.
///
#[deriving(Show)]
pub struct Heading {
    contents: String,
    depth: uint,
}

impl Heading {
    pub fn new(title: String, depth: uint) -> Heading {
        Heading {
            contents: title,
            depth: depth,
        }
    }
}

impl PartialEq for Heading {
    fn eq(&self, other: &Heading) -> bool {
        self.depth == other.depth && self.contents == other.contents
    }

    fn ne(&self, other: &Heading) -> bool {
        !self.eq(other)
    }
}

impl ToHtml for Heading {
    fn to_html(&self) -> Html {
        let name: String = format!("h{}", self.depth);
        Html::new_simple(name, self.contents.clone())
    }
}

/// Parse all the different types of headings, regardless
/// of how they're formatted. Return `Some(heading)` if one was found
/// in this block.
pub fn parse_heading(block: &Vec<String>) -> Option<Heading> {
    parse!(pound_heading(block));
    parse!(line_heading(block));
    None
}

/// A heading created with a pound sign, like:
///
///     # Heading
///
fn pound_heading(b: &Vec<String>) -> Option<Heading> {
    if b.len() != 1 { return None }
    let s = b[0].as_slice();
    match text::starting_chars(s, '#') {
        Some((title, count)) => Some(Heading::new(title.to_string(), count)),
        None => None,
    }
}

/// A heading created with an underline, like:
///
///     Heading
///     =======
///
fn line_heading(b: &Vec<String>) -> Option<Heading> {
    if b.len() != 2 { return None }
    let mut depth = 0u;
    if text::all_chars_are('=', b[1].as_slice()) {
        return Some(Heading::new(b[0].to_string(), 1));
    }
    if text::all_chars_are('-', b[1].as_slice()) {
        return Some(Heading::new(b[0].to_string(), 2));
    }
    return None;
}

#[cfg(test)]
mod tests {
    use super::Heading;
    use super::pound_heading;
    use super::line_heading;
    use html::ToHtml;

    fn pound_heading_equals(input: &str, result: &str) {
        let block = &vec![input.to_string()];
        match pound_heading(block) {
            Some(heading) => {
                assert_eq!(format!("{}", heading.to_html()), result.to_string());
            }
            None => { fail!("Didn't count as a heading") }
        }
    }

    #[test]
    fn test_pound_heads() {
        assert_eq!(pound_heading(&vec!["# Hello, world".to_string()]),
                   Some(Heading {
                       contents: "Hello, world".to_string(),
                       depth: 1,
                   })
                   );
        assert_eq!(pound_heading(&vec!("## Hello again, world!".to_string())),
                   Some(Heading {
                       contents: "Hello again, world!".to_string(),
                       depth: 2,
                   }));
    }

    fn test_line_heads() {
        assert_eq!(line_heading(&vec!("Hello\n=====".to_string())),
                   Some(Heading {
                       contents: "Hello".to_string(),
                       depth: 1,
                   }));
        assert_eq!(line_heading(&vec!("Hello\n-----".to_string())),
                   Some(Heading {
                       contents: "Hello".to_string(),
                       depth: 2,
                   }));
        assert_eq!(line_heading(&vec!("Hello\n =====".to_string())),
                   None);
    }

    #[test]
    fn test_pound_heads_fmt() {
        pound_heading_equals("# Hello, world", "
Hello, world
");
        pound_heading_equals("## Hello again, world!",
                             "
Hello again, world!
");
        pound_heading_equals("##
Hello again, world!
",
                             "
<h2>Hello again, world!</h2>
");
    }

    #[test]
    fn test_non_pound_heads() {
        assert_eq!(pound_heading(&vec!("Hello, world".to_string())), None);
        assert_eq!(pound_heading(&vec!(" ## Hello, world".to_string())), None);
    }
}
<commit_msg>update macro_rules for rust semicolon requirement<commit_after>// Copyright 2014 The Rustdown 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.

use std::cmp::PartialEq;

use html::ToHtml;
use html::Html;
use text;

macro_rules! parse (
    ($e:expr) => (match $e { Some(e) => return Some(e), None => () })
);

/// A markdown text heading. `depth` signifies the level of the heading, e.g.,
/// `# head` is depth 1, `## head` is depth 2, and so on.
///
#[deriving(Show)]
pub struct Heading {
    contents: String,
    depth: uint,
}

impl Heading {
    pub fn new(title: String, depth: uint) -> Heading {
        Heading {
            contents: title,
            depth: depth,
        }
    }
}

impl PartialEq for Heading {
    fn eq(&self, other: &Heading) -> bool {
        self.depth == other.depth && self.contents == other.contents
    }

    fn ne(&self, other: &Heading) -> bool {
        !self.eq(other)
    }
}

impl ToHtml for Heading {
    fn to_html(&self) -> Html {
        let name: String = format!("h{}", self.depth);
        Html::new_simple(name, self.contents.clone())
    }
}

/// Parse all the different types of headings, regardless
/// of how they're formatted. Return `Some(heading)` if one was found
/// in this block.
pub fn parse_heading(block: &Vec<String>) -> Option<Heading> {
    parse!(pound_heading(block));
    parse!(line_heading(block));
    None
}

/// A heading created with a pound sign, like:
///
///     # Heading
///
fn pound_heading(b: &Vec<String>) -> Option<Heading> {
    if b.len() != 1 { return None }
    let s = b[0].as_slice();
    match text::starting_chars(s, '#') {
        Some((title, count)) => Some(Heading::new(title.to_string(), count)),
        None => None,
    }
}

/// A heading created with an underline, like:
///
///     Heading
///     =======
///
fn line_heading(b: &Vec<String>) -> Option<Heading> {
    if b.len() != 2 { return None }
    let mut depth = 0u;
    if text::all_chars_are('=', b[1].as_slice()) {
        return Some(Heading::new(b[0].to_string(), 1));
    }
    if text::all_chars_are('-', b[1].as_slice()) {
        return Some(Heading::new(b[0].to_string(), 2));
    }
    return None;
}

#[cfg(test)]
mod tests {
    use super::Heading;
    use super::pound_heading;
    use super::line_heading;
    use html::ToHtml;

    fn pound_heading_equals(input: &str, result: &str) {
        let block = &vec![input.to_string()];
        match pound_heading(block) {
            Some(heading) => {
                assert_eq!(format!("{}", heading.to_html()), result.to_string());
            }
            None => { fail!("Didn't count as a heading") }
        }
    }

    #[test]
    fn test_pound_heads() {
        assert_eq!(pound_heading(&vec!["# Hello, world".to_string()]),
                   Some(Heading {
                       contents: "Hello, world".to_string(),
                       depth: 1,
                   })
                   );
        assert_eq!(pound_heading(&vec!("## Hello again, world!".to_string())),
                   Some(Heading {
                       contents: "Hello again, world!".to_string(),
                       depth: 2,
                   }));
    }

    fn test_line_heads() {
        assert_eq!(line_heading(&vec!("Hello\n=====".to_string())),
                   Some(Heading {
                       contents: "Hello".to_string(),
                       depth: 1,
                   }));
        assert_eq!(line_heading(&vec!("Hello\n-----".to_string())),
                   Some(Heading {
                       contents: "Hello".to_string(),
                       depth: 2,
                   }));
        assert_eq!(line_heading(&vec!("Hello\n =====".to_string())),
                   None);
    }

    #[test]
    fn test_pound_heads_fmt() {
        pound_heading_equals("# Hello, world", "
Hello, world
");
        pound_heading_equals("## Hello again, world!",
                             "
Hello again, world!
");
        pound_heading_equals("##
Hello again, world!
",
                             "
<h2>Hello again, world!</h2>
");
    }

    #[test]
    fn test_non_pound_heads() {
        assert_eq!(pound_heading(&vec!("Hello, world".to_string())), None);
        assert_eq!(pound_heading(&vec!(" ## Hello, world".to_string())), None);
    }
}
<|endoftext|>


<commit_before>extern crate num_traits;

use std::f64;
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
use std::ops::{Add, Sub, Mul, Div};
use self::num_traits::{One, Zero};

#[derive(PartialOrd, Debug, Default, Clone, Copy)]
pub struct LogProb {
    value: f64
}

impl LogProb {
    pub fn new(value: f64) -> Result<Self, String> {
        if 0.0 <= value && value <= 1.0 {
            Ok(LogProb { value: - value.ln() })
        } else {
            Err(format!("{} is not a probability (i.e. not in the interval [0,1]).", value))
        }
    }

    pub fn probability(&self) -> f64 {
        (- &self.value).exp()
    }
}

/// Flipped instance to reflect that a probability of ``0.25`` is "more expensive" than ``0.5``.
impl Ord for LogProb {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.partial_cmp(&other) {
            Some(ordering) => ordering,
            None => {
                if self.value.is_nan() {
                    if other.value.is_nan() {
                        Ordering::Equal
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            }
        }
    }
}

impl PartialEq for LogProb {
    fn eq(&self, other: &Self) -> bool {
        if self.value.is_nan() {
            if other.value.is_nan() { true } else { false }
        } else if other.value.is_nan() {
            false
        } else {
            self.value - other.value < f64::EPSILON
        }
    }
}

impl Eq for LogProb {}

impl Add for LogProb {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let (a, b) = (self.value, other.value);

        let (x, y) = if a > b {
            (a, b)
        } else {
            (b, a)
        };

        match x - (x - y).exp().ln_1p() {
            s if s >= 0.0 => LogProb { value: s },
            _             => panic!(format!("exp(-{}) + exp(-{}) is not a probability, i.e. not in the interval [0,1].", x, y)),
        }
    }
}

impl Sub for LogProb {
    type Output = Self ;

    fn sub(self, other: Self) -> Self {
        match (self.value, other.value) {
            (x, y) if x <= y => LogProb { value: (x - (-(x - y).exp_m1()).ln()) },
            (x, y) if x >  y => panic!(format!("exp(-{}) - exp(-{}) is not a probability, i.e. not on the interval [0,1].", x, y)),
            _                => unreachable!(),
        }
    }
}

#[test]
fn test_add_sub() {
    match (LogProb::new(0.5), LogProb::new(0.25), LogProb::new(0.75)) {
        (Ok(x), Ok(y), Ok(z)) => {
            assert_eq!(x + y, z);
            assert_eq!(y + x, z);
            assert_eq!(z - x, y);
            assert_eq!(z - y, x);
        },
        _ => panic!(),
    }
}

impl Mul for LogProb {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        LogProb { value: self.value.add(other.value) }
    }
}

impl Div for LogProb {
    type Output = Self;

    fn div(self, other: Self) -> Self {
        match self.value.sub(other.value) {
            x if x >= 0.0
                => LogProb { value: x },
            x if x <  0.0
                => panic!(format!("{} is not a probability (i.e. not in the interval [0,1]).", x)),
            _   => unreachable!(),
        }
    }
}

impl Zero for LogProb {
    fn zero() -> LogProb {
        LogProb { value: f64::INFINITY }
    }

    fn is_zero(&self) -> bool {
        self.value.is_infinite()
    }
}

impl One for LogProb {
    fn one() -> LogProb {
        LogProb { value: 0.0 }
    }
}

impl FromStr for LogProb {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.parse() {
            Ok(p) => LogProb::new(p),
            Err(e) => Err(e.to_string())
        }
    }
}

impl fmt::Display for LogProb {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.probability())
    }
}
<commit_msg>src/util/log_prob.rs:  allowed probabilities greater than 1 during calculations<commit_after>extern crate num_traits;

use std::f64;
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
use std::ops::{Add, Sub, Mul, Div};
use self::num_traits::{One, Zero};

/// A struct that represents probabilities.
/// A probability is internally represented as its negative natural logarithm.
/// Probabilities greater than 1 are allowed during calculations.
#[derive(PartialOrd, Debug, Default, Clone, Copy)]
pub struct LogProb {
    value: f64
}

impl LogProb {
    /// Creates a new `LogProb` from a given value in the interval [0,1].
    pub fn new(value: f64) -> Result<Self, String> {
        if 0.0 <= value && value <= 1.0 {
            Ok(LogProb { value: - value.ln() })
        } else {
            Err(format!("{} is not a probability (i.e. not in the interval [0,1]).", value))
        }
    }

    /// Same as `new` but without bounds check.
    pub fn new_unchecked(value: f64) -> Self {
        LogProb { value: - value.ln() }
    }

    /// Probability that is represented by the given `LogProb`.
    pub fn probability(&self) -> f64 {
        (- &self.value).exp()
    }
}

/// Flipped instance to reflect that a probability of ``0.25`` is "more expensive" than ``0.5``.
impl Ord for LogProb {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.partial_cmp(&other) {
            Some(ordering) => ordering,
            None => {
                if self.value.is_nan() {
                    if other.value.is_nan() {
                        Ordering::Equal
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            }
        }
    }
}

impl PartialEq for LogProb {
    fn eq(&self, other: &Self) -> bool {
        if self.value.is_nan() {
            if other.value.is_nan() { true } else { false }
        } else if other.value.is_nan() {
            false
        } else {
            self.value - other.value < f64::EPSILON
        }
    }
}

impl Eq for LogProb {}

impl Add for LogProb {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let (a, b) = (self.value, other.value);

        let (x, y) = if a > b {
            (a, b)
        } else {
            (b, a)
        };

        LogProb { value: x - (x - y).exp().ln_1p() }
    }
}

impl Sub for LogProb {
    type Output = Self ;

    fn sub(self, other: Self) -> Self {
        match (self.value, other.value) {
            (x, y) if x <= y => LogProb { value: (x - (-(x - y).exp_m1()).ln()) },
            (x, y) if x >  y => panic!(format!("exp(-{}) - exp(-{}) is less than zero", x, y)),
            _                => unreachable!(),
        }
    }
}

#[test]
fn test_arithmetic() {
    match (LogProb::new(0.5), LogProb::new(0.25), LogProb::new(0.75)) {
        (Ok(x), Ok(y), Ok(z)) => {
            assert_eq!(x + y, z);
            assert_eq!(y + x, z);
            assert_eq!(z - x, y);
            assert_eq!(z - y, x);
            assert_eq!(x * x, y);
            assert_eq!(x / y, y);
            assert_eq!(z / z, LogProb::one());
        },
        _ => panic!(),
    }
}

impl Mul for LogProb {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        LogProb { value: self.value.add(other.value) }
    }
}

impl Div for LogProb {
    type Output = Self;

    fn div(self, other: Self) -> Self {
        LogProb { value: self.value.sub(other.value) }
    }
}

impl Zero for LogProb {
    fn zero() -> LogProb {
        LogProb { value: f64::INFINITY }
    }

    fn is_zero(&self) -> bool {
        self.value.is_infinite()
    }
}

impl One for LogProb {
    fn one() -> LogProb {
        LogProb { value: 0.0 }
    }
}

impl FromStr for LogProb {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.parse() {
            Ok(p) => LogProb::new(p),
            Err(e) => Err(e.to_string())
        }
    }
}

impl fmt::Display for LogProb {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.probability())
    }
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::path::Path;

use encoding::label::encoding_from_whatwg_label;
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};

use crate::util::{self, FileError};

#[derive(Debug)]
pub enum TextFileError {
    Open(FileError),
    Read(io::Error),
    Write(io::Error),
    Encoding(Cow<'static, str>),
    Encode(Cow<'static, str>),
}

impl fmt::Display for TextFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self {
            TextFileError::Open(err) => write!(f, "{}", err),
            TextFileError::Read(err) => write!(f, "{}", err),
            TextFileError::Write(err) => write!(f, "{}", err),
            TextFileError::Encoding(err) => write!(f, "{}", err),
            TextFileError::Encode(err) => write!(f, "{}", err),
        }
    }
}

pub fn read_text_file(file_path: &Path, encoding: Option<&str>) -> Result<String, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // Open template file.
    let mut file = util::open_file(file_path).map_err(|err| TextFileError::Open(err))?;

    // If an encoding was specified...
    Ok(if let Some(encoding) = encoding {
        // Create a buffer for raw template data.
        let mut buffer: Vec<u8> = Vec::new();

        // Read raw template data into buffer.
        file.read_to_end(&mut buffer).map_err(|err| TextFileError::Read(err))?;

        // Decode the raw data to a string using the specified encoding.
        encoding.decode(&buffer, DecoderTrap::Strict).expect("Error decoding!")
    } else {
        // Create an empty string.
        let mut string: String = String::new();

        // Read template data into the string, assuming it is valid UTF-8.
        file.read_to_string(&mut string)
            .map_err(|err| TextFileError::Read(err))?;

        string
    })
}

pub fn write_text_file(
    file_path: &Path,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    // Create file
    let mut file = util::create_file(&file_path).map_err(|err| TextFileError::Open(err))?;

    // Write text to it
    write_text(&mut file, text, encoding)
}

pub fn write_text(
    writable: &mut impl Write,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    let text = text.as_ref();

    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // If an encoding was specified...
    if let Some(encoding) = encoding {
        // Encode the template data using the specified encoding.
        let enc_bytes = encoding
            .encode(&text, EncoderTrap::Strict)
            .map_err(TextFileError::Encode)?;

        // Write bytes
        let bytes_written = writable.write(&enc_bytes).map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    } else {
        // If no encoding was specified, write the text directly as bytes.
        let bytes_written = writable
            .write(text.as_bytes())
            .map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    }
}

fn get_encoding(encoding: Option<&str>) -> Result<Option<EncodingRef>, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    Ok(match encoding {
        Some(enc_name) => Some(
            encoding_from_whatwg_label(enc_name)
                .ok_or_else(|| TextFileError::Encoding(Cow::Borrowed("Cannot get encoding!")))?,
        ),
        None => None,
    })
}
<commit_msg>fix: Panic if decoding text file fails<commit_after>use std::borrow::Cow;
use std::fmt;
use std::io::{self, Read, Write};
use std::path::Path;

use encoding::label::encoding_from_whatwg_label;
use encoding::{DecoderTrap, EncoderTrap, EncodingRef};

use crate::util::{self, FileError};

#[derive(Debug)]
pub enum TextFileError {
    Open(FileError),
    Read(io::Error),
    Write(io::Error),
    Encoding(Cow<'static, str>),
    Encode(Cow<'static, str>),
    Decode(Cow<'static, str>),
}

impl fmt::Display for TextFileError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match &self {
            TextFileError::Open(err) => write!(f, "{}", err),
            TextFileError::Read(err) => write!(f, "{}", err),
            TextFileError::Write(err) => write!(f, "{}", err),
            TextFileError::Encoding(err) => write!(f, "{}", err),
            TextFileError::Encode(err) => write!(f, "{}", err),
            TextFileError::Decode(err) => write!(f, "{}", err),
        }
    }
}

pub fn read_text_file(file_path: &Path, encoding: Option<&str>) -> Result<String, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // Open template file.
    let mut file = util::open_file(file_path).map_err(|err| TextFileError::Open(err))?;

    // If an encoding was specified...
    Ok(if let Some(encoding) = encoding {
        // Create a buffer for raw template data.
        let mut buffer: Vec<u8> = Vec::new();

        // Read raw template data into buffer.
        file.read_to_end(&mut buffer).map_err(|err| TextFileError::Read(err))?;

        // Decode the raw data to a string using the specified encoding.
        encoding
            .decode(&buffer, DecoderTrap::Strict)
            .map_err(|err| TextFileError::Decode(err))?
    } else {
        // Create an empty string.
        let mut string: String = String::new();

        // Read template data into the string, assuming it is valid UTF-8.
        file.read_to_string(&mut string)
            .map_err(|err| TextFileError::Read(err))?;

        string
    })
}

pub fn write_text_file(
    file_path: &Path,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    // Create file
    let mut file = util::create_file(&file_path).map_err(|err| TextFileError::Open(err))?;

    // Write text to it
    write_text(&mut file, text, encoding)
}

pub fn write_text(
    writable: &mut impl Write,
    text: impl AsRef<str>,
    encoding: Option<&str>,
) -> Result<usize, TextFileError> {
    let text = text.as_ref();

    // If an encoding was specified, try to get an implementation for it.
    let encoding = get_encoding(encoding)?;

    // If an encoding was specified...
    if let Some(encoding) = encoding {
        // Encode the template data using the specified encoding.
        let enc_bytes = encoding
            .encode(&text, EncoderTrap::Strict)
            .map_err(TextFileError::Encode)?;

        // Write bytes
        let bytes_written = writable.write(&enc_bytes).map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    } else {
        // If no encoding was specified, write the text directly as bytes.
        let bytes_written = writable
            .write(text.as_bytes())
            .map_err(|err| TextFileError::Write(err))?;

        Ok(bytes_written)
    }
}

fn get_encoding(encoding: Option<&str>) -> Result<Option<EncodingRef>, TextFileError> {
    // If an encoding was specified, try to get an implementation for it.
    Ok(match encoding {
        Some(enc_name) => Some(
            encoding_from_whatwg_label(enc_name)
                .ok_or_else(|| TextFileError::Encoding(Cow::Borrowed("Cannot get encoding!")))?,
        ),
        None => None,
    })
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use super::{WeightNum, Weights};
use super::square_matrix::SquareMatrix;

#[derive(Debug)]
pub struct WeightMatrix<T: WeightNum> {
    c: SquareMatrix<T>,
}

impl<T: WeightNum> Weights for WeightMatrix<T> {
    type T = T;
    #[inline(always)]
    fn n(&self) -> usize {
        self.c.n()
    }

    #[inline]
    fn element_at(&self, pos: (usize, usize)) -> T {
        self.c[pos]
    }

    #[inline]
    fn is_element_zero(&self, pos: (usize, usize)) -> bool {
        self.c[pos].partial_cmp(&T::zero()) == Some(Ordering::Equal)
    }

    // for each row, subtracts the minimum of that row from each other value in the
    // row.
    fn sub_min_of_each_row(&mut self) {
        for row in 0..self.n() {
            let min = self.min_of_row(row);
            self.sub_row(row, min);
        }
    }

    // Add `val` to every element in row `row`.
    fn add_row(&mut self, row: usize, val: T) {
        self.c.map_row(row, |cur| cur + val);
    }

    // Subtract `val` from every element in column `col`.
    fn sub_col(&mut self, col: usize, val: T) {
        self.c.map_col(col, |cur| cur - val);
    }
}

impl<T: WeightNum> WeightMatrix<T> {
    pub fn from_row_vec(n: usize, data: Vec<T>) -> WeightMatrix<T> {
        assert!(n > 0);
        WeightMatrix { c: SquareMatrix::from_row_vec(n, data) }
    }

    pub fn from_fn<F: Fn((usize, usize)) -> T>(n: usize, f: F) -> WeightMatrix<T> {
        assert!(n > 0);
        WeightMatrix { c: SquareMatrix::from_fn(n, f) }
    }

    /// Return the minimum element of row `row`.
    fn min_of_row(&self, row: usize) -> T {
        let row_slice = self.c.row_slice(row);
        let mut min = row_slice[0];
        for &val in &row_slice[1..] {
            if val < min {
                min = val;
            }
        }
        min
    }

    // Subtract `val` from every element in row `row`.
    fn sub_row(&mut self, row: usize, val: T) {
        self.c.map_row(row, |cur| cur - val);
    }

    pub fn as_slice(&self) -> &[T] {
        self.c.as_slice()
    }
}
<commit_msg>Add test case for WeightMatrix<commit_after>use std::cmp::Ordering;
use super::{WeightNum, Weights};
use super::square_matrix::SquareMatrix;

#[derive(Debug)]
pub struct WeightMatrix<T: WeightNum> {
    c: SquareMatrix<T>,
}

impl<T: WeightNum> Weights for WeightMatrix<T> {
    type T = T;

    #[inline(always)]
    fn n(&self) -> usize {
        self.c.n()
    }

    #[inline]
    fn element_at(&self, pos: (usize, usize)) -> T {
        self.c[pos]
    }

    #[inline]
    fn is_element_zero(&self, pos: (usize, usize)) -> bool {
        self.c[pos].partial_cmp(&T::zero()) == Some(Ordering::Equal)
    }

    // for each row, subtracts the minimum of that row from each other value in the
    // row.
    fn sub_min_of_each_row(&mut self) {
        for row in 0..self.n() {
            let min = self.min_of_row(row);
            self.sub_row(row, min);
        }
    }

    // Add `val` to every element in row `row`.
    fn add_row(&mut self, row: usize, val: T) {
        self.c.map_row(row, |cur| cur + val);
    }

    // Subtract `val` from every element in column `col`.
    fn sub_col(&mut self, col: usize, val: T) {
        self.c.map_col(col, |cur| cur - val);
    }
}

impl<T: WeightNum> WeightMatrix<T> {
    pub fn from_row_vec(n: usize, data: Vec<T>) -> WeightMatrix<T> {
        assert!(n > 0);
        WeightMatrix { c: SquareMatrix::from_row_vec(n, data) }
    }

    pub fn from_fn<F: Fn((usize, usize)) -> T>(n: usize, f: F) -> WeightMatrix<T> {
        assert!(n > 0);
        WeightMatrix { c: SquareMatrix::from_fn(n, f) }
    }

    /// Return the minimum element of row `row`.
    fn min_of_row(&self, row: usize) -> T {
        let row_slice = self.c.row_slice(row);
        let mut min = row_slice[0];
        for &val in &row_slice[1..] {
            if val < min {
                min = val;
            }
        }
        min
    }

    // Subtract `val` from every element in row `row`.
    fn sub_row(&mut self, row: usize, val: T) {
        self.c.map_row(row, |cur| cur - val);
    }

    pub fn as_slice(&self) -> &[T] {
        self.c.as_slice()
    }
}

#[test]
fn test_weight_matrix() {
    assert_eq!(0, WeightMatrix::from_row_vec(1, vec![0]).min_of_row(0));
    assert_eq!(1, WeightMatrix::from_row_vec(1, vec![1]).min_of_row(0));
    assert_eq!(1, WeightMatrix::from_row_vec(2, vec![5, 1, 0, 0]).min_of_row(0));

    let mut mat = WeightMatrix::from_row_vec(2, vec![0, 1, 2, 3]);
    mat.sub_row(1, 1);
    assert_eq!(&[0, 1, 1, 2], mat.as_slice());

    let mut mat = WeightMatrix::from_row_vec(2, vec![5, 3, 2, 3]);
    mat.sub_min_of_each_row();
    assert_eq!(&[2, 0, 0, 1], mat.as_slice());
}
<|endoftext|>


<commit_before>use std::collections::HashSet;
use std::fs::{read_dir, read_link, remove_file, metadata};
use std::io::{self, stdout, stderr};
use std::ffi::OsStr;
use std::path::Path;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use std::os::unix::fs::MetadataExt;

use argparse::{ArgumentParser, PushConst, StoreTrue, StoreOption};
use scan_dir::ScanDir;
use humantime;

use super::setup;
use super::Wrapper;
use container::util::clean_dir;
use file_util::{read_visible_entries, Lock};
use wrapper::build::get_version_hash;

#[derive(Clone, Copy)]
enum Action {
    Temporary,
    Old,
    Unused,
    Everything,
    Transient,
}

pub fn clean_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut global = false;
    let mut dry_run = false;
    let mut actions = vec!();
    let mut duration = None::<humantime::Duration>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Performs various cleanup tasks
            ");
        ap.refer(&mut actions)
          .add_option(&["--tmp", "--tmp-folders"],
                PushConst(Action::Temporary),
                "Clean temporary containers (failed builds)")
          .add_option(&["--old", "--old-containers"], PushConst(Action::Old), "
                Clean old versions of containers (those which doesn't have a \
                symlink in .vagga)")
          .add_option(&["--unused"], PushConst(Action::Unused), "
                Clean unused containers, or versions thereof. (This is not \
                `--old` for historical reasons, we will probably merge the \
                commands later on)")
          .add_option(&["--transient"], PushConst(Action::Transient),
                "Clean unneeded transient folders (left from containers with
                 `write-mode` set to transient-something). The pid of process
                 is checked for liveness first.")
          .add_option(&["--everything"], PushConst(Action::Everything),
                "Clean whole `.vagga` folder. Useful when deleting a project.
                 With ``--global`` cleans whole storage-dir and cache-dir")
          .required();
        ap.refer(&mut global)
          .add_option(&["--global"], StoreTrue,
                "Apply cleanup command to all containers. Works only \
                if `storage-dir` is configured in settings");
        ap.refer(&mut dry_run)
          .add_option(&["-n", "--dry-run"], StoreTrue,
                "Dry run. Don't delete everything, just print");
        ap.refer(&mut duration)
          .add_option(&["--at-least"], StoreOption, "
            Only in combination with `--unused`. Treat as unused \
            containers that are unused for specified time, rather than \
            the ones not used by current version of config");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(1);
            }
        }
    }
    if global && wrapper.ext_settings.storage_dir.is_none() {
        error!("The --global flag is only meaningful if you configure \
            storage-dir in settings");
        return Ok(2);
    }
    let duration = duration.map(|x| x.into());
    for action in actions.iter() {
        let res = match *action {
            Action::Temporary => clean_temporary(wrapper, global, dry_run),
            Action::Old => clean_old(wrapper, global, dry_run),
            Action::Unused => {
                clean_unused(wrapper, global, duration, dry_run)
            }
            Action::Transient => clean_transient(wrapper, global, dry_run),
            _ => unimplemented!(),
        };
        match res {
            Ok(()) => {}
            Err(err) => {
                error!("Error cleaning up: {}", err);
                return Ok(3);
            }
        }
    }
    return Ok(0);
}

fn clean_dir_wrapper(path: &Path, dry_run: bool) -> Result<(), String> {
    // TODO(tailhook) chroot to dir for removing
    if dry_run {
        println!("Would remove {:?}", path);
    } else {
        let mut n = path.to_path_buf().into_os_string();
        n.push(".lock");
        let lock_name = Path::new(&n);
        let lock_guard = if lock_name.exists() {
            match Lock::exclusive(&lock_name) {
                Ok(x) => Some(x),
                Err(e) => {
                    error!("Failed to lock {:?}: {}, skipping", lock_name, e);
                    return Ok(());
                }
            }
        } else {
            None
        };
        debug!("Removing {:?}", path);
        try!(clean_dir(path, true));
        if let Some(_lock) = lock_guard {
            try!(remove_file(lock_name)
                .map_err(|e| format!("Error removing lock file {:?}: {}",
                    lock_name, e)));
        }
    }
    Ok(())
}

fn clean_temporary(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let roots = base.join(".roots");
    for entry in try_msg!(read_dir(&roots),
        "Can't read dir {r:?}: {err}", r=roots)
    {
        let entry = try_msg!(entry, "Can't read dir {r:?}: {err}", r=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if typ.is_dir() &&
           entry.file_name()[..].to_str().map(|n| n.starts_with(".tmp"))
                                         .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }

    return Ok(());
}

fn clean_old(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let useful: HashSet<String> = try_msg!(
        read_visible_entries(&wrapper.project_root.join(".vagga")),
            "Can't read vagga directory: {err}")
        .into_iter()
        .filter_map(|path| read_link(&path)
             .map_err(|e| warn!("Can't readlink {:?}: {}", path, e))
             .ok()
             .and_then(|f| {
                 // The container name is next to the last component
                 f.iter().rev().nth(1)
                 .and_then(|x| x.to_str()).map(ToString::to_string)
             }))
        .collect();

    info!("Useful images {:?}", useful);
    try!(clean_dirs_except(&base.join(".roots"), &useful, dry_run));

    return Ok(());
}

fn clean_transient(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let procfs = Path::new("/proc");
    for entry in try_msg!(read_dir(&base.join(".transient")),
                        "Can't read .vagga/.transient dir: {err}")
    {
        let entry = try_msg!(entry, "Error reading .vagga/transient: {err}");
        if let Some(fname) = entry.file_name()[..].to_str() {
            if let Some(idx) = fname.find('.') {
                if u32::from_str(&fname[idx+1..]).is_ok() &&
                    procfs.join(&fname[idx+1..]).exists()
                {
                    continue;
                }
            }
        }
        try!(clean_dir_wrapper(&entry.path(), dry_run));
    }

    return Ok(());
}

fn clean_dirs_except<P: AsRef<Path>>(roots: P, useful: &HashSet<String>,
    dry_run: bool)
    -> Result<(), String>
{
    let roots = roots.as_ref();
    for entry in try_msg!(read_dir(&roots),
                         "Can't read dir {dir:?}: {err}", dir=roots)
    {
        let entry = try_msg!(entry,
                             "Can't read dir {dir:?}: {err}", dir=roots);
        let path = entry.path();
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=path);
        if !typ.is_dir() {
            if path.extension() == Some(OsStr::new("lock")) &&
               path.with_extension("").is_dir()
            {
                debug!("Skipping lock file {:?}", path);
            } else {
                try_msg!(remove_file(&path),
                    "Can't remove file {p:?}: {err}", p=path);
            }
        } else if !typ.is_dir() || entry.file_name()[..].to_str()
            .map(|n| !useful.contains(&n.to_string()))
            .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), dry_run));
        }
    }
    Ok(())
}

fn clean_unused(wrapper: &Wrapper, global: bool, duration: Option<Duration>,
    dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }

    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    let mut useful: HashSet<String> = HashSet::new();
    if let Some(duration) = duration {
        let unixtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let cut_off = (unixtime - duration).as_secs() as i64;
        ScanDir::dirs().skip_hidden(false).read("/vagga/base/.roots", |iter| {
            for (entry, name) in iter {
                let luse_path = entry.path().join("last_use");
                match metadata(&luse_path) {
                    Ok(ref meta) if meta.mtime() > cut_off => {
                        useful.insert(name);
                    }
                    Ok(_) => {}
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
                    Err(e) => {
                        error!("Error trying to stat {:?}: {}", luse_path, e);
                    }
                }
            }
        }).map_err(|e| {
            error!("Error reading `.vagga/.roots`: {}", e);
        }).ok();
    } else {
        for (name, _) in &wrapper.config.containers {
            if let Some(version) = try!(get_version_hash(name, wrapper)) {
                useful.insert(format!("{}.{}", name, &version[..8]));
            }
        }
    }
    info!("Useful images {:?}", useful);
    try!(clean_dirs_except("/vagga/base/.roots", &useful, dry_run));

    return Ok(());
}
<commit_msg>Implement `_clean --everything` (#277)<commit_after>use std::collections::HashSet;
use std::fs::{read_dir, read_link, remove_file, metadata};
use std::io::{self, stdout, stderr};
use std::ffi::OsStr;
use std::path::Path;
use std::str::FromStr;
use std::time::{SystemTime, UNIX_EPOCH, Duration};
use std::os::unix::fs::MetadataExt;

use argparse::{ArgumentParser, PushConst, StoreTrue, StoreOption};
use scan_dir::ScanDir;
use humantime;

use super::setup;
use super::Wrapper;
use container::util::clean_dir;
use file_util::{read_visible_entries, Lock};
use wrapper::build::get_version_hash;

#[derive(Clone, Copy)]
enum Action {
    Temporary,
    Old,
    Unused,
    Everything,
    Transient,
}

pub fn clean_cmd(wrapper: &Wrapper, cmdline: Vec<String>)
    -> Result<i32, String>
{
    let mut global = false;
    let mut dry_run = false;
    let mut actions = vec!();
    let mut duration = None::<humantime::Duration>;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            Performs various cleanup tasks
            ");
        ap.refer(&mut actions)
          .add_option(&["--tmp", "--tmp-folders"],
                PushConst(Action::Temporary),
                "Clean temporary containers (failed builds)")
          .add_option(&["--old", "--old-containers"], PushConst(Action::Old), "
                Clean old versions of containers (those which doesn't have a \
                symlink in .vagga)")
          .add_option(&["--unused"], PushConst(Action::Unused), "
                Clean unused containers, or versions thereof. (This is not \
                `--old` for historical reasons, we will probably merge the \
                commands later on)")
          .add_option(&["--transient"], PushConst(Action::Transient),
                "Clean unneeded transient folders (left from containers with
                 `write-mode` set to transient-something). The pid of process
                 is checked for liveness first.")
          .add_option(&["--everything"], PushConst(Action::Everything),
                "Clean whole `.vagga` folder. Useful when deleting a project.
                 With ``--global`` cleans whole storage-dir and cache-dir")
          .required();
        ap.refer(&mut global)
          .add_option(&["--global"], StoreTrue,
                "Apply cleanup command to all containers. Works only \
                if `storage-dir` is configured in settings");
        ap.refer(&mut dry_run)
          .add_option(&["-n", "--dry-run"], StoreTrue,
                "Dry run. Don't delete everything, just print");
        ap.refer(&mut duration)
          .add_option(&["--at-least"], StoreOption, "
            Only in combination with `--unused`. Treat as unused \
            containers that are unused for specified time, rather than \
            the ones not used by current version of config");
        match ap.parse(cmdline, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => {
                return Ok(1);
            }
        }
    }
    if global && wrapper.ext_settings.storage_dir.is_none() {
        error!("The --global flag is only meaningful if you configure \
            storage-dir in settings");
        return Ok(2);
    }
    let duration = duration.map(|x| x.into());
    for action in actions.iter() {
        let res = match *action {
            Action::Temporary => clean_temporary(wrapper, global, dry_run),
            Action::Old => clean_old(wrapper, global, dry_run),
            Action::Unused => {
                clean_unused(wrapper, global, duration, dry_run)
            }
            Action::Transient => clean_transient(wrapper, global, dry_run),
            Action::Everything => clean_everything(wrapper, global, dry_run),
        };
        match res {
            Ok(()) => {}
            Err(err) => {
                error!("Error cleaning up: {}", err);
                return Ok(3);
            }
        }
    }
    return Ok(0);
}

fn clean_dir_wrapper(path: &Path,
        remove_dir_itself: bool, dry_run: bool) -> Result<(), String> {
    // TODO(tailhook) chroot to dir for removing
    if dry_run {
        println!("Would remove {:?}", path);
    } else {
        let mut n = path.to_path_buf().into_os_string();
        n.push(".lock");
        let lock_name = Path::new(&n);
        let lock_guard = if lock_name.exists() {
            match Lock::exclusive(&lock_name) {
                Ok(x) => Some(x),
                Err(e) => {
                    error!("Failed to lock {:?}: {}, skipping", lock_name, e);
                    return Ok(());
                }
            }
        } else {
            None
        };
        debug!("Removing {:?}", path);
        try!(clean_dir(path, remove_dir_itself));
        if let Some(_lock) = lock_guard {
            try!(remove_file(lock_name)
                .map_err(|e| format!("Error removing lock file {:?}: {}",
                    lock_name, e)));
        }
    }
    Ok(())
}

fn clean_everything(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        if let Some(ref cache_dir) = wrapper.ext_settings.cache_dir {
            try!(clean_dir_wrapper(&cache_dir, false, dry_run));
        }
        if let Some(ref storage_dir) = wrapper.ext_settings.storage_dir {
            try!(clean_dir_wrapper(&storage_dir, false, dry_run));
        }
    } else {
        let base = match try!(setup::get_vagga_base(
            wrapper.project_root, wrapper.ext_settings))
        {
            Some(base) => base,
            None => {
                warn!("No vagga directory exists");
                return Ok(());
            }
        };
        try!(clean_dir_wrapper(&base, true, dry_run));
        let inner = wrapper.project_root.join(".vagga");
        if base != inner {
            try!(clean_dir_wrapper(&inner, true, dry_run));
        }

    }
    return Ok(());
}

fn clean_temporary(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let roots = base.join(".roots");
    for entry in try_msg!(read_dir(&roots),
        "Can't read dir {r:?}: {err}", r=roots)
    {
        let entry = try_msg!(entry, "Can't read dir {r:?}: {err}", r=roots);
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=entry.path());
        if typ.is_dir() &&
           entry.file_name()[..].to_str().map(|n| n.starts_with(".tmp"))
                                         .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), true, dry_run));
        }
    }

    return Ok(());
}

fn clean_old(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let useful: HashSet<String> = try_msg!(
        read_visible_entries(&wrapper.project_root.join(".vagga")),
            "Can't read vagga directory: {err}")
        .into_iter()
        .filter_map(|path| read_link(&path)
             .map_err(|e| warn!("Can't readlink {:?}: {}", path, e))
             .ok()
             .and_then(|f| {
                 // The container name is next to the last component
                 f.iter().rev().nth(1)
                 .and_then(|x| x.to_str()).map(ToString::to_string)
             }))
        .collect();

    info!("Useful images {:?}", useful);
    try!(clean_dirs_except(&base.join(".roots"), &useful, dry_run));

    return Ok(());
}

fn clean_transient(wrapper: &Wrapper, global: bool, dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }
    let base = match try!(setup::get_vagga_base(
        wrapper.project_root, wrapper.ext_settings))
    {
        Some(base) => base,
        None => {
            warn!("No vagga directory exists");
            return Ok(());
        }
    };
    let procfs = Path::new("/proc");
    for entry in try_msg!(read_dir(&base.join(".transient")),
                        "Can't read .vagga/.transient dir: {err}")
    {
        let entry = try_msg!(entry, "Error reading .vagga/transient: {err}");
        if let Some(fname) = entry.file_name()[..].to_str() {
            if let Some(idx) = fname.find('.') {
                if u32::from_str(&fname[idx+1..]).is_ok() &&
                    procfs.join(&fname[idx+1..]).exists()
                {
                    continue;
                }
            }
        }
        try!(clean_dir_wrapper(&entry.path(), true, dry_run));
    }

    return Ok(());
}

fn clean_dirs_except<P: AsRef<Path>>(roots: P, useful: &HashSet<String>,
    dry_run: bool)
    -> Result<(), String>
{
    let roots = roots.as_ref();
    for entry in try_msg!(read_dir(&roots),
                         "Can't read dir {dir:?}: {err}", dir=roots)
    {
        let entry = try_msg!(entry,
                             "Can't read dir {dir:?}: {err}", dir=roots);
        let path = entry.path();
        let typ = try_msg!(entry.file_type(),
            "Can't stat {p:?}: {err}", p=path);
        if !typ.is_dir() {
            if path.extension() == Some(OsStr::new("lock")) &&
               path.with_extension("").is_dir()
            {
                debug!("Skipping lock file {:?}", path);
            } else {
                try_msg!(remove_file(&path),
                    "Can't remove file {p:?}: {err}", p=path);
            }
        } else if !typ.is_dir() || entry.file_name()[..].to_str()
            .map(|n| !useful.contains(&n.to_string()))
            .unwrap_or(false)
        {
            try!(clean_dir_wrapper(&entry.path(), true, dry_run));
        }
    }
    Ok(())
}

fn clean_unused(wrapper: &Wrapper, global: bool, duration: Option<Duration>,
    dry_run: bool)
    -> Result<(), String>
{
    if global {
        panic!("Global cleanup is not implemented yet");
    }

    try!(setup::setup_base_filesystem(
        wrapper.project_root, wrapper.ext_settings));

    let mut useful: HashSet<String> = HashSet::new();
    if let Some(duration) = duration {
        let unixtime = SystemTime::now().duration_since(UNIX_EPOCH).unwrap();
        let cut_off = (unixtime - duration).as_secs() as i64;
        ScanDir::dirs().skip_hidden(false).read("/vagga/base/.roots", |iter| {
            for (entry, name) in iter {
                let luse_path = entry.path().join("last_use");
                match metadata(&luse_path) {
                    Ok(ref meta) if meta.mtime() > cut_off => {
                        useful.insert(name);
                    }
                    Ok(_) => {}
                    Err(ref e) if e.kind() == io::ErrorKind::NotFound => {}
                    Err(e) => {
                        error!("Error trying to stat {:?}: {}", luse_path, e);
                    }
                }
            }
        }).map_err(|e| {
            error!("Error reading `.vagga/.roots`: {}", e);
        }).ok();
    } else {
        for (name, _) in &wrapper.config.containers {
            if let Some(version) = try!(get_version_hash(name, wrapper)) {
                useful.insert(format!("{}.{}", name, &version[..8]));
            }
        }
    }
    info!("Useful images {:?}", useful);
    try!(clean_dirs_except("/vagga/base/.roots", &useful, dry_run));

    return Ok(());
}
<|endoftext|>


<commit_before>use std::str::FromStr;

use core::settings::{ParseSettings, Settings};

use self::ParseColorSchemeError::*;

#[derive(Default)]
pub struct ColorScheme {
    settings: ColorSchemeSettings,
    scopes: Vec<ColorSchemeScope>
}

#[derive(Default)]
pub struct ColorSchemeSettings {
    /// Foreground color for the view.
    foreground: Color,
    /// Backgound color of the view.
    background: Color,
    /// Color of the caret.
    caret: Color,
    /// Color of the line the caret is in.
    /// Only used when the `higlight_line` setting is set to `true`.
    line_highlight: Color,

    /// Color of bracketed sections of text when the caret is in a bracketed section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    bracket_contents_foreground: Color,
    /// Controls certain options when the caret is in a bracket section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    bracket_contents_options: UnderlineOption,
    /// Foreground color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    brackets_foreground: Color,
    /// Background color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    brackets_background: Color,
    /// Controls certain options when the caret is next to a bracket.
    /// Only applied when the match_brackets setting is set to `true`.
    brackets_options: UnderlineOption,

    /// Color of tags when the caret is next to a tag.
    /// Only used when the `match_tags` setting is set to `true`.
    tags_foreground: Color,
    /// Controls certain options when the caret is next to a tag.
    /// Only applied when the match_tags setting is set to `true`.
    tagsOptions: UnderlineOption,

    /// Background color of regions matching the current search.
    find_highlight: Color,
    /// Background color of regions matching the current search.
    find_highlight_foreground: Color,

    /// Background color of the gutter.
    gutter: Color,
    /// Foreground color of the gutter.
    gutter_foreground: Color,

    /// Color of the selection regions.
    selection: Color,
    /// Background color of the selection regions.
    selection_background: Color,
    /// Color of the selection regions border.
    selection_border: Color,
    /// Color of inactive selections (inactive view).
    inactive_selection: Color,

    /// Color of the guides displayed to indicate nesting levels.
    guide: Color,
    /// Color of the guide lined up with the caret.
    /// Only applied if the `indent_guide_options` setting is set to `draw_active`.
    active_guide: Color,
    /// Color of the current guide’s parent guide level.
    /// Only used if the `indent_guide_options` setting is set to `draw_active`.
    stack_guide: Color,

    /// Background color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    highlight: Color,
    /// Foreground color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    highlight_foreground: Color
}

#[derive(Default)]
pub struct ColorSchemeScope {
    /// Target scope name.
    scope: String,
    /// Style of the font.
    font_style: FontStyle,
    /// Foreground color.
    foreground: Option<Color>,
    /// Background color.
    background: Option<Color>
}

#[derive(Default)]
pub struct Color {
    r: u8,
    g: u8,
    b: u8,
    a: u8
}

bitflags! {
    flags FontStyle: u8 {
        const FONT_STYLE_BOLD = 1,
        const FONT_STYLE_UNDERLNINE = 2,
        const FONT_STYLE_ITALIC = 4,
    }
}

pub enum UnderlineOption {
    None,
    Underline,
    StippledUnderline,
    SquigglyUnderline
}

pub enum ParseColorSchemeError {
    IncorrectUnderlineOption,
    IncorrectFontStyle,
    IncorrectColor,
    IncorrectSyntax,
    UndefinedSettings,
    UndefinedScopeSettings,
    ColorShemeScopeIsNotObject,
    ColorShemeSettingsIsNotObject,
    ScopeSelectorIsNotString
}

impl Default for UnderlineOption {
    fn default() -> UnderlineOption {
        UnderlineOption::None
    }
}

impl Default for FontStyle {
    fn default() -> FontStyle {
        FontStyle::empty()
    }
}

impl FromStr for UnderlineOption {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
        Ok(match s {
            "underline" => UnderlineOption::Underline,
            "stippled_underline" => UnderlineOption::StippledUnderline,
            "squiggly_underline" => UnderlineOption::SquigglyUnderline,
            _ => return Err(IncorrectUnderlineOption)
        })
    }
}

impl ParseSettings for UnderlineOption {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(UnderlineOption::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl FromStr for FontStyle {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
        let mut font_style = FontStyle::empty();
        for i in s.split(' ') {
            font_style.insert(match s {
                "bold" => FONT_STYLE_BOLD,
                "underline" => FONT_STYLE_UNDERLNINE,
                "italic" => FONT_STYLE_ITALIC,
                _ => return Err(IncorrectFontStyle),
            })
        }
        Ok(font_style)
    }
}

impl ParseSettings for FontStyle {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(FontStyle::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl FromStr for Color {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<Color, Self::Err> {
        let mut chars = s.chars();
        if chars.next() != Some('#') {
            return Err(IncorrectColor);
        }
        let mut d = Vec::new();
        for char in chars {
            d.push(try!(char.to_digit(16).ok_or(IncorrectColor)) as u8);
        }
        Ok(match d.len() {
            3 => Color { r: d[0], g: d[1], b: d[2], a: 255 },
            6 => Color { r: d[0]*16+d[1], g: d[2]*16+d[3], b: d[4]*16+d[5], a: 255 },
            8 => Color { r: d[0]*16+d[1], g: d[2]*16+d[3], b: d[4]*16+d[5], a: d[6]*16+d[7] },
            _ => return Err(IncorrectColor)
        })
    }
}

impl ParseSettings for Color {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(Color::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl ParseSettings for ColorSchemeScope {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<ColorSchemeScope, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let scope = match obj.remove("scope") {
            Some(Settings::String(value)) => value,
            _ => return Err(ScopeSelectorIsNotString),
        };
        let font_style = match obj.remove("fontStyle") {
            Some(Settings::String(value)) => try!(FontStyle::from_str(&value)),
            _ => return Err(IncorrectFontStyle),
        };
        let foreground = match obj.remove("foreground") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };
        let background = match obj.remove("background") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };

        Ok(ColorSchemeScope {
            scope: scope,
            font_style: font_style,
            foreground: foreground,
            background: background
        })
    }
}

impl ParseSettings for ColorSchemeSettings {
    type Error = ParseColorSchemeError;

    fn parse_settings(json: Settings) -> Result<ColorSchemeSettings, Self::Error> {
        let mut settings = ColorSchemeSettings::default();

        let mut obj = match json {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeSettingsIsNotObject),
        };

        for (key, value) in obj {
            match &key[..] {
                "foreground" =>
                    settings.foreground = try!(Color::parse_settings(value)),
                "background" =>
                    settings.background = try!(Color::parse_settings(value)),
                "caret" =>
                    settings.caret = try!(Color::parse_settings(value)),
                "lineHighlight" =>
                    settings.line_highlight = try!(Color::parse_settings(value)),
                "bracketContentsForeground" =>
                    settings.bracket_contents_foreground = try!(Color::parse_settings(value)),
                "bracketContentsOptions" =>
                    settings.bracket_contents_options = try!(UnderlineOption::parse_settings(value)),
                "bracketsForeground" =>
                    settings.brackets_foreground = try!(Color::parse_settings(value)),
                "bracketsBackground" =>
                    settings.brackets_background = try!(Color::parse_settings(value)),
                "bracketsOptions" =>
                    settings.brackets_options = try!(UnderlineOption::parse_settings(value)),
                "tagsForeground" =>
                    settings.tags_foreground = try!(Color::parse_settings(value)),
                "tagsOptions" =>
                    settings.tagsOptions = try!(UnderlineOption::parse_settings(value)),
                "findHighlight" =>
                    settings.find_highlight = try!(Color::parse_settings(value)),
                "findHighlightForeground" =>
                    settings.find_highlight_foreground = try!(Color::parse_settings(value)),
                "gutter" =>
                    settings.gutter = try!(Color::parse_settings(value)),
                "gutterForeground" =>
                    settings.gutter_foreground = try!(Color::parse_settings(value)),
                "selection" =>
                    settings.selection = try!(Color::parse_settings(value)),
                "selectionBackground" =>
                    settings.selection_background = try!(Color::parse_settings(value)),
                "selectionBorder" =>
                    settings.selection_border = try!(Color::parse_settings(value)),
                "inactiveSelection" =>
                    settings.inactive_selection = try!(Color::parse_settings(value)),
                "guide" =>
                    settings.guide = try!(Color::parse_settings(value)),
                "activeGuide" =>
                    settings.active_guide = try!(Color::parse_settings(value)),
                "stackGuide" =>
                    settings.stack_guide = try!(Color::parse_settings(value)),
                "highlight" =>
                    settings.highlight = try!(Color::parse_settings(value)),
                "highlightForeground" =>
                    settings.highlight_foreground = try!(Color::parse_settings(value)),
                _ => return Err(UndefinedScopeSettings)
            }
        };
        Ok(settings)
    }
}

impl ParseSettings for ColorScheme {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<ColorScheme, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(IncorrectSyntax)
        };
        let mut items = match obj.remove("settings") {
            Some(Settings::Array(items)) => items,
            _ => return Err(IncorrectSyntax)
        };
        let mut iter = items.into_iter();
        let settings = iter.next().and_then(|settings| {
            let mut obj = match settings {
                Settings::Object(obj) => obj,
                _ => return None
            };
            match obj.remove("settings") {
                Some(settings) => match ColorSchemeSettings::parse_settings(settings) {
                    Ok(settings) => Some(settings),
                    Err(..) => {
                        // TODO: error
                        None
                    }
                },
                _ => None
            }
        }).unwrap_or_else(ColorSchemeSettings::default);
        let mut scopes = Vec::new();
        for json in iter {
            match ColorSchemeScope::parse_settings(json) {
                Ok(scope) => scopes.push(scope),
                Err(..) => {
                    // TODO: error
                }
            }
        }
        Ok(ColorScheme { settings: settings, scopes: scopes })
    }
}
<commit_msg>minor fixes<commit_after>use std::str::FromStr;

use core::settings::{ParseSettings, Settings};

use self::ParseColorSchemeError::*;

#[derive(Default)]
pub struct ColorScheme {
    settings: ColorSchemeSettings,
    scopes: Vec<ColorSchemeScope>
}

#[derive(Default)]
pub struct ColorSchemeSettings {
    /// Foreground color for the view.
    foreground: Color,
    /// Backgound color of the view.
    background: Color,
    /// Color of the caret.
    caret: Color,
    /// Color of the line the caret is in.
    /// Only used when the `higlight_line` setting is set to `true`.
    line_highlight: Color,

    /// Color of bracketed sections of text when the caret is in a bracketed section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    bracket_contents_foreground: Color,
    /// Controls certain options when the caret is in a bracket section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    bracket_contents_options: UnderlineOption,
    /// Foreground color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    brackets_foreground: Color,
    /// Background color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    brackets_background: Color,
    /// Controls certain options when the caret is next to a bracket.
    /// Only applied when the match_brackets setting is set to `true`.
    brackets_options: UnderlineOption,

    /// Color of tags when the caret is next to a tag.
    /// Only used when the `match_tags` setting is set to `true`.
    tags_foreground: Color,
    /// Controls certain options when the caret is next to a tag.
    /// Only applied when the match_tags setting is set to `true`.
    tags_options: UnderlineOption,

    /// Background color of regions matching the current search.
    find_highlight: Color,
    /// Background color of regions matching the current search.
    find_highlight_foreground: Color,

    /// Background color of the gutter.
    gutter: Color,
    /// Foreground color of the gutter.
    gutter_foreground: Color,

    /// Color of the selection regions.
    selection: Color,
    /// Background color of the selection regions.
    selection_background: Color,
    /// Color of the selection regions border.
    selection_border: Color,
    /// Color of inactive selections (inactive view).
    inactive_selection: Color,

    /// Color of the guides displayed to indicate nesting levels.
    guide: Color,
    /// Color of the guide lined up with the caret.
    /// Only applied if the `indent_guide_options` setting is set to `draw_active`.
    active_guide: Color,
    /// Color of the current guide’s parent guide level.
    /// Only used if the `indent_guide_options` setting is set to `draw_active`.
    stack_guide: Color,

    /// Background color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    highlight: Color,
    /// Foreground color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    highlight_foreground: Color
}

#[derive(Default)]
pub struct ColorSchemeScope {
    /// Target scope name.
    scope: String,
    /// Style of the font.
    font_style: FontStyle,
    /// Foreground color.
    foreground: Option<Color>,
    /// Background color.
    background: Option<Color>
}

#[derive(Default)]
pub struct Color {
    r: u8,
    g: u8,
    b: u8,
    a: u8
}

bitflags! {
    flags FontStyle: u8 {
        const FONT_STYLE_BOLD = 1,
        const FONT_STYLE_UNDERLNINE = 2,
        const FONT_STYLE_ITALIC = 4,
    }
}

pub enum UnderlineOption {
    None,
    Underline,
    StippledUnderline,
    SquigglyUnderline
}

pub enum ParseColorSchemeError {
    IncorrectUnderlineOption,
    IncorrectFontStyle,
    IncorrectColor,
    IncorrectSyntax,
    UndefinedSettings,
    UndefinedScopeSettings,
    ColorShemeScopeIsNotObject,
    ColorShemeSettingsIsNotObject,
    ScopeSelectorIsNotString
}

impl Default for UnderlineOption {
    fn default() -> UnderlineOption {
        UnderlineOption::None
    }
}

impl Default for FontStyle {
    fn default() -> FontStyle {
        FontStyle::empty()
    }
}

impl FromStr for UnderlineOption {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
        Ok(match s {
            "underline" => UnderlineOption::Underline,
            "stippled_underline" => UnderlineOption::StippledUnderline,
            "squiggly_underline" => UnderlineOption::SquigglyUnderline,
            _ => return Err(IncorrectUnderlineOption)
        })
    }
}

impl ParseSettings for UnderlineOption {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(UnderlineOption::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl FromStr for FontStyle {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
        let mut font_style = FontStyle::empty();
        for i in s.split(' ') {
            font_style.insert(match i {
                "bold" => FONT_STYLE_BOLD,
                "underline" => FONT_STYLE_UNDERLNINE,
                "italic" => FONT_STYLE_ITALIC,
                _ => return Err(IncorrectFontStyle),
            })
        }
        Ok(font_style)
    }
}

impl ParseSettings for FontStyle {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(FontStyle::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl FromStr for Color {
    type Err = ParseColorSchemeError;

    fn from_str(s: &str) -> Result<Color, Self::Err> {
        let mut chars = s.chars();
        if chars.next() != Some('#') {
            return Err(IncorrectColor);
        }
        let mut d = Vec::new();
        for char in chars {
            d.push(try!(char.to_digit(16).ok_or(IncorrectColor)) as u8);
        }
        Ok(match d.len() {
            3 => Color { r: d[0], g: d[1], b: d[2], a: 255 },
            6 => Color { r: d[0]*16+d[1], g: d[2]*16+d[3], b: d[4]*16+d[5], a: 255 },
            8 => Color { r: d[0]*16+d[1], g: d[2]*16+d[3], b: d[4]*16+d[5], a: d[6]*16+d[7] },
            _ => return Err(IncorrectColor)
        })
    }
}

impl ParseSettings for Color {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(Color::from_str(&value))),
            _ => Err(IncorrectFontStyle)
        }
    }
}

impl ParseSettings for ColorSchemeScope {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<ColorSchemeScope, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let scope = match obj.remove("scope") {
            Some(Settings::String(value)) => value,
            _ => return Err(ScopeSelectorIsNotString),
        };
        let font_style = match obj.remove("fontStyle") {
            Some(Settings::String(value)) => try!(FontStyle::from_str(&value)),
            _ => return Err(IncorrectFontStyle),
        };
        let foreground = match obj.remove("foreground") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };
        let background = match obj.remove("background") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };

        Ok(ColorSchemeScope {
            scope: scope,
            font_style: font_style,
            foreground: foreground,
            background: background
        })
    }
}

impl ParseSettings for ColorSchemeSettings {
    type Error = ParseColorSchemeError;

    fn parse_settings(json: Settings) -> Result<ColorSchemeSettings, Self::Error> {
        let mut settings = ColorSchemeSettings::default();

        let obj = match json {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeSettingsIsNotObject),
        };

        for (key, value) in obj {
            match &key[..] {
                "foreground" =>
                    settings.foreground = try!(Color::parse_settings(value)),
                "background" =>
                    settings.background = try!(Color::parse_settings(value)),
                "caret" =>
                    settings.caret = try!(Color::parse_settings(value)),
                "lineHighlight" =>
                    settings.line_highlight = try!(Color::parse_settings(value)),
                "bracketContentsForeground" =>
                    settings.bracket_contents_foreground = try!(Color::parse_settings(value)),
                "bracketContentsOptions" =>
                    settings.bracket_contents_options = try!(UnderlineOption::parse_settings(value)),
                "bracketsForeground" =>
                    settings.brackets_foreground = try!(Color::parse_settings(value)),
                "bracketsBackground" =>
                    settings.brackets_background = try!(Color::parse_settings(value)),
                "bracketsOptions" =>
                    settings.brackets_options = try!(UnderlineOption::parse_settings(value)),
                "tagsForeground" =>
                    settings.tags_foreground = try!(Color::parse_settings(value)),
                "tagsOptions" =>
                    settings.tags_options = try!(UnderlineOption::parse_settings(value)),
                "findHighlight" =>
                    settings.find_highlight = try!(Color::parse_settings(value)),
                "findHighlightForeground" =>
                    settings.find_highlight_foreground = try!(Color::parse_settings(value)),
                "gutter" =>
                    settings.gutter = try!(Color::parse_settings(value)),
                "gutterForeground" =>
                    settings.gutter_foreground = try!(Color::parse_settings(value)),
                "selection" =>
                    settings.selection = try!(Color::parse_settings(value)),
                "selectionBackground" =>
                    settings.selection_background = try!(Color::parse_settings(value)),
                "selectionBorder" =>
                    settings.selection_border = try!(Color::parse_settings(value)),
                "inactiveSelection" =>
                    settings.inactive_selection = try!(Color::parse_settings(value)),
                "guide" =>
                    settings.guide = try!(Color::parse_settings(value)),
                "activeGuide" =>
                    settings.active_guide = try!(Color::parse_settings(value)),
                "stackGuide" =>
                    settings.stack_guide = try!(Color::parse_settings(value)),
                "highlight" =>
                    settings.highlight = try!(Color::parse_settings(value)),
                "highlightForeground" =>
                    settings.highlight_foreground = try!(Color::parse_settings(value)),
                _ => return Err(UndefinedScopeSettings)
            }
        };
        Ok(settings)
    }
}

impl ParseSettings for ColorScheme {
    type Error = ParseColorSchemeError;

    fn parse_settings(settings: Settings) -> Result<ColorScheme, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(IncorrectSyntax)
        };
        let items = match obj.remove("settings") {
            Some(Settings::Array(items)) => items,
            _ => return Err(IncorrectSyntax)
        };
        let mut settings = None;
        let mut scopes = Vec::new();
        for json in items {
            let mut obj = match json {
                Settings::Object(obj) => obj,
                _ => continue
            };
            match obj.remove("settings") {
                Some(json) => {
                    settings = settings.or_else(||
                        match ColorSchemeSettings::parse_settings(json) {
                            Ok(settings) => Some(settings),
                            Err(..) => {
                                // TODO: error
                                None
                            }
                        }
                    );
                }
                None => match ColorSchemeScope::parse_settings(Settings::Object(obj)) {
                    Ok(scope) => scopes.push(scope),
                    Err(..) => {
                        // TODO: error
                    }
                }
            }
        }
        Ok(ColorScheme {
            settings: settings.unwrap_or_else(ColorSchemeSettings::default),
            scopes: scopes
        })
    }
}
<|endoftext|>


<commit_before>use cairo;
use cairo::MatrixTrait;

use aspect_ratio::AspectRatio;
use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use util::*;
use viewbox::*;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ClipMode {
    ClipToViewport,
    ClipToVbox
}

pub fn draw_in_viewport<F>(vx: f64, vy: f64, vw: f64, vh: f64,
                           clip_mode: ClipMode,
                           do_clip: bool,
                           vbox: Option<ViewBox>,
                           preserve_aspect_ratio: AspectRatio,
                           affine: cairo::Matrix,
                           draw_ctx: *const RsvgDrawingCtx,
                           draw_fn: F)
    where F: FnOnce()
{
    let mut ctx = RsvgDrawingCtxWrapper(draw_ctx);

    in_viewport(&mut ctx,
                vx, vy, vw, vh,
                clip_mode,
                do_clip,
                vbox,
                preserve_aspect_ratio,
                affine,
                draw_fn);
}

trait ViewportCtx {
    fn push_view_box(&mut self, width: f64, height: f64);
    fn pop_view_box(&mut self, );
    fn push_discrete_layer(&mut self);
    fn pop_discrete_layer(&mut self);
    fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64);
    fn set_affine(&mut self, affine: cairo::Matrix);
}

struct RsvgDrawingCtxWrapper(pub *const RsvgDrawingCtx);

impl ViewportCtx for RsvgDrawingCtxWrapper {
    fn push_view_box(&mut self, width: f64, height: f64) {
        drawing_ctx::push_view_box(self.0, width, height);
    }

    fn pop_view_box(&mut self) {
        drawing_ctx::pop_view_box(self.0);
    }

    fn push_discrete_layer(&mut self) {
        drawing_ctx::push_discrete_layer(self.0);
    }

    fn pop_discrete_layer(&mut self) {
        drawing_ctx::pop_discrete_layer(self.0);
    }

    fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
        drawing_ctx::add_clipping_rect(self.0, x, y, w, h);
    }

    fn set_affine(&mut self, affine: cairo::Matrix) {
        drawing_ctx::set_current_state_affine(self.0, affine);
    }
}

fn in_viewport<F>(ctx: &mut ViewportCtx,
                  vx: f64, vy: f64, vw: f64, vh: f64,
                  clip_mode: ClipMode,
                  do_clip: bool,
                  vbox: Option<ViewBox>,
                  preserve_aspect_ratio: AspectRatio,
                  mut affine: cairo::Matrix,
                  draw_fn: F)
    where F: FnOnce()
{
    // width or height set to 0 disables rendering of the element
    // https://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
    // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
    // https://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute
    // https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute

    if double_equals(vw, 0.0) || double_equals(vh, 0.0) {
        return;
    }

    let old_affine = affine;

    if let Some(vbox) = vbox {
        // the preserveAspectRatio attribute is only used if viewBox is specified
        // https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

        let (x, y, w, h) = preserve_aspect_ratio.compute(vbox.0.width, vbox.0.height,
                                                         vx, vy, vw, vh);

        affine.translate(x, y);
        affine.scale(w / vbox.0.width, h / vbox.0.height);
        affine.translate(-vbox.0.x, -vbox.0.y);

        ctx.set_affine(affine);

        ctx.push_view_box(vbox.0.width, vbox.0.height);

        ctx.push_discrete_layer();

        if do_clip && clip_mode == ClipMode::ClipToVbox {
            ctx.add_clipping_rect(vbox.0.x, vbox.0.y, vbox.0.width, vbox.0.height);
        }
    } else {
        affine.translate(vx, vy);
        ctx.set_affine(affine);

        ctx.push_view_box(vw, vh);
        ctx.push_discrete_layer();
    }

    if do_clip && clip_mode == ClipMode::ClipToViewport {
        ctx.set_affine(old_affine);
        ctx.add_clipping_rect(vx, vy, vw, vh);
        ctx.set_affine(affine);
    }

    draw_fn();

    ctx.pop_discrete_layer();
    ctx.pop_view_box();
}

#[cfg(test)]
mod tests {
    use super::*;
    use parsers::Parse;

    #[derive(Default, PartialEq)]
    struct Ctx {
        pub view_box_size: Option<(f64, f64)>,
        pub clipping_rect: Option<(f64, f64, f64, f64)>,
        pub affine:        Option<cairo::Matrix>,

        pub expected_view_box_size: Option<(f64, f64)>,
        pub expected_clipping_rect: Option<(f64, f64, f64, f64)>,
        pub expected_affine:        Option<cairo::Matrix>,
    }

    impl ViewportCtx for Ctx {
        fn push_view_box(&mut self, width: f64, height: f64) {
            self.view_box_size = Some((width, height));
        }

        fn pop_view_box(&mut self) {
        }

        fn push_discrete_layer(&mut self) {
        }

        fn pop_discrete_layer(&mut self) {
        }

        fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
            self.clipping_rect = Some((x, y, w, h));
        }

        fn set_affine(&mut self, affine: cairo::Matrix) {
            self.affine = Some(affine);
        }
    }

    fn call_in_viewport(vx: f64, vy: f64, vw: f64, vh: f64,
                        clip_mode: ClipMode,
                        do_clip: bool,
                        vbox: Option<ViewBox>,
                        preserve_aspect_ratio: AspectRatio,
                        affine: cairo::Matrix,
                        ctx: &mut Ctx) {
        in_viewport(ctx,
                    vx, vy, vw, vh,
                    clip_mode,
                    do_clip,
                    vbox,
                    preserve_aspect_ratio,
                    affine,
                    || ());

        assert_eq!(ctx.view_box_size, ctx.expected_view_box_size);
        assert_eq!(ctx.clipping_rect, ctx.expected_clipping_rect);
        assert_eq!(ctx.affine, ctx.expected_affine);
    }

    #[test]
    fn clip_after_layer_push() {
        let mut affine = cairo::Matrix::identity();
        affine.scale(0.20, 0.20);

        let mut ctx = Ctx {
            view_box_size: None,
            clipping_rect: None,
            affine:        None,

            expected_view_box_size: Some((50.0, 50.0)),
            expected_clipping_rect: Some((10.0, 10.0, 10.0, 10.0)),
            expected_affine: Some(affine)
        };

        call_in_viewport(10.0, 10.0, 10.0, 10.0,
                         ClipMode::ClipToViewport,
                         true,
                         Some(ViewBox(cairo::Rectangle {
                             x: 50.0,
                             y: 50.0,
                             width: 50.0,
                             height: 50.0,
                         })),
                         AspectRatio::parse("xMidYMid meet", ()).unwrap(),
                         cairo::Matrix::identity(),
                         &mut ctx);
    }

    #[test]
    fn clip_before_layer_push() {
        let mut affine = cairo::Matrix::identity();
        affine.translate(10.0, 10.0);
        affine.scale(0.40, 0.40);

        let mut ctx = Ctx {
            view_box_size: None,
            clipping_rect: None,
            affine:        None,

            expected_view_box_size: Some((50.0, 50.0)),
            expected_clipping_rect: Some((0.0, 0.0, 50.0, 50.0)),
            expected_affine: Some(affine)
        };

        call_in_viewport(10.0, 10.0, 20.0, 20.0,
                         ClipMode::ClipToVbox,
                         true,
                         Some(ViewBox(cairo::Rectangle {
                             x: 0.0,
                             y: 0.0,
                             width: 50.0,
                             height: 50.0,
                         })),
                         AspectRatio::parse("xMidYMid meet", ()).unwrap(),
                         cairo::Matrix::identity(),
                         &mut ctx);
    }
}
<commit_msg>viewport.rs: Disable rendering if the viewBox size is 0, per the spec<commit_after>use cairo;
use cairo::MatrixTrait;

use aspect_ratio::AspectRatio;
use drawing_ctx;
use drawing_ctx::RsvgDrawingCtx;
use util::*;
use viewbox::*;

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum ClipMode {
    ClipToViewport,
    ClipToVbox
}

pub fn draw_in_viewport<F>(vx: f64, vy: f64, vw: f64, vh: f64,
                           clip_mode: ClipMode,
                           do_clip: bool,
                           vbox: Option<ViewBox>,
                           preserve_aspect_ratio: AspectRatio,
                           affine: cairo::Matrix,
                           draw_ctx: *const RsvgDrawingCtx,
                           draw_fn: F)
    where F: FnOnce()
{
    let mut ctx = RsvgDrawingCtxWrapper(draw_ctx);

    in_viewport(&mut ctx,
                vx, vy, vw, vh,
                clip_mode,
                do_clip,
                vbox,
                preserve_aspect_ratio,
                affine,
                draw_fn);
}

trait ViewportCtx {
    fn push_view_box(&mut self, width: f64, height: f64);
    fn pop_view_box(&mut self, );
    fn push_discrete_layer(&mut self);
    fn pop_discrete_layer(&mut self);
    fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64);
    fn set_affine(&mut self, affine: cairo::Matrix);
}

struct RsvgDrawingCtxWrapper(pub *const RsvgDrawingCtx);

impl ViewportCtx for RsvgDrawingCtxWrapper {
    fn push_view_box(&mut self, width: f64, height: f64) {
        drawing_ctx::push_view_box(self.0, width, height);
    }

    fn pop_view_box(&mut self) {
        drawing_ctx::pop_view_box(self.0);
    }

    fn push_discrete_layer(&mut self) {
        drawing_ctx::push_discrete_layer(self.0);
    }

    fn pop_discrete_layer(&mut self) {
        drawing_ctx::pop_discrete_layer(self.0);
    }

    fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
        drawing_ctx::add_clipping_rect(self.0, x, y, w, h);
    }

    fn set_affine(&mut self, affine: cairo::Matrix) {
        drawing_ctx::set_current_state_affine(self.0, affine);
    }
}

fn in_viewport<F>(ctx: &mut ViewportCtx,
                  vx: f64, vy: f64, vw: f64, vh: f64,
                  clip_mode: ClipMode,
                  do_clip: bool,
                  vbox: Option<ViewBox>,
                  preserve_aspect_ratio: AspectRatio,
                  mut affine: cairo::Matrix,
                  draw_fn: F)
    where F: FnOnce()
{
    // width or height set to 0 disables rendering of the element
    // https://www.w3.org/TR/SVG/struct.html#SVGElementWidthAttribute
    // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
    // https://www.w3.org/TR/SVG/struct.html#ImageElementWidthAttribute
    // https://www.w3.org/TR/SVG/painting.html#MarkerWidthAttribute

    if double_equals(vw, 0.0) || double_equals(vh, 0.0) {
        return;
    }

    let old_affine = affine;

    if let Some(vbox) = vbox {
        // the preserveAspectRatio attribute is only used if viewBox is specified
        // https://www.w3.org/TR/SVG/coords.html#PreserveAspectRatioAttribute

        if double_equals(vbox.0.width, 0.0) || double_equals(vbox.0.height, 0.0) {
            // Width or height of 0 for the viewBox disables rendering of the element
            // https://www.w3.org/TR/SVG/coords.html#ViewBoxAttribute
            return;
        }
        let (x, y, w, h) = preserve_aspect_ratio.compute(vbox.0.width, vbox.0.height,
                                                         vx, vy, vw, vh);

        affine.translate(x, y);
        affine.scale(w / vbox.0.width, h / vbox.0.height);
        affine.translate(-vbox.0.x, -vbox.0.y);

        ctx.set_affine(affine);

        ctx.push_view_box(vbox.0.width, vbox.0.height);

        ctx.push_discrete_layer();

        if do_clip && clip_mode == ClipMode::ClipToVbox {
            ctx.add_clipping_rect(vbox.0.x, vbox.0.y, vbox.0.width, vbox.0.height);
        }
    } else {
        affine.translate(vx, vy);
        ctx.set_affine(affine);

        ctx.push_view_box(vw, vh);
        ctx.push_discrete_layer();
    }

    if do_clip && clip_mode == ClipMode::ClipToViewport {
        ctx.set_affine(old_affine);
        ctx.add_clipping_rect(vx, vy, vw, vh);
        ctx.set_affine(affine);
    }

    draw_fn();

    ctx.pop_discrete_layer();
    ctx.pop_view_box();
}

#[cfg(test)]
mod tests {
    use super::*;
    use parsers::Parse;

    #[derive(Default, PartialEq)]
    struct Ctx {
        pub view_box_size: Option<(f64, f64)>,
        pub clipping_rect: Option<(f64, f64, f64, f64)>,
        pub affine:        Option<cairo::Matrix>,

        pub expected_view_box_size: Option<(f64, f64)>,
        pub expected_clipping_rect: Option<(f64, f64, f64, f64)>,
        pub expected_affine:        Option<cairo::Matrix>,
    }

    impl ViewportCtx for Ctx {
        fn push_view_box(&mut self, width: f64, height: f64) {
            self.view_box_size = Some((width, height));
        }

        fn pop_view_box(&mut self) {
        }

        fn push_discrete_layer(&mut self) {
        }

        fn pop_discrete_layer(&mut self) {
        }

        fn add_clipping_rect(&mut self, x: f64, y: f64, w: f64, h: f64) {
            self.clipping_rect = Some((x, y, w, h));
        }

        fn set_affine(&mut self, affine: cairo::Matrix) {
            self.affine = Some(affine);
        }
    }

    fn call_in_viewport(vx: f64, vy: f64, vw: f64, vh: f64,
                        clip_mode: ClipMode,
                        do_clip: bool,
                        vbox: Option<ViewBox>,
                        preserve_aspect_ratio: AspectRatio,
                        affine: cairo::Matrix,
                        ctx: &mut Ctx) {
        in_viewport(ctx,
                    vx, vy, vw, vh,
                    clip_mode,
                    do_clip,
                    vbox,
                    preserve_aspect_ratio,
                    affine,
                    || ());

        assert_eq!(ctx.view_box_size, ctx.expected_view_box_size);
        assert_eq!(ctx.clipping_rect, ctx.expected_clipping_rect);
        assert_eq!(ctx.affine, ctx.expected_affine);
    }

    #[test]
    fn clip_after_layer_push() {
        let mut affine = cairo::Matrix::identity();
        affine.scale(0.20, 0.20);

        let mut ctx = Ctx {
            view_box_size: None,
            clipping_rect: None,
            affine:        None,

            expected_view_box_size: Some((50.0, 50.0)),
            expected_clipping_rect: Some((10.0, 10.0, 10.0, 10.0)),
            expected_affine: Some(affine)
        };

        call_in_viewport(10.0, 10.0, 10.0, 10.0,
                         ClipMode::ClipToViewport,
                         true,
                         Some(ViewBox(cairo::Rectangle {
                             x: 50.0,
                             y: 50.0,
                             width: 50.0,
                             height: 50.0,
                         })),
                         AspectRatio::parse("xMidYMid meet", ()).unwrap(),
                         cairo::Matrix::identity(),
                         &mut ctx);
    }

    #[test]
    fn clip_before_layer_push() {
        let mut affine = cairo::Matrix::identity();
        affine.translate(10.0, 10.0);
        affine.scale(0.40, 0.40);

        let mut ctx = Ctx {
            view_box_size: None,
            clipping_rect: None,
            affine:        None,

            expected_view_box_size: Some((50.0, 50.0)),
            expected_clipping_rect: Some((0.0, 0.0, 50.0, 50.0)),
            expected_affine: Some(affine)
        };

        call_in_viewport(10.0, 10.0, 20.0, 20.0,
                         ClipMode::ClipToVbox,
                         true,
                         Some(ViewBox(cairo::Rectangle {
                             x: 0.0,
                             y: 0.0,
                             width: 50.0,
                             height: 50.0,
                         })),
                         AspectRatio::parse("xMidYMid meet", ()).unwrap(),
                         cairo::Matrix::identity(),
                         &mut ctx);
    }
}
<|endoftext|>