<commit_before>//!

use super::{gpu::text::Extra, Color, Rect, ZIndex};
use crate::{filesystem::Filesystem, GameError, GameResult};
use glyph_brush::{ab_glyph, FontId};
use std::{collections::HashMap, io::Read, path::Path};

/// Font data that can be used to create a new font in [super::context::GraphicsContext].
#[derive(Debug)]
pub struct FontData {
    pub(crate) font: ab_glyph::FontArc,
}

impl FontData {
    /// Loads font data from a given path in the filesystem.
    #[allow(unused_results)]
    pub fn from_path(fs: &Filesystem, path: impl AsRef<Path>) -> GameResult<Self> {
        let mut bytes = vec![];
        fs.open(path)?.read_to_end(&mut bytes)?;
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_vec(bytes)?,
        })
    }

    /// Loads font data from owned bytes.
    pub fn from_vec(data: Vec<u8>) -> GameResult<Self> {
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_vec(data)?,
        })
    }

    /// Loads font data from static bytes.
    pub fn from_slice(data: &'static [u8]) -> GameResult<Self> {
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_slice(data)?,
        })
    }
}

/// Parameters of a single piece of text, including font, color, size, and Z position.
#[derive(Debug, Clone)]
pub struct Text {
    /// The text itself.
    pub text: String,
    /// Font name of the text.
    pub font: String,
    /// Pixel size of text.
    pub size: f32,
    /// Color of text.
    pub color: Color,
}

impl Default for Text {
    fn default() -> Self {
        Text {
            text: "".into(),
            font: "LiberationMono-Regular".into(),
            size: 16.,
            color: Color::WHITE,
        }
    }
}

impl Text {
    /// Equivalent to `Text::default()`.
    pub fn new() -> Self {
        Text::default()
    }

    /// Sets the `text` field.
    pub fn text(self, text: impl Into<String>) -> Self {
        Text {
            text: text.into(),
            ..self
        }
    }

    /// Sets the `font` field.
    pub fn font(self, font: impl Into<String>) -> Self {
        Text {
            font: font.into(),
            ..self
        }
    }

    /// Sets the `size` field.
    pub fn size(self, size: f32) -> Self {
        Text { size, ..self }
    }

    /// Sets the `color` field.
    pub fn color(self, color: impl Into<Color>) -> Self {
        Text {
            color: color.into(),
            ..self
        }
    }
}

/// A struct containing parameters pertaining to drawing text.
///
/// This does not describe the text itself, but rather how the text
/// should be positioned and layed out.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TextParam {
    /// Text layout boundaries. The top-left of this [Rect] determines
    /// the positioning of the text.
    ///
    /// The size of the [Rect] only becomes relevant if drawing with [TextLayout::Wrap].
    pub bounds: Rect,
    /// Rotation of the text in radians.
    pub rotation: f32,
    /// How the text should be layed out.
    pub layout: TextLayout,
    /// The Z coordinate of the text.
    pub z: ZIndex,
}

impl Default for TextParam {
    fn default() -> Self {
        Self {
            bounds: Rect {
                x: 0.0,
                y: 0.0,
                w: f32::INFINITY,
                h: f32::INFINITY,
            },
            rotation: 0.0,
            layout: TextLayout::tl_wrap(),
            z: 0,
        }
    }
}

impl TextParam {
    /// Create a new [TextParam] with default values.
    pub fn new() -> Self {
        TextParam::default()
    }

    /// Set the layout boundaries.
    pub fn bounds(mut self, bounds: Rect) -> Self {
        self.bounds = bounds;
        self
    }

    /// Set the position of the layout boundaries.
    pub fn dest(mut self, dest: impl Into<mint::Vector2<f32>>) -> Self {
        let dest = dest.into();
        self.bounds.x = dest.x;
        self.bounds.y = dest.y;
        self
    }

    /// Set the size of the layout boundaries.
    pub fn size(mut self, size: impl Into<mint::Vector2<f32>>) -> Self {
        let size = size.into();
        self.bounds.w = size.x;
        self.bounds.h = size.y;
        self
    }

    /// Set the size to infinity such that the text layout is unbounded.
    pub fn unbounded(mut self) -> Self {
        self.bounds.w = f32::INFINITY;
        self.bounds.h = f32::INFINITY;
        self
    }

    /// Set the rotation of the text in radians.
    pub fn rotation(mut self, rotation: f32) -> Self {
        self.rotation = rotation;
        self
    }

    /// Set the layout mode of the text.
    pub fn layout(mut self, layout: TextLayout) -> Self {
        self.layout = layout;
        self
    }

    /// Set the Z coordinate.
    pub fn z(mut self, z: ZIndex) -> Self {
        self.z = z;
        self
    }
}

/// Describes text alignment along a single axis.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TextAlign {
    /// Text is aligned to the beginning of the axis (left, top).
    Begin,
    /// Text is aligned to the center of the axis.
    Middle,
    /// Text is aligned to the end of the axis (right, bottom).
    End,
}

impl From<TextAlign> for glyph_brush::HorizontalAlign {
    fn from(align: TextAlign) -> Self {
        match align {
            TextAlign::Begin => glyph_brush::HorizontalAlign::Left,
            TextAlign::Middle => glyph_brush::HorizontalAlign::Center,
            TextAlign::End => glyph_brush::HorizontalAlign::Right,
        }
    }
}

impl From<TextAlign> for glyph_brush::VerticalAlign {
    fn from(align: TextAlign) -> Self {
        match align {
            TextAlign::Begin => glyph_brush::VerticalAlign::Top,
            TextAlign::Middle => glyph_brush::VerticalAlign::Center,
            TextAlign::End => glyph_brush::VerticalAlign::Bottom,
        }
    }
}

/// Describes text alignment along both axes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TextLayout {
    /// Text is layed out in a single line.
    SingleLine {
        /// Horizontal alignment.
        h_align: TextAlign,
        /// Vertical alignment.
        v_align: TextAlign,
    },
    /// Text wraps around the bounds.
    Wrap {
        /// Horizontal alignment.
        h_align: TextAlign,
        /// Vertical alignment.
        v_align: TextAlign,
    },
}

impl TextLayout {
    /// Text on a single line aligned to the top-left.
    pub fn tl_single_line() -> Self {
        TextLayout::SingleLine {
            h_align: TextAlign::Begin,
            v_align: TextAlign::Begin,
        }
    }

    /// Text wrapped and aligned to the top-left.
    pub fn tl_wrap() -> Self {
        TextLayout::Wrap {
            h_align: TextAlign::Begin,
            v_align: TextAlign::Begin,
        }
    }

    /// Returns the horizontal alignment, regardless of wrapping behaviour.
    pub fn h_align(&self) -> TextAlign {
        match self {
            TextLayout::SingleLine { h_align, .. } | TextLayout::Wrap { h_align, .. } => *h_align,
        }
    }

    /// Returns the vertical alignment, regardless of wrapping behaviour.
    pub fn v_align(&self) -> TextAlign {
        match self {
            TextLayout::SingleLine { v_align, .. } | TextLayout::Wrap { v_align, .. } => *v_align,
        }
    }
}

pub(crate) fn text_to_section<'a>(
    fonts: &HashMap<String, FontId>,
    text: &'a [Text],
    mut param: TextParam,
) -> GameResult<glyph_brush::Section<'a, Extra>> {
    let obounds = param.bounds;

    match param.layout.h_align() {
        TextAlign::Begin => {}
        TextAlign::Middle => param.bounds.x += param.bounds.w / 2.,
        TextAlign::End => param.bounds.x += param.bounds.w,
    }

    match param.layout.v_align() {
        TextAlign::Begin => {}
        TextAlign::Middle => param.bounds.y += param.bounds.h / 2.,
        TextAlign::End => param.bounds.y += param.bounds.h,
    }

    Ok(glyph_brush::Section {
        screen_position: (param.bounds.x, param.bounds.y),
        bounds: (param.bounds.w, param.bounds.h),
        layout: match param.layout {
            TextLayout::SingleLine { h_align, v_align } => {
                glyph_brush::Layout::default_single_line()
                    .h_align(h_align.into())
                    .v_align(v_align.into())
            }
            TextLayout::Wrap { h_align, v_align } => glyph_brush::Layout::default_wrap()
                .h_align(h_align.into())
                .v_align(v_align.into()),
        },
        text: text
            .iter()
            .map(|text| {
                Ok(glyph_brush::Text {
                    text: &text.text,
                    scale: text.size.into(),
                    font_id: *fonts
                        .get(&text.font)
                        .ok_or_else(|| GameError::FontSelectError(text.font.to_string()))?,
                    extra: Extra {
                        color: text.color.into(),
                        origin: obounds.point().into(),
                        rotation: param.rotation,
                    },
                })
            })
            .collect::<GameResult<Vec<_>>>()?,
    })
}
<commit_msg>layed -> laid in textparam docs<commit_after>//!

use super::{gpu::text::Extra, Color, Rect, ZIndex};
use crate::{filesystem::Filesystem, GameError, GameResult};
use glyph_brush::{ab_glyph, FontId};
use std::{collections::HashMap, io::Read, path::Path};

/// Font data that can be used to create a new font in [super::context::GraphicsContext].
#[derive(Debug)]
pub struct FontData {
    pub(crate) font: ab_glyph::FontArc,
}

impl FontData {
    /// Loads font data from a given path in the filesystem.
    #[allow(unused_results)]
    pub fn from_path(fs: &Filesystem, path: impl AsRef<Path>) -> GameResult<Self> {
        let mut bytes = vec![];
        fs.open(path)?.read_to_end(&mut bytes)?;
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_vec(bytes)?,
        })
    }

    /// Loads font data from owned bytes.
    pub fn from_vec(data: Vec<u8>) -> GameResult<Self> {
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_vec(data)?,
        })
    }

    /// Loads font data from static bytes.
    pub fn from_slice(data: &'static [u8]) -> GameResult<Self> {
        Ok(FontData {
            font: ab_glyph::FontArc::try_from_slice(data)?,
        })
    }
}

/// Parameters of a single piece of text, including font, color, size, and Z position.
#[derive(Debug, Clone)]
pub struct Text {
    /// The text itself.
    pub text: String,
    /// Font name of the text.
    pub font: String,
    /// Pixel size of text.
    pub size: f32,
    /// Color of text.
    pub color: Color,
}

impl Default for Text {
    fn default() -> Self {
        Text {
            text: "".into(),
            font: "LiberationMono-Regular".into(),
            size: 16.,
            color: Color::WHITE,
        }
    }
}

impl Text {
    /// Equivalent to `Text::default()`.
    pub fn new() -> Self {
        Text::default()
    }

    /// Sets the `text` field.
    pub fn text(self, text: impl Into<String>) -> Self {
        Text {
            text: text.into(),
            ..self
        }
    }

    /// Sets the `font` field.
    pub fn font(self, font: impl Into<String>) -> Self {
        Text {
            font: font.into(),
            ..self
        }
    }

    /// Sets the `size` field.
    pub fn size(self, size: f32) -> Self {
        Text { size, ..self }
    }

    /// Sets the `color` field.
    pub fn color(self, color: impl Into<Color>) -> Self {
        Text {
            color: color.into(),
            ..self
        }
    }
}

/// A struct containing parameters pertaining to drawing text.
///
/// This does not describe the text itself, but rather how the text
/// should be positioned and laid out.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct TextParam {
    /// Text layout boundaries. The top-left of this [Rect] determines
    /// the positioning of the text.
    ///
    /// The size of the [Rect] only becomes relevant if drawing with [TextLayout::Wrap].
    pub bounds: Rect,
    /// Rotation of the text in radians.
    pub rotation: f32,
    /// How the text should be laid out.
    pub layout: TextLayout,
    /// The Z coordinate of the text.
    pub z: ZIndex,
}

impl Default for TextParam {
    fn default() -> Self {
        Self {
            bounds: Rect {
                x: 0.0,
                y: 0.0,
                w: f32::INFINITY,
                h: f32::INFINITY,
            },
            rotation: 0.0,
            layout: TextLayout::tl_wrap(),
            z: 0,
        }
    }
}

impl TextParam {
    /// Create a new [TextParam] with default values.
    pub fn new() -> Self {
        TextParam::default()
    }

    /// Set the layout boundaries.
    pub fn bounds(mut self, bounds: Rect) -> Self {
        self.bounds = bounds;
        self
    }

    /// Set the position of the layout boundaries.
    pub fn dest(mut self, dest: impl Into<mint::Vector2<f32>>) -> Self {
        let dest = dest.into();
        self.bounds.x = dest.x;
        self.bounds.y = dest.y;
        self
    }

    /// Set the size of the layout boundaries.
    pub fn size(mut self, size: impl Into<mint::Vector2<f32>>) -> Self {
        let size = size.into();
        self.bounds.w = size.x;
        self.bounds.h = size.y;
        self
    }

    /// Set the size to infinity such that the text layout is unbounded.
    pub fn unbounded(mut self) -> Self {
        self.bounds.w = f32::INFINITY;
        self.bounds.h = f32::INFINITY;
        self
    }

    /// Set the rotation of the text in radians.
    pub fn rotation(mut self, rotation: f32) -> Self {
        self.rotation = rotation;
        self
    }

    /// Set the layout mode of the text.
    pub fn layout(mut self, layout: TextLayout) -> Self {
        self.layout = layout;
        self
    }

    /// Set the Z coordinate.
    pub fn z(mut self, z: ZIndex) -> Self {
        self.z = z;
        self
    }
}

/// Describes text alignment along a single axis.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TextAlign {
    /// Text is aligned to the beginning of the axis (left, top).
    Begin,
    /// Text is aligned to the center of the axis.
    Middle,
    /// Text is aligned to the end of the axis (right, bottom).
    End,
}

impl From<TextAlign> for glyph_brush::HorizontalAlign {
    fn from(align: TextAlign) -> Self {
        match align {
            TextAlign::Begin => glyph_brush::HorizontalAlign::Left,
            TextAlign::Middle => glyph_brush::HorizontalAlign::Center,
            TextAlign::End => glyph_brush::HorizontalAlign::Right,
        }
    }
}

impl From<TextAlign> for glyph_brush::VerticalAlign {
    fn from(align: TextAlign) -> Self {
        match align {
            TextAlign::Begin => glyph_brush::VerticalAlign::Top,
            TextAlign::Middle => glyph_brush::VerticalAlign::Center,
            TextAlign::End => glyph_brush::VerticalAlign::Bottom,
        }
    }
}

/// Describes text alignment along both axes.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum TextLayout {
    /// Text is layed out in a single line.
    SingleLine {
        /// Horizontal alignment.
        h_align: TextAlign,
        /// Vertical alignment.
        v_align: TextAlign,
    },
    /// Text wraps around the bounds.
    Wrap {
        /// Horizontal alignment.
        h_align: TextAlign,
        /// Vertical alignment.
        v_align: TextAlign,
    },
}

impl TextLayout {
    /// Text on a single line aligned to the top-left.
    pub fn tl_single_line() -> Self {
        TextLayout::SingleLine {
            h_align: TextAlign::Begin,
            v_align: TextAlign::Begin,
        }
    }

    /// Text wrapped and aligned to the top-left.
    pub fn tl_wrap() -> Self {
        TextLayout::Wrap {
            h_align: TextAlign::Begin,
            v_align: TextAlign::Begin,
        }
    }

    /// Returns the horizontal alignment, regardless of wrapping behaviour.
    pub fn h_align(&self) -> TextAlign {
        match self {
            TextLayout::SingleLine { h_align, .. } | TextLayout::Wrap { h_align, .. } => *h_align,
        }
    }

    /// Returns the vertical alignment, regardless of wrapping behaviour.
    pub fn v_align(&self) -> TextAlign {
        match self {
            TextLayout::SingleLine { v_align, .. } | TextLayout::Wrap { v_align, .. } => *v_align,
        }
    }
}

pub(crate) fn text_to_section<'a>(
    fonts: &HashMap<String, FontId>,
    text: &'a [Text],
    mut param: TextParam,
) -> GameResult<glyph_brush::Section<'a, Extra>> {
    let obounds = param.bounds;

    match param.layout.h_align() {
        TextAlign::Begin => {}
        TextAlign::Middle => param.bounds.x += param.bounds.w / 2.,
        TextAlign::End => param.bounds.x += param.bounds.w,
    }

    match param.layout.v_align() {
        TextAlign::Begin => {}
        TextAlign::Middle => param.bounds.y += param.bounds.h / 2.,
        TextAlign::End => param.bounds.y += param.bounds.h,
    }

    Ok(glyph_brush::Section {
        screen_position: (param.bounds.x, param.bounds.y),
        bounds: (param.bounds.w, param.bounds.h),
        layout: match param.layout {
            TextLayout::SingleLine { h_align, v_align } => {
                glyph_brush::Layout::default_single_line()
                    .h_align(h_align.into())
                    .v_align(v_align.into())
            }
            TextLayout::Wrap { h_align, v_align } => glyph_brush::Layout::default_wrap()
                .h_align(h_align.into())
                .v_align(v_align.into()),
        },
        text: text
            .iter()
            .map(|text| {
                Ok(glyph_brush::Text {
                    text: &text.text,
                    scale: text.size.into(),
                    font_id: *fonts
                        .get(&text.font)
                        .ok_or_else(|| GameError::FontSelectError(text.font.to_string()))?,
                    extra: Extra {
                        color: text.color.into(),
                        origin: obounds.point().into(),
                        rotation: param.rotation,
                    },
                })
            })
            .collect::<GameResult<Vec<_>>>()?,
    })
}
<|endoftext|>


<commit_before>//! The datum module provides a data structure, Datum, that implements the Unify trait.
//! Datum aims to be a drop-in for any algorithm in ai_kit that operates on the Unify trait.

#[cfg(feature = "with-constraint")]
use constraints::ConstraintValue;
use core;
use std;
use std::collections::{BTreeMap, HashMap};
use std::str;
use utils;

#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd)]
pub enum Datum {
    #[serde(rename = "null")] Nil,
    #[serde(rename = "str")] String(String),
    #[serde(rename = "int")] Int(i64),
    #[serde(rename = "float")] Float(f64),
    #[serde(rename = "var")] Variable(String),
    #[serde(rename = "vec")] Vector(Vec<Datum>),
    #[serde(rename = "map")] Map(BTreeMap<String, Datum>),
    #[serde(rename = "fn")] Function { head: Box<Datum>, args: Vec<Datum> },
}

impl Datum {
    pub fn to_string(&self) -> Option<String> {
        match *self {
            Datum::String(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn to_int(&self) -> Option<i64> {
        match *self {
            Datum::Int(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn to_float(&self) -> Option<f64> {
        match *self {
            Datum::Float(ref f_value) => Some(f_value.clone()),
            Datum::Int(ref i_value) => Some((i_value.clone() as f64)),
            _ => None,
        }
    }

    pub fn to_variable(&self) -> Option<String> {
        match *self {
            Datum::Variable(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn pprint(&self) -> String {
        match *self {
            Datum::String(ref s) => format!("{}", s),
            Datum::Int(ref i) => format!("{}", i),
            Datum::Float(ref f) => format!("{}", f),
            Datum::Variable(ref v) => format!("{}", v),
            Datum::Vector(ref args) => {
                let elements: Vec<String> = args.iter().map(|e| e.pprint()).collect();
                format!("({})", elements.join(","))
            }
            Datum::Map(ref args) => {
                let elements: Vec<String> = args.iter()
                    .map(|(k, v)| format!("{} => {}", k, v.pprint()))
                    .collect();
                format!("({})", elements.join(","))
            }
            Datum::Nil => format!("nil"),
            Datum::Function { ref head, ref args } => {
                let elements: Vec<String> = args.iter().map(|e| e.pprint()).collect();
                format!("({} ({}))", head, elements.join(","))
            }
        }
    }
}

impl Default for Datum {
    fn default() -> Self {
        Datum::Nil
    }
}

impl PartialEq for Datum {
    fn eq(&self, other: &Datum) -> bool {
        match *self {
            Datum::String(ref s) => match *other {
                Datum::String(ref s2) => s == s2,
                _ => false,
            },
            Datum::Int(ref i) => match *other {
                Datum::Int(ref i2) => i == i2,
                _ => false,
            },
            Datum::Float(ref f) => match *other {
                Datum::Float(ref f2) => f == f2,
                _ => false,
            },
            Datum::Variable(ref v) => match *other {
                Datum::Variable(ref v2) => v == v2,
                _ => false,
            },
            Datum::Vector(ref args) => match *other {
                Datum::Vector(ref args2) => args == args2,
                _ => false,
            },
            Datum::Map(ref args) => match *other {
                Datum::Map(ref args2) => args == args2,
                _ => false,
            },
            Datum::Nil => match *other {
                Datum::Nil => true,
                _ => false,
            },
            Datum::Function { ref head, ref args } => match *other {
                Datum::Function {
                    head: ref head2,
                    args: ref args2,
                } if args.len() == args2.len() =>
                {
                    head == head2 && args == args2
                }
                _ => false,
            },
        }
    }
}

impl Eq for Datum {}
impl Ord for Datum {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl core::BindingsValue for Datum {
    fn variable(s: &String) -> Option<Self> {
        Some(Datum::Variable(s.clone()))
    }
    fn to_variable(&self) -> Option<String> {
        self.to_variable()
    }
}

#[cfg(feature = "with-constraint")]
impl ConstraintValue for Datum {
    fn to_float(&self) -> Option<f64> {
        self.to_float()
    }
    fn float(c: f64) -> Self {
        Datum::Float(c)
    }
}

impl core::Unify<Datum> for Datum {
    fn unify(&self, other: &Datum, bindings: &core::Bindings<Self>) -> Option<core::Bindings<Self>> {
        fn unify_args(args: &Vec<Datum>, args2: &Vec<Datum>, bindings: &core::Bindings<Datum>) -> Option<core::Bindings<Datum>> {
            utils::fold_while_some(
                bindings.clone(),
                &mut args.iter().zip(args2.iter()),
                &|bindings: core::Bindings<Datum>, (ref a, ref b): (&Datum, &Datum)| a.unify(&b, &bindings),
            )
        }

        fn unify_maps(
            args: &BTreeMap<String, Datum>,
            args2: &BTreeMap<String, Datum>,
            bindings: &core::Bindings<Datum>,
        ) -> Option<core::Bindings<Datum>> {
            if args.len() != args2.len() {
                None
            } else {
                utils::fold_while_some(
                    bindings.clone(),
                    &mut args.iter(),
                    &|bindings: core::Bindings<Datum>, (ref k, ref v): (&String, &Datum)| args2.get(*k).and_then(|v2| v.unify(v2, &bindings)),
                )
            }
        }
        match *self {
            Datum::Variable(ref var_name) => bindings.update_bindings(var_name, other),
            Datum::Vector(ref args) => match *other {
                Datum::Vector(ref args2) if args.len() == args2.len() => unify_args(args, args2, &bindings),
                _ => None,
            },
            Datum::Map(ref args) => match *other {
                Datum::Map(ref args2) if args.len() == args2.len() => unify_maps(args, args2, &bindings),
                _ => None,
            },
            _ => match *other {
                Datum::Variable(ref var_name) => bindings.update_bindings(var_name, self),
                _ => {
                    if self == other {
                        Some(bindings.clone())
                    } else {
                        None
                    }
                }
            },
        }
    }

    fn apply_bindings(&self, bindings: &core::Bindings<Self>) -> Option<Self> {
        fn apply_bindings_to_args(args: &Vec<Datum>, bindings: &core::Bindings<Datum>) -> Option<Vec<Datum>> {
            utils::map_while_some(&mut args.iter(), &|arg| {
                arg.apply_bindings(bindings)
            })
        }
        fn apply_bindings_to_map(args: &BTreeMap<String, Datum>, bindings: &core::Bindings<Datum>) -> Option<BTreeMap<String, Datum>> {
            utils::map_while_some(&mut args.iter(), &|(k, v)| {
                v.apply_bindings(bindings)
                    .and_then(|v| Some((k.clone(), v)))
            }).and_then(|tuple_vec| Some(tuple_vec.into_iter().collect::<BTreeMap<String, Datum>>()))
        }
        match *self {
            Datum::Variable(ref var_name) => bindings
                .get_binding(var_name)
                .or_else(|| Some(self.clone())),
            Datum::Vector(ref args) => apply_bindings_to_args(args, bindings).and_then(|args| Some(Datum::Vector(args))),
            Datum::Map(ref args) => apply_bindings_to_map(args, bindings).and_then(|args| Some(Datum::Map(args))),
            _ => Some(self.clone()),
        }
    }

    fn variables(&self) -> Vec<String> {
        match *self {
            Datum::Variable(ref v) => vec![v.clone()],
            Datum::Vector(ref args) => {
                let mut variables = Vec::new();
                for arg in args.iter() {
                    variables.extend(arg.variables().into_iter());
                }
                variables
            }
            Datum::Map(ref args) => {
                let mut variables = Vec::new();
                for v in args.values() {
                    variables.extend(v.variables().into_iter());
                }
                variables
            }
            _ => Vec::new(),
        }
    }

    fn rename_variables(&self, renamed_variables: &HashMap<String, String>) -> Self {
        match *self {
            Datum::Variable(ref v) => renamed_variables
                .get(v)
                .and_then(|new_v| Some(Datum::Variable(new_v.clone())))
                .or_else(|| Some(self.clone()))
                .unwrap(),
            Datum::Vector(ref args) => Datum::Vector(
                args.iter()
                    .map(|arg| arg.rename_variables(renamed_variables))
                    .collect(),
            ),
            Datum::Map(ref args) => Datum::Map(
                args.iter()
                    .map(|(k, v)| (k.clone(), v.rename_variables(renamed_variables)))
                    .collect(),
            ),
            _ => self.clone(),
        }
    }

    fn nil() -> Self {
        Datum::Nil
    }
}

impl std::fmt::Display for Datum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.pprint())
    }
}

#[cfg(test)]
mod tests;
<commit_msg>Added new `bool` type to Datum<commit_after>//! The datum module provides a data structure, Datum, that implements the Unify trait.
//! Datum aims to be a drop-in for any algorithm in ai_kit that operates on the Unify trait.

#[cfg(feature = "with-constraint")]
use constraints::ConstraintValue;
use core;
use std;
use std::collections::{BTreeMap, HashMap};
use std::str;
use utils;

#[derive(Clone, Debug, Serialize, Deserialize, PartialOrd)]
pub enum Datum {
    #[serde(rename = "null")] Nil,
    #[serde(rename = "bool")] Bool(bool),
    #[serde(rename = "str")] String(String),
    #[serde(rename = "int")] Int(i64),
    #[serde(rename = "float")] Float(f64),
    #[serde(rename = "var")] Variable(String),
    #[serde(rename = "vec")] Vector(Vec<Datum>),
    #[serde(rename = "map")] Map(BTreeMap<String, Datum>),
    #[serde(rename = "fn")] Function { head: Box<Datum>, args: Vec<Datum> },
}

impl Datum {
    pub fn to_string(&self) -> Option<String> {
        match *self {
            Datum::String(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn to_int(&self) -> Option<i64> {
        match *self {
            Datum::Int(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn to_float(&self) -> Option<f64> {
        match *self {
            Datum::Float(ref f_value) => Some(f_value.clone()),
            Datum::Int(ref i_value) => Some((i_value.clone() as f64)),
            _ => None,
        }
    }

    pub fn to_variable(&self) -> Option<String> {
        match *self {
            Datum::Variable(ref value) => Some(value.clone()),
            _ => None,
        }
    }

    pub fn pprint(&self) -> String {
        match *self {
            Datum::Nil => format!("nil"),
            Datum::Bool(ref b) => format!("{}", b),
            Datum::String(ref s) => format!("{}", s),
            Datum::Int(ref i) => format!("{}", i),
            Datum::Float(ref f) => format!("{}", f),
            Datum::Variable(ref v) => format!("{}", v),
            Datum::Vector(ref args) => {
                let elements: Vec<String> = args.iter().map(|e| e.pprint()).collect();
                format!("({})", elements.join(","))
            }
            Datum::Map(ref args) => {
                let elements: Vec<String> = args.iter()
                    .map(|(k, v)| format!("{} => {}", k, v.pprint()))
                    .collect();
                format!("({})", elements.join(","))
            }
            Datum::Function { ref head, ref args } => {
                let elements: Vec<String> = args.iter().map(|e| e.pprint()).collect();
                format!("({} ({}))", head, elements.join(","))
            }
        }
    }
}

impl Default for Datum {
    fn default() -> Self {
        Datum::Nil
    }
}

impl PartialEq for Datum {
    fn eq(&self, other: &Datum) -> bool {
        match *self {
            Datum::Nil => match *other {
                Datum::Nil => true,
                _ => false,
            },
            Datum::Bool(b) => match *other {
                Datum::Bool(b2) => b == b2,
                _ => false,
            },
            Datum::String(ref s) => match *other {
                Datum::String(ref s2) => s == s2,
                _ => false,
            },
            Datum::Int(ref i) => match *other {
                Datum::Int(ref i2) => i == i2,
                _ => false,
            },
            Datum::Float(ref f) => match *other {
                Datum::Float(ref f2) => f == f2,
                _ => false,
            },
            Datum::Variable(ref v) => match *other {
                Datum::Variable(ref v2) => v == v2,
                _ => false,
            },
            Datum::Vector(ref args) => match *other {
                Datum::Vector(ref args2) => args == args2,
                _ => false,
            },
            Datum::Map(ref args) => match *other {
                Datum::Map(ref args2) => args == args2,
                _ => false,
            },
            Datum::Function { ref head, ref args } => match *other {
                Datum::Function {
                    head: ref head2,
                    args: ref args2,
                } if args.len() == args2.len() =>
                {
                    head == head2 && args == args2
                }
                _ => false,
            },
        }
    }
}

impl Eq for Datum {}
impl Ord for Datum {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl core::BindingsValue for Datum {
    fn variable(s: &String) -> Option<Self> {
        Some(Datum::Variable(s.clone()))
    }
    fn to_variable(&self) -> Option<String> {
        self.to_variable()
    }
}

#[cfg(feature = "with-constraint")]
impl ConstraintValue for Datum {
    fn to_float(&self) -> Option<f64> {
        self.to_float()
    }
    fn float(c: f64) -> Self {
        Datum::Float(c)
    }
}

impl core::Unify<Datum> for Datum {
    fn unify(&self, other: &Datum, bindings: &core::Bindings<Self>) -> Option<core::Bindings<Self>> {
        fn unify_args(args: &Vec<Datum>, args2: &Vec<Datum>, bindings: &core::Bindings<Datum>) -> Option<core::Bindings<Datum>> {
            utils::fold_while_some(
                bindings.clone(),
                &mut args.iter().zip(args2.iter()),
                &|bindings: core::Bindings<Datum>, (ref a, ref b): (&Datum, &Datum)| a.unify(&b, &bindings),
            )
        }

        fn unify_maps(
            args: &BTreeMap<String, Datum>,
            args2: &BTreeMap<String, Datum>,
            bindings: &core::Bindings<Datum>,
        ) -> Option<core::Bindings<Datum>> {
            if args.len() != args2.len() {
                None
            } else {
                utils::fold_while_some(
                    bindings.clone(),
                    &mut args.iter(),
                    &|bindings: core::Bindings<Datum>, (ref k, ref v): (&String, &Datum)| args2.get(*k).and_then(|v2| v.unify(v2, &bindings)),
                )
            }
        }
        match *self {
            Datum::Variable(ref var_name) => bindings.update_bindings(var_name, other),
            Datum::Vector(ref args) => match *other {
                Datum::Vector(ref args2) if args.len() == args2.len() => unify_args(args, args2, &bindings),
                _ => None,
            },
            Datum::Map(ref args) => match *other {
                Datum::Map(ref args2) if args.len() == args2.len() => unify_maps(args, args2, &bindings),
                _ => None,
            },
            _ => match *other {
                Datum::Variable(ref var_name) => bindings.update_bindings(var_name, self),
                _ => {
                    if self == other {
                        Some(bindings.clone())
                    } else {
                        None
                    }
                }
            },
        }
    }

    fn apply_bindings(&self, bindings: &core::Bindings<Self>) -> Option<Self> {
        fn apply_bindings_to_args(args: &Vec<Datum>, bindings: &core::Bindings<Datum>) -> Option<Vec<Datum>> {
            utils::map_while_some(&mut args.iter(), &|arg| {
                arg.apply_bindings(bindings)
            })
        }
        fn apply_bindings_to_map(args: &BTreeMap<String, Datum>, bindings: &core::Bindings<Datum>) -> Option<BTreeMap<String, Datum>> {
            utils::map_while_some(&mut args.iter(), &|(k, v)| {
                v.apply_bindings(bindings)
                    .and_then(|v| Some((k.clone(), v)))
            }).and_then(|tuple_vec| Some(tuple_vec.into_iter().collect::<BTreeMap<String, Datum>>()))
        }
        match *self {
            Datum::Variable(ref var_name) => bindings
                .get_binding(var_name)
                .or_else(|| Some(self.clone())),
            Datum::Vector(ref args) => apply_bindings_to_args(args, bindings).and_then(|args| Some(Datum::Vector(args))),
            Datum::Map(ref args) => apply_bindings_to_map(args, bindings).and_then(|args| Some(Datum::Map(args))),
            _ => Some(self.clone()),
        }
    }

    fn variables(&self) -> Vec<String> {
        match *self {
            Datum::Variable(ref v) => vec![v.clone()],
            Datum::Vector(ref args) => {
                let mut variables = Vec::new();
                for arg in args.iter() {
                    variables.extend(arg.variables().into_iter());
                }
                variables
            }
            Datum::Map(ref args) => {
                let mut variables = Vec::new();
                for v in args.values() {
                    variables.extend(v.variables().into_iter());
                }
                variables
            }
            _ => Vec::new(),
        }
    }

    fn rename_variables(&self, renamed_variables: &HashMap<String, String>) -> Self {
        match *self {
            Datum::Variable(ref v) => renamed_variables
                .get(v)
                .and_then(|new_v| Some(Datum::Variable(new_v.clone())))
                .or_else(|| Some(self.clone()))
                .unwrap(),
            Datum::Vector(ref args) => Datum::Vector(
                args.iter()
                    .map(|arg| arg.rename_variables(renamed_variables))
                    .collect(),
            ),
            Datum::Map(ref args) => Datum::Map(
                args.iter()
                    .map(|(k, v)| (k.clone(), v.rename_variables(renamed_variables)))
                    .collect(),
            ),
            _ => self.clone(),
        }
    }

    fn nil() -> Self {
        Datum::Nil
    }
}

impl std::fmt::Display for Datum {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}", self.pprint())
    }
}

#[cfg(test)]
mod tests;
<|endoftext|>


<commit_before>use config::Config;
use email_bridge;
use emailaddress::EmailAddress;
use error::{BrokerError, BrokerResult};
use iron::{Handler, IronResult, Plugin, Request, Response, Url};
use iron::headers::{ContentType, Location};
use iron::method::Method;
use iron::modifiers;
use iron::status;
use oidc_bridge;
use serde_json::builder::{ArrayBuilder, ObjectBuilder};
use std::sync::Arc;
use super::{RedirectUri, handle_error, json_response};
use urlencoded::{UrlEncodedBody, UrlEncodedQuery};
use validation::{valid_uri, only_origin, same_origin};

/// Iron handler to return the OpenID Discovery document.
///
/// Most of this is hard-coded for now, although the URLs are constructed by
/// using the base URL as configured in the `public_url` configuration value.
broker_handler!(Discovery, |app, _req| {
    json_response(&ObjectBuilder::new()
        .insert("issuer", &app.public_url)
        .insert("authorization_endpoint",
                format!("{}/auth", app.public_url))
        .insert("jwks_uri", format!("{}/keys.json", app.public_url))
        .insert("scopes_supported", vec!["openid", "email"])
        .insert("claims_supported",
                vec!["aud", "email", "email_verified", "exp", "iat", "iss", "sub"])
        .insert("response_types_supported", vec!["id_token"])
        .insert("response_modes_supported", vec!["form_post"])
        .insert("grant_types_supported", vec!["implicit"])
        .insert("subject_types_supported", vec!["public"])
        .insert("id_token_signing_alg_values_supported", vec!["RS256"])
        .build())
});

/// Iron handler for the JSON Web Key Set document.
///
/// Respond with the JWK key set containing all of the configured keys.
///
/// Relying Parties will need to fetch this data to be able to verify identity
/// tokens issued by this daemon instance.
broker_handler!(KeySet, |app, _req| {
    let mut keys = ArrayBuilder::new();
    for key in &app.keys {
        keys = keys.push(key.public_jwk())
    }
    json_response(&ObjectBuilder::new()
                      .insert("keys", keys.build())
                      .build())
});

/// Iron handler for authentication requests from the RP.
///
/// Calls the `oidc::request()` function if the provided email address's
/// domain matches one of the configured famous providers. Otherwise, calls the
/// `email::request()` function to allow authentication through the email loop.
broker_handler!(Auth, |app, req| {
    let params = try!(
        match req.method {
            Method::Get => {
                req.compute::<UrlEncodedQuery>()
                    .map_err(|_| BrokerError::Input("no query string in GET request".to_string()))
            },
            Method::Post => {
                req.compute::<UrlEncodedBody>()
                    .map_err(|_| BrokerError::Input("no query string in POST data".to_string()))
            },
            _ => {
                panic!("Unsupported method: {}", req.method)
            }
        }
    );

    let client_id = try_get_param!(params, "client_id");
    let redirect_uri = try_get_param!(params, "redirect_uri");

    try!(valid_uri(redirect_uri));
    try!(valid_uri(client_id));
    try!(same_origin(client_id, redirect_uri));
    try!(only_origin(client_id));
    if let Some(ref whitelist) = app.allowed_origins {
        if !whitelist.contains(&client_id.to_string()) {
            return Err(BrokerError::Input("the origin is not whitelisted".to_string()));
        }
    }

    // Per the OAuth2 spec, we may redirect to the RP once we have validated client_id and
    // redirect_uri. In our case, this means we make redirect_uri available to error handling.
    let parsed_redirect_uri = Url::parse(redirect_uri).unwrap();
    req.extensions.insert::<RedirectUri>(parsed_redirect_uri.clone());

    if try_get_param!(params, "response_type") != "id_token" {
        return Err(BrokerError::Input("unsupported response_type, only id_token is supported".to_string()));
    }
    if try_get_param!(params, "response_mode", "fragment") != "form_post" {
        return Err(BrokerError::Input("unsupported response_mode, only form_post is supported".to_string()))
    }
    let email_addr = try!(
        EmailAddress::new(try_get_param!(params, "login_hint"))
            .map_err(|_| BrokerError::Input("login_hint is not a valid email address".to_string()))
    );
    let nonce = try_get_param!(params, "nonce");
    if app.providers.contains_key(&email_addr.domain) {

        // OIDC authentication. Redirect to the identity provider.
        let auth_url = try!(oidc_bridge::request(app, email_addr, client_id, nonce, &parsed_redirect_uri));
        Ok(Response::with((status::SeeOther, modifiers::Header(Location(auth_url.to_string())))))

    } else {

        // Email loop authentication. Render a message and form.
        let session_id = try!(email_bridge::request(app, email_addr, client_id, nonce, &parsed_redirect_uri));
        Ok(Response::with((status::Ok,
                           modifiers::Header(ContentType::html()),
                           app.templates.confirm_email.render(&[
                               ("client_id", &client_id),
                               ("session_id", &session_id),
                           ]))))

    }
});
<commit_msg>Lowercase the domain when deciding which flow to follow<commit_after>use config::Config;
use email_bridge;
use emailaddress::EmailAddress;
use error::{BrokerError, BrokerResult};
use iron::{Handler, IronResult, Plugin, Request, Response, Url};
use iron::headers::{ContentType, Location};
use iron::method::Method;
use iron::modifiers;
use iron::status;
use oidc_bridge;
use serde_json::builder::{ArrayBuilder, ObjectBuilder};
use std::sync::Arc;
use super::{RedirectUri, handle_error, json_response};
use urlencoded::{UrlEncodedBody, UrlEncodedQuery};
use validation::{valid_uri, only_origin, same_origin};

/// Iron handler to return the OpenID Discovery document.
///
/// Most of this is hard-coded for now, although the URLs are constructed by
/// using the base URL as configured in the `public_url` configuration value.
broker_handler!(Discovery, |app, _req| {
    json_response(&ObjectBuilder::new()
        .insert("issuer", &app.public_url)
        .insert("authorization_endpoint",
                format!("{}/auth", app.public_url))
        .insert("jwks_uri", format!("{}/keys.json", app.public_url))
        .insert("scopes_supported", vec!["openid", "email"])
        .insert("claims_supported",
                vec!["aud", "email", "email_verified", "exp", "iat", "iss", "sub"])
        .insert("response_types_supported", vec!["id_token"])
        .insert("response_modes_supported", vec!["form_post"])
        .insert("grant_types_supported", vec!["implicit"])
        .insert("subject_types_supported", vec!["public"])
        .insert("id_token_signing_alg_values_supported", vec!["RS256"])
        .build())
});

/// Iron handler for the JSON Web Key Set document.
///
/// Respond with the JWK key set containing all of the configured keys.
///
/// Relying Parties will need to fetch this data to be able to verify identity
/// tokens issued by this daemon instance.
broker_handler!(KeySet, |app, _req| {
    let mut keys = ArrayBuilder::new();
    for key in &app.keys {
        keys = keys.push(key.public_jwk())
    }
    json_response(&ObjectBuilder::new()
                      .insert("keys", keys.build())
                      .build())
});

/// Iron handler for authentication requests from the RP.
///
/// Calls the `oidc::request()` function if the provided email address's
/// domain matches one of the configured famous providers. Otherwise, calls the
/// `email::request()` function to allow authentication through the email loop.
broker_handler!(Auth, |app, req| {
    let params = try!(
        match req.method {
            Method::Get => {
                req.compute::<UrlEncodedQuery>()
                    .map_err(|_| BrokerError::Input("no query string in GET request".to_string()))
            },
            Method::Post => {
                req.compute::<UrlEncodedBody>()
                    .map_err(|_| BrokerError::Input("no query string in POST data".to_string()))
            },
            _ => {
                panic!("Unsupported method: {}", req.method)
            }
        }
    );

    let client_id = try_get_param!(params, "client_id");
    let redirect_uri = try_get_param!(params, "redirect_uri");

    try!(valid_uri(redirect_uri));
    try!(valid_uri(client_id));
    try!(same_origin(client_id, redirect_uri));
    try!(only_origin(client_id));
    if let Some(ref whitelist) = app.allowed_origins {
        if !whitelist.contains(&client_id.to_string()) {
            return Err(BrokerError::Input("the origin is not whitelisted".to_string()));
        }
    }

    // Per the OAuth2 spec, we may redirect to the RP once we have validated client_id and
    // redirect_uri. In our case, this means we make redirect_uri available to error handling.
    let parsed_redirect_uri = Url::parse(redirect_uri).unwrap();
    req.extensions.insert::<RedirectUri>(parsed_redirect_uri.clone());

    if try_get_param!(params, "response_type") != "id_token" {
        return Err(BrokerError::Input("unsupported response_type, only id_token is supported".to_string()));
    }
    if try_get_param!(params, "response_mode", "fragment") != "form_post" {
        return Err(BrokerError::Input("unsupported response_mode, only form_post is supported".to_string()))
    }
    let email_addr = try!(
        EmailAddress::new(try_get_param!(params, "login_hint"))
            .map_err(|_| BrokerError::Input("login_hint is not a valid email address".to_string()))
    );
    let nonce = try_get_param!(params, "nonce");
    if app.providers.contains_key(&email_addr.domain.to_lowercase()) {

        // OIDC authentication. Redirect to the identity provider.
        let auth_url = try!(oidc_bridge::request(app, email_addr, client_id, nonce, &parsed_redirect_uri));
        Ok(Response::with((status::SeeOther, modifiers::Header(Location(auth_url.to_string())))))

    } else {

        // Email loop authentication. Render a message and form.
        let session_id = try!(email_bridge::request(app, email_addr, client_id, nonce, &parsed_redirect_uri));
        Ok(Response::with((status::Ok,
                           modifiers::Header(ContentType::html()),
                           app.templates.confirm_email.render(&[
                               ("client_id", &client_id),
                               ("session_id", &session_id),
                           ]))))

    }
});
<|endoftext|>


<commit_before>use config::{Benchmark, Config, parse_config};
use Commit;
use git2;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus, Output};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<HashMap<String, BenchmarkResult<f64>>>>,
}

fn run_benchmark(workdir: &str, cfg: &Config, bench: &Benchmark) -> Output {
    let mut cmd;

    if cfg.version.is_none() || cfg.version.unwrap() < 2 {
        // older taster configs assume an implied "cargo" prefix on each benchmark command
        cmd = Command::new("cargo");
        cmd.current_dir(workdir)
            .env("RUST_BACKTRACE", "1")
            .arg(&bench.cmd)
            .args(bench.args.as_slice());
    } else {
        // from taster config version 2, we no longer assume an implicit "cargo" prefix on
        // benchmark commands
        cmd = Command::new(&bench.cmd);
        cmd.current_dir(workdir)
            .env("RUST_BACKTRACE", "1")
            .args(bench.args.as_slice());
    }

    cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", bench.name))
}

fn benchmark(
    workdir: &str,
    cfg: &Config,
    bench: &Benchmark,
    commit_id: git2::Oid,
    previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>,
) -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    use std::fs::File;
    use std::io::Write;

    // Run the benchmark and collect its output
    let output = run_benchmark(workdir, cfg, bench);

    // Write the output to a log file
    let mut stdout_file = File::create(&format!("{}-{}-stdout.log", commit_id, bench.name))
        .expect(&format!(
            "Failed to create stdout log file for benchmark '{}' at commit '{}'.",
            bench.name,
            commit_id
        ));
    stdout_file
        .write_all(output.stdout.as_slice())
        .expect("Failed to write benchmark output to stdout log file!");
    let mut stderr_file = File::create(&format!("{}-{}-stderr.log", commit_id, bench.name))
        .expect(&format!(
            "Failed to create stderr log file for benchmark '{}' at commit '{}'.",
            bench.name,
            commit_id
        ));
    stderr_file
        .write_all(output.stderr.as_slice())
        .expect("Failed to write benchmark output to stderr log file!");

    let lines = str::from_utf8(output.stdout.as_slice()).unwrap().lines();
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in bench.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", bench.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!(
                                "failed to parse value '{}' for {} into f64 number, ignoring",
                                c,
                                bm_name
                            );
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => {
                                    match *pv {
                                        BenchmarkResult::Improvement(v, _) => v,
                                        BenchmarkResult::Regression(v, _) => v,
                                        BenchmarkResult::Neutral(v, _) => v,
                                    }
                                }
                            };
                            let new_result = if bench.lower_is_better {
                                if val >= old_val * (1.0 + bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .env("RUST_BACKTRACE", "1")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(
    ws: &Workspace,
    history: &mut HashMap<String, HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
    push: &Push,
    commit: &Commit,
    def_improvement_threshold: f64,
    def_regression_threshold: f64,
) -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => {
            match pr.rfind("/") {
                None => None,
                Some(i) => Some(String::from(&pr[i + 1..])),
            }
        }
    };

    let build_success = update(&ws.path).success() && build(&ws.path).success();
    let test_success = test(&ws.path).success();

    let cfg = match parse_config(
        Path::new(&format!("{}/taster.toml", ws.path)),
        def_improvement_threshold,
        def_regression_threshold,
    ) {
        Ok(c) => c,
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!(
                        "Skipping commit {} which doesn't have a Taster config.",
                        commit.id
                    );
                    return Ok((
                        None,
                        TastingResult {
                            branch: branch,
                            commit: commit.clone(),
                            build: build_success,
                            test: test_success,
                            bench: false,
                            results: None,
                        },
                    ));
                }
                io::ErrorKind::InvalidInput => {
                    println!(
                        "Skipping commit {} which has an invalid Taster config.",
                        commit.id
                    );
                    return Ok((
                        None,
                        TastingResult {
                            branch: branch,
                            commit: commit.clone(),
                            build: build_success,
                            test: test_success,
                            bench: false,
                            results: None,
                        },
                    ));
                }
                _ => unimplemented!(),
            }
        }
    };

    let bench_out = match branch {
        Some(ref branch) => {
            let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
            cfg.benchmarks
                .iter()
                .map(|b| {
                    let new_result =
                        benchmark(&ws.path, &cfg, b, commit.id, branch_history.get(&b.name));
                    branch_history.insert(b.name.clone(), new_result.1.clone());
                    new_result
                })
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
        None => {
            cfg.benchmarks
                .iter()
                .map(|b| benchmark(&ws.path, &cfg, b, commit.id, None))
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
    };
    let bench_success = bench_out.iter().all(|x| x.0.success());
    let bench_results = bench_out.iter().map(|x| x.1.clone()).collect();

    Ok((
        Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_success,
            bench: bench_success,
            results: Some(bench_results),
        },
    ))
}

fn test(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("test")
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .status()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<commit_msg>Look for benchmark regex in stderr, too<commit_after>use config::{Benchmark, Config, parse_config};
use Commit;
use git2;
use Push;
use repo::Workspace;

use std::collections::HashMap;
use std::io;
use std::path::Path;
use std::process::{Command, ExitStatus, Output};
use std::str;

/// `(val, percentage_change)`
#[derive(Debug, Clone)]
pub enum BenchmarkResult<T> {
    Improvement(T, f64),
    Regression(T, f64),
    Neutral(T, f64),
}

#[derive(Debug, Clone)]
pub struct TastingResult {
    pub branch: Option<String>,
    pub commit: Commit,
    pub build: bool,
    pub test: bool,
    pub bench: bool,
    pub results: Option<Vec<HashMap<String, BenchmarkResult<f64>>>>,
}

fn run_benchmark(workdir: &str, cfg: &Config, bench: &Benchmark) -> Output {
    let mut cmd;

    if cfg.version.is_none() || cfg.version.unwrap() < 2 {
        // older taster configs assume an implied "cargo" prefix on each benchmark command
        cmd = Command::new("cargo");
        cmd.current_dir(workdir)
            .env("RUST_BACKTRACE", "1")
            .arg(&bench.cmd)
            .args(bench.args.as_slice());
    } else {
        // from taster config version 2, we no longer assume an implicit "cargo" prefix on
        // benchmark commands
        cmd = Command::new(&bench.cmd);
        cmd.current_dir(workdir)
            .env("RUST_BACKTRACE", "1")
            .args(bench.args.as_slice());
    }

    cmd.output()
        .expect(&format!("Failed to execute benchmark '{}'!", bench.name))
}

fn benchmark(
    workdir: &str,
    cfg: &Config,
    bench: &Benchmark,
    commit_id: git2::Oid,
    previous_result: Option<&HashMap<String, BenchmarkResult<f64>>>,
) -> (ExitStatus, HashMap<String, BenchmarkResult<f64>>) {
    use std::fs::File;
    use std::io::Write;

    // Run the benchmark and collect its output
    let output = run_benchmark(workdir, cfg, bench);

    // Write the output to a log file
    let mut stdout_file = File::create(&format!("{}-{}-stdout.log", commit_id, bench.name))
        .expect(&format!(
            "Failed to create stdout log file for benchmark '{}' at commit '{}'.",
            bench.name,
            commit_id
        ));
    stdout_file
        .write_all(output.stdout.as_slice())
        .expect("Failed to write benchmark output to stdout log file!");
    let mut stderr_file = File::create(&format!("{}-{}-stderr.log", commit_id, bench.name))
        .expect(&format!(
            "Failed to create stderr log file for benchmark '{}' at commit '{}'.",
            bench.name,
            commit_id
        ));
    stderr_file
        .write_all(output.stderr.as_slice())
        .expect("Failed to write benchmark output to stderr log file!");

    let lines = str::from_utf8(output.stdout.as_slice())
        .unwrap()
        .lines()
        .chain(str::from_utf8(output.stderr.as_slice()).unwrap().lines());
    let mut res = HashMap::new();

    // Don't try parsing the output if we didn't succeed
    if !output.status.success() {
        return (output.status, res);
    }

    // Success, so let's look for the results
    for l in lines {
        for (i, regex) in bench.result_expr.iter().enumerate() {
            for cap in regex.captures_iter(l) {
                let (metric, value) = if cap.len() > 2 {
                    (String::from(cap.at(1).unwrap()), cap.at(2))
                } else {
                    (format!("{}", i), cap.at(1))
                };
                let bm_name = format!("{}/{}", bench.name, &metric);
                if let Some(c) = value {
                    use std::str::FromStr;
                    let val = match f64::from_str(&c) {
                        Ok(f) => f,
                        Err(_) => {
                            println!(
                                "failed to parse value '{}' for {} into f64 number, ignoring",
                                c,
                                bm_name
                            );
                            continue;
                        }
                    };
                    let new_result = match previous_result {
                        None => BenchmarkResult::Improvement(val, 0.0),
                        Some(prev_res) => {
                            let old_val = match prev_res.get(&bm_name) {
                                None => val,
                                Some(pv) => {
                                    match *pv {
                                        BenchmarkResult::Improvement(v, _) => v,
                                        BenchmarkResult::Regression(v, _) => v,
                                        BenchmarkResult::Neutral(v, _) => v,
                                    }
                                }
                            };
                            let new_result = if bench.lower_is_better {
                                if val >= old_val * (1.0 + bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            } else {
                                if val >= old_val * (1.0 + bench.improvement_threshold) {
                                    BenchmarkResult::Improvement(val, (val / old_val) - 1.0)
                                } else if val < old_val * (1.0 - bench.regression_threshold) {
                                    BenchmarkResult::Regression(val, (val / old_val) - 1.0)
                                } else {
                                    BenchmarkResult::Neutral(val, (val / old_val) - 1.0)
                                }
                            };
                            new_result
                        }
                    };
                    res.insert(bm_name, new_result);
                }
            }
        }
    }
    (output.status, res)
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .env("RUST_BACKTRACE", "1")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(
    ws: &Workspace,
    history: &mut HashMap<String, HashMap<String, HashMap<String, BenchmarkResult<f64>>>>,
    push: &Push,
    commit: &Commit,
    def_improvement_threshold: f64,
    def_regression_threshold: f64,
) -> Result<(Option<Config>, TastingResult), String> {
    println!("Tasting commit {}", commit.id);
    ws.checkout_commit(&commit.id)?;

    let branch = match push.push_ref {
        None => None,
        Some(ref pr) => {
            match pr.rfind("/") {
                None => None,
                Some(i) => Some(String::from(&pr[i + 1..])),
            }
        }
    };

    let build_success = update(&ws.path).success() && build(&ws.path).success();
    let test_success = test(&ws.path).success();

    let cfg = match parse_config(
        Path::new(&format!("{}/taster.toml", ws.path)),
        def_improvement_threshold,
        def_regression_threshold,
    ) {
        Ok(c) => c,
        Err(e) => {
            match e.kind() {
                io::ErrorKind::NotFound => {
                    println!(
                        "Skipping commit {} which doesn't have a Taster config.",
                        commit.id
                    );
                    return Ok((
                        None,
                        TastingResult {
                            branch: branch,
                            commit: commit.clone(),
                            build: build_success,
                            test: test_success,
                            bench: false,
                            results: None,
                        },
                    ));
                }
                io::ErrorKind::InvalidInput => {
                    println!(
                        "Skipping commit {} which has an invalid Taster config.",
                        commit.id
                    );
                    return Ok((
                        None,
                        TastingResult {
                            branch: branch,
                            commit: commit.clone(),
                            build: build_success,
                            test: test_success,
                            bench: false,
                            results: None,
                        },
                    ));
                }
                _ => unimplemented!(),
            }
        }
    };

    let bench_out = match branch {
        Some(ref branch) => {
            let branch_history = history.entry(branch.clone()).or_insert(HashMap::new());
            cfg.benchmarks
                .iter()
                .map(|b| {
                    let new_result =
                        benchmark(&ws.path, &cfg, b, commit.id, branch_history.get(&b.name));
                    branch_history.insert(b.name.clone(), new_result.1.clone());
                    new_result
                })
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
        None => {
            cfg.benchmarks
                .iter()
                .map(|b| benchmark(&ws.path, &cfg, b, commit.id, None))
                .collect::<Vec<(ExitStatus, HashMap<String, BenchmarkResult<f64>>)>>()
        }
    };
    let bench_success = bench_out.iter().all(|x| x.0.success());
    let bench_results = bench_out.iter().map(|x| x.1.clone()).collect();

    Ok((
        Some(cfg),
        TastingResult {
            branch: branch,
            commit: commit.clone(),
            build: build_success,
            test: test_success,
            bench: bench_success,
            results: Some(bench_results),
        },
    ))
}

fn test(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("test")
        .env("RUST_BACKTRACE", "1")
        .env("RUST_TEST_THREADS", "1")
        .status()
        .expect("Failed to execute 'cargo test'!")
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<|endoftext|>


<commit_before>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GrayImage,
    GenericImage,
    ImageBuffer
};

use definitions::{
    Image
};

/// Compute the 2d running sum of a grayscale image.
///
/// An integral image I has width and height one greater than its source image F,
/// and is defined by I(x, y) = sum of F(x', y') for x' < x, y' < y, i.e. each pixel
/// in the integral image contains the sum of the pixel intensities of all input pixels
/// that are strictly above it and strictly to its left. In particular, the left column
/// and top row of an integral image are all 0, and the value of the bottom right pixel of
/// an integral image is equal to the sum of all pixels in the source image.
///
/// Integral images have the helpful property of allowing us to
/// compute the sum of pixel intensities in a rectangular region of an image
/// in constant time. Specifically, given a rectangle [l, r] * [t, b] in F,
/// the sum of the pixels in this rectangle is
/// I(r + 1, b + 1) - I(r + 1, t) - I(l, b + 1) + I(l, t).
// TODO: Support more formats, make faster, add a new IntegralImage type
// TODO: to make it harder to make off-by-one errors when computing sums of regions.
pub fn integral_image(image: &GrayImage) -> Image<Luma<u32>> {
    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 1;
    let out_height = in_height + 1;

    let mut out = ImageBuffer::from_pixel(out_width, out_height, Luma([0u32]));

    if in_width == 0 || in_height == 0 {
        return out;
    }

    for y in 1..out_height {
        let mut sum = 0;
        for x in 1..out_width {
            unsafe {
                sum += image.unsafe_get_pixel(x - 1, y - 1)[0] as u32;
                let above = out.unsafe_get_pixel(x, y - 1)[0];
                out.unsafe_put_pixel(x, y, Luma([above + sum]))
            }
        }
    }

    out
}

/// Sums the pixels in positions [left, right] * [top, bottom] in F, where `integral_image` is the
/// integral image of F.
// TODO: better type-safety. It's too easy to pass the original image in here by mistake.
pub fn sum_image_pixels(integral_image: &Image<Luma<u32>>, left: u32, top: u32, right: u32, bottom: u32) -> u32 {
    let sum = integral_image.get_pixel(right + 1, bottom + 1)[0] as i32
            - integral_image.get_pixel(right + 1, top)[0] as i32
            - integral_image.get_pixel(left, bottom + 1)[0] as i32
            + integral_image.get_pixel(left, top)[0] as i32;
    sum as u32
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
// TODO: faster, more formats
pub fn row_running_sum(image: &GrayImage, row: u32, buffer: &mut [u32], padding: u32) {

    let (width, height) = image.dimensions();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < height, format!("row out of bounds: {} >= {}", row, height));

    unsafe {
        let mut sum = 0;
        for x in 0..padding {
            sum += image.unsafe_get_pixel(0, row)[0] as u32;
            *buffer.get_unchecked_mut(x as usize) = sum;
        }

        for x in 0..width {
            sum += image.unsafe_get_pixel(x, row)[0] as u32;
            *buffer.get_unchecked_mut((x + padding) as usize) = sum;
        }

        for x in 0..padding {
            sum += image.unsafe_get_pixel(width - 1, row)[0] as u32;
            *buffer.get_unchecked_mut((x + width + padding) as usize) = sum;
        }
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum(image: &GrayImage, column: u32, buffer: &mut [u32], padding: u32) {

    let (width, height) = image.dimensions();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < width, format!("column out of bounds: {} >= {}", column, width));

    unsafe {
        let mut sum = 0;
        for y in 0..padding {
            sum += image.unsafe_get_pixel(column, 0)[0] as u32;
            *buffer.get_unchecked_mut(y as usize) = sum;
        }

        for y in 0..height {
            sum += image.unsafe_get_pixel(column, y)[0] as u32;
            *buffer.get_unchecked_mut((y + padding) as usize) = sum;
        }

        for y in 0..padding {
            sum += image.unsafe_get_pixel(column, height - 1)[0] as u32;
            *buffer.get_unchecked_mut((y + height + padding) as usize) = sum;
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use utils::{
        gray_bench_image,
        GrayTestImage,
        pixel_diff_summary
    };
    use image::{
        GenericImage,
        ImageBuffer,
        Luma
    };
    use quickcheck::{
        quickcheck,
        TestResult
    };
    use definitions::{
        Image
    };
    use test;

    #[test]
    fn test_sum_image_pixels() {
        let image = gray_image!(
            1, 2;
            3, 4);

        let integral = ::integralimage::integral_image(&image);

        assert_eq!(sum_image_pixels(&integral, 0, 0, 0, 0), 1);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 1, 0), 3);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 0, 1), 4);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 1, 1), 10);
        assert_eq!(sum_image_pixels(&integral, 1, 0, 1, 0), 2);
        assert_eq!(sum_image_pixels(&integral, 1, 0, 1, 1), 6);
        assert_eq!(sum_image_pixels(&integral, 0, 1, 0, 1), 3);
        assert_eq!(sum_image_pixels(&integral, 0, 1, 1, 1), 7);
        assert_eq!(sum_image_pixels(&integral, 1, 1, 1, 1), 4);
    }

    #[test]
    fn test_integral_image() {
        let image = gray_image!(
            1, 2, 3;
            4, 5, 6);

        let expected = gray_image_u32!(
            0,  0,  0,  0;
            0,  1,  3,  6;
            0,  5, 12, 21);

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    /// Simple implementation of integral_image to validate faster versions against.
    fn integral_image_ref<I>(image: &I) -> Image<Luma<u32>>
        where I: GenericImage<Pixel=Luma<u8>>
    {
        let (in_width, in_height) = image.dimensions();
        let (out_width, out_height) = (in_width + 1, in_height + 1);
        let mut out = ImageBuffer::from_pixel(out_width, out_height, Luma([0u32]));

        for y in 1..out_height {
            for x in 0..out_width {
                let mut sum = 0u32;

                for iy in 0..y {
                    for ix in 0..x {
                        sum += image.get_pixel(ix, iy)[0] as u32;
                    }
                }

                out.put_pixel(x, y, Luma([sum]));
            }
        }

        out
    }

    #[test]
    fn test_integral_image_matches_reference_implementation() {
        fn prop(image: GrayTestImage) -> TestResult {
            let expected = integral_image_ref(&image.0);
            let actual = integral_image(&image.0);
            match pixel_diff_summary(&actual, &expected) {
                None => TestResult::passed(),
                Some(err) => TestResult::error(err)
            }
        }
        quickcheck(prop as fn(GrayTestImage) -> TestResult);
    }

    #[test]
    fn test_row_running_sum() {
        let image = gray_image!(
            1, 2, 3;
            4, 5, 6);

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image = gray_image!(
            1, 4;
            2, 5;
            3, 6);

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<commit_msg>Add doctest example for integral_image<commit_after>//! Functions for computing [integral images](https://en.wikipedia.org/wiki/Summed_area_table)
//! and running sums of rows and columns.

extern crate image;

use image::{
    Luma,
    GrayImage,
    GenericImage,
    ImageBuffer
};

use definitions::Image;

/// Compute the 2d running sum of a grayscale image.
///
/// An integral image I has width and height one greater than its source image F,
/// and is defined by I(x, y) = sum of F(x', y') for x' < x, y' < y, i.e. each pixel
/// in the integral image contains the sum of the pixel intensities of all input pixels
/// that are strictly above it and strictly to its left. In particular, the left column
/// and top row of an integral image are all 0, and the value of the bottom right pixel of
/// an integral image is equal to the sum of all pixels in the source image.
///
/// Integral images have the helpful property of allowing us to
/// compute the sum of pixel intensities in a rectangular region of an image
/// in constant time. Specifically, given a rectangle [l, r] * [t, b] in F,
/// the sum of the pixels in this rectangle is
/// I(r + 1, b + 1) - I(r + 1, t) - I(l, b + 1) + I(l, t).
///
/// ```
/// # extern crate image;
/// # #[macro_use]
/// # extern crate imageproc;
/// # fn main() {
/// use imageproc::integralimage::{integral_image, sum_image_pixels};
///
/// let image = gray_image!(
///     1, 2, 3;
///     4, 5, 6);
///
/// let integral = gray_image_u32!(
///     0,  0,  0,  0;
///     0,  1,  3,  6;
///     0,  5, 12, 21);
///
/// assert_pixels_eq!(integral_image(&image), integral);
///
/// // Compute the sum of all pixels in the right two columns
/// assert_eq!(sum_image_pixels(&integral, 1, 0, 2, 1), 2 + 3 + 5 + 6);
///
/// // Compute the sum of all pixels in the top row
/// assert_eq!(sum_image_pixels(&integral, 0, 0, 2, 0), 1 + 2 + 3);
/// # }
/// ```
pub fn integral_image(image: &GrayImage) -> Image<Luma<u32>> {
    // TODO: Support more formats, make faster, add a new IntegralImage type
    // TODO: to make it harder to make off-by-one errors when computing sums of regions.
    let (in_width, in_height) = image.dimensions();
    let out_width = in_width + 1;
    let out_height = in_height + 1;

    let mut out = ImageBuffer::from_pixel(out_width, out_height, Luma([0u32]));

    if in_width == 0 || in_height == 0 {
        return out;
    }

    for y in 1..out_height {
        let mut sum = 0;
        for x in 1..out_width {
            unsafe {
                sum += image.unsafe_get_pixel(x - 1, y - 1)[0] as u32;
                let above = out.unsafe_get_pixel(x, y - 1)[0];
                out.unsafe_put_pixel(x, y, Luma([above + sum]))
            }
        }
    }

    out
}

/// Sums the pixels in positions [left, right] * [top, bottom] in F, where `integral_image` is the
/// integral image of F.
///
/// See the [`integral_image`](fn.integral_image.html) documentation for examples.
pub fn sum_image_pixels(integral_image: &Image<Luma<u32>>, left: u32, top: u32, right: u32, bottom: u32) -> u32 {
    // TODO: better type-safety. It's too easy to pass the original image in here by mistake.
    let sum = integral_image.get_pixel(right + 1, bottom + 1)[0] as i32
            - integral_image.get_pixel(right + 1, top)[0] as i32
            - integral_image.get_pixel(left, bottom + 1)[0] as i32
            + integral_image.get_pixel(left, top)[0] as i32;
    sum as u32
}

/// Computes the running sum of one row of image, padded
/// at the beginning and end. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all rows in an image.
pub fn row_running_sum(image: &GrayImage, row: u32, buffer: &mut [u32], padding: u32) {
    // TODO: faster, more formats
    let (width, height) = image.dimensions();
    assert!(buffer.len() >= (width + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), width, padding));
    assert!(row < height, format!("row out of bounds: {} >= {}", row, height));

    unsafe {
        let mut sum = 0;
        for x in 0..padding {
            sum += image.unsafe_get_pixel(0, row)[0] as u32;
            *buffer.get_unchecked_mut(x as usize) = sum;
        }

        for x in 0..width {
            sum += image.unsafe_get_pixel(x, row)[0] as u32;
            *buffer.get_unchecked_mut((x + padding) as usize) = sum;
        }

        for x in 0..padding {
            sum += image.unsafe_get_pixel(width - 1, row)[0] as u32;
            *buffer.get_unchecked_mut((x + width + padding) as usize) = sum;
        }
    }
}

/// Computes the running sum of one column of image, padded
/// at the top and bottom. The padding is by continuity.
/// Takes a reference to buffer so that this can be reused
/// for all columns in an image.
// TODO: faster, more formats
pub fn column_running_sum(image: &GrayImage, column: u32, buffer: &mut [u32], padding: u32) {

    let (width, height) = image.dimensions();
    assert!(buffer.len() >= (height + 2 * padding) as usize,
        format!("Buffer length {} is less than {} + 2 * {}", buffer.len(), height, padding));
    assert!(column < width, format!("column out of bounds: {} >= {}", column, width));

    unsafe {
        let mut sum = 0;
        for y in 0..padding {
            sum += image.unsafe_get_pixel(column, 0)[0] as u32;
            *buffer.get_unchecked_mut(y as usize) = sum;
        }

        for y in 0..height {
            sum += image.unsafe_get_pixel(column, y)[0] as u32;
            *buffer.get_unchecked_mut((y + padding) as usize) = sum;
        }

        for y in 0..padding {
            sum += image.unsafe_get_pixel(column, height - 1)[0] as u32;
            *buffer.get_unchecked_mut((y + height + padding) as usize) = sum;
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use utils::{
        gray_bench_image,
        GrayTestImage,
        pixel_diff_summary
    };
    use image::{
        GenericImage,
        ImageBuffer,
        Luma
    };
    use quickcheck::{
        quickcheck,
        TestResult
    };
    use definitions::{
        Image
    };
    use test;

    #[test]
    fn test_sum_image_pixels() {
        let image = gray_image!(
            1, 2;
            3, 4);

        let integral = ::integralimage::integral_image(&image);

        assert_eq!(sum_image_pixels(&integral, 0, 0, 0, 0), 1);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 1, 0), 3);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 0, 1), 4);
        assert_eq!(sum_image_pixels(&integral, 0, 0, 1, 1), 10);
        assert_eq!(sum_image_pixels(&integral, 1, 0, 1, 0), 2);
        assert_eq!(sum_image_pixels(&integral, 1, 0, 1, 1), 6);
        assert_eq!(sum_image_pixels(&integral, 0, 1, 0, 1), 3);
        assert_eq!(sum_image_pixels(&integral, 0, 1, 1, 1), 7);
        assert_eq!(sum_image_pixels(&integral, 1, 1, 1, 1), 4);
    }

    #[test]
    fn test_integral_image() {
        let image = gray_image!(
            1, 2, 3;
            4, 5, 6);

        let expected = gray_image_u32!(
            0,  0,  0,  0;
            0,  1,  3,  6;
            0,  5, 12, 21);

        assert_pixels_eq!(integral_image(&image), expected);
    }

    #[bench]
    fn bench_integral_image(b: &mut test::Bencher) {
        let image = gray_bench_image(500, 500);
        b.iter(|| {
            let integral = integral_image(&image);
            test::black_box(integral);
            });
    }

    /// Simple implementation of integral_image to validate faster versions against.
    fn integral_image_ref<I>(image: &I) -> Image<Luma<u32>>
        where I: GenericImage<Pixel=Luma<u8>>
    {
        let (in_width, in_height) = image.dimensions();
        let (out_width, out_height) = (in_width + 1, in_height + 1);
        let mut out = ImageBuffer::from_pixel(out_width, out_height, Luma([0u32]));

        for y in 1..out_height {
            for x in 0..out_width {
                let mut sum = 0u32;

                for iy in 0..y {
                    for ix in 0..x {
                        sum += image.get_pixel(ix, iy)[0] as u32;
                    }
                }

                out.put_pixel(x, y, Luma([sum]));
            }
        }

        out
    }

    #[test]
    fn test_integral_image_matches_reference_implementation() {
        fn prop(image: GrayTestImage) -> TestResult {
            let expected = integral_image_ref(&image.0);
            let actual = integral_image(&image.0);
            match pixel_diff_summary(&actual, &expected) {
                None => TestResult::passed(),
                Some(err) => TestResult::error(err)
            }
        }
        quickcheck(prop as fn(GrayTestImage) -> TestResult);
    }

    #[test]
    fn test_row_running_sum() {
        let image = gray_image!(
            1, 2, 3;
            4, 5, 6);

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        row_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_row_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(1000, 1);
        let mut buffer = [0; 1010];
        b.iter(|| {
            row_running_sum(&image, 0, &mut buffer, 5);
            });
    }

    #[test]
    fn test_column_running_sum() {
        let image = gray_image!(
            1, 4;
            2, 5;
            3, 6);

        let expected = [1, 2, 4, 7, 10];

        let mut buffer = [0; 5];
        column_running_sum(&image, 0, &mut buffer, 1);

        assert_eq!(buffer, expected);
    }

    #[bench]
    fn bench_column_running_sum(b: &mut test::Bencher) {
        let image = gray_bench_image(100, 1000);
        let mut buffer = [0; 1010];
        b.iter(|| {
            column_running_sum(&image, 0, &mut buffer, 5);
            });
    }
}
<|endoftext|>


<commit_before>mod terminal;
mod ansi;

use std::io::{self, Write};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, cursor, style};
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    Exit,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    terminal: Terminal
}

impl Interface {
    /// Creates a new Interface with the provided input choices.
    pub fn with_choices(choices: Vec<String>) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: choices,
            matching: Vec::new(),
            selected: 0,
            search: String::new(),
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') | Key::Ctrl('d') => {
                        self.clear()?;
                        return Err(Error::Exit);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_choices();
                        self.render()?;
                    },

                    Key::Backspace | Key::Ctrl('h') => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            iter().
            cloned().
            filter_map(|choice| Choice::new(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn render(&mut self) -> io::Result<()> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        write!(self.terminal, "> {}", self.search)?;

        let n = self.render_choices()?;

        if n > 0 {
            let column = format!("> {}", self.search).len() as u16;
            write!(self.terminal, "{}{}", cursor::Up(n), cursor::Column(column + 1))?;
        }

        Ok(())
    }

    fn render_choices(&mut self) -> io::Result<u16> {
        let max_width = self.terminal.max_width as usize;

        let choices = self.matching.
            iter().
            map(|c| c.text()).
            map(|c| {
                c.chars().take(max_width).collect::<String>()
            }).take(10);

        let number_of_choices = choices.len() as u16;

        for (i, choice) in choices.enumerate() {
            write!(self.terminal, "\r\n")?;

            if i == self.selected {
                let invert = style::Invert;
                let reset = style::NoInvert;
                write!(self.terminal, "{}{}{}", invert, choice, reset)?;
            } else {
                write!(self.terminal, "{}", choice)?;
            }
        }

        Ok(number_of_choices)
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<commit_msg>Add c-u to clear search<commit_after>mod terminal;
mod ansi;

use std::io::{self, Write};
use self::terminal::{Terminal, Event, Key};
use self::ansi::{clear, cursor, style};
use rff::choice::Choice;

#[derive(Debug)]
pub enum Error {
    Exit,
    Write(io::Error),
    Reset(terminal::Error)
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Write(err)
    }
}

impl From<terminal::Error> for Error {
    fn from(err: terminal::Error) -> Error {
        Error::Reset(err)
    }
}

pub struct Interface {
    choices: Vec<String>,
    matching: Vec<Choice>,
    selected: usize,
    search: String,
    terminal: Terminal
}

impl Interface {
    /// Creates a new Interface with the provided input choices.
    pub fn with_choices(choices: Vec<String>) -> Interface {
        let mut term = Terminal::from("/dev/tty").unwrap();
        term.set_raw_mode().unwrap();

        Interface {
            choices: choices,
            matching: Vec::new(),
            selected: 0,
            search: String::new(),
            terminal: term
        }
    }

    // Starts the interface
    pub fn run(&mut self) -> Result<&str, Error> {
        self.filter_choices();
        self.render()?;

        for event in self.terminal.events()? {
            if let Event::Key(key) = event? {
                match key {
                    Key::Ctrl('c') | Key::Ctrl('d') => {
                        self.clear()?;
                        return Err(Error::Exit);
                    }

                    Key::Char('\n') => {
                        break;
                    },

                    Key::Ctrl('n') => {
                        self.selected += 1;
                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Ctrl('p') => {
                        self.selected = self.selected.
                            checked_sub(1).
                            unwrap_or(0);

                        self.clamp_selected();
                        self.render()?;
                    },

                    Key::Char(ch) => {
                        self.search.push(ch);
                        self.filter_choices();
                        self.render()?;
                    },

                    Key::Backspace | Key::Ctrl('h') => {
                        self.search.pop();
                        self.filter_choices();
                        self.render()?;
                    }

                    Key::Ctrl('u') => {
                        self.search.clear();
                        self.filter_choices();
                        self.render()?;
                    }

                    _ => {}
                }
            };
        }

        self.clear()?;
        Ok(self.result())
    }

    fn filter_choices(&mut self) {
        let mut matches = self.choices.
            iter().
            cloned().
            filter_map(|choice| Choice::new(&self.search, choice)).
            collect::<Vec<_>>();

        matches.sort_by(|a, b| b.partial_cmp(a).unwrap());

        self.matching = matches;
    }

    fn render(&mut self) -> io::Result<()> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        write!(self.terminal, "> {}", self.search)?;

        let n = self.render_choices()?;

        if n > 0 {
            let column = format!("> {}", self.search).len() as u16;
            write!(self.terminal, "{}{}", cursor::Up(n), cursor::Column(column + 1))?;
        }

        Ok(())
    }

    fn render_choices(&mut self) -> io::Result<u16> {
        let max_width = self.terminal.max_width as usize;

        let choices = self.matching.
            iter().
            map(|c| c.text()).
            map(|c| {
                c.chars().take(max_width).collect::<String>()
            }).take(10);

        let number_of_choices = choices.len() as u16;

        for (i, choice) in choices.enumerate() {
            write!(self.terminal, "\r\n")?;

            if i == self.selected {
                let invert = style::Invert;
                let reset = style::NoInvert;
                write!(self.terminal, "{}{}{}", invert, choice, reset)?;
            } else {
                write!(self.terminal, "{}", choice)?;
            }
        }

        Ok(number_of_choices)
    }

    #[inline(always)]
    fn clamp_selected(&mut self) {
        let mut max = self.matching.len();
        if max > 10 { max = 10; }

        if self.selected >= max {
            self.selected = max - 1;
        }
    }

    fn clear(&mut self) -> Result<(), Error> {
        write!(self.terminal, "{}{}", cursor::Column(1), clear::Screen)?;
        self.terminal.reset()?;
        Ok(())
    }

    fn result(&mut self) -> &str {
        self.matching.iter().
            map(|c| c.text()).
            nth(self.selected).
            unwrap_or(&self.search)
    }
}
<|endoftext|>