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

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword(
    "vector-effect",
    "none non-scaling-stroke",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
)}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type(
    "stop-opacity",
    "Opacity",
    "1.0",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty",
)}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type(
    "flood-opacity",
    "Opacity",
    "1.0",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://drafts.fxtf.org/filter-effects/#FloodOpacityProperty",
)}

${helpers.predefined_type(
    "lighting-color",
    "Color",
    "RGBA::new(255, 255, 255, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword(
    "mask-type",
    "luminance alpha",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
)}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    engines="gecko",
    animation_value_type="basic_shape::ClippingShape",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword(
    "mask-mode",
    "match-source alpha luminance",
    engines="gecko",
    gecko_enum_prefix="StyleMaskMode",
    vector=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
)}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    engines="gecko",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        "computed::LengthPercentage::zero()",
        engines="gecko",
        extra_prefixes="webkit",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    engines="gecko",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    engines="gecko",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    engines="gecko",
    initial_specified_value="specified::BackgroundSize::auto()",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword(
    "mask-composite",
    "add subtract intersect exclude",
    engines="gecko",
    vector=True,
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
)}

${helpers.predefined_type(
    "mask-image",
    "ImageLayer",
    engines="gecko",
    initial_value="computed::ImageLayer::none()",
    initial_specified_value="specified::ImageLayer::none()",
    parse_method="parse_with_cors_anonymous",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT",
)}

${helpers.predefined_type(
    "x",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#X",
)}

${helpers.predefined_type(
    "y",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#Y",
)}

${helpers.predefined_type(
    "cx",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#CX",
)}

${helpers.predefined_type(
    "cy",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#CY",
)}

${helpers.predefined_type(
    "rx",
    "NonNegativeLengthPercentageOrAuto",
    "computed::NonNegativeLengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="LengthPercentageOrAuto",
    spec="https://svgwg.org/svg2-draft/geometry.html#RX",
)}

${helpers.predefined_type(
    "ry",
    "NonNegativeLengthPercentageOrAuto",
    "computed::NonNegativeLengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="LengthPercentageOrAuto",
    spec="https://svgwg.org/svg2-draft/geometry.html#RY",
)}

${helpers.predefined_type(
    "r",
    "NonNegativeLengthPercentage",
    "computed::NonNegativeLengthPercentage::zero()",
    engines="gecko",
    animation_value_type="LengthPercentage",
    spec="https://svgwg.org/svg2-draft/geometry.html#R",
)}
<commit_msg>style: Convert mask-type #defines to an enum class.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<% data.new_style_struct("SVG", inherited=False, gecko_name="SVGReset") %>

${helpers.single_keyword(
    "vector-effect",
    "none non-scaling-stroke",
    engines="gecko",
    animation_value_type="discrete",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#VectorEffectProperty",
)}

// Section 13 - Gradients and Patterns

${helpers.predefined_type(
    "stop-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedRGBA",
    spec="https://www.w3.org/TR/SVGTiny12/painting.html#StopColorProperty",
)}

${helpers.predefined_type(
    "stop-opacity",
    "Opacity",
    "1.0",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/pservers.html#StopOpacityProperty",
)}

// Section 15 - Filter Effects

${helpers.predefined_type(
    "flood-color",
    "Color",
    "RGBA::new(0, 0, 0, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#FloodColorProperty",
)}

${helpers.predefined_type(
    "flood-opacity",
    "Opacity",
    "1.0",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://drafts.fxtf.org/filter-effects/#FloodOpacityProperty",
)}

${helpers.predefined_type(
    "lighting-color",
    "Color",
    "RGBA::new(255, 255, 255, 255).into()",
    engines="gecko",
    animation_value_type="AnimatedColor",
    spec="https://www.w3.org/TR/SVG/filters.html#LightingColorProperty",
)}

// CSS Masking Module Level 1
// https://drafts.fxtf.org/css-masking
${helpers.single_keyword(
    "mask-type",
    "luminance alpha",
    engines="gecko",
    gecko_enum_prefix="StyleMaskType",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-type",
)}

${helpers.predefined_type(
    "clip-path",
    "basic_shape::ClippingShape",
    "generics::basic_shape::ShapeSource::None",
    engines="gecko",
    animation_value_type="basic_shape::ClippingShape",
    flags="CREATES_STACKING_CONTEXT",
    spec="https://drafts.fxtf.org/css-masking/#propdef-clip-path",
)}

${helpers.single_keyword(
    "mask-mode",
    "match-source alpha luminance",
    engines="gecko",
    gecko_enum_prefix="StyleMaskMode",
    vector=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-mode",
)}

${helpers.predefined_type(
    "mask-repeat",
    "BackgroundRepeat",
    "computed::BackgroundRepeat::repeat()",
    engines="gecko",
    initial_specified_value="specified::BackgroundRepeat::repeat()",
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-repeat",
    vector=True,
)}

% for (axis, direction) in [("x", "Horizontal"), ("y", "Vertical")]:
    ${helpers.predefined_type(
        "mask-position-" + axis,
        "position::" + direction + "Position",
        "computed::LengthPercentage::zero()",
        engines="gecko",
        extra_prefixes="webkit",
        initial_specified_value="specified::PositionComponent::Center",
        spec="https://drafts.fxtf.org/css-masking/#propdef-mask-position",
        animation_value_type="ComputedValue",
        vector_animation_type="repeatable_list",
        vector=True,
    )}
% endfor

${helpers.single_keyword(
    "mask-clip",
    "border-box content-box padding-box",
    engines="gecko",
    extra_gecko_values="fill-box stroke-box view-box no-clip",
    vector=True,
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-clip",
)}

${helpers.single_keyword(
    "mask-origin",
    "border-box content-box padding-box",
    engines="gecko",
    extra_gecko_values="fill-box stroke-box view-box",
    vector=True,
    extra_prefixes="webkit",
    gecko_enum_prefix="StyleGeometryBox",
    gecko_inexhaustive=True,
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-origin",
)}

${helpers.predefined_type(
    "mask-size",
    "background::BackgroundSize",
    "computed::BackgroundSize::auto()",
    engines="gecko",
    initial_specified_value="specified::BackgroundSize::auto()",
    extra_prefixes="webkit",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-size",
    animation_value_type="MaskSizeList",
    vector=True,
    vector_animation_type="repeatable_list",
)}

${helpers.single_keyword(
    "mask-composite",
    "add subtract intersect exclude",
    engines="gecko",
    vector=True,
    extra_prefixes="webkit",
    animation_value_type="discrete",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-composite",
)}

${helpers.predefined_type(
    "mask-image",
    "ImageLayer",
    engines="gecko",
    initial_value="computed::ImageLayer::none()",
    initial_specified_value="specified::ImageLayer::none()",
    parse_method="parse_with_cors_anonymous",
    spec="https://drafts.fxtf.org/css-masking/#propdef-mask-image",
    vector=True,
    extra_prefixes="webkit",
    animation_value_type="discrete",
    flags="CREATES_STACKING_CONTEXT",
)}

${helpers.predefined_type(
    "x",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#X",
)}

${helpers.predefined_type(
    "y",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#Y",
)}

${helpers.predefined_type(
    "cx",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#CX",
)}

${helpers.predefined_type(
    "cy",
    "LengthPercentage",
    "computed::LengthPercentage::zero()",
    engines="gecko",
    animation_value_type="ComputedValue",
    spec="https://svgwg.org/svg2-draft/geometry.html#CY",
)}

${helpers.predefined_type(
    "rx",
    "NonNegativeLengthPercentageOrAuto",
    "computed::NonNegativeLengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="LengthPercentageOrAuto",
    spec="https://svgwg.org/svg2-draft/geometry.html#RX",
)}

${helpers.predefined_type(
    "ry",
    "NonNegativeLengthPercentageOrAuto",
    "computed::NonNegativeLengthPercentageOrAuto::auto()",
    engines="gecko",
    animation_value_type="LengthPercentageOrAuto",
    spec="https://svgwg.org/svg2-draft/geometry.html#RY",
)}

${helpers.predefined_type(
    "r",
    "NonNegativeLengthPercentage",
    "computed::NonNegativeLengthPercentage::zero()",
    engines="gecko",
    animation_value_type="LengthPercentage",
    spec="https://svgwg.org/svg2-draft/geometry.html#R",
)}
<|endoftext|>


<commit_before>#![feature(if_let)]
#![feature(phase)]

pub mod ast;
pub mod parser;
pub mod eval;
<commit_msg>Removed if-let feature enabling, since it's not part of Rust.<commit_after>#![feature(phase)]

pub mod ast;
pub mod parser;
pub mod eval;
<|endoftext|>


<commit_before>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
use std::io;

use command::*;
use datatypes::args::*;
use grapheme_tables as gr;

mod ansi;
mod notty;

use self::ansi::AnsiData;
use self::notty::NottyData;
use self::State::*;

/// The `Output` struct processes data written to the terminal from the controlling process,
/// parsing it into structured commands. It is implemented as an `Iterator`.
pub struct Output<R: io::BufRead> {
    tty: io::Chars<R>,
    state: State,
    ansi: AnsiData,
    notty: NottyData,
}

impl<R: io::BufRead> Output<R> {

    /// Create a new output processor wrapping a buffered read interface to the tty.
    pub fn new(tty: R) -> Output<R> {
        Output {
            tty: tty.chars(),
            state: Character,
            ansi: AnsiData::default(),
            notty: NottyData::default(),
        }
    }

    fn character(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        use grapheme_tables::GraphemeCat::*;
        match gr::grapheme_category(ch) {
            GC_Any                      => (Character, wrap(Put::new_char(ch))),
            GC_Control                  => match ch {
                '\x07'      => (Character, wrap(Bell)),
                '\x08'      => (Character, wrap(Move::new(To(Left, 1, true)))),
                '\t'        => (Character, wrap(Move::new(Tab(Right, 1, true)))),
                '\n'        => (Character, wrap(Move::new(NextLine(1)))),
                '\r'        => (Character, wrap(Move::new(ToEdge(Left)))),
                '\x1b'      => (EscCode, None),
                '\x7f'      => (Character, wrap(Erase::new(CursorCell))),
                '\u{90}'    => (DcsCode, None),
                '\u{9b}'    => (CsiCode, None),
                '\u{9d}'    => (OscCode, None),
                '\u{9e}'    => (PrivMsg, None),
                '\u{9f}'    => (ApcCode, None),
                _           => (Character, None),
            },
            GC_Extend | GC_SpacingMark  => (Character, wrap(Put::new_extension(ch))),
            _                           => unimplemented!(),
        }
    }

    fn esc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match ch {
            ' ' => {
                static IGNORE: &'static [char] = &['F', 'G', 'L', 'N'];
                (Ignore(IGNORE), None)
            }
            '#' => {
                static IGNORE: &'static [char] = &['3', '4', '5', '6', '8'];
                (Ignore(IGNORE), None)
            }
            '%' => {
                static IGNORE: &'static [char] = &['@', 'G'];
                (Ignore(IGNORE), None)
            }
            '('...'/'   => {
                static IGNORE: &'static [char] = &[
                    '0', '<', '>', '%', 'A', 'B', '4', 'C', '5', 'R', 'f', 'Q', '9', 'K', 'Y',
                    '`', 'E', '6', 'Z', 'H', '7', '=',
                ];
                (Ignore(IGNORE), None)
            }
            'E' => (Character, wrap(Move::new(NextLine(1)))),
            'P' => (DcsCode, None),
            '[' => (CsiCode, None),
            ']' => (OscCode, None),
            '^' => (PrivMsg, None),
            '_' => (ApcCode, None),
            _   => (Character, wrap(NoFeature(ch.to_string()))),
        }
    }

    fn csi_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        static CSI_PRIVATE_MODES:   &'static [char] = &['>', '?'];
        static CSI_PRETERMINALS:    &'static [char] = &[' ', '!', '"', '$', '\'', '*'];
        static CSI_TERMINALS:       &'static [char] = &[
            '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'P', 'S',
            'T', 'X', 'Z', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'l', 'm', 'n',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
        ];

        // Private mode.
        if self.ansi.private_mode == '\0' && self.ansi.args.len() == 0 &&
            CSI_PRIVATE_MODES.contains(&ch) {
                self.ansi.private_mode = ch;
                (CsiCode, None)
        }
        // Digit.
        else if self.ansi.preterminal == '\0' && ch.is_digit(10) {
            self.ansi.arg_buf.push(ch);
            (CsiCode, None)
        }
        // Arg separator.
        else if self.ansi.preterminal == '\0' && ch == ';' {
            let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
            self.ansi.args.push(n);
            self.ansi.arg_buf.clear();
            (CsiCode, None)
        }
        // Preterminal.
        else if self.ansi.preterminal == '\0' && CSI_PRETERMINALS.contains(&ch) {
            if self.ansi.arg_buf.len() > 0 {
                let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
                self.ansi.args.push(n);
                self.ansi.arg_buf.clear();
            }
            self.ansi.preterminal = ch;
            (CsiCode, None)
        }
        // Terminal.
        else if CSI_TERMINALS.contains(&ch) {
            if self.ansi.arg_buf.len() > 0 {
                let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
                self.ansi.args.push(n);
                self.ansi.arg_buf.clear();
            }
            let ret = (Character, self.ansi.csi(ch));
            self.ansi.clear();
            ret
        }
        // Invalid.
        else {
            self.ansi.clear();
            (Character, None)
        }
    }

    #[allow(unused)]
    fn dcs_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        unimplemented!()
    }

    fn osc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        if ch.is_digit(10) && self.ansi.private_mode == '\0' {
            self.ansi.arg_buf.push(ch);
            (OscCode, None)
        }
        else if ch == ';' && self.ansi.private_mode == '\0' {
            let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
            self.ansi.args.push(n);
            self.ansi.arg_buf.clear();
            self.ansi.private_mode = ';';
            (OscCode, None)
        }
        else if ch == '\x1b' && self.ansi.preterminal == '\0' {
            self.ansi.preterminal == '\x1b';
            (OscCode, None)
        }
        else if ch == '\u{9c}' || ch == '\x07' || (ch == '\\' && self.ansi.preterminal == '\x1b') {
            let ret = (Character, self.ansi.osc());
            self.ansi.clear();
            ret
        }
        else if self.ansi.private_mode == ';' {
            if self.ansi.preterminal == '\x1b' {
                self.ansi.arg_buf.push('\x1b');
                self.ansi.preterminal = '\0';
            }
            self.ansi.arg_buf.push(ch);
            (OscCode, None)
        }
        else {
            self.ansi.clear();
            self.character(ch)
        }
    }

    fn apc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match ch {
            '[' => (NottyCode, None),
            _   => self.privacy_message(ch),
        }
    }

    fn privacy_message(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match (self.ansi.preterminal, ch) {
            ('\0', '\x1b')                                  => {
                self.ansi.preterminal = ch;
                (PrivMsg, None)
            }
            (_, '\u{9c}') | (_, '\x07') | ('\x1b', '\\')    => (Character, None),
            ('\0', _)                                       => (PrivMsg, None),
            (_, _)                                          => self.character(ch),
        }
    }

    fn notty_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        if ch.is_digit(16) || ch == ';' || ch == '.' {
            self.notty.args.push(ch);
            (NottyCode, None)
        }
        else if ch == '#' {
            (NottyAttach, None)
        }
        else if ch == '\x07' {
            let ret = (Character, self.notty.parse());
            self.notty.clear();
            ret
        }
        else {
            self.notty.clear();
            (Character, None)
        }
    }

}

impl<R: io::BufRead> Iterator for super::Output<R> {
    type Item = io::Result<Box<Command>>;
    fn next(&mut self) -> Option<io::Result<Box<Command>>> {
        loop {
            match self.tty.next() {
                Some(Ok(ch))                            => {
                    let (state, cmd) = match self.state {
                        Character       => self.character(ch),
                        EscCode         => self.esc_code(ch),
                        CsiCode         => self.csi_code(ch),
                        DcsCode         => self.dcs_code(ch),
                        OscCode         => self.osc_code(ch),
                        ApcCode         => self.apc_code(ch),
                        PrivMsg         => self.privacy_message(ch),
                        NottyCode       => self.notty_code(ch),
                        NottyAttach     => {
                            match self.notty.attachments.append(ch) {
                                Some(true)  => (Character, self.notty.parse()),
                                Some(false) => (Character, None),
                                None        => (NottyAttach, None),
                            }
                        }
                        Ignore(chars)   => {
                            if chars.contains(&ch) { continue }
                            else { self.character(ch) }
                        }
                    };
                    self.state = state;
                    match cmd {
                        Some(cmd)   => return Some(Ok(cmd)),
                        None        => continue
                    }
                },
                Some(Err(io::CharsError::NotUtf8))      => continue,
                Some(Err(io::CharsError::Other(err)))   => return Some(Err(err)),
                None                                    => return None,
            }
        }
    }
}

enum State {
    Character,
    EscCode,
    CsiCode,
    #[allow(dead_code)]
    DcsCode,
    OscCode,
    ApcCode,
    PrivMsg,
    NottyCode,
    NottyAttach,
    Ignore(&'static [char]),
}

fn wrap<T: Command>(cmd: T) -> Option<Box<Command>> {
    Some(Box::new(cmd) as Box<Command>)
}

#[cfg(test)]
mod tests {

    use std::io::BufReader;

    use command::*;
    use super::*;

    fn setup(data: &[u8]) -> Output<BufReader<&[u8]>> {
        Output::new(BufReader::new(data))
    }

    #[test]
    fn graphemes() {
        let mut output = setup("E\u{301}\u{1f4a9}E".as_bytes());
        assert_eq!(&output.next().unwrap().unwrap().repr(), "E");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "\u{301}");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "\u{1f4a9}");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "E");
    }

    #[test]
    fn ctrl_codes() {
        let mut output = setup(b"AB\x07C\n");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "BELL");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "C");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE NEXT LINE 1");
    }

    #[test]
    fn csi_code() {
        let mut output = setup(b"\x1b[7;7HB\x1b[7A\x1b[$rA\x1b[?12h");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE TO 6,6");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE UP 7");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "DEFAULT STYLE IN AREA");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SERIES: SET CURSOR STYLE");
    }

    #[test]
    fn osc_code() {
        let mut output = setup(b"A\x1b]0;Hello, world!\x07B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SET TITLE");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
    }

    #[test]
    fn notty_code() {
        let mut output = setup(b"A\x1b_[30;8.ff.ff.ff\x07\x1b_[19;1;2\x07B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SET TEXT STYLE");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SCROLL SCREEN");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
    }

}
<commit_msg>Use U+009C as notty terminator.<commit_after>//  notty is a new kind of terminal emulator.
//  Copyright (C) 2015 without boats
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
//
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
use std::io;

use command::*;
use datatypes::args::*;
use grapheme_tables as gr;

mod ansi;
mod notty;

use self::ansi::AnsiData;
use self::notty::NottyData;
use self::State::*;

/// The `Output` struct processes data written to the terminal from the controlling process,
/// parsing it into structured commands. It is implemented as an `Iterator`.
pub struct Output<R: io::BufRead> {
    tty: io::Chars<R>,
    state: State,
    ansi: AnsiData,
    notty: NottyData,
}

impl<R: io::BufRead> Output<R> {

    /// Create a new output processor wrapping a buffered read interface to the tty.
    pub fn new(tty: R) -> Output<R> {
        Output {
            tty: tty.chars(),
            state: Character,
            ansi: AnsiData::default(),
            notty: NottyData::default(),
        }
    }

    fn character(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        use grapheme_tables::GraphemeCat::*;
        match gr::grapheme_category(ch) {
            GC_Any                      => (Character, wrap(Put::new_char(ch))),
            GC_Control                  => match ch {
                '\x07'      => (Character, wrap(Bell)),
                '\x08'      => (Character, wrap(Move::new(To(Left, 1, true)))),
                '\t'        => (Character, wrap(Move::new(Tab(Right, 1, true)))),
                '\n'        => (Character, wrap(Move::new(NextLine(1)))),
                '\r'        => (Character, wrap(Move::new(ToEdge(Left)))),
                '\x1b'      => (EscCode, None),
                '\x7f'      => (Character, wrap(Erase::new(CursorCell))),
                '\u{90}'    => (DcsCode, None),
                '\u{9b}'    => (CsiCode, None),
                '\u{9d}'    => (OscCode, None),
                '\u{9e}'    => (PrivMsg, None),
                '\u{9f}'    => (ApcCode, None),
                _           => (Character, None),
            },
            GC_Extend | GC_SpacingMark  => (Character, wrap(Put::new_extension(ch))),
            _                           => unimplemented!(),
        }
    }

    fn esc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match ch {
            ' ' => {
                static IGNORE: &'static [char] = &['F', 'G', 'L', 'N'];
                (Ignore(IGNORE), None)
            }
            '#' => {
                static IGNORE: &'static [char] = &['3', '4', '5', '6', '8'];
                (Ignore(IGNORE), None)
            }
            '%' => {
                static IGNORE: &'static [char] = &['@', 'G'];
                (Ignore(IGNORE), None)
            }
            '('...'/'   => {
                static IGNORE: &'static [char] = &[
                    '0', '<', '>', '%', 'A', 'B', '4', 'C', '5', 'R', 'f', 'Q', '9', 'K', 'Y',
                    '`', 'E', '6', 'Z', 'H', '7', '=',
                ];
                (Ignore(IGNORE), None)
            }
            'E' => (Character, wrap(Move::new(NextLine(1)))),
            'P' => (DcsCode, None),
            '[' => (CsiCode, None),
            ']' => (OscCode, None),
            '^' => (PrivMsg, None),
            '_' => (ApcCode, None),
            _   => (Character, wrap(NoFeature(ch.to_string()))),
        }
    }

    fn csi_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        static CSI_PRIVATE_MODES:   &'static [char] = &['>', '?'];
        static CSI_PRETERMINALS:    &'static [char] = &[' ', '!', '"', '$', '\'', '*'];
        static CSI_TERMINALS:       &'static [char] = &[
            '@', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'P', 'S',
            'T', 'X', 'Z', '`', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'l', 'm', 'n',
            'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '{', '|', '}', '~',
        ];

        // Private mode.
        if self.ansi.private_mode == '\0' && self.ansi.args.len() == 0 &&
            CSI_PRIVATE_MODES.contains(&ch) {
                self.ansi.private_mode = ch;
                (CsiCode, None)
        }
        // Digit.
        else if self.ansi.preterminal == '\0' && ch.is_digit(10) {
            self.ansi.arg_buf.push(ch);
            (CsiCode, None)
        }
        // Arg separator.
        else if self.ansi.preterminal == '\0' && ch == ';' {
            let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
            self.ansi.args.push(n);
            self.ansi.arg_buf.clear();
            (CsiCode, None)
        }
        // Preterminal.
        else if self.ansi.preterminal == '\0' && CSI_PRETERMINALS.contains(&ch) {
            if self.ansi.arg_buf.len() > 0 {
                let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
                self.ansi.args.push(n);
                self.ansi.arg_buf.clear();
            }
            self.ansi.preterminal = ch;
            (CsiCode, None)
        }
        // Terminal.
        else if CSI_TERMINALS.contains(&ch) {
            if self.ansi.arg_buf.len() > 0 {
                let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
                self.ansi.args.push(n);
                self.ansi.arg_buf.clear();
            }
            let ret = (Character, self.ansi.csi(ch));
            self.ansi.clear();
            ret
        }
        // Invalid.
        else {
            self.ansi.clear();
            (Character, None)
        }
    }

    #[allow(unused)]
    fn dcs_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        unimplemented!()
    }

    fn osc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        if ch.is_digit(10) && self.ansi.private_mode == '\0' {
            self.ansi.arg_buf.push(ch);
            (OscCode, None)
        }
        else if ch == ';' && self.ansi.private_mode == '\0' {
            let n = u32::from_str_radix(&self.ansi.arg_buf, 10).unwrap();
            self.ansi.args.push(n);
            self.ansi.arg_buf.clear();
            self.ansi.private_mode = ';';
            (OscCode, None)
        }
        else if ch == '\x1b' && self.ansi.preterminal == '\0' {
            self.ansi.preterminal == '\x1b';
            (OscCode, None)
        }
        else if ch == '\u{9c}' || ch == '\x07' || (ch == '\\' && self.ansi.preterminal == '\x1b') {
            let ret = (Character, self.ansi.osc());
            self.ansi.clear();
            ret
        }
        else if self.ansi.private_mode == ';' {
            if self.ansi.preterminal == '\x1b' {
                self.ansi.arg_buf.push('\x1b');
                self.ansi.preterminal = '\0';
            }
            self.ansi.arg_buf.push(ch);
            (OscCode, None)
        }
        else {
            self.ansi.clear();
            self.character(ch)
        }
    }

    fn apc_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match ch {
            '[' => (NottyCode, None),
            _   => self.privacy_message(ch),
        }
    }

    fn privacy_message(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        match (self.ansi.preterminal, ch) {
            ('\0', '\x1b')                                  => {
                self.ansi.preterminal = ch;
                (PrivMsg, None)
            }
            (_, '\u{9c}') | (_, '\x07') | ('\x1b', '\\')    => (Character, None),
            ('\0', _)                                       => (PrivMsg, None),
            (_, _)                                          => self.character(ch),
        }
    }

    fn notty_code(&mut self, ch: char) -> (State, Option<Box<Command>>) {
        if ch.is_digit(16) || ch == ';' || ch == '.' {
            self.notty.args.push(ch);
            (NottyCode, None)
        }
        else if ch == '#' {
            (NottyAttach, None)
        }
        else if ch == '\u{9c}' {
            let ret = (Character, self.notty.parse());
            self.notty.clear();
            ret
        }
        else {
            self.notty.clear();
            (Character, None)
        }
    }

}

impl<R: io::BufRead> Iterator for super::Output<R> {
    type Item = io::Result<Box<Command>>;
    fn next(&mut self) -> Option<io::Result<Box<Command>>> {
        loop {
            match self.tty.next() {
                Some(Ok(ch))                            => {
                    let (state, cmd) = match self.state {
                        Character       => self.character(ch),
                        EscCode         => self.esc_code(ch),
                        CsiCode         => self.csi_code(ch),
                        DcsCode         => self.dcs_code(ch),
                        OscCode         => self.osc_code(ch),
                        ApcCode         => self.apc_code(ch),
                        PrivMsg         => self.privacy_message(ch),
                        NottyCode       => self.notty_code(ch),
                        NottyAttach     => {
                            match self.notty.attachments.append(ch) {
                                Some(true)  => (Character, self.notty.parse()),
                                Some(false) => (Character, None),
                                None        => (NottyAttach, None),
                            }
                        }
                        Ignore(chars)   => {
                            if chars.contains(&ch) { continue }
                            else { self.character(ch) }
                        }
                    };
                    self.state = state;
                    match cmd {
                        Some(cmd)   => return Some(Ok(cmd)),
                        None        => continue
                    }
                },
                Some(Err(io::CharsError::NotUtf8))      => continue,
                Some(Err(io::CharsError::Other(err)))   => return Some(Err(err)),
                None                                    => return None,
            }
        }
    }
}

enum State {
    Character,
    EscCode,
    CsiCode,
    #[allow(dead_code)]
    DcsCode,
    OscCode,
    ApcCode,
    PrivMsg,
    NottyCode,
    NottyAttach,
    Ignore(&'static [char]),
}

fn wrap<T: Command>(cmd: T) -> Option<Box<Command>> {
    Some(Box::new(cmd) as Box<Command>)
}

#[cfg(test)]
mod tests {

    use std::io::BufReader;

    use command::*;
    use super::*;

    fn setup(data: &[u8]) -> Output<BufReader<&[u8]>> {
        Output::new(BufReader::new(data))
    }

    #[test]
    fn graphemes() {
        let mut output = setup("E\u{301}\u{1f4a9}E".as_bytes());
        assert_eq!(&output.next().unwrap().unwrap().repr(), "E");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "\u{301}");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "\u{1f4a9}");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "E");
    }

    #[test]
    fn ctrl_codes() {
        let mut output = setup(b"AB\x07C\n");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "BELL");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "C");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE NEXT LINE 1");
    }

    #[test]
    fn csi_code() {
        let mut output = setup(b"\x1b[7;7HB\x1b[7A\x1b[$rA\x1b[?12h");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE TO 6,6");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "MOVE UP 7");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "DEFAULT STYLE IN AREA");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SERIES: SET CURSOR STYLE");
    }

    #[test]
    fn osc_code() {
        let mut output = setup(b"A\x1b]0;Hello, world!\x07B");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SET TITLE");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
    }

    #[test]
    fn notty_code() {
        let mut output = setup("A\x1b_[30;8.ff.ff.ff\u{9c}\x1b_[19;1;2\u{9c}B".as_bytes());
        assert_eq!(&output.next().unwrap().unwrap().repr(), "A");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SET TEXT STYLE");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "SCROLL SCREEN");
        assert_eq!(&output.next().unwrap().unwrap().repr(), "B");
    }

}
<|endoftext|>


<commit_before>//! # Command Parser
//!
//! The `commands::parser` module provides:
//!
//! * Functionality for building a tree of commands based on their names.
//! * Parsing tokenized input to select a command. Input tokenization can
//!   be performed by the `commands::tokenizer` module.
//! * Validating parameters.
//! * Performing completion on commands and parameters.
//!
//! The parser is still a work in progress but is rapidly advancing.

pub mod nodes;
pub mod completion;

use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
use parser::nodes::*;
use tokenizer::{Token, TokenType};
use parser::completion::{Complete, Completion};

/// Command parser
///
/// The lifetime parameter `'p` refers to the lifetime of the
/// parser itself, the root node of the command tree that was
/// used to create this parser, as well as the tokens passed
/// into the parser.
///
/// ```
/// use commands::parser::nodes::RootNode;
/// use commands::parser::Parser;
///
/// let mut root = RootNode::new();
/// let mut parser = Parser::new(root);
/// ```
///
/// The parser is constructed as a `mut`able object as most of
/// the methods on it will modify its state.
pub struct Parser<'p> {
    current_node: Rc<Node>,
    /// The nodes which have been accepted during `parse` or `advance`.
    pub nodes: Vec<Rc<Node>>,
    /// The tokens which have been accepted during `parse` or `advance`.
    pub tokens: Vec<Token<'p>>,
    commands: Vec<Rc<CommandNode>>,
    parameters: HashMap<String, String>,
}

impl<'p> Parser<'p> {
    /// Construct a parser with a root node.
    pub fn new(initial_node: Rc<RootNode>) -> Parser<'p> {
        Parser {
            current_node: initial_node,
            nodes: vec![],
            tokens: vec![],
            commands: vec![],
            parameters: HashMap::new(),
        }
    }

    /// Given an optional token, get the possible valid completions
    /// for the current parser state.
    ///
    /// Possible completions are successors of the current node which
    /// are not `hidden`, are `acceptable`, and which match the token,
    /// if one has been provided.
    ///
    /// Nodes may customize the `Complete` trait to customize the
    /// `Completion` and `CompletionOption`s which are generated
    /// for that node.
    ///
    /// Each valid successor node will have one `Completion` in the
    /// result vector. Each `Completion` will have one or more
    /// `CompletionOption` for each valid way that the value may be
    /// entered.
    pub fn complete(&self, token: Option<Token<'p>>) -> Vec<Completion> {
        self.current_node
            .successors()
            .into_iter()
            .filter(|n| {
                // To be a possible completion, the node should not be
                // hidden, it should be acceptable, and if there's a token,
                // it should be a valid match for the node.
                !n.hidden() && n.acceptable(self) &&
                if let Some(t) = token {
                    n.matches(self, t)
                } else {
                    true
                }
            })
            .map(|n| n.complete(token))
            .collect::<Vec<_>>()
    }

    /// Parse a vector of tokens, advancing through the
    /// node hierarchy.
    ///
    /// ```
    /// use commands::parser::nodes::RootNode;
    /// use commands::parser::Parser;
    /// use commands::tokenizer::tokenize;
    ///
    /// let mut root = RootNode::new();
    /// let mut parser = Parser::new(root);
    ///
    /// if let Ok(tokens) = tokenize("show interface") {
    ///     parser.parse(tokens);
    /// }
    /// ```
    pub fn parse(&mut self, tokens: Vec<Token<'p>>) -> Result<(), ParseError<'p>> {
        for token in tokens {
            match token.token_type {
                TokenType::Invalid => unreachable!(),
                TokenType::Whitespace => {}
                TokenType::Word => try!(self.advance(token)),
            }
        }
        Ok(())
    }

    /// Parse a single token, advancing through the node hierarchy.
    pub fn advance(&mut self, token: Token<'p>) -> Result<(), ParseError<'p>> {
        // We clone the current node so that it doesn't stay borrowed
        // and break things when we try to modify it below.
        let cn = self.current_node.clone();
        let matches = cn.successors()
                        .into_iter()
                        .filter(|n| n.acceptable(self) && n.matches(self, token))
                        .collect::<Vec<_>>();
        match matches.len() {
            1 => {
                let matching_node = matches[0];
                matching_node.accept(self, token);
                self.current_node = matching_node.clone();
                self.nodes.push(matching_node.clone());
                self.tokens.push(token);
                Ok(())
            }
            0 => Err(ParseError::NoMatches(token)),
            _ => Err(ParseError::AmbiguousMatch(token)),
        }
    }

    /// Execute the command that has been accepted by the parser.
    ///
    /// * XXX: This should be returning a Result probably.
    pub fn execute(&self) {
        if !self.commands.is_empty() {
            unimplemented!();
            // self.commands[0].execute(self)
        }
    }

    /// Verify that the parser is in a valid state with
    /// respect to having accepted a command and all
    /// required parameters.
    pub fn verify(&self) -> Result<(), VerifyError> {
        if self.commands.is_empty() {
            return Err(VerifyError::NoCommandAccepted);
        } else {
            for expected in self.commands[0].parameters() {
                if expected.required() && !self.parameters.contains_key(expected.name()) {
                    return Err(VerifyError::MissingParameter(expected.name().clone()));
                }
            }
        }
        Ok(())
    }
}

/// Errors that calling `parse` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum ParseError<'t> {
    /// The parser is in an invalid state.
    InvalidState,
    /// There were no matches for the token.
    NoMatches(Token<'t>),
    /// There was more than 1 possible match for the token.
    AmbiguousMatch(Token<'t>), // XXX: One day, add: Vec<&'p Rc<Node>>),
}

impl<'t> Error for ParseError<'t> {
    fn description(&self) -> &str {
        match *self {
            ParseError::InvalidState => "Invalid state.",
            ParseError::NoMatches(_) => "No match.",
            ParseError::AmbiguousMatch(_) => "Ambiguous match.",
        }
    }
}

impl<'t> fmt::Display for ParseError<'t> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Errors that calling `verify` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum VerifyError {
    /// No command has been accepted by the parser.
    NoCommandAccepted,
    /// A required parameter is missing.
    MissingParameter(String),
}

impl Error for VerifyError {
    fn description(&self) -> &str {
        match *self {
            VerifyError::NoCommandAccepted => "No command has been accepted by the parser.",
            VerifyError::MissingParameter(_) => "A required parameter is missing.",
        }
    }
}

impl fmt::Display for VerifyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Can this node be accepted in the current parser state?
pub trait Acceptable {
    /// Can this node be accepted in the current parser state?
    fn acceptable(&self, parser: &Parser) -> bool;
}

impl Acceptable for Rc<Node> {
    /// By default, a node can be accepted when it hasn't been seen yet.
    fn acceptable(&self, parser: &Parser) -> bool {
        !parser.nodes.contains(self)
    }
}

impl Acceptable for RepeatableNode {
    /// A repeatable node can be accepted.
    fn acceptable(&self, _parser: &Parser) -> bool {
        if self.repeatable() {
            return true;
        }
        unimplemented!()
        // This should check nodes.contains, but then go on to check
        // for a repeat marker and whether or not that's been seen.
    }
}

/// Define whether or not a node matches a token.
pub trait Matches {
    /// Does this node match the specified `token`?
    fn matches(&self, parser: &Parser, token: Token) -> bool;
}

impl Matches for Node {
    /// By default, a node matches a `token` when the name of the
    /// node starts with the `token`.
    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.name().starts_with(token.text)
    }
}

/// Define the behavior of a node when it is accepted by the parser.
pub trait Accept {
    /// Accept this node with the given `token` as data.
    ///
    /// This is where parameters are stored, commands added.
    fn accept<'p>(&self, parser: &mut Parser<'p>, token: Token);
}

impl Accept for Node {
    /// By default, nothing needs to happen for `accept`.
    fn accept<'p>(&self, _parser: &mut Parser<'p>, _token: Token) {}
}

impl Accept for Rc<CommandNode> {
    /// Record this command.
    fn accept<'p>(&self, parser: &mut Parser<'p>, _token: Token) {
        if let Some(_) = self.handler() {
            parser.commands.push(self.clone())
        }
    }
}

impl Accept for ParameterNode {
    /// Record this parameter value.
    fn accept<'p>(&self, parser: &mut Parser<'p>, token: Token) {
        if self.repeatable() {
            unimplemented!();
        } else {
            parser.parameters.insert(self.name().clone(), token.text.to_string());
        }
    }
}
<commit_msg>Add test for VerifyErrr::NoCommandAccepted.<commit_after>//! # Command Parser
//!
//! The `commands::parser` module provides:
//!
//! * Functionality for building a tree of commands based on their names.
//! * Parsing tokenized input to select a command. Input tokenization can
//!   be performed by the `commands::tokenizer` module.
//! * Validating parameters.
//! * Performing completion on commands and parameters.
//!
//! The parser is still a work in progress but is rapidly advancing.

pub mod nodes;
pub mod completion;

use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::rc::Rc;
use parser::nodes::*;
use tokenizer::{Token, TokenType};
use parser::completion::{Complete, Completion};

/// Command parser
///
/// The lifetime parameter `'p` refers to the lifetime of the
/// parser itself, the root node of the command tree that was
/// used to create this parser, as well as the tokens passed
/// into the parser.
///
/// ```
/// use commands::parser::nodes::RootNode;
/// use commands::parser::Parser;
///
/// let mut root = RootNode::new();
/// let mut parser = Parser::new(root);
/// ```
///
/// The parser is constructed as a `mut`able object as most of
/// the methods on it will modify its state.
pub struct Parser<'p> {
    current_node: Rc<Node>,
    /// The nodes which have been accepted during `parse` or `advance`.
    pub nodes: Vec<Rc<Node>>,
    /// The tokens which have been accepted during `parse` or `advance`.
    pub tokens: Vec<Token<'p>>,
    commands: Vec<Rc<CommandNode>>,
    parameters: HashMap<String, String>,
}

impl<'p> Parser<'p> {
    /// Construct a parser with a root node.
    pub fn new(initial_node: Rc<RootNode>) -> Parser<'p> {
        Parser {
            current_node: initial_node,
            nodes: vec![],
            tokens: vec![],
            commands: vec![],
            parameters: HashMap::new(),
        }
    }

    /// Given an optional token, get the possible valid completions
    /// for the current parser state.
    ///
    /// Possible completions are successors of the current node which
    /// are not `hidden`, are `acceptable`, and which match the token,
    /// if one has been provided.
    ///
    /// Nodes may customize the `Complete` trait to customize the
    /// `Completion` and `CompletionOption`s which are generated
    /// for that node.
    ///
    /// Each valid successor node will have one `Completion` in the
    /// result vector. Each `Completion` will have one or more
    /// `CompletionOption` for each valid way that the value may be
    /// entered.
    pub fn complete(&self, token: Option<Token<'p>>) -> Vec<Completion> {
        self.current_node
            .successors()
            .into_iter()
            .filter(|n| {
                // To be a possible completion, the node should not be
                // hidden, it should be acceptable, and if there's a token,
                // it should be a valid match for the node.
                !n.hidden() && n.acceptable(self) &&
                if let Some(t) = token {
                    n.matches(self, t)
                } else {
                    true
                }
            })
            .map(|n| n.complete(token))
            .collect::<Vec<_>>()
    }

    /// Parse a vector of tokens, advancing through the
    /// node hierarchy.
    ///
    /// ```
    /// use commands::parser::nodes::RootNode;
    /// use commands::parser::Parser;
    /// use commands::tokenizer::tokenize;
    ///
    /// let mut root = RootNode::new();
    /// let mut parser = Parser::new(root);
    ///
    /// if let Ok(tokens) = tokenize("show interface") {
    ///     parser.parse(tokens);
    /// }
    /// ```
    pub fn parse(&mut self, tokens: Vec<Token<'p>>) -> Result<(), ParseError<'p>> {
        for token in tokens {
            match token.token_type {
                TokenType::Invalid => unreachable!(),
                TokenType::Whitespace => {}
                TokenType::Word => try!(self.advance(token)),
            }
        }
        Ok(())
    }

    /// Parse a single token, advancing through the node hierarchy.
    pub fn advance(&mut self, token: Token<'p>) -> Result<(), ParseError<'p>> {
        // We clone the current node so that it doesn't stay borrowed
        // and break things when we try to modify it below.
        let cn = self.current_node.clone();
        let matches = cn.successors()
                        .into_iter()
                        .filter(|n| n.acceptable(self) && n.matches(self, token))
                        .collect::<Vec<_>>();
        match matches.len() {
            1 => {
                let matching_node = matches[0];
                matching_node.accept(self, token);
                self.current_node = matching_node.clone();
                self.nodes.push(matching_node.clone());
                self.tokens.push(token);
                Ok(())
            }
            0 => Err(ParseError::NoMatches(token)),
            _ => Err(ParseError::AmbiguousMatch(token)),
        }
    }

    /// Execute the command that has been accepted by the parser.
    ///
    /// * XXX: This should be returning a Result probably.
    pub fn execute(&self) {
        if !self.commands.is_empty() {
            unimplemented!();
            // self.commands[0].execute(self)
        }
    }

    /// Verify that the parser is in a valid state with
    /// respect to having accepted a command and all
    /// required parameters.
    pub fn verify(&self) -> Result<(), VerifyError> {
        if self.commands.is_empty() {
            return Err(VerifyError::NoCommandAccepted);
        } else {
            for expected in self.commands[0].parameters() {
                if expected.required() && !self.parameters.contains_key(expected.name()) {
                    return Err(VerifyError::MissingParameter(expected.name().clone()));
                }
            }
        }
        Ok(())
    }
}

/// Errors that calling `parse` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum ParseError<'t> {
    /// The parser is in an invalid state.
    InvalidState,
    /// There were no matches for the token.
    NoMatches(Token<'t>),
    /// There was more than 1 possible match for the token.
    AmbiguousMatch(Token<'t>), // XXX: One day, add: Vec<&'p Rc<Node>>),
}

impl<'t> Error for ParseError<'t> {
    fn description(&self) -> &str {
        match *self {
            ParseError::InvalidState => "Invalid state.",
            ParseError::NoMatches(_) => "No match.",
            ParseError::AmbiguousMatch(_) => "Ambiguous match.",
        }
    }
}

impl<'t> fmt::Display for ParseError<'t> {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Errors that calling `verify` on the `Parser` can raise.
#[derive(Clone,Debug)]
pub enum VerifyError {
    /// No command has been accepted by the parser.
    NoCommandAccepted,
    /// A required parameter is missing.
    MissingParameter(String),
}

impl Error for VerifyError {
    fn description(&self) -> &str {
        match *self {
            VerifyError::NoCommandAccepted => "No command has been accepted by the parser.",
            VerifyError::MissingParameter(_) => "A required parameter is missing.",
        }
    }
}

impl fmt::Display for VerifyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        self.description().fmt(f)
    }
}

/// Can this node be accepted in the current parser state?
pub trait Acceptable {
    /// Can this node be accepted in the current parser state?
    fn acceptable(&self, parser: &Parser) -> bool;
}

impl Acceptable for Rc<Node> {
    /// By default, a node can be accepted when it hasn't been seen yet.
    fn acceptable(&self, parser: &Parser) -> bool {
        !parser.nodes.contains(self)
    }
}

impl Acceptable for RepeatableNode {
    /// A repeatable node can be accepted.
    fn acceptable(&self, _parser: &Parser) -> bool {
        if self.repeatable() {
            return true;
        }
        unimplemented!()
        // This should check nodes.contains, but then go on to check
        // for a repeat marker and whether or not that's been seen.
    }
}

/// Define whether or not a node matches a token.
pub trait Matches {
    /// Does this node match the specified `token`?
    fn matches(&self, parser: &Parser, token: Token) -> bool;
}

impl Matches for Node {
    /// By default, a node matches a `token` when the name of the
    /// node starts with the `token`.
    fn matches(&self, _parser: &Parser, token: Token) -> bool {
        self.name().starts_with(token.text)
    }
}

/// Define the behavior of a node when it is accepted by the parser.
pub trait Accept {
    /// Accept this node with the given `token` as data.
    ///
    /// This is where parameters are stored, commands added.
    fn accept<'p>(&self, parser: &mut Parser<'p>, token: Token);
}

impl Accept for Node {
    /// By default, nothing needs to happen for `accept`.
    fn accept<'p>(&self, _parser: &mut Parser<'p>, _token: Token) {}
}

impl Accept for Rc<CommandNode> {
    /// Record this command.
    fn accept<'p>(&self, parser: &mut Parser<'p>, _token: Token) {
        if let Some(_) = self.handler() {
            parser.commands.push(self.clone())
        }
    }
}

impl Accept for ParameterNode {
    /// Record this parameter value.
    fn accept<'p>(&self, parser: &mut Parser<'p>, token: Token) {
        if self.repeatable() {
            unimplemented!();
        } else {
            parser.parameters.insert(self.name().clone(), token.text.to_string());
        }
    }
}

#[cfg(test)]
mod test {
    use super::nodes::*;
    use super::*;

    #[test]
    #[should_panic]
    fn verify_signals_no_command() {
        let root = RootNode::new();
        let parser = Parser::new(root);
        match parser.verify() {
            Err(VerifyError::NoCommandAccepted) => panic!(),
            _ => {}
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

/// The GdkPixbuf structure contains information that describes an image in memory.

use std::mem;
use std::ptr;
use std::slice;
use libc::c_uchar;
use glib::translate::*;
use glib::types::{StaticType, Type};
use glib::{Error, to_gboolean, GlibContainer};
use object::Object;
use ffi;

use {InterpType};

pub mod animation;
pub mod format;
pub mod loader;

pub use self::animation::{PixbufAnimation, PixbufAnimationIter, PixbufSimpleAnim};
pub use self::format::PixbufFormat;
pub use self::loader::PixbufLoader;

/// This is the main structure in the &gdk-pixbuf; library. It is used to represent images. It contains information about the image's pixel
/// data, its color space, bits per sample, width and height, and the rowstride (the number of bytes between the start of one row and the
/// start of the next).
pub type Pixbuf = Object<ffi::GdkPixbuf>;

impl StaticType for Pixbuf {
    fn static_type() -> Type { unsafe { from_glib(ffi::gdk_pixbuf_get_type()) } }
}

impl Pixbuf {
    pub unsafe fn new(colorspace: ::ColorSpace, has_alpha: bool, bits_per_sample: i32, width: i32,
            height: i32) -> Result<Pixbuf, ()> {
        Option::from_glib_full(ffi::gdk_pixbuf_new(colorspace, has_alpha.to_glib(),
                                                   bits_per_sample, width, height)).ok_or(())
    }

    /// Creates a `Pixbuf` using a `Vec` as image data.
    ///
    /// Only `bits_per_sample == 8` supported.
    pub fn new_from_vec(mut vec: Vec<u8>, colorspace: ::ColorSpace, has_alpha: bool,
            bits_per_sample: i32, width: i32, height: i32, row_stride: i32) -> Pixbuf {
        extern "C" fn destroy_vec(_: *mut c_uchar, data: ffi::gpointer) {
            unsafe{
                let _vec: Box<Vec<u8>> = mem::transmute(data); // the vector will be destroyed now
            }
        }

        assert!(bits_per_sample == 8);
        let n_channels = if has_alpha { 4 } else { 3 };
        let last_row_len = width * ((n_channels * bits_per_sample + 7) / 8);
        assert!(vec.len() == ((height - 1) * row_stride + last_row_len) as usize);
        let ptr = vec.as_mut_ptr();
        let vec: Box<Vec<u8>> = Box::new(vec);
        unsafe {
            from_glib_full(
                ffi::gdk_pixbuf_new_from_data(ptr, colorspace, has_alpha.to_glib(), bits_per_sample,
                    width, height, row_stride, destroy_vec, mem::transmute(vec)))
        }
    }

    pub fn new_from_file(filename: &str) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file(filename.to_glib_none().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_file_at_size(filename: &str, width: i32, height: i32) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file_at_size(filename.to_glib_none().0, width, height, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_file_at_scale(filename: &str, width: i32, height: i32, preserve_aspect_ratio: bool) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file_at_scale(filename.to_glib_none().0, width, height,
            to_gboolean(preserve_aspect_ratio), &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn get_file_info(filename: &str, width: &mut i32, height: &mut i32) -> Option<PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_get_file_info(filename.to_glib_none().0, width, height) };

        unsafe { from_glib_full(tmp) }
    }

    pub fn new_from_resource(resource_path: &str) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_resource(resource_path.to_glib_none().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_resource_at_scale(resource_path: &str, width: i32, height: i32, preserve_aspect_ratio: bool) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_resource_at_scale(resource_path.to_glib_none().0, width, height,
            to_gboolean(preserve_aspect_ratio), &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_subpixbuf(&self, src_x: i32, src_y: i32, width: i32, height: i32) -> Pixbuf {
        unsafe {
            from_glib_full(
                ffi::gdk_pixbuf_new_subpixbuf(self.to_glib_none().0, src_x, src_y, width, height))
        }
    }

    pub fn get_colorspace(&self) -> ::ColorSpace {
        unsafe { ffi::gdk_pixbuf_get_colorspace(self.to_glib_none().0) }
    }

    pub fn get_n_channels(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_n_channels(self.to_glib_none().0) }
    }

    pub fn get_has_alpha(&self) -> bool {
        unsafe { ::glib::to_bool(ffi::gdk_pixbuf_get_has_alpha(self.to_glib_none().0)) }
    }

    pub fn get_bits_per_sample(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_bits_per_sample(self.to_glib_none().0) }
    }

    pub unsafe fn get_pixels(&self) -> &mut [u8] {
        let mut len = 0;
        let ptr = ffi::gdk_pixbuf_get_pixels_with_length(self.to_glib_none().0, &mut len);
        slice::from_raw_parts_mut(ptr, len as usize)
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_width(self.to_glib_none().0) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_height(self.to_glib_none().0) }
    }

    pub fn get_rowstride(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_rowstride(self.to_glib_none().0) }
    }

    pub fn get_byte_length(&self) -> usize {
        unsafe { ffi::gdk_pixbuf_get_byte_length(self.to_glib_none().0) as usize }
    }

    pub fn get_option(&self, key: &str) -> Option<String> {
        unsafe {
            from_glib_none(ffi::gdk_pixbuf_get_option(self.to_glib_none().0, key.to_glib_none().0))
        }
    }

    /// Create a new GdkPixbuf containing a copy of the current pixbuf scaled
    /// to `dest_width` x `dest_height`. The calling pixbuf remains unaffected.
    /// `interp_type` should be `Nearest` if you want maximum speed (but when
    /// scaling down `Nearest` is usually unusably ugly). The default
    /// `interp_type` should be `Bilinear` which offers reasonable quality and
    /// speed.
    ///
    /// You can scale a sub-portion of `src` by creating a sub-pixbuf pointing
    /// into src ; see `new_subpixbuf()`.
    ///
    /// For more complicated scaling/compositing see `scale()` and
    /// `composite()`.
    pub fn scale_simple(&self, dest_width: i32, dest_height: i32, interp_type: InterpType)
        -> Result<Pixbuf, ()> {
        unsafe {
            Option::from_glib_full(ffi::gdk_pixbuf_scale_simple(self.to_glib_none().0, dest_width,
                                                                dest_height, interp_type)).ok_or(())
        }
    }

    /// Creates a transformation of the calling pixbuf by scaling by
    /// `scale_x` and `scale_y` then translating by `offset_x` and `offset_y`,
    /// then renders the rectangle (`dest_x`, `dest_y`, `dest_width`,
    /// `dest_height`) of the resulting pixbuf onto the destination pixbuf,
    /// replacing the previous contents.
    ///
    /// Try to use `scale_simple()` first; this function is the
    /// industrial-strength power tool you can fall back to if `scale_simple()`
    /// isn't powerful enough.
    ///
    /// If the source rectangle overlaps the destination rectangle on the same
    /// pixbuf, it will be overwritten during the scaling which results in
    /// rendering artifacts.
    pub fn scale(&self, dest: &Pixbuf, dest_x: i32, dest_y: i32, dest_width: i32, dest_height: i32,
                 offset_x: f64, offset_y: f64, scale_x: f64, scale_y: f64,
                 interp_type: InterpType) {
        unsafe {
            ffi::gdk_pixbuf_scale(self.to_glib_none().0, dest.to_glib_none().0, dest_x, dest_y,
                                  dest_width, dest_height, offset_x, offset_y, scale_x, scale_y,
                                  interp_type);
        }
    }

    /// Creates a transformation of the calling pixbuf by scaling by `scale_x`
    /// and `scale_y` then translating by `offset_x` and `offset_y`. This gives
    /// an image in the coordinates of the destination pixbuf. The rectangle
    /// (`dest_x`, `dest_y`, `dest_width`, `dest_height`) is then composited
    /// onto the corresponding rectangle of the original destination image.
    ///
    /// ![Diagram of `composite` process](https://developer.gnome.org/gdk-pixbuf/unstable/composite.png)
    ///
    /// When the destination rectangle contains parts not in the source image,
    /// the data at the edges of the source image is replicated to infinity.
    pub fn composite(&self, dest: &Pixbuf, dest_x: i32, dest_y: i32, dest_width: i32,
                     dest_height: i32, offset_x: f64, offset_y: f64, scale_x: f64, scale_y: f64,
                     interp_type: InterpType, overall_alpha: i32) {
        unsafe {
            ffi::gdk_pixbuf_composite(self.to_glib_none().0, dest.to_glib_none().0, dest_x, dest_y,
                                      dest_width, dest_height, offset_x, offset_y, scale_x,
                                      scale_y, interp_type, overall_alpha);
        }
    }

    /// Flips a pixbuf horizontally or vertically and returns the result in a
    /// new pixbuf, or `None` if not enough memory could be allocated for it.
    pub fn flip(&self, horizontal: bool) -> Option<Pixbuf> {
        unsafe {
            from_glib_none(ffi::gdk_pixbuf_flip(self.to_glib_none().0, horizontal.to_glib()))
        }
    }

    /// a convenient function
    /// It won't work for pixbufs with images that are other than 8 bits per sample or channel, but it will work for most of the
    /// pixbufs that GTK+ uses.
    pub fn put_pixel(&self, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) {
        unsafe {
            let n_channels = self.get_n_channels();
            let rowstride = self.get_rowstride();
            let pixels = self.get_pixels();
            let pos = (y * rowstride + x * n_channels) as usize;

            pixels[pos] = red;
            pixels[pos + 1] = green;
            pixels[pos + 2] = blue;
            pixels[pos + 3] = alpha;
        }
    }
}
<commit_msg>Fixing return type for `flip`<commit_after>// Copyright 2013-2015, The Rust-GNOME Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

/// The GdkPixbuf structure contains information that describes an image in memory.

use std::mem;
use std::ptr;
use std::slice;
use libc::c_uchar;
use glib::translate::*;
use glib::types::{StaticType, Type};
use glib::{Error, to_gboolean, GlibContainer};
use object::Object;
use ffi;

use {InterpType};

pub mod animation;
pub mod format;
pub mod loader;

pub use self::animation::{PixbufAnimation, PixbufAnimationIter, PixbufSimpleAnim};
pub use self::format::PixbufFormat;
pub use self::loader::PixbufLoader;

/// This is the main structure in the &gdk-pixbuf; library. It is used to represent images. It contains information about the image's pixel
/// data, its color space, bits per sample, width and height, and the rowstride (the number of bytes between the start of one row and the
/// start of the next).
pub type Pixbuf = Object<ffi::GdkPixbuf>;

impl StaticType for Pixbuf {
    fn static_type() -> Type { unsafe { from_glib(ffi::gdk_pixbuf_get_type()) } }
}

impl Pixbuf {
    pub unsafe fn new(colorspace: ::ColorSpace, has_alpha: bool, bits_per_sample: i32, width: i32,
            height: i32) -> Result<Pixbuf, ()> {
        Option::from_glib_full(ffi::gdk_pixbuf_new(colorspace, has_alpha.to_glib(),
                                                   bits_per_sample, width, height)).ok_or(())
    }

    /// Creates a `Pixbuf` using a `Vec` as image data.
    ///
    /// Only `bits_per_sample == 8` supported.
    pub fn new_from_vec(mut vec: Vec<u8>, colorspace: ::ColorSpace, has_alpha: bool,
            bits_per_sample: i32, width: i32, height: i32, row_stride: i32) -> Pixbuf {
        extern "C" fn destroy_vec(_: *mut c_uchar, data: ffi::gpointer) {
            unsafe{
                let _vec: Box<Vec<u8>> = mem::transmute(data); // the vector will be destroyed now
            }
        }

        assert!(bits_per_sample == 8);
        let n_channels = if has_alpha { 4 } else { 3 };
        let last_row_len = width * ((n_channels * bits_per_sample + 7) / 8);
        assert!(vec.len() == ((height - 1) * row_stride + last_row_len) as usize);
        let ptr = vec.as_mut_ptr();
        let vec: Box<Vec<u8>> = Box::new(vec);
        unsafe {
            from_glib_full(
                ffi::gdk_pixbuf_new_from_data(ptr, colorspace, has_alpha.to_glib(), bits_per_sample,
                    width, height, row_stride, destroy_vec, mem::transmute(vec)))
        }
    }

    pub fn new_from_file(filename: &str) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file(filename.to_glib_none().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_file_at_size(filename: &str, width: i32, height: i32) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file_at_size(filename.to_glib_none().0, width, height, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_file_at_scale(filename: &str, width: i32, height: i32, preserve_aspect_ratio: bool) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_file_at_scale(filename.to_glib_none().0, width, height,
            to_gboolean(preserve_aspect_ratio), &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn get_file_info(filename: &str, width: &mut i32, height: &mut i32) -> Option<PixbufFormat> {
        let tmp = unsafe { ffi::gdk_pixbuf_get_file_info(filename.to_glib_none().0, width, height) };

        unsafe { from_glib_full(tmp) }
    }

    pub fn new_from_resource(resource_path: &str) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_resource(resource_path.to_glib_none().0, &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_from_resource_at_scale(resource_path: &str, width: i32, height: i32, preserve_aspect_ratio: bool) -> Result<Pixbuf, Error> {
        let mut error = ptr::null_mut();
        let tmp = unsafe { ffi::gdk_pixbuf_new_from_resource_at_scale(resource_path.to_glib_none().0, width, height,
            to_gboolean(preserve_aspect_ratio), &mut error) };

        if error.is_null() {
            assert!(!tmp.is_null());
            unsafe { Ok(from_glib_full(tmp)) }
        } else {
            Err(Error::wrap(error))
        }
    }

    pub fn new_subpixbuf(&self, src_x: i32, src_y: i32, width: i32, height: i32) -> Pixbuf {
        unsafe {
            from_glib_full(
                ffi::gdk_pixbuf_new_subpixbuf(self.to_glib_none().0, src_x, src_y, width, height))
        }
    }

    pub fn get_colorspace(&self) -> ::ColorSpace {
        unsafe { ffi::gdk_pixbuf_get_colorspace(self.to_glib_none().0) }
    }

    pub fn get_n_channels(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_n_channels(self.to_glib_none().0) }
    }

    pub fn get_has_alpha(&self) -> bool {
        unsafe { ::glib::to_bool(ffi::gdk_pixbuf_get_has_alpha(self.to_glib_none().0)) }
    }

    pub fn get_bits_per_sample(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_bits_per_sample(self.to_glib_none().0) }
    }

    pub unsafe fn get_pixels(&self) -> &mut [u8] {
        let mut len = 0;
        let ptr = ffi::gdk_pixbuf_get_pixels_with_length(self.to_glib_none().0, &mut len);
        slice::from_raw_parts_mut(ptr, len as usize)
    }

    pub fn get_width(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_width(self.to_glib_none().0) }
    }

    pub fn get_height(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_height(self.to_glib_none().0) }
    }

    pub fn get_rowstride(&self) -> i32 {
        unsafe { ffi::gdk_pixbuf_get_rowstride(self.to_glib_none().0) }
    }

    pub fn get_byte_length(&self) -> usize {
        unsafe { ffi::gdk_pixbuf_get_byte_length(self.to_glib_none().0) as usize }
    }

    pub fn get_option(&self, key: &str) -> Option<String> {
        unsafe {
            from_glib_none(ffi::gdk_pixbuf_get_option(self.to_glib_none().0, key.to_glib_none().0))
        }
    }

    /// Create a new GdkPixbuf containing a copy of the current pixbuf scaled
    /// to `dest_width` x `dest_height`. The calling pixbuf remains unaffected.
    /// `interp_type` should be `Nearest` if you want maximum speed (but when
    /// scaling down `Nearest` is usually unusably ugly). The default
    /// `interp_type` should be `Bilinear` which offers reasonable quality and
    /// speed.
    ///
    /// You can scale a sub-portion of `src` by creating a sub-pixbuf pointing
    /// into src ; see `new_subpixbuf()`.
    ///
    /// For more complicated scaling/compositing see `scale()` and
    /// `composite()`.
    pub fn scale_simple(&self, dest_width: i32, dest_height: i32, interp_type: InterpType)
        -> Result<Pixbuf, ()> {
        unsafe {
            Option::from_glib_full(ffi::gdk_pixbuf_scale_simple(self.to_glib_none().0, dest_width,
                                                                dest_height, interp_type)).ok_or(())
        }
    }

    /// Creates a transformation of the calling pixbuf by scaling by
    /// `scale_x` and `scale_y` then translating by `offset_x` and `offset_y`,
    /// then renders the rectangle (`dest_x`, `dest_y`, `dest_width`,
    /// `dest_height`) of the resulting pixbuf onto the destination pixbuf,
    /// replacing the previous contents.
    ///
    /// Try to use `scale_simple()` first; this function is the
    /// industrial-strength power tool you can fall back to if `scale_simple()`
    /// isn't powerful enough.
    ///
    /// If the source rectangle overlaps the destination rectangle on the same
    /// pixbuf, it will be overwritten during the scaling which results in
    /// rendering artifacts.
    pub fn scale(&self, dest: &Pixbuf, dest_x: i32, dest_y: i32, dest_width: i32, dest_height: i32,
                 offset_x: f64, offset_y: f64, scale_x: f64, scale_y: f64,
                 interp_type: InterpType) {
        unsafe {
            ffi::gdk_pixbuf_scale(self.to_glib_none().0, dest.to_glib_none().0, dest_x, dest_y,
                                  dest_width, dest_height, offset_x, offset_y, scale_x, scale_y,
                                  interp_type);
        }
    }

    /// Creates a transformation of the calling pixbuf by scaling by `scale_x`
    /// and `scale_y` then translating by `offset_x` and `offset_y`. This gives
    /// an image in the coordinates of the destination pixbuf. The rectangle
    /// (`dest_x`, `dest_y`, `dest_width`, `dest_height`) is then composited
    /// onto the corresponding rectangle of the original destination image.
    ///
    /// ![Diagram of `composite` process](https://developer.gnome.org/gdk-pixbuf/unstable/composite.png)
    ///
    /// When the destination rectangle contains parts not in the source image,
    /// the data at the edges of the source image is replicated to infinity.
    pub fn composite(&self, dest: &Pixbuf, dest_x: i32, dest_y: i32, dest_width: i32,
                     dest_height: i32, offset_x: f64, offset_y: f64, scale_x: f64, scale_y: f64,
                     interp_type: InterpType, overall_alpha: i32) {
        unsafe {
            ffi::gdk_pixbuf_composite(self.to_glib_none().0, dest.to_glib_none().0, dest_x, dest_y,
                                      dest_width, dest_height, offset_x, offset_y, scale_x,
                                      scale_y, interp_type, overall_alpha);
        }
    }

    /// Flips a pixbuf horizontally or vertically and returns the result in a
    /// new pixbuf, or `Err` if not enough memory could be allocated for it.
    pub fn flip(&self, horizontal: bool) -> Result<Pixbuf, ()> {
        unsafe {
            Option::from_glib_full((ffi::gdk_pixbuf_flip(self.to_glib_none().0,
                                                         horizontal.to_glib()))).ok_or(())
        }
    }

    /// a convenient function
    /// It won't work for pixbufs with images that are other than 8 bits per sample or channel, but it will work for most of the
    /// pixbufs that GTK+ uses.
    pub fn put_pixel(&self, x: i32, y: i32, red: u8, green: u8, blue: u8, alpha: u8) {
        unsafe {
            let n_channels = self.get_n_channels();
            let rowstride = self.get_rowstride();
            let pixels = self.get_pixels();
            let pos = (y * rowstride + x * n_channels) as usize;

            pixels[pos] = red;
            pixels[pos + 1] = green;
            pixels[pos + 2] = blue;
            pixels[pos + 3] = alpha;
        }
    }
}
<|endoftext|>


<commit_before>use domination::Domination;
use multi_objective::MultiObjective;
use crowding_distance::SolutionRankDist;
use selection::select_solutions;
use std::iter::FromIterator;
use rayon::par_iter::*;
use selection::tournament_selection_fast;
use rand::Rng;

/// An unrated Population of individuals.
pub struct UnratedPopulation<I>
    where I: Clone
{
    individuals: Vec<I>,
}

/// A rated Population of individuals, i.e.
/// each individual has a fitness assigned.
pub struct RatedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    individuals: Vec<I>,
    fitness: Vec<F>,
}

/// A selected (ranked) Population.
pub struct SelectedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    individuals: Vec<I>,
    fitness: Vec<F>,
    rank_dist: Vec<SolutionRankDist>,
}

impl<I> From<Vec<I>> for UnratedPopulation<I> where I: Clone
{
    fn from(v: Vec<I>) -> UnratedPopulation<I> {
        UnratedPopulation { individuals: v }
    }
}

impl<I> FromIterator<I> for UnratedPopulation<I> where I: Clone
{
    fn from_iter<T>(iterator: T) -> UnratedPopulation<I>
        where T: IntoIterator<Item = I>
    {
        UnratedPopulation { individuals: iterator.into_iter().collect() }
    }
}

impl<I> AsRef<[I]> for UnratedPopulation<I> where I: Clone
{
    fn as_ref(&self) -> &[I] {
        &self.individuals
    }
}

impl<I> UnratedPopulation<I> where I: Clone + Sync
{
    pub fn individuals(&self) -> &[I] {
        &self.individuals
    }

    pub fn new() -> UnratedPopulation<I> {
        UnratedPopulation { individuals: Vec::new() }
    }

    pub fn push(&mut self, ind: I) {
        self.individuals.push(ind);
    }

    pub fn len(&self) -> usize {
        self.individuals.len()
    }

    /// Rate the individuals in parallel.

    pub fn rate_in_parallel<F, E>(self, eval: &E, weight: f64) -> RatedPopulation<I, F>
        where F: MultiObjective + Clone + Send,
              E: Sync + Fn(&I) -> F
    {
        let mut fitness = Vec::new();
        self.individuals.into_par_iter().map(eval).weight(weight).collect_into(&mut fitness);
        assert!(fitness.len() == self.individuals.len());

        RatedPopulation {
            individuals: self.individuals,
            fitness: fitness,
        }
    }
}

impl<I, F> RatedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    pub fn select<D>(self,
                     population_size: usize,
                     num_objectives: usize,
                     domination: &mut D)
                     -> SelectedPopulation<I, F>
        where D: Domination<F>
    {
        // evaluate rank and crowding distance
        let rank_dist = select_solutions(&self.fitness,
                                         population_size,
                                         num_objectives,
                                         domination);
        assert!(rank_dist.len() <= population_size);
        SelectedPopulation {
            individuals: self.individuals,
            fitness: self.fitness,
            rank_dist: rank_dist,
        }
    }

    pub fn new() -> RatedPopulation<I, F> {
        RatedPopulation {
            individuals: Vec::new(),
            fitness: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.individuals.len()
    }

    pub fn fitness(&self) -> &[F] {
        &self.fitness
    }
    pub fn fitness_mut(&mut self) -> &mut [F] {
        &mut self.fitness
    }

}

impl<I, F> SelectedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    /// Generate an offspring population.
    /// XXX: Factor out selection into a separate Trait  SelectionMethod
    pub fn reproduce<R, M>(&self,
                           rng: &mut R,
                           offspring_size: usize,
                           tournament_k: usize,
                           mate: &M)
                           -> UnratedPopulation<I>
        where R: Rng,
              M: Fn(&mut R, &I, &I) -> I
    {
        assert!(self.individuals.len() == self.fitness.len());
        assert!(self.individuals.len() >= self.rank_dist.len());
        assert!(tournament_k > 0);

        let rank_dist = &self.rank_dist[..];

        // create `offspring_size` new offspring using k-tournament (
        // select the best individual out of k randomly choosen individuals)
        let offspring: Vec<I> =
            (0..offspring_size)
                .map(|_| {

                    // first parent. k candidates
                    let p1 = tournament_selection_fast(rng,
                                                       |i1, i2| rank_dist[i1] < rank_dist[i2],
                                                       rank_dist.len(),
                                                       tournament_k);

                    // second parent. k candidates
                    let p2 = tournament_selection_fast(rng,
                                                       |i1, i2| rank_dist[i1] < rank_dist[i2],
                                                       rank_dist.len(),
                                                       tournament_k);

                    // cross-over the two parents and produce one child (throw away
                    // second child XXX)

                    // The potentially dominating individual is gives as first
                    // parameter.
                    let (p1, p2) = if rank_dist[p1] < rank_dist[p2] {
                        (p1, p2)
                    } else if rank_dist[p2] < rank_dist[p1] {
                        (p2, p1)
                    } else {
                        (p1, p2)
                    };

                    mate(rng, &self.individuals[p1], &self.individuals[p2])
                })
                .collect();

        assert!(offspring.len() == offspring_size);

        UnratedPopulation { individuals: offspring }
    }

    pub fn new() -> SelectedPopulation<I, F> {
        SelectedPopulation {
            individuals: Vec::new(),
            fitness: Vec::new(),
            rank_dist: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        // NOTE: the population and fitness arrays might still be larger.
        self.rank_dist.len()
    }

    /// Merging a selected population result in a rated population as the selection
    /// criteria is no longer met.
    pub fn merge(self, offspring: RatedPopulation<I, F>) -> RatedPopulation<I, F> {
        let mut new_ind = Vec::with_capacity(self.len() + offspring.len());
        let mut new_fit = Vec::with_capacity(self.len() + offspring.len());
        self.all(&mut |ind, fit| {
            // XXX: Optimize. Avoid clone
            new_ind.push(ind.clone());
            new_fit.push(fit.clone());
        });

        new_ind.extend(offspring.individuals);
        new_fit.extend(offspring.fitness);

        assert!(new_ind.len() == new_fit.len());

        RatedPopulation {
            individuals: new_ind,
            fitness: new_fit,
        }
    }

    pub fn all<C>(&self, f: &mut C)
        where C: FnMut(&I, &F)
    {
        for rd in self.rank_dist.iter() {
            f(&self.individuals[rd.idx], &self.fitness[rd.idx]);
        }
    }

    pub fn all_with_rank_dist<C>(&self, f: &mut C)
        where C: FnMut(&I, &F, usize, f64)
    {
        for rd in self.rank_dist.iter() {
            f(&self.individuals[rd.idx],
              &self.fitness[rd.idx],
              rd.rank as usize,
              rd.dist);
        }
    }

    // XXX: fitness_iter()
    pub fn fitness_to_vec(&self) -> Vec<F> {
        let mut v = Vec::new();
        self.all(&mut |_, f| v.push(f.clone()));
        v
    }

    pub fn max_rank(&self) -> Option<usize> {
        self.rank_dist.iter().map(|rd| rd.rank).max().map(|r| r as usize)
    }

    pub fn all_of_rank<C>(&self, rank: usize, f: &mut C)
        where C: FnMut(&I, &F)
    {
        for rd in self.rank_dist.iter() {
            if rd.rank as usize == rank {
                f(&self.individuals[rd.idx], &self.fitness[rd.idx]);
            }
        }
    }
}
<commit_msg>Optimize merge<commit_after>use domination::Domination;
use multi_objective::MultiObjective;
use crowding_distance::SolutionRankDist;
use selection::select_solutions;
use std::iter::FromIterator;
use rayon::par_iter::*;
use selection::tournament_selection_fast;
use rand::Rng;

/// An unrated Population of individuals.
pub struct UnratedPopulation<I>
    where I: Clone
{
    individuals: Vec<I>,
}

/// A rated Population of individuals, i.e.
/// each individual has a fitness assigned.
pub struct RatedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    individuals: Vec<I>,
    fitness: Vec<F>,
}

/// A selected (ranked) Population.
pub struct SelectedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    individuals: Vec<I>,
    fitness: Vec<F>,
    rank_dist: Vec<SolutionRankDist>,
}

impl<I> From<Vec<I>> for UnratedPopulation<I> where I: Clone
{
    fn from(v: Vec<I>) -> UnratedPopulation<I> {
        UnratedPopulation { individuals: v }
    }
}

impl<I> FromIterator<I> for UnratedPopulation<I> where I: Clone
{
    fn from_iter<T>(iterator: T) -> UnratedPopulation<I>
        where T: IntoIterator<Item = I>
    {
        UnratedPopulation { individuals: iterator.into_iter().collect() }
    }
}

impl<I> AsRef<[I]> for UnratedPopulation<I> where I: Clone
{
    fn as_ref(&self) -> &[I] {
        &self.individuals
    }
}

impl<I> UnratedPopulation<I> where I: Clone + Sync
{
    pub fn individuals(&self) -> &[I] {
        &self.individuals
    }

    pub fn new() -> UnratedPopulation<I> {
        UnratedPopulation { individuals: Vec::new() }
    }

    pub fn push(&mut self, ind: I) {
        self.individuals.push(ind);
    }

    pub fn len(&self) -> usize {
        self.individuals.len()
    }

    /// Rate the individuals in parallel.

    pub fn rate_in_parallel<F, E>(self, eval: &E, weight: f64) -> RatedPopulation<I, F>
        where F: MultiObjective + Clone + Send,
              E: Sync + Fn(&I) -> F
    {
        let mut fitness = Vec::new();
        self.individuals.into_par_iter().map(eval).weight(weight).collect_into(&mut fitness);
        assert!(fitness.len() == self.individuals.len());

        RatedPopulation {
            individuals: self.individuals,
            fitness: fitness,
        }
    }
}

impl<I, F> RatedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    pub fn select<D>(self,
                     population_size: usize,
                     num_objectives: usize,
                     domination: &mut D)
                     -> SelectedPopulation<I, F>
        where D: Domination<F>
    {
        // evaluate rank and crowding distance
        let rank_dist = select_solutions(&self.fitness,
                                         population_size,
                                         num_objectives,
                                         domination);
        assert!(rank_dist.len() <= population_size);
        SelectedPopulation {
            individuals: self.individuals,
            fitness: self.fitness,
            rank_dist: rank_dist,
        }
    }

    pub fn new() -> RatedPopulation<I, F> {
        RatedPopulation {
            individuals: Vec::new(),
            fitness: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        self.individuals.len()
    }

    pub fn fitness(&self) -> &[F] {
        &self.fitness
    }
    pub fn fitness_mut(&mut self) -> &mut [F] {
        &mut self.fitness
    }

}

impl<I, F> SelectedPopulation<I, F>
    where I: Clone,
          F: MultiObjective + Clone
{
    /// Generate an offspring population.
    /// XXX: Factor out selection into a separate Trait  SelectionMethod
    pub fn reproduce<R, M>(&self,
                           rng: &mut R,
                           offspring_size: usize,
                           tournament_k: usize,
                           mate: &M)
                           -> UnratedPopulation<I>
        where R: Rng,
              M: Fn(&mut R, &I, &I) -> I
    {
        assert!(self.individuals.len() == self.fitness.len());
        assert!(self.individuals.len() >= self.rank_dist.len());
        assert!(tournament_k > 0);

        let rank_dist = &self.rank_dist[..];

        // create `offspring_size` new offspring using k-tournament (
        // select the best individual out of k randomly choosen individuals)
        let offspring: Vec<I> =
            (0..offspring_size)
                .map(|_| {

                    // first parent. k candidates
                    let p1 = tournament_selection_fast(rng,
                                                       |i1, i2| rank_dist[i1] < rank_dist[i2],
                                                       rank_dist.len(),
                                                       tournament_k);

                    // second parent. k candidates
                    let p2 = tournament_selection_fast(rng,
                                                       |i1, i2| rank_dist[i1] < rank_dist[i2],
                                                       rank_dist.len(),
                                                       tournament_k);

                    // cross-over the two parents and produce one child (throw away
                    // second child XXX)

                    // The potentially dominating individual is gives as first
                    // parameter.
                    let (p1, p2) = if rank_dist[p1] < rank_dist[p2] {
                        (p1, p2)
                    } else if rank_dist[p2] < rank_dist[p1] {
                        (p2, p1)
                    } else {
                        (p1, p2)
                    };

                    mate(rng, &self.individuals[p1], &self.individuals[p2])
                })
                .collect();

        assert!(offspring.len() == offspring_size);

        UnratedPopulation { individuals: offspring }
    }

    pub fn new() -> SelectedPopulation<I, F> {
        SelectedPopulation {
            individuals: Vec::new(),
            fitness: Vec::new(),
            rank_dist: Vec::new(),
        }
    }

    pub fn len(&self) -> usize {
        // NOTE: the population and fitness arrays might still be larger.
        self.rank_dist.len()
    }

    /// Merging a selected population result in a rated population as the selection
    /// criteria is no longer met.
    pub fn merge(self, offspring: RatedPopulation<I, F>) -> RatedPopulation<I, F> {
        let new_len = self.len() + offspring.len();
        let mut new_ind = Vec::with_capacity(new_len);
        let mut new_fit = Vec::with_capacity(new_len);

        let SelectedPopulation{individuals, fitness, ..} = self;

        new_ind.extend(individuals);
        new_ind.extend(offspring.individuals);

        new_fit.extend(fitness);
        new_fit.extend(offspring.fitness);

        assert!(new_ind.len() == new_len);
        assert!(new_fit.len() == new_len);

        RatedPopulation {
            individuals: new_ind,
            fitness: new_fit,
        }
    }

    pub fn all<C>(&self, f: &mut C)
        where C: FnMut(&I, &F)
    {
        for rd in self.rank_dist.iter() {
            f(&self.individuals[rd.idx], &self.fitness[rd.idx]);
        }
    }

    pub fn all_with_rank_dist<C>(&self, f: &mut C)
        where C: FnMut(&I, &F, usize, f64)
    {
        for rd in self.rank_dist.iter() {
            f(&self.individuals[rd.idx],
              &self.fitness[rd.idx],
              rd.rank as usize,
              rd.dist);
        }
    }

    // XXX: fitness_iter()
    pub fn fitness_to_vec(&self) -> Vec<F> {
        let mut v = Vec::new();
        self.all(&mut |_, f| v.push(f.clone()));
        v
    }

    pub fn max_rank(&self) -> Option<usize> {
        self.rank_dist.iter().map(|rd| rd.rank).max().map(|r| r as usize)
    }

    pub fn all_of_rank<C>(&self, rank: usize, f: &mut C)
        where C: FnMut(&I, &F)
    {
        for rd in self.rank_dist.iter() {
            if rd.rank as usize == rank {
                f(&self.individuals[rd.idx], &self.fitness[rd.idx]);
            }
        }
    }
}
<|endoftext|>