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

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

<%helpers:shorthand name="text-decoration"
                    sub_properties="text-decoration-color
                                    text-decoration-line
                                    text-decoration-style"
                    products="gecko"
                    disable_when_testing="True"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
    use cssparser::Color as CSSParserColor;
    use properties::longhands::{text_decoration_color, text_decoration_line, text_decoration_style};
    use values::specified::CSSColor;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (mut color, mut line, mut style, mut any) = (None, None, None, false);
        loop {
            macro_rules! parse_component {
                ($value:ident, $module:ident) => (
                    if $value.is_none() {
                        if let Ok(value) = input.try(|input| $module::parse(context, input)) {
                            $value = Some(value);
                            any = true;
                            continue;
                        }
                    }
                )
            }

            parse_component!(color, text_decoration_color);
            parse_component!(line, text_decoration_line);
            parse_component!(style, text_decoration_style);
            break;
        }

        if !any {
            return Err(());
        }

        Ok(Longhands {
            text_decoration_color: color.or(Some(CSSColor { parsed: CSSParserColor::CurrentColor,
                                                            authored: None })),
            text_decoration_line: line.or(Some(text_decoration_line::computed_value::none)),
            text_decoration_style: style.or(Some(text_decoration_style::computed_value::T::solid)),
        })
    }

    impl<'a> LonghandsToSerialize<'a>  {
        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self.text_decoration_line {
                DeclaredValue::Value(ref line) => {
                    try!(line.to_css(dest));
                },
                _ => {
                    try!(write!(dest, "none"));
                }
            };

            if let DeclaredValue::Value(ref style) = *self.text_decoration_style {
                try!(write!(dest, " "));
                try!(style.to_css(dest));
            }

            if let DeclaredValue::Value(ref color) = *self.text_decoration_color {
                try!(write!(dest, " "));
                try!(color.to_css(dest));
            }

            Ok(())
        }
    }
</%helpers:shorthand>
<commit_msg>Ignore initial values in text-decoration<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

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

<%helpers:shorthand name="text-decoration"
                    sub_properties="text-decoration-color
                                    text-decoration-line
                                    text-decoration-style"
                    products="gecko"
                    disable_when_testing="True"
                    spec="https://drafts.csswg.org/css-text-decor/#propdef-text-decoration">
    use cssparser::Color as CSSParserColor;
    use properties::longhands::{text_decoration_color, text_decoration_line, text_decoration_style};
    use values::specified::CSSColor;

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let (mut color, mut line, mut style, mut any) = (None, None, None, false);
        loop {
            macro_rules! parse_component {
                ($value:ident, $module:ident) => (
                    if $value.is_none() {
                        if let Ok(value) = input.try(|input| $module::parse(context, input)) {
                            $value = Some(value);
                            any = true;
                            continue;
                        }
                    }
                )
            }

            parse_component!(color, text_decoration_color);
            parse_component!(line, text_decoration_line);
            parse_component!(style, text_decoration_style);
            break;
        }

        if !any {
            return Err(());
        }

        Ok(Longhands {
            text_decoration_color: color.or(Some(CSSColor { parsed: CSSParserColor::CurrentColor,
                                                            authored: None })),
            text_decoration_line: line.or(Some(text_decoration_line::computed_value::none)),
            text_decoration_style: style.or(Some(text_decoration_style::computed_value::T::solid)),
        })
    }

    impl<'a> LonghandsToSerialize<'a>  {
        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            match *self.text_decoration_line {
                DeclaredValue::Value(ref line) => {
                    try!(line.to_css(dest));
                },
                _ => {
                    try!(write!(dest, "none"));
                }
            };

            if let DeclaredValue::Value(ref style) = *self.text_decoration_style {
                if *style != text_decoration_style::computed_value::T::solid {
                    try!(write!(dest, " "));
                    try!(style.to_css(dest));
                }
            }

            if let DeclaredValue::Value(ref color) = *self.text_decoration_color {
                if color.parsed != CSSParserColor::CurrentColor {
                    try!(write!(dest, " "));
                    try!(color.to_css(dest));
                }
            }

            Ok(())
        }
    }
</%helpers:shorthand>
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::cmp::Ordering;
use std::convert::TryFrom;

use tidb_query_codegen::AggrFunction;
use tidb_query_datatype::{EvalType, FieldTypeAccessor};
use tipb::{Expr, ExprType, FieldType};

use tidb_query_common::Result;
use tidb_query_datatype::codec::data_type::*;
use tidb_query_datatype::expr::EvalContext;
use tidb_query_vec_expr::{RpnExpression, RpnExpressionBuilder};

/// A trait for MAX/MIN aggregation functions
pub trait Extremum: Clone + std::fmt::Debug + Send + Sync + 'static {
    const TP: ExprType;
    const ORD: Ordering;
}

#[derive(Debug, Clone, Copy)]
pub struct Max;

#[derive(Debug, Clone, Copy)]
pub struct Min;

impl Extremum for Max {
    const TP: ExprType = ExprType::Max;
    const ORD: Ordering = Ordering::Less;
}

impl Extremum for Min {
    const TP: ExprType = ExprType::Min;
    const ORD: Ordering = Ordering::Greater;
}

/// The parser for `MAX/MIN` aggregate functions.
pub struct AggrFnDefinitionParserExtremum<T: Extremum>(std::marker::PhantomData<T>);

impl<T: Extremum> AggrFnDefinitionParserExtremum<T> {
    pub fn new() -> Self {
        Self(std::marker::PhantomData)
    }
}

impl<T: Extremum> super::AggrDefinitionParser for AggrFnDefinitionParserExtremum<T> {
    fn check_supported(&self, aggr_def: &Expr) -> Result<()> {
        assert_eq!(aggr_def.get_tp(), T::TP);
        super::util::check_aggr_exp_supported_one_child(aggr_def)
    }

    fn parse(
        &self,
        mut aggr_def: Expr,
        ctx: &mut EvalContext,
        // We use the same structure for all data types, so this parameter is not needed.
        src_schema: &[FieldType],
        out_schema: &mut Vec<FieldType>,
        out_exp: &mut Vec<RpnExpression>,
    ) -> Result<Box<dyn super::AggrFunction>> {
        assert_eq!(aggr_def.get_tp(), T::TP);
        let child = aggr_def.take_children().into_iter().next().unwrap();
        let eval_type = EvalType::try_from(child.get_field_type().as_accessor().tp()).unwrap();

        let out_ft = aggr_def.take_field_type();
        let out_et = box_try!(EvalType::try_from(out_ft.as_accessor().tp()));

        if out_et != eval_type {
            return Err(other_err!(
                "Unexpected return field type {}",
                out_ft.as_accessor().tp()
            ));
        }

        // `MAX/MIN` outputs one column which has the same type with its child
        out_schema.push(out_ft);
        out_exp.push(RpnExpressionBuilder::build_from_expr_tree(
            child,
            ctx,
            src_schema.len(),
        )?);

        match_template_evaluable! {
            TT, match eval_type {
                EvalType::TT => Ok(Box::new(AggFnExtremum::<TT, T>::new()))
            }
        }
    }
}

/// The MAX/MIN aggregate functions.
#[derive(Debug, AggrFunction)]
#[aggr_function(state = AggFnStateExtremum::<T>::new(self.ord))]
pub struct AggFnExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    _phantom: std::marker::PhantomData<(T, E)>,
    ord: Ordering,
}

impl<T, E> AggFnExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    fn new() -> Self {
        Self {
            _phantom: std::marker::PhantomData,
            ord: E::ORD,
        }
    }
}

/// The state of the MAX/MIN aggregate function.
#[derive(Debug)]
pub struct AggFnStateExtremum<T>
where
    T: Evaluable + Ord,
    VectorValue: VectorValueExt<T>,
{
    ord: Ordering,
    extremum: Option<T>,
}

impl<T> AggFnStateExtremum<T>
where
    T: Evaluable + Ord,
    VectorValue: VectorValueExt<T>,
{
    pub fn new(ord: Ordering) -> Self {
        Self {
            ord,
            extremum: None,
        }
    }
}

impl<T> super::ConcreteAggrFunctionState for AggFnStateExtremum<T>
where
    T: Evaluable + Ord,
    VectorValue: VectorValueExt<T>,
{
    type ParameterType = T;

    #[inline]
    fn update_concrete(
        &mut self,
        _ctx: &mut EvalContext,
        value: &Option<Self::ParameterType>,
    ) -> Result<()> {
        if value.is_some() && (self.extremum.is_none() || self.extremum.cmp(value) == self.ord) {
            self.extremum = value.clone();
        }
        Ok(())
    }

    #[inline]
    fn push_result(&self, _ctx: &mut EvalContext, target: &mut [VectorValue]) -> Result<()> {
        target[0].push(self.extremum.clone());
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use tidb_query_datatype::EvalType;
    use tipb_helper::ExprDefBuilder;

    use super::*;
    use crate::parser::AggrDefinitionParser;
    use crate::AggrFunction;
    use tidb_query_datatype::codec::batch::{LazyBatchColumn, LazyBatchColumnVec};
    use tidb_query_datatype::{FieldTypeAccessor, FieldTypeTp};

    #[test]
    fn test_max() {
        let mut ctx = EvalContext::default();
        let function = AggFnExtremum::<Int, Max>::new();
        let mut state = function.create_state();

        let mut result = [VectorValue::with_capacity(0, EvalType::Int)];

        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Option::<Int>::None).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Some(7i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(7)]);

        state.update(&mut ctx, &Some(4i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(7)]);

        state.update_repeat(&mut ctx, &Some(20), 10).unwrap();
        state
            .update_repeat(&mut ctx, &Option::<Int>::None, 7)
            .unwrap();

        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(20)]);

        // update vector
        state.update(&mut ctx, &Some(7i64)).unwrap();
        state
            .update_vector(&mut ctx, &[Some(21i64), None, Some(22i64)], &[0, 1, 2])
            .unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(22)]);

        state.update(&mut ctx, &Some(40i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(40)]);
    }

    #[test]
    fn test_min() {
        let mut ctx = EvalContext::default();
        let function = AggFnExtremum::<Int, Min>::new();
        let mut state = function.create_state();

        let mut result = [VectorValue::with_capacity(0, EvalType::Int)];

        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Option::<Int>::None).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Some(100i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(100)]);

        state.update(&mut ctx, &Some(90i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(90)]);

        state.update_repeat(&mut ctx, &Some(80), 10).unwrap();
        state
            .update_repeat(&mut ctx, &Option::<Int>::None, 10)
            .unwrap();

        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(80)]);

        // update vector
        state.update(&mut ctx, &Some(70i64)).unwrap();
        state
            .update_vector(&mut ctx, &[Some(69i64), None, Some(68i64)], &[0, 1, 2])
            .unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(68)]);

        state.update(&mut ctx, &Some(2i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(2)]);

        state.update(&mut ctx, &Some(-1i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(-1i64)]);
    }

    #[test]
    fn test_integration() {
        let max_parser = AggrFnDefinitionParserExtremum::<Max>::new();
        let min_parser = AggrFnDefinitionParserExtremum::<Min>::new();

        let max = ExprDefBuilder::aggr_func(ExprType::Max, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        max_parser.check_supported(&max).unwrap();

        let min = ExprDefBuilder::aggr_func(ExprType::Min, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        min_parser.check_supported(&min).unwrap();

        let src_schema = [FieldTypeTp::LongLong.into()];
        let mut columns = LazyBatchColumnVec::from(vec![{
            let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(0, EvalType::Int);
            col.mut_decoded().push_int(Some(10000));
            col.mut_decoded().push_int(Some(1));
            col.mut_decoded().push_int(Some(23));
            col.mut_decoded().push_int(Some(42));
            col.mut_decoded().push_int(Some(-10000));
            col.mut_decoded().push_int(None);
            col.mut_decoded().push_int(Some(99));
            col.mut_decoded().push_int(Some(-1));
            col
        }]);
        let logical_rows = vec![3, 2, 6, 5, 1, 7];

        let mut schema = vec![];
        let mut exp = vec![];

        let mut ctx = EvalContext::default();
        let max_fn = max_parser
            .parse(max, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap();
        assert_eq!(schema.len(), 1);
        assert_eq!(schema[0].as_accessor().tp(), FieldTypeTp::LongLong);
        assert_eq!(exp.len(), 1);

        let min_fn = min_parser
            .parse(min, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap();
        assert_eq!(schema.len(), 2);
        assert_eq!(schema[1].as_accessor().tp(), FieldTypeTp::LongLong);
        assert_eq!(exp.len(), 2);

        let mut ctx = EvalContext::default();
        let mut max_state = max_fn.create_state();
        let mut min_state = min_fn.create_state();

        let mut aggr_result = [VectorValue::with_capacity(0, EvalType::Int)];

        // max
        {
            let max_result = exp[0]
                .eval(&mut ctx, &src_schema, &mut columns, &logical_rows, 6)
                .unwrap();
            let max_result = max_result.vector_value().unwrap();
            let max_slice: &[Option<Int>] = max_result.as_ref().as_ref();
            max_state
                .update_vector(&mut ctx, max_slice, max_result.logical_rows())
                .unwrap();
            max_state.push_result(&mut ctx, &mut aggr_result).unwrap();
        }

        // min
        {
            let min_result = exp[0]
                .eval(&mut ctx, &src_schema, &mut columns, &logical_rows, 6)
                .unwrap();
            let min_result = min_result.vector_value().unwrap();
            let min_slice: &[Option<Int>] = min_result.as_ref().as_ref();
            min_state
                .update_vector(&mut ctx, min_slice, min_result.logical_rows())
                .unwrap();
            min_state.push_result(&mut ctx, &mut aggr_result).unwrap();
        }

        assert_eq!(aggr_result[0].as_int_slice(), &[Some(99), Some(-1i64),]);
    }

    #[test]
    fn test_illegal_request() {
        let expr = ExprDefBuilder::aggr_func(ExprType::Max, FieldTypeTp::Double) // Expect LongLong but give Real
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        AggrFnDefinitionParserExtremum::<Max>::new()
            .check_supported(&expr)
            .unwrap();

        let src_schema = [FieldTypeTp::LongLong.into()];
        let mut schema = vec![];
        let mut exp = vec![];
        let mut ctx = EvalContext::default();
        AggrFnDefinitionParserExtremum::<Max>::new()
            .parse(expr, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap_err();
    }
}
<commit_msg>Zero cost MIN / MAX abstraction (#8072)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::cmp::Ordering;
use std::convert::TryFrom;

use tidb_query_codegen::AggrFunction;
use tidb_query_datatype::{EvalType, FieldTypeAccessor};
use tipb::{Expr, ExprType, FieldType};

use tidb_query_common::Result;
use tidb_query_datatype::codec::data_type::*;
use tidb_query_datatype::expr::EvalContext;
use tidb_query_vec_expr::{RpnExpression, RpnExpressionBuilder};

/// A trait for MAX/MIN aggregation functions
pub trait Extremum: Clone + std::fmt::Debug + Send + Sync + 'static {
    const TP: ExprType;
    const ORD: Ordering;
}

#[derive(Debug, Clone, Copy)]
pub struct Max;

#[derive(Debug, Clone, Copy)]
pub struct Min;

impl Extremum for Max {
    const TP: ExprType = ExprType::Max;
    const ORD: Ordering = Ordering::Less;
}

impl Extremum for Min {
    const TP: ExprType = ExprType::Min;
    const ORD: Ordering = Ordering::Greater;
}

/// The parser for `MAX/MIN` aggregate functions.
pub struct AggrFnDefinitionParserExtremum<T: Extremum>(std::marker::PhantomData<T>);

impl<T: Extremum> AggrFnDefinitionParserExtremum<T> {
    pub fn new() -> Self {
        Self(std::marker::PhantomData)
    }
}

impl<T: Extremum> super::AggrDefinitionParser for AggrFnDefinitionParserExtremum<T> {
    fn check_supported(&self, aggr_def: &Expr) -> Result<()> {
        assert_eq!(aggr_def.get_tp(), T::TP);
        super::util::check_aggr_exp_supported_one_child(aggr_def)
    }

    fn parse(
        &self,
        mut aggr_def: Expr,
        ctx: &mut EvalContext,
        // We use the same structure for all data types, so this parameter is not needed.
        src_schema: &[FieldType],
        out_schema: &mut Vec<FieldType>,
        out_exp: &mut Vec<RpnExpression>,
    ) -> Result<Box<dyn super::AggrFunction>> {
        assert_eq!(aggr_def.get_tp(), T::TP);
        let child = aggr_def.take_children().into_iter().next().unwrap();
        let eval_type = EvalType::try_from(child.get_field_type().as_accessor().tp()).unwrap();

        let out_ft = aggr_def.take_field_type();
        let out_et = box_try!(EvalType::try_from(out_ft.as_accessor().tp()));

        if out_et != eval_type {
            return Err(other_err!(
                "Unexpected return field type {}",
                out_ft.as_accessor().tp()
            ));
        }

        // `MAX/MIN` outputs one column which has the same type with its child
        out_schema.push(out_ft);
        out_exp.push(RpnExpressionBuilder::build_from_expr_tree(
            child,
            ctx,
            src_schema.len(),
        )?);

        match_template_evaluable! {
            TT, match eval_type {
                EvalType::TT => Ok(Box::new(AggFnExtremum::<TT, T>::new()))
            }
        }
    }
}

/// The MAX/MIN aggregate functions.
#[derive(Debug, AggrFunction)]
#[aggr_function(state = AggFnStateExtremum::<T, E>::new())]
pub struct AggFnExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    _phantom: std::marker::PhantomData<(T, E)>,
}

impl<T, E> AggFnExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    fn new() -> Self {
        Self {
            _phantom: std::marker::PhantomData,
        }
    }
}

/// The state of the MAX/MIN aggregate function.
#[derive(Debug)]
pub struct AggFnStateExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    extremum_value: Option<T>,
    _phantom: std::marker::PhantomData<E>,
}

impl<T, E> AggFnStateExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    pub fn new() -> Self {
        Self {
            extremum_value: None,
            _phantom: std::marker::PhantomData,
        }
    }
}

impl<T, E> super::ConcreteAggrFunctionState for AggFnStateExtremum<T, E>
where
    T: Evaluable + Ord,
    E: Extremum,
    VectorValue: VectorValueExt<T>,
{
    type ParameterType = T;

    #[inline]
    fn update_concrete(
        &mut self,
        _ctx: &mut EvalContext,
        value: &Option<Self::ParameterType>,
    ) -> Result<()> {
        if value.is_some()
            && (self.extremum_value.is_none() || self.extremum_value.cmp(value) == E::ORD)
        {
            self.extremum_value = value.clone();
        }
        Ok(())
    }

    #[inline]
    fn push_result(&self, _ctx: &mut EvalContext, target: &mut [VectorValue]) -> Result<()> {
        target[0].push(self.extremum_value.clone());
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use tidb_query_datatype::EvalType;
    use tipb_helper::ExprDefBuilder;

    use super::*;
    use crate::parser::AggrDefinitionParser;
    use crate::AggrFunction;
    use tidb_query_datatype::codec::batch::{LazyBatchColumn, LazyBatchColumnVec};
    use tidb_query_datatype::{FieldTypeAccessor, FieldTypeTp};

    #[test]
    fn test_max() {
        let mut ctx = EvalContext::default();
        let function = AggFnExtremum::<Int, Max>::new();
        let mut state = function.create_state();

        let mut result = [VectorValue::with_capacity(0, EvalType::Int)];

        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Option::<Int>::None).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Some(7i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(7)]);

        state.update(&mut ctx, &Some(4i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(7)]);

        state.update_repeat(&mut ctx, &Some(20), 10).unwrap();
        state
            .update_repeat(&mut ctx, &Option::<Int>::None, 7)
            .unwrap();

        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(20)]);

        // update vector
        state.update(&mut ctx, &Some(7i64)).unwrap();
        state
            .update_vector(&mut ctx, &[Some(21i64), None, Some(22i64)], &[0, 1, 2])
            .unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(22)]);

        state.update(&mut ctx, &Some(40i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(40)]);
    }

    #[test]
    fn test_min() {
        let mut ctx = EvalContext::default();
        let function = AggFnExtremum::<Int, Min>::new();
        let mut state = function.create_state();

        let mut result = [VectorValue::with_capacity(0, EvalType::Int)];

        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Option::<Int>::None).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[None]);

        state.update(&mut ctx, &Some(100i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(100)]);

        state.update(&mut ctx, &Some(90i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(90)]);

        state.update_repeat(&mut ctx, &Some(80), 10).unwrap();
        state
            .update_repeat(&mut ctx, &Option::<Int>::None, 10)
            .unwrap();

        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(80)]);

        // update vector
        state.update(&mut ctx, &Some(70i64)).unwrap();
        state
            .update_vector(&mut ctx, &[Some(69i64), None, Some(68i64)], &[0, 1, 2])
            .unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(68)]);

        state.update(&mut ctx, &Some(2i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(2)]);

        state.update(&mut ctx, &Some(-1i64)).unwrap();
        result[0].clear();
        state.push_result(&mut ctx, &mut result).unwrap();
        assert_eq!(result[0].as_int_slice(), &[Some(-1i64)]);
    }

    #[test]
    fn test_integration() {
        let max_parser = AggrFnDefinitionParserExtremum::<Max>::new();
        let min_parser = AggrFnDefinitionParserExtremum::<Min>::new();

        let max = ExprDefBuilder::aggr_func(ExprType::Max, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        max_parser.check_supported(&max).unwrap();

        let min = ExprDefBuilder::aggr_func(ExprType::Min, FieldTypeTp::LongLong)
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        min_parser.check_supported(&min).unwrap();

        let src_schema = [FieldTypeTp::LongLong.into()];
        let mut columns = LazyBatchColumnVec::from(vec![{
            let mut col = LazyBatchColumn::decoded_with_capacity_and_tp(0, EvalType::Int);
            col.mut_decoded().push_int(Some(10000));
            col.mut_decoded().push_int(Some(1));
            col.mut_decoded().push_int(Some(23));
            col.mut_decoded().push_int(Some(42));
            col.mut_decoded().push_int(Some(-10000));
            col.mut_decoded().push_int(None);
            col.mut_decoded().push_int(Some(99));
            col.mut_decoded().push_int(Some(-1));
            col
        }]);
        let logical_rows = vec![3, 2, 6, 5, 1, 7];

        let mut schema = vec![];
        let mut exp = vec![];

        let mut ctx = EvalContext::default();
        let max_fn = max_parser
            .parse(max, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap();
        assert_eq!(schema.len(), 1);
        assert_eq!(schema[0].as_accessor().tp(), FieldTypeTp::LongLong);
        assert_eq!(exp.len(), 1);

        let min_fn = min_parser
            .parse(min, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap();
        assert_eq!(schema.len(), 2);
        assert_eq!(schema[1].as_accessor().tp(), FieldTypeTp::LongLong);
        assert_eq!(exp.len(), 2);

        let mut ctx = EvalContext::default();
        let mut max_state = max_fn.create_state();
        let mut min_state = min_fn.create_state();

        let mut aggr_result = [VectorValue::with_capacity(0, EvalType::Int)];

        // max
        {
            let max_result = exp[0]
                .eval(&mut ctx, &src_schema, &mut columns, &logical_rows, 6)
                .unwrap();
            let max_result = max_result.vector_value().unwrap();
            let max_slice: &[Option<Int>] = max_result.as_ref().as_ref();
            max_state
                .update_vector(&mut ctx, max_slice, max_result.logical_rows())
                .unwrap();
            max_state.push_result(&mut ctx, &mut aggr_result).unwrap();
        }

        // min
        {
            let min_result = exp[0]
                .eval(&mut ctx, &src_schema, &mut columns, &logical_rows, 6)
                .unwrap();
            let min_result = min_result.vector_value().unwrap();
            let min_slice: &[Option<Int>] = min_result.as_ref().as_ref();
            min_state
                .update_vector(&mut ctx, min_slice, min_result.logical_rows())
                .unwrap();
            min_state.push_result(&mut ctx, &mut aggr_result).unwrap();
        }

        assert_eq!(aggr_result[0].as_int_slice(), &[Some(99), Some(-1i64),]);
    }

    #[test]
    fn test_illegal_request() {
        let expr = ExprDefBuilder::aggr_func(ExprType::Max, FieldTypeTp::Double) // Expect LongLong but give Real
            .push_child(ExprDefBuilder::column_ref(0, FieldTypeTp::LongLong))
            .build();
        AggrFnDefinitionParserExtremum::<Max>::new()
            .check_supported(&expr)
            .unwrap();

        let src_schema = [FieldTypeTp::LongLong.into()];
        let mut schema = vec![];
        let mut exp = vec![];
        let mut ctx = EvalContext::default();
        AggrFnDefinitionParserExtremum::<Max>::new()
            .parse(expr, &mut ctx, &src_schema, &mut schema, &mut exp)
            .unwrap_err();
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::ops::IndexMut;

use ::{SMF,Event,SMFFormat,MetaEvent,MidiMessage,Track,TrackEvent};

/// An AbsoluteEvent is an event that has an absolute time
/// This is useful for apps that want to store events internally
/// with absolute times and then quickly build an SMF file for saving etc...
pub struct AbsoluteEvent {
    time: u64,
    event: Event,
}

impl AbsoluteEvent {
    pub fn new_midi(time: u64, midi: MidiMessage) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Midi(midi),
        }
    }
    pub fn new_meta(time: u64, meta: MetaEvent) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Meta(meta),
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a midi
    /// event, false if it's a meta event
    pub fn is_midi(&self) -> bool {
        match self.event {
            Event::Midi(_) => true,
            Event::Meta(_) => false,
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a meta
    /// event, false if it's a midi event
    pub fn is_meta(&self) -> bool {
        match self.event {
            Event::Midi(_) => false,
            Event::Meta(_) => true,
        }
    }

    pub fn get_event(&self) -> &Event {
        &self.event
    }

    pub fn get_time(&self) -> u64 {
        self.time
    }
}

impl Eq for AbsoluteEvent {}

impl PartialEq for AbsoluteEvent {
    fn eq(&self, other: &AbsoluteEvent) -> bool {
        if self.time == other.time {
            match (&self.event,&other.event) {
                (&Event::Midi(_),&Event::Meta(_)) => false,
                (&Event::Meta(_),&Event::Midi(_)) => false,
                (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                    me.command == you.command
                },
                (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                    me.data(0) == you.data(0)
                        &&
                    me.data(1) == me.data(1)
                },
            }
        } else {
            false
        }
    }

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

// Implement `Ord` and sort messages by time
impl Ord for AbsoluteEvent {
    fn cmp(&self, other: &AbsoluteEvent) -> Ordering {
        let res = self.time.cmp(&other.time);
        match res {
            // vtime takes priority
            Ordering::Less | Ordering::Greater => res,
            // if vtime is the same, check types and make meta events
            // sort before standard events
            Ordering::Equal => {
                match (&self.event,&other.event) {
                    // I'm midi, other is meta, so I'm greater
                    (&Event::Midi(_),&Event::Meta(_)) => Ordering::Greater,
                    // I'm meta, other is midi, so I'm less
                    (&Event::Meta(_),&Event::Midi(_)) => Ordering::Less,
                    (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                        me.command.cmp(&you.command)
                    },
                    (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                        if      me.data(0) < you.data(0) { Ordering::Less }
                        else if me.data(0) > you.data(0) { Ordering::Greater }
                        else {
                            if me.data(1) < you.data(1) {
                                Ordering::Less
                            } else if me.data(1) > you.data(1) {
                                Ordering::Greater
                            } else {
                                res
                            }
                        }
                    },
                }
            }
        }
    }
}

impl PartialOrd for AbsoluteEvent {
    fn partial_cmp(&self, other: &AbsoluteEvent) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

enum EventContainer {
    Heap(BinaryHeap<AbsoluteEvent>),
    Static(Vec<TrackEvent>),
}

struct TrackBuilder {
    copyright: Option<String>,
    name: Option<String>,
    events: EventContainer,
}

impl TrackBuilder {

    fn result(self) -> Track {
        Track {
            copyright: self.copyright,
            name: self.name,
            events: match self.events {
                EventContainer::Heap(heap) => {
                    let mut events = Vec::with_capacity(heap.len());
                    let absevents = heap.into_sorted_vec();
                    let mut prev_time = 0;
                    for ev in absevents.into_iter() {
                        let vtime =
                            if prev_time == 0 {
                                ev.time
                            } else {
                                ev.time - prev_time
                            };
                        prev_time = ev.time;
                        events.push(TrackEvent {
                            vtime: vtime,
                            event: ev.event,
                        });
                    }
                    events
                },
                EventContainer::Static(vec) => vec,
            },
        }
    }

    fn abs_time_from_delta(&self,delta: u64) -> u64 {
        match self.events {
            EventContainer::Heap(ref heap) => {
                match heap.peek() {
                    Some(e) => { e.time + delta }
                    None => { delta }
                }
            }
            _ => { panic!("Can't call abs_time_from_delta on non-heap builder") }
        }
    }
}

/// An SMFBuilder can be used to create an SMF file.  This is done by
/// adding tracks to the builder via `add_track` and then adding
/// events to each track.
pub struct SMFBuilder {
    tracks:Vec<TrackBuilder>
}

impl SMFBuilder {
    /// Create a new SMFBuilder.  Initially the builder will have no tracks
    pub fn new() -> SMFBuilder {
        SMFBuilder {
            tracks: Vec::new(),
        }
    }

    /// Get the number of tracks currenly in the builder
    pub fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    /// Add new a track to this builder
    pub fn add_track(&mut self) {
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Heap(BinaryHeap::new()),
        });
    }

    /// Add a static track to the builder (note this will clone all events in the passed iterator)
    pub fn add_static_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut cur_time: u64 = 0;
        let vec = track.map(|bev| {
            assert!(bev.time >= cur_time);
            let vtime = bev.time - cur_time;
            cur_time = vtime;
            TrackEvent {
                vtime: vtime,
                event: bev.event.clone(),
            }
        }).collect();
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Static(vec),
        });
    }

    /// Set the copyright for the track at index `track`.  This will
    /// also cause a copyright meta event to be inserted.
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a copyright set.
    pub fn set_copyright(&mut self, track: usize, copyright: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].copyright.is_none());
        // let event = AbsoluteEvent {
        //     time: 0,
        //     event: Event::Meta(MetaEvent::copyright_notice(copyright.clone())),
        // };
        self.tracks[track].copyright = Some(copyright);
    }

    /// Set the name for the track at index `track`.  This will
    /// also cause a name meta event to be inserted.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a name set.
    pub fn set_name(&mut self, track: usize, name: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].name.is_none());
        // let event = AbsoluteEvent{
        //     time: 0,
        //     event: Event::Meta(MetaEvent::sequence_or_track_name(name.clone())),
        // };
        self.tracks[track].name = Some(name);
    }

    /// Add a midi message to track at index `track` at absolute time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_abs(&mut self, track: usize, time: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Midi(msg),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a midi message to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_rel(&mut self, track: usize, delta: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_midi_abs(track,time,msg);
    }

    /// Add a meta event to track at index `track` at absolute  time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_abs(&mut self, track: usize, time: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Meta(event),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a meta event to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_rel(&mut self, track: usize, delta: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_meta_abs(track,time,event);
    }

    /// Add a TrackEvent to the track at index `track`.  The event
    /// will be added at `event.vtime` after the last event currently
    /// in the builder for the track.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_event(&mut self, track: usize, event: TrackEvent) {
        assert!(self.tracks.len() > track);
        let bevent = AbsoluteEvent {
            time: self.tracks[track].abs_time_from_delta(event.vtime),
            event: event.event,
        };
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(bevent);
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Generate an SMF file with the events that have been added to
    /// the builder
    pub fn result(self) -> SMF {
        SMF {
            format: SMFFormat::MultiTrack,
            tracks: self.tracks.into_iter().map(|tb| tb.result()).collect(),
            division: 0,
        }
    }
}

#[test]
fn simple_build() {
    let note_on = MidiMessage::note_on(69,100,0);
    let note_off = MidiMessage::note_off(69,100,0);

    let mut builder = SMFBuilder::new();
    builder.add_track();

    builder.add_event(0, TrackEvent{vtime: 0, event: Event::Midi(note_on)});
    builder.add_event(0, TrackEvent{vtime: 10, event: Event::Midi(note_off)});
    builder.result();
}
<commit_msg>Fix bug in absolute MIDI event writing<commit_after>use std::cmp::Ordering;
use std::collections::BinaryHeap;
use std::ops::IndexMut;

use ::{SMF,Event,SMFFormat,MetaEvent,MidiMessage,Track,TrackEvent};

/// An AbsoluteEvent is an event that has an absolute time
/// This is useful for apps that want to store events internally
/// with absolute times and then quickly build an SMF file for saving etc...
pub struct AbsoluteEvent {
    time: u64,
    event: Event,
}

impl AbsoluteEvent {
    pub fn new_midi(time: u64, midi: MidiMessage) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Midi(midi),
        }
    }
    pub fn new_meta(time: u64, meta: MetaEvent) -> AbsoluteEvent {
        AbsoluteEvent {
            time: time,
            event: Event::Meta(meta),
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a midi
    /// event, false if it's a meta event
    pub fn is_midi(&self) -> bool {
        match self.event {
            Event::Midi(_) => true,
            Event::Meta(_) => false,
        }
    }

    /// Return true if the event inside this AbsoluteEvent is a meta
    /// event, false if it's a midi event
    pub fn is_meta(&self) -> bool {
        match self.event {
            Event::Midi(_) => false,
            Event::Meta(_) => true,
        }
    }

    pub fn get_event(&self) -> &Event {
        &self.event
    }

    pub fn get_time(&self) -> u64 {
        self.time
    }
}

impl Eq for AbsoluteEvent {}

impl PartialEq for AbsoluteEvent {
    fn eq(&self, other: &AbsoluteEvent) -> bool {
        if self.time == other.time {
            match (&self.event,&other.event) {
                (&Event::Midi(_),&Event::Meta(_)) => false,
                (&Event::Meta(_),&Event::Midi(_)) => false,
                (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                    me.command == you.command
                },
                (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                    me.data(0) == you.data(0)
                        &&
                    me.data(1) == me.data(1)
                },
            }
        } else {
            false
        }
    }

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

// Implement `Ord` and sort messages by time
impl Ord for AbsoluteEvent {
    fn cmp(&self, other: &AbsoluteEvent) -> Ordering {
        let res = self.time.cmp(&other.time);
        match res {
            // vtime takes priority
            Ordering::Less | Ordering::Greater => res,
            // if vtime is the same, check types and make meta events
            // sort before standard events
            Ordering::Equal => {
                match (&self.event,&other.event) {
                    // I'm midi, other is meta, so I'm greater
                    (&Event::Midi(_),&Event::Meta(_)) => Ordering::Greater,
                    // I'm meta, other is midi, so I'm less
                    (&Event::Meta(_),&Event::Midi(_)) => Ordering::Less,
                    (&Event::Meta(ref me),&Event::Meta(ref you)) => {
                        me.command.cmp(&you.command)
                    },
                    (&Event::Midi(ref me),&Event::Midi(ref you)) => {
                        if      me.data(0) < you.data(0) { Ordering::Less }
                        else if me.data(0) > you.data(0) { Ordering::Greater }
                        else {
                            if me.data(1) < you.data(1) {
                                Ordering::Less
                            } else if me.data(1) > you.data(1) {
                                Ordering::Greater
                            } else {
                                res
                            }
                        }
                    },
                }
            }
        }
    }
}

impl PartialOrd for AbsoluteEvent {
    fn partial_cmp(&self, other: &AbsoluteEvent) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

enum EventContainer {
    Heap(BinaryHeap<AbsoluteEvent>),
    Static(Vec<TrackEvent>),
}

struct TrackBuilder {
    copyright: Option<String>,
    name: Option<String>,
    events: EventContainer,
}

impl TrackBuilder {

    fn result(self) -> Track {
        Track {
            copyright: self.copyright,
            name: self.name,
            events: match self.events {
                EventContainer::Heap(heap) => {
                    let mut events = Vec::with_capacity(heap.len());
                    let absevents = heap.into_sorted_vec();
                    let mut prev_time = 0;
                    for ev in absevents.into_iter() {
                        let vtime =
                            if prev_time == 0 {
                                ev.time
                            } else {
                                ev.time - prev_time
                            };
                        prev_time = ev.time;
                        events.push(TrackEvent {
                            vtime: vtime,
                            event: ev.event,
                        });
                    }
                    events
                },
                EventContainer::Static(vec) => vec,
            },
        }
    }

    fn abs_time_from_delta(&self,delta: u64) -> u64 {
        match self.events {
            EventContainer::Heap(ref heap) => {
                match heap.peek() {
                    Some(e) => { e.time + delta }
                    None => { delta }
                }
            }
            _ => { panic!("Can't call abs_time_from_delta on non-heap builder") }
        }
    }
}

/// An SMFBuilder can be used to create an SMF file.  This is done by
/// adding tracks to the builder via `add_track` and then adding
/// events to each track.
pub struct SMFBuilder {
    tracks:Vec<TrackBuilder>
}

impl SMFBuilder {
    /// Create a new SMFBuilder.  Initially the builder will have no tracks
    pub fn new() -> SMFBuilder {
        SMFBuilder {
            tracks: Vec::new(),
        }
    }

    /// Get the number of tracks currenly in the builder
    pub fn num_tracks(&self) -> usize {
        self.tracks.len()
    }

    /// Add new a track to this builder
    pub fn add_track(&mut self) {
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Heap(BinaryHeap::new()),
        });
    }

    /// Add a static track to the builder (note this will clone all events in the passed iterator)
    pub fn add_static_track<'a,I>(&mut self, track: I) where I: Iterator<Item=&'a AbsoluteEvent> {
        let mut cur_time: u64 = 0;
        let vec = track.map(|bev| {
            assert!(bev.time >= cur_time);
            let vtime = bev.time - cur_time;
            cur_time = bev.time;
            TrackEvent {
                vtime: vtime,
                event: bev.event.clone(),
            }
        }).collect();
        self.tracks.push(TrackBuilder {
            copyright: None,
            name: None,
            events: EventContainer::Static(vec),
        });
    }

    /// Set the copyright for the track at index `track`.  This will
    /// also cause a copyright meta event to be inserted.
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a copyright set.
    pub fn set_copyright(&mut self, track: usize, copyright: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].copyright.is_none());
        // let event = AbsoluteEvent {
        //     time: 0,
        //     event: Event::Meta(MetaEvent::copyright_notice(copyright.clone())),
        // };
        self.tracks[track].copyright = Some(copyright);
    }

    /// Set the name for the track at index `track`.  This will
    /// also cause a name meta event to be inserted.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this
    /// builder, or if the track already has a name set.
    pub fn set_name(&mut self, track: usize, name: String) {
        assert!(self.tracks.len() > track);
        assert!(self.tracks[track].name.is_none());
        // let event = AbsoluteEvent{
        //     time: 0,
        //     event: Event::Meta(MetaEvent::sequence_or_track_name(name.clone())),
        // };
        self.tracks[track].name = Some(name);
    }

    /// Add a midi message to track at index `track` at absolute time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_abs(&mut self, track: usize, time: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Midi(msg),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a midi message to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_midi_rel(&mut self, track: usize, delta: u64, msg: MidiMessage) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_midi_abs(track,time,msg);
    }

    /// Add a meta event to track at index `track` at absolute  time
    /// `time`.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_abs(&mut self, track: usize, time: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(AbsoluteEvent {
                    time: time,
                    event: Event::Meta(event),
                });
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Add a meta event to track at index `track` at `delta` ticks
    /// after the last message (or at `delta` if no current messages
    /// exist)
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_meta_rel(&mut self, track: usize, delta: u64, event: MetaEvent) {
        assert!(self.tracks.len() > track);
        let time = self.tracks[track].abs_time_from_delta(delta);
        self.add_meta_abs(track,time,event);
    }

    /// Add a TrackEvent to the track at index `track`.  The event
    /// will be added at `event.vtime` after the last event currently
    /// in the builder for the track.
    ///
    /// ## Panics
    ///
    /// Panics if `track` is >= to the number of tracks in this builder
    pub fn add_event(&mut self, track: usize, event: TrackEvent) {
        assert!(self.tracks.len() > track);
        let bevent = AbsoluteEvent {
            time: self.tracks[track].abs_time_from_delta(event.vtime),
            event: event.event,
        };
        match self.tracks.index_mut(track).events {
            EventContainer::Heap(ref mut heap) => {
                heap.push(bevent);
            }
            _ => { panic!("Can't add events to static tracks") }
        }
    }

    /// Generate an SMF file with the events that have been added to
    /// the builder
    pub fn result(self) -> SMF {
        SMF {
            format: SMFFormat::MultiTrack,
            tracks: self.tracks.into_iter().map(|tb| tb.result()).collect(),
            division: 0,
        }
    }
}

#[test]
fn simple_build() {
    let note_on = MidiMessage::note_on(69,100,0);
    let note_off = MidiMessage::note_off(69,100,0);

    let mut builder = SMFBuilder::new();
    builder.add_track();

    builder.add_event(0, TrackEvent{vtime: 0, event: Event::Midi(note_on)});
    builder.add_event(0, TrackEvent{vtime: 10, event: Event::Midi(note_off)});
    builder.result();
}
<|endoftext|>


<commit_before>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};
use reply::ReplySender;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(target_os = "macos")]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(target_os = "macos"))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use std::fs::canonicalize() once stable (rust-lang/rust#27706)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| CString::new(s.as_bytes()).unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

impl ReplySender for ChannelSender {
    fn send(&self, data: &[&[u8]]) {
        if let Err(err) = ChannelSender::send(self, data) {
            error!("Failed to send FUSE reply: {}", err);
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.

    #[cfg(target_os = "macos")] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }

    #[cfg(not(target_os = "macos"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int {
        use fuse::fuse_unmount_compat22;
        use std::io::ErrorKind::PermissionDenied;

        let rc = unsafe { libc::umount(mnt.as_ptr()) };
        if rc < 0 && io::Error::last_os_error().kind() == PermissionDenied {
            // Linux always returns EPERM for non-root users.  We have to let the
            // library go through the setuid-root "fusermount -u" to unmount.
            unsafe { fuse_unmount_compat22(mnt.as_ptr()); }
            0
        } else {
            rc
        }
    }

    let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<commit_msg>fix unmount call for bsd systems<commit_after>//!
//! Raw communication channel to the FUSE kernel driver.
//!

use std::io;
use std::ffi::{CString, CStr, OsStr};
use std::os::unix::ffi::OsStrExt;
use std::path::{PathBuf, Path};
use libc::{c_char, c_int, c_void, size_t};
use fuse::{fuse_args, fuse_mount_compat25};
use reply::ReplySender;

// Libc provides iovec based I/O using readv and writev functions
#[allow(dead_code, non_camel_case_types)]
mod libc {
    use libc::{c_char, c_int, c_void, size_t, ssize_t};

    /// Iovec data structure for readv and writev calls.
    #[repr(C)]
    pub struct iovec {
        pub iov_base: *const c_void,
        pub iov_len: size_t,
    }

    extern "system" {
        /// Read data from fd into multiple buffers
        pub fn readv (fd: c_int, iov: *mut iovec, iovcnt: c_int) -> ssize_t;
        /// Write data from multiple buffers to fd
        pub fn writev (fd: c_int, iov: *const iovec, iovcnt: c_int) -> ssize_t;

        pub fn realpath (file_name: *const c_char, resolved_name: *mut c_char) -> *const c_char;

        #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))]
        pub fn unmount(dir: *const c_char, flags: c_int) -> c_int;
        #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                      target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))]
        pub fn umount(dir: *const c_char) -> c_int;
    }

    /// Max length for path names. 4096 should be reasonable safe (OS X uses 1024, Linux uses 4096)
    pub const PATH_MAX: usize = 4096;
}

/// Wrapper around libc's realpath.  Returns the errno value if the real path cannot be obtained.
/// FIXME: Use std::fs::canonicalize() once stable (rust-lang/rust#27706)
fn real_path (path: &CStr) -> io::Result<CString> {
    let mut resolved: Vec<c_char> = Vec::with_capacity(libc::PATH_MAX);
    unsafe {
        if libc::realpath(path.as_ptr(), resolved.as_mut_ptr()).is_null() {
            Err(io::Error::last_os_error())
        } else {
            // Using CStr::from_ptr gets the correct string length via strlen()
            let cresolved = CStr::from_ptr(resolved.as_ptr());
            Ok(CString::new(cresolved.to_bytes()).unwrap())
        }
    }
}

/// Helper function to provide options as a fuse_args struct
/// (which contains an argc count and an argv pointer)
fn with_fuse_args<T, F: FnOnce(&fuse_args) -> T> (options: &[&OsStr], f: F) -> T {
    let mut args: Vec<CString> = vec![CString::new("rust-fuse").unwrap()];
    args.extend(options.iter().map(|s| CString::new(s.as_bytes()).unwrap() ));
    let argptrs: Vec<*const i8> = args.iter().map(|s| s.as_ptr()).collect();
    f(&fuse_args { argc: argptrs.len() as i32, argv: argptrs.as_ptr(), allocated: 0 })
}

/// A raw communication channel to the FUSE kernel driver
pub struct Channel {
    mountpoint: PathBuf,
    fd: c_int,
}

impl Channel {
    /// Create a new communication channel to the kernel driver by mounting the
    /// given path. The kernel driver will delegate filesystem operations of
    /// the given path to the channel. If the channel is dropped, the path is
    /// unmounted.
    pub fn new (mountpoint: &Path, options: &[&OsStr]) -> io::Result<Channel> {
        let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
        real_path(&mnt).and_then(|mnt| {
            with_fuse_args(options, |args| {
                let fd = unsafe { fuse_mount_compat25(mnt.as_ptr(), args) };
                if fd < 0 {
                    Err(io::Error::last_os_error())
                } else {
                    let mountpoint = PathBuf::from(<OsStr as OsStrExt>::from_bytes(mnt.as_bytes()));
                    Ok(Channel { mountpoint: mountpoint, fd: fd })
                }
            })
        })
    }

    /// Return path of the mounted filesystem
    pub fn mountpoint (&self) -> &Path {
        &self.mountpoint
    }

    /// Receives data up to the capacity of the given buffer (can block).
    pub fn receive (&self, buffer: &mut Vec<u8>) -> io::Result<()> {
        let rc = unsafe { ::libc::read(self.fd, buffer.as_ptr() as *mut c_void, buffer.capacity() as size_t) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            unsafe { buffer.set_len(rc as usize); }
            Ok(())
        }
    }

    /// Returns a sender object for this channel. The sender object can be
    /// used to send to the channel. Multiple sender objects can be used
    /// and they can safely be sent to other threads.
    pub fn sender (&self) -> ChannelSender {
        // Since write/writev syscalls are threadsafe, we can simply create
        // a sender by using the same fd and use it in other threads. Only
        // the channel closes the fd when dropped. If any sender is used after
        // dropping the channel, it'll return an EBADF error.
        ChannelSender { fd: self.fd }
    }
}

impl Drop for Channel {
    fn drop (&mut self) {
        // TODO: send ioctl FUSEDEVIOCSETDAEMONDEAD on OS X before closing the fd
        // Close the communication channel to the kernel driver
        // (closing it before unnmount prevents sync unmount deadlock)
        unsafe { ::libc::close(self.fd); }
        // Unmount this channel's mount point
        let _ = unmount(&self.mountpoint);
    }
}

#[derive(Clone, Copy)]
pub struct ChannelSender {
    fd: c_int,
}

impl ChannelSender {
    /// Send all data in the slice of slice of bytes in a single write (can block).
    pub fn send (&self, buffer: &[&[u8]]) -> io::Result<()> {
        let iovecs: Vec<libc::iovec> = buffer.iter().map(|d| {
            libc::iovec { iov_base: d.as_ptr() as *const c_void, iov_len: d.len() as size_t }
        }).collect();
        let rc = unsafe { libc::writev(self.fd, iovecs.as_ptr(), iovecs.len() as c_int) };
        if rc < 0 {
            Err(io::Error::last_os_error())
        } else {
            Ok(())
        }
    }
}

impl ReplySender for ChannelSender {
    fn send(&self, data: &[&[u8]]) {
        if let Err(err) = ChannelSender::send(self, data) {
            error!("Failed to send FUSE reply: {}", err);
        }
    }
}

/// Unmount an arbitrary mount point
pub fn unmount (mountpoint: &Path) -> io::Result<()> {
    // fuse_unmount_compat22 unfortunately doesn't return a status. Additionally,
    // it attempts to call realpath, which in turn calls into the filesystem. So
    // if the filesystem returns an error, the unmount does not take place, with
    // no indication of the error available to the caller. So we call unmount
    // directly, which is what osxfuse does anyway, since we already converted
    // to the real path when we first mounted.

    #[cfg(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
              target_os = "openbsd", target_os = "bitrig", target_os = "netbsd"))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int { unsafe { libc::unmount(mnt.as_ptr(), 0) } }

    #[cfg(not(any(target_os = "macos", target_os = "freebsd", target_os = "dragonfly",
                  target_os = "openbsd", target_os = "bitrig", target_os = "netbsd")))] #[inline]
    fn libc_umount (mnt: &CStr) -> c_int {
        use fuse::fuse_unmount_compat22;
        use std::io::ErrorKind::PermissionDenied;

        let rc = unsafe { libc::umount(mnt.as_ptr()) };
        if rc < 0 && io::Error::last_os_error().kind() == PermissionDenied {
            // Linux always returns EPERM for non-root users.  We have to let the
            // library go through the setuid-root "fusermount -u" to unmount.
            unsafe { fuse_unmount_compat22(mnt.as_ptr()); }
            0
        } else {
            rc
        }
    }

    let mnt = try!(CString::new(mountpoint.as_os_str().as_bytes()));
    let rc = libc_umount(&mnt);
    if rc < 0 {
        Err(io::Error::last_os_error())
    } else {
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::with_fuse_args;
    use std::ffi::{CStr, OsStr};

    #[test]
    fn fuse_args () {
        with_fuse_args(&[OsStr::new("foo"), OsStr::new("bar")], |args| {
            assert_eq!(args.argc, 3);
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(0)).to_bytes() }, b"rust-fuse");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(1)).to_bytes() }, b"foo");
            assert_eq!(unsafe { CStr::from_ptr(*args.argv.offset(2)).to_bytes() }, b"bar");
        });
    }
}
<|endoftext|>


<commit_before>
#[derive(PartialEq)]
#[derive(Debug)]
enum PrizeType {
    SPECIAL,
    GRAND,
    FIRST,
    SECOND,
    THIRD,
    FOURTH,
    FIFTH,
    SIXTH,
    ADDITIONAL,
    NONE
}

struct WinningNumbers {
    special_prize: String,
    grand_prize: String,
    regular_prizes: [String; 3],
    additional_prize: String
}

fn check_ticket(winning_number: &str, ticket_number: &str) -> usize {
    let mut max_length = 0;

    for length in (3..9).rev() {
        let winning_reverse: String = winning_number.chars().rev().collect();
        let ticket_reverse: String = ticket_number.chars().rev().collect();
        if winning_reverse[0..length] == ticket_reverse[0..length] {
            max_length = length;
            break;
        }
    }

    max_length
}

fn check_ticket_all(winning_numbers: &WinningNumbers, ticket_number: &str) -> PrizeType
{
    PrizeType::NONE
}

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

    #[test]
    fn check_last_digit_off() {
        assert_eq!(0, check_ticket("AB-87654320","AB-87654321"));
    }

    #[test]
    fn check_full_match() {
        assert_eq!(8, check_ticket("AB-87654321","AB-87654321"));
    }

    #[test]
    fn check_last_three() {
        assert_eq!(3, check_ticket("AB-87654321","AB-00000321"));
    }

    #[test]
    fn check_last_two_no_match() {
        assert_eq!(0, check_ticket("AB-87654321","AB-00000021"));
    }

    #[test]
    fn check_full_ticket() {
        let winning_numbers = WinningNumbers {
            special_prize: String::from("01234567"),
            grand_prize: String::from("12345678"),
            regular_prizes: [String::from("98765432"),
                             String::from("87654321"),
                             String::from("76543210")],
            additional_prize: String::from("765")
        };

        assert_eq!(PrizeType::NONE, check_ticket_all(&winning_numbers, "AZ-39842818"));
        assert_eq!(PrizeType::SPECIAL, check_ticket_all(&winning_numbers, "AD-01234567"));
    }
}
<commit_msg>Added full checking of an individual ticket<commit_after>
#[derive(PartialEq)]
#[derive(Debug)]
enum PrizeType {
    SPECIAL,
    GRAND,
    FIRST,
    SECOND,
    THIRD,
    FOURTH,
    FIFTH,
    SIXTH,
    ADDITIONAL,
    NONE
}

struct WinningNumbers {
    special_prize: String,
    grand_prize: String,
    regular_prizes: [String; 3],
    additional_prize: String
}

fn check_ticket(winning_number: &str, ticket_number: &str) -> usize {
    let mut max_length = 0;

    for length in (3..9).rev() {
        let winning_reverse: String = winning_number.chars().rev().collect();
        let ticket_reverse: String = ticket_number.chars().rev().collect();
        if winning_reverse[0..length] == ticket_reverse[0..length] {
            max_length = length;
            break;
        }
    }

    max_length
}

fn convert_number_to_prize(number: usize) -> PrizeType {
    match number {
        8 => PrizeType::FIRST,
        7 => PrizeType::SECOND,
        6 => PrizeType::THIRD,
        5 => PrizeType::FOURTH,
        4 => PrizeType::FIFTH,
        3 => PrizeType::SIXTH,
        _ => PrizeType::NONE,
    }
}

fn check_ticket_regular(regular_prizes: &[String; 3], ticket_number: &str) -> PrizeType {
    let regular1 = check_ticket(&regular_prizes[0], ticket_number);
    let regular2 = check_ticket(&regular_prizes[1], ticket_number);
    let regular3 = check_ticket(&regular_prizes[2], ticket_number);

    let results = [regular1, regular2, regular3];
    let best_result = results.iter().max();

    match best_result {
        Some(number) => convert_number_to_prize(*number),
        None => PrizeType::NONE,
    }
}

fn check_ticket_all(winning_numbers: &WinningNumbers, ticket_number: &str) -> PrizeType
{
    let special_rev = winning_numbers.special_prize.chars().rev();
    let grand_rev = winning_numbers.grand_prize.chars().rev();
    let ticket_rev : String = ticket_number.chars().rev().collect();

    let regular_prize_result = check_ticket_regular(&winning_numbers.regular_prizes, ticket_number);

    if ticket_rev.chars().take(8).eq(special_rev) {
        PrizeType::SPECIAL
    }
    else if ticket_rev.chars().take(8).eq(grand_rev) {
        PrizeType::GRAND
    }
    else if regular_prize_result != PrizeType::NONE {
        regular_prize_result
    }
    else if ticket_rev.chars().take(3).eq(winning_numbers.additional_prize.chars().rev()) {
        PrizeType::ADDITIONAL
    }
    else
    {
        PrizeType::NONE
    }
}

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

    #[test]
    fn check_last_digit_off() {
        assert_eq!(0, check_ticket("AB-87654320","AB-87654321"));
    }

    #[test]
    fn check_full_match() {
        assert_eq!(8, check_ticket("AB-87654321","AB-87654321"));
    }

    #[test]
    fn check_last_three() {
        assert_eq!(3, check_ticket("AB-87654321","AB-00000321"));
    }

    #[test]
    fn check_last_two_no_match() {
        assert_eq!(0, check_ticket("AB-87654321","AB-00000021"));
    }
/////////////////////// Convert number to prize
    #[test]
    fn test_convert_number_to_prize() {
        assert_eq!(PrizeType::FIRST, convert_number_to_prize(8));
        assert_eq!(PrizeType::SECOND, convert_number_to_prize(7));
        assert_eq!(PrizeType::THIRD, convert_number_to_prize(6));
        assert_eq!(PrizeType::FOURTH, convert_number_to_prize(5));
        assert_eq!(PrizeType::FIFTH, convert_number_to_prize(4));
        assert_eq!(PrizeType::SIXTH, convert_number_to_prize(3));
        assert_eq!(PrizeType::NONE, convert_number_to_prize(2));
        assert_eq!(PrizeType::NONE, convert_number_to_prize(1));
        assert_eq!(PrizeType::NONE, convert_number_to_prize(0));
        assert_eq!(PrizeType::NONE, convert_number_to_prize(9));
    }
/////////////////////// check_ticket_regular
    #[test]
    fn test_check_regular() {
        let regular_numbers = [String::from("98765432"),
                               String::from("87654321"),
                               String::from("76543210")];

        assert_eq!(PrizeType::FIRST, check_ticket_regular(&regular_numbers,  "98765432"));
        assert_eq!(PrizeType::FIRST, check_ticket_regular(&regular_numbers,  "87654321"));
        assert_eq!(PrizeType::FIRST, check_ticket_regular(&regular_numbers,  "76543210"));
        assert_eq!(PrizeType::SECOND, check_ticket_regular(&regular_numbers, "08765432"));
        assert_eq!(PrizeType::THIRD, check_ticket_regular(&regular_numbers,  "00765432"));
        assert_eq!(PrizeType::FOURTH, check_ticket_regular(&regular_numbers, "00065432"));
        assert_eq!(PrizeType::FIFTH, check_ticket_regular(&regular_numbers,  "00005432"));
        assert_eq!(PrizeType::SIXTH, check_ticket_regular(&regular_numbers,  "00000432"));
        assert_eq!(PrizeType::NONE, check_ticket_regular(&regular_numbers,   "00000032"));

        // Double check that smaller, non-first prizes are checked on the other winning numbers
        assert_eq!(PrizeType::FIFTH, check_ticket_regular(&regular_numbers,  "00004321"));
        assert_eq!(PrizeType::SIXTH, check_ticket_regular(&regular_numbers,  "00000210"));
    }

///////////////////// check_ticket_all
    #[test]
    fn check_full_ticket() {
        let winning_numbers = WinningNumbers {
            special_prize: String::from("01234567"),
            grand_prize: String::from("12345678"),
            regular_prizes: [String::from("98765432"),
                             String::from("87654321"),
                             String::from("76543210")],
            additional_prize: String::from("765")
        };

        assert_eq!(PrizeType::NONE, check_ticket_all(&winning_numbers, "AZ-39842818"));
        assert_eq!(PrizeType::SPECIAL, check_ticket_all(&winning_numbers, "AD-01234567"));
        assert_eq!(PrizeType::GRAND, check_ticket_all(&winning_numbers, "AD-12345678"));
        assert_eq!(PrizeType::FIRST, check_ticket_all(&winning_numbers, "AD-98765432"));
        assert_eq!(PrizeType::SIXTH, check_ticket_all(&winning_numbers, "BB-00000210"));
        assert_eq!(PrizeType::ADDITIONAL, check_ticket_all(&winning_numbers, "ZZ-00000765"));
    }
}
<|endoftext|>


<commit_before>use std::io::{BufRead, Write};

use quick_xml::events::attributes::Attributes;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Error as XmlError;
use quick_xml::Reader;
use quick_xml::Writer;

use crate::error::Error;
use crate::fromxml::FromXml;
use crate::toxml::ToXml;
use crate::util::atom_any_text;

/// Represents the content of an Atom entry
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "builders", derive(Builder))]
#[cfg_attr(
    feature = "builders",
    builder(
        setter(into),
        default,
        build_fn(name = "build_impl", private, error = "never::Never")
    )
)]
pub struct Content {
    /// Base URL for resolving any relative references found in the element.
    pub base: Option<String>,
    /// Indicates the natural language for the element.
    pub lang: Option<String>,
    /// The text value of the content.
    pub value: Option<String>,
    /// The URI of where the content can be found.
    pub src: Option<String>,
    /// Either "text", "html", "xhtml", or the MIME type of the content.
    pub content_type: Option<String>,
}

impl Content {
    /// Return base URL of the content.
    pub fn base(&self) -> Option<&str> {
        self.base.as_deref()
    }

    /// Set base URL of the content.
    pub fn set_base<V>(&mut self, base: V)
    where
        V: Into<Option<String>>,
    {
        self.base = base.into();
    }

    /// Return natural language of the content.
    pub fn lang(&self) -> Option<&str> {
        self.lang.as_deref()
    }

    /// Set the base URL of the content.
    pub fn set_lang<V>(&mut self, lang: V)
    where
        V: Into<Option<String>>,
    {
        self.lang = lang.into();
    }

    /// Return the text value of the content.
    ///
    /// If the `content_type` is neither `"text"`, `"html"`, or `"xhtml"` then the value should
    /// be a base64 encoded document of the indicated MIME type.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_value("Example content".to_string());
    /// assert_eq!(content.value(), Some("Example content"));
    /// ```
    pub fn value(&self) -> Option<&str> {
        self.value.as_deref()
    }

    /// Set the text value of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_value("Example content".to_string());
    /// ```
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<Option<String>>,
    {
        self.value = value.into();
    }

    /// Return the URI where the content can be found.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_src("http://example.com/content.html".to_string());
    /// assert_eq!(content.src(), Some("http://example.com/content.html"));
    /// ```
    pub fn src(&self) -> Option<&str> {
        self.src.as_deref()
    }

    /// Set the URI where the content can be found.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_src("http://example.com/content.html".to_string());
    /// ```
    pub fn set_src<V>(&mut self, src: V)
    where
        V: Into<Option<String>>,
    {
        self.src = src.into();
    }

    /// Return the type of the content.
    ///
    /// The type is either `"text"`, `"html"`, `"xhtml"`, or the MIME type of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_content_type("image/png".to_string());
    /// assert_eq!(content.content_type(), Some("image/png"));
    /// ```
    pub fn content_type(&self) -> Option<&str> {
        self.content_type.as_deref()
    }

    /// Set the type of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_content_type("image/png".to_string());
    /// assert_eq!(content.content_type(), Some("image/png"));
    /// ```
    pub fn set_content_type<V>(&mut self, content_type: V)
    where
        V: Into<Option<String>>,
    {
        self.content_type = content_type.into();
    }
}

impl FromXml for Content {
    fn from_xml<B: BufRead>(
        reader: &mut Reader<B>,
        mut atts: Attributes<'_>,
    ) -> Result<Self, Error> {
        let mut content = Content::default();

        for att in atts.with_checks(false).flatten() {
            match att.key {
                b"xml:base" => content.base = Some(att.unescape_and_decode_value(reader)?),
                b"xml:lang" => content.lang = Some(att.unescape_and_decode_value(reader)?),
                b"type" => content.content_type = Some(att.unescape_and_decode_value(reader)?),
                b"src" => content.src = Some(att.unescape_and_decode_value(reader)?),
                _ => {}
            }
        }

        content.value = atom_any_text(reader, atts)?;

        Ok(content)
    }
}

impl ToXml for Content {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = b"content";
        let mut element = BytesStart::borrowed(name, name.len());

        if let Some(ref base) = self.base {
            element.push_attribute(("xml:base", base.as_str()));
        }

        if let Some(ref lang) = self.lang {
            element.push_attribute(("xml:lang", lang.as_str()));
        }

        if let Some(ref content_type) = self.content_type {
            if content_type == "xhtml" {
                element.push_attribute(("type", "xhtml"));
            } else {
                element.push_attribute(("type", &**content_type));
            }
        }

        if let Some(ref src) = self.src {
            element.push_attribute(("src", &**src));
        }

        writer.write_event(Event::Start(element))?;

        if let Some(ref value) = self.value {
            writer.write_event(Event::Text(BytesText::from_escaped(value.as_bytes())))?;
        }

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;

        Ok(())
    }
}

#[cfg(feature = "builders")]
impl ContentBuilder {
    /// Builds a new `Content`.
    pub fn build(&self) -> Content {
        self.build_impl().unwrap()
    }
}
<commit_msg>Escape Content value when not xhtml<commit_after>use std::io::{BufRead, Write};

use quick_xml::events::attributes::Attributes;
use quick_xml::events::{BytesEnd, BytesStart, BytesText, Event};
use quick_xml::Error as XmlError;
use quick_xml::Reader;
use quick_xml::Writer;

use crate::error::Error;
use crate::fromxml::FromXml;
use crate::toxml::ToXml;
use crate::util::atom_any_text;

/// Represents the content of an Atom entry
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Debug, Default, Clone, PartialEq)]
#[cfg_attr(feature = "builders", derive(Builder))]
#[cfg_attr(
    feature = "builders",
    builder(
        setter(into),
        default,
        build_fn(name = "build_impl", private, error = "never::Never")
    )
)]
pub struct Content {
    /// Base URL for resolving any relative references found in the element.
    pub base: Option<String>,
    /// Indicates the natural language for the element.
    pub lang: Option<String>,
    /// The text value of the content.
    pub value: Option<String>,
    /// The URI of where the content can be found.
    pub src: Option<String>,
    /// Either "text", "html", "xhtml", or the MIME type of the content.
    pub content_type: Option<String>,
}

impl Content {
    /// Return base URL of the content.
    pub fn base(&self) -> Option<&str> {
        self.base.as_deref()
    }

    /// Set base URL of the content.
    pub fn set_base<V>(&mut self, base: V)
    where
        V: Into<Option<String>>,
    {
        self.base = base.into();
    }

    /// Return natural language of the content.
    pub fn lang(&self) -> Option<&str> {
        self.lang.as_deref()
    }

    /// Set the base URL of the content.
    pub fn set_lang<V>(&mut self, lang: V)
    where
        V: Into<Option<String>>,
    {
        self.lang = lang.into();
    }

    /// Return the text value of the content.
    ///
    /// If the `content_type` is neither `"text"`, `"html"`, or `"xhtml"` then the value should
    /// be a base64 encoded document of the indicated MIME type.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_value("Example content".to_string());
    /// assert_eq!(content.value(), Some("Example content"));
    /// ```
    pub fn value(&self) -> Option<&str> {
        self.value.as_deref()
    }

    /// Set the text value of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_value("Example content".to_string());
    /// ```
    pub fn set_value<V>(&mut self, value: V)
    where
        V: Into<Option<String>>,
    {
        self.value = value.into();
    }

    /// Return the URI where the content can be found.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_src("http://example.com/content.html".to_string());
    /// assert_eq!(content.src(), Some("http://example.com/content.html"));
    /// ```
    pub fn src(&self) -> Option<&str> {
        self.src.as_deref()
    }

    /// Set the URI where the content can be found.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_src("http://example.com/content.html".to_string());
    /// ```
    pub fn set_src<V>(&mut self, src: V)
    where
        V: Into<Option<String>>,
    {
        self.src = src.into();
    }

    /// Return the type of the content.
    ///
    /// The type is either `"text"`, `"html"`, `"xhtml"`, or the MIME type of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_content_type("image/png".to_string());
    /// assert_eq!(content.content_type(), Some("image/png"));
    /// ```
    pub fn content_type(&self) -> Option<&str> {
        self.content_type.as_deref()
    }

    /// Set the type of the content.
    ///
    /// # Examples
    ///
    /// ```
    /// use atom_syndication::Content;
    ///
    /// let mut content = Content::default();
    /// content.set_content_type("image/png".to_string());
    /// assert_eq!(content.content_type(), Some("image/png"));
    /// ```
    pub fn set_content_type<V>(&mut self, content_type: V)
    where
        V: Into<Option<String>>,
    {
        self.content_type = content_type.into();
    }
}

impl FromXml for Content {
    fn from_xml<B: BufRead>(
        reader: &mut Reader<B>,
        mut atts: Attributes<'_>,
    ) -> Result<Self, Error> {
        let mut content = Content::default();

        for att in atts.with_checks(false).flatten() {
            match att.key {
                b"xml:base" => content.base = Some(att.unescape_and_decode_value(reader)?),
                b"xml:lang" => content.lang = Some(att.unescape_and_decode_value(reader)?),
                b"type" => content.content_type = Some(att.unescape_and_decode_value(reader)?),
                b"src" => content.src = Some(att.unescape_and_decode_value(reader)?),
                _ => {}
            }
        }

        content.value = atom_any_text(reader, atts)?;

        Ok(content)
    }
}

impl ToXml for Content {
    fn to_xml<W: Write>(&self, writer: &mut Writer<W>) -> Result<(), XmlError> {
        let name = b"content";
        let mut element = BytesStart::borrowed(name, name.len());

        if let Some(ref base) = self.base {
            element.push_attribute(("xml:base", base.as_str()));
        }

        if let Some(ref lang) = self.lang {
            element.push_attribute(("xml:lang", lang.as_str()));
        }

        if let Some(ref content_type) = self.content_type {
            if content_type == "xhtml" {
                element.push_attribute(("type", "xhtml"));
            } else {
                element.push_attribute(("type", &**content_type));
            }
        }

        if let Some(ref src) = self.src {
            element.push_attribute(("src", &**src));
        }

        writer.write_event(Event::Start(element))?;

        if let Some(ref value) = self.value {
            writer.write_event(Event::Text(
                if self.content_type.as_deref() == Some("xhtml") {
                    BytesText::from_escaped(value.as_bytes())
                } else {
                    BytesText::from_plain(value.as_bytes())
                },
            ))?;
        }

        writer.write_event(Event::End(BytesEnd::borrowed(name)))?;

        Ok(())
    }
}

#[cfg(feature = "builders")]
impl ContentBuilder {
    /// Builds a new `Content`.
    pub fn build(&self) -> Content {
        self.build_impl().unwrap()
    }
}
<|endoftext|>


<commit_before>use std;
use raw;
use { IV, AV };
use convert::{ FromRaw, IntoSV };
use std::ffi::CStr;

pub struct Context<'a> {
    pthx: raw::Interpreter,
    stack: raw::Stack,
    marker: std::marker::PhantomData<&'a raw::PerlThreadContext>
}

impl<'a> Context<'a> {
    pub fn new(pthx: &'a raw::PerlThreadContext) -> Self {
        unsafe {
            let mut ctx = Context {
                pthx: raw::Interpreter::new(*pthx),
                stack: std::mem::uninitialized(),
                marker: std::marker::PhantomData,
            };

            ctx.pthx.stack_init(&mut ctx.stack);

            ctx
        }
    }

    // STACK

    pub fn st_prepush(&mut self) {
        unsafe { self.pthx.stack_prepush(&mut self.stack) };
    }

    pub fn st_putback(&mut self) {
        unsafe { self.pthx.stack_putback(&mut self.stack) };
    }

    pub fn st_fetch<T>(&mut self, idx: isize) -> T where
        T: FromRaw<raw::SV>
    {
        /* FIXME: panic if idx > items */
        let svp = unsafe { self.pthx.stack_fetch(&mut self.stack, idx as raw::SSize_t) };
        unsafe { T::from_raw(self.pthx, svp) }
    }

    pub fn st_push<T>(&mut self, val: T) where T: IntoSV {
        let sv = val.into_sv(self.pthx);
        unsafe { self.pthx.stack_push_sv(&mut self.stack, sv.into_raw()) };
    }

    // XSUB

    pub fn new_xs(&mut self, name: &CStr, xsaddr: raw::XSUBADDR_t) {
        let filename = 0i8;
        unsafe { self.pthx.new_xs(name.as_ptr(), xsaddr, &filename) };
    }

    // GLOBALS

    pub fn get_av(&mut self, name: &CStr) -> Option<AV> {
        let avp = unsafe { self.pthx.get_av(name.as_ptr(), 0) };
        if avp.is_null() {
            None
        } else {
            Some(unsafe { AV::from_raw_borrowed(self.pthx, avp) })
        }
    }

    pub fn call_pv(&mut self, name: &CStr, flags: IV) {
        unsafe { self.pthx.call_pv(name.as_ptr(), flags as raw::I32) };
    }
}
<commit_msg>Fix SV pushing.<commit_after>use std;
use raw;
use { IV, AV };
use convert::{ FromRaw, IntoSV };
use std::ffi::CStr;

pub struct Context<'a> {
    pthx: raw::Interpreter,
    stack: raw::Stack,
    marker: std::marker::PhantomData<&'a raw::PerlThreadContext>
}

impl<'a> Context<'a> {
    pub fn new(pthx: &'a raw::PerlThreadContext) -> Self {
        unsafe {
            let mut ctx = Context {
                pthx: raw::Interpreter::new(*pthx),
                stack: std::mem::uninitialized(),
                marker: std::marker::PhantomData,
            };

            ctx.pthx.stack_init(&mut ctx.stack);

            ctx
        }
    }

    // STACK

    pub fn st_prepush(&mut self) {
        unsafe { self.pthx.stack_prepush(&mut self.stack) };
    }

    pub fn st_putback(&mut self) {
        unsafe { self.pthx.stack_putback(&mut self.stack) };
    }

    pub fn st_fetch<T>(&mut self, idx: isize) -> T where
        T: FromRaw<raw::SV>
    {
        /* FIXME: panic if idx > items */
        let svp = unsafe { self.pthx.stack_fetch(&mut self.stack, idx as raw::SSize_t) };
        unsafe { T::from_raw(self.pthx, svp) }
    }

    pub fn st_push<T>(&mut self, val: T) where T: IntoSV {
        let sv = val.into_sv(self.pthx);
        unsafe { self.pthx.stack_xpush_sv_mortal(&mut self.stack, sv.into_raw()) };
    }

    // XSUB

    pub fn new_xs(&mut self, name: &CStr, xsaddr: raw::XSUBADDR_t) {
        let filename = 0i8;
        unsafe { self.pthx.new_xs(name.as_ptr(), xsaddr, &filename) };
    }

    // GLOBALS

    pub fn get_av(&mut self, name: &CStr) -> Option<AV> {
        let avp = unsafe { self.pthx.get_av(name.as_ptr(), 0) };
        if avp.is_null() {
            None
        } else {
            Some(unsafe { AV::from_raw_borrowed(self.pthx, avp) })
        }
    }

    pub fn call_pv(&mut self, name: &CStr, flags: IV) {
        unsafe { self.pthx.call_pv(name.as_ptr(), flags as raw::I32) };
    }
}
<|endoftext|>