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

//! The [`@counter-style`][counter-style] at-rule.
//!
//! [counter-style]: https://drafts.csswg.org/css-counter-styles/

use Atom;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, Token};
use cssparser::{serialize_string, serialize_identifier};
#[cfg(feature = "gecko")] use gecko::rules::CounterStyleDescriptors;
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSCounterDesc;
use parser::{ParserContext, log_css_error, Parse};
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::ascii::AsciiExt;
use std::fmt;
use std::ops::Range;
use style_traits::{ToCss, OneOrMoreCommaSeparated};
use values::CustomIdent;

/// Parse the prelude of an @counter-style rule
pub fn parse_counter_style_name(input: &mut Parser) -> Result<CustomIdent, ()> {
    CustomIdent::from_ident(input.expect_ident()?, &["decimal", "none"])
}

/// Parse the body (inside `{}`) of an @counter-style rule
pub fn parse_counter_style_body(name: CustomIdent, context: &ParserContext, input: &mut Parser)
                            -> Result<CounterStyleRule, ()> {
    let mut rule = CounterStyleRule::initial(name);
    {
        let parser = CounterStyleRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err(range) = declaration {
                let pos = range.start;
                let message = format!("Unsupported @counter-style descriptor declaration: '{}'",
                                      iter.input.slice(range));
                log_css_error(iter.input, pos, &*message, context);
            }
        }
    }
    Ok(rule)
}

struct CounterStyleRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut CounterStyleRule,
}

/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for CounterStyleRuleParser<'a, 'b> {
    type Prelude = ();
    type AtRule = ();
}

macro_rules! counter_style_descriptors {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty = $initial: expr; )+
    ) => {
        /// An @counter-style rule
        #[derive(Debug)]
        pub struct CounterStyleRule {
            name: CustomIdent,
            $(
                #[$doc]
                $ident: $ty,
            )+
        }

        impl CounterStyleRule {
            fn initial(name: CustomIdent) -> Self {
                CounterStyleRule {
                    name: name,
                    $(
                        $ident: $initial,
                    )+
                }
            }

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(&self, descriptors: &mut CounterStyleDescriptors) {
                $(
                    descriptors[nsCSSCounterDesc::$gecko_ident as usize].set_from(&self.$ident);
                )*
            }
        }

       impl<'a, 'b> DeclarationParser for CounterStyleRuleParser<'a, 'b> {
            type Declaration = ();

            fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
                match_ignore_ascii_case! { name,
                    $(
                        $name => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = value
                        }
                    )*
                    _ => return Err(())
                }
                Ok(())
            }
        }

        impl ToCssWithGuard for CounterStyleRule {
            fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
            where W: fmt::Write {
                dest.write_str("@counter-style ")?;
                self.name.to_css(dest)?;
                dest.write_str(" {\n")?;
                $(
                    dest.write_str(concat!("  ", $name, ": "))?;
                    ToCss::to_css(&self.$ident, dest)?;
                    dest.write_str(";\n")?;
                )+
                dest.write_str("}")
            }
        }
    }
}

counter_style_descriptors! {
    /// https://drafts.csswg.org/css-counter-styles/#counter-style-system
    "system" system / eCSSCounterDesc_System: System = System::Symbolic;

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
    "negative" negative / eCSSCounterDesc_Negative: Negative =
        Negative(Symbol::String("-".to_owned()), None);

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-prefix
    "prefix" prefix / eCSSCounterDesc_Prefix: Symbol = Symbol::String("".to_owned());

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-suffix
    "suffix" suffix / eCSSCounterDesc_Suffix: Symbol = Symbol::String(". ".to_owned());

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-range
    "range" range / eCSSCounterDesc_Range: Ranges =
        Ranges(Vec::new());  // Empty Vec represents 'auto'

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
    "pad" pad / eCSSCounterDesc_Pad: Pad = Pad(0, Symbol::String("".to_owned()));

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
    "fallback" fallback / eCSSCounterDesc_Fallback: Fallback =
        Fallback(CustomIdent(Atom::from("decimal")));

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-symbols
    "symbols" symbols / eCSSCounterDesc_Symbols: Symbols = Symbols(Vec::new());
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-system
#[derive(Debug)]
pub enum System {
    /// 'cyclic'
    Cyclic,
    /// 'numeric'
    Numeric,
    /// 'alphabetic'
    Alphabetic,
    /// 'symbolic'
    Symbolic,
    /// 'additive'
    Additive,
    /// 'fixed <integer>?'
    Fixed {
        /// '<integer>?'
        first_symbol_value: Option<i32>
    },
    /// 'extends <counter-style-name>'
    Extends(CustomIdent),
}

impl Parse for System {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        match_ignore_ascii_case! { &input.expect_ident()?,
            "cyclic" => Ok(System::Cyclic),
            "numeric" => Ok(System::Numeric),
            "alphabetic" => Ok(System::Alphabetic),
            "symbolic" => Ok(System::Symbolic),
            "additive" => Ok(System::Additive),
            "fixed" => {
                let first_symbol_value = input.try(|i| i.expect_integer()).ok();
                Ok(System::Fixed { first_symbol_value: first_symbol_value })
            }
            "extends" => {
                let other = parse_counter_style_name(input)?;
                Ok(System::Extends(other))
            }
            _ => Err(())
        }
    }
}

impl ToCss for System {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            System::Cyclic => dest.write_str("cyclic"),
            System::Numeric => dest.write_str("numeric"),
            System::Alphabetic => dest.write_str("alphabetic"),
            System::Symbolic => dest.write_str("symbolic"),
            System::Additive => dest.write_str("additive"),
            System::Fixed { first_symbol_value } => {
                if let Some(value) = first_symbol_value {
                    write!(dest, "fixed {}", value)
                } else {
                    dest.write_str("fixed")
                }
            }
            System::Extends(ref other) => {
                dest.write_str("extends ")?;
                other.to_css(dest)
            }
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#typedef-symbol
#[derive(Debug)]
pub enum Symbol {
    /// <string>
    String(String),
    /// <ident>
    Ident(String),
    // Not implemented:
    // /// <image>
    // Image(Image),
}

impl Parse for Symbol {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        match input.next() {
            Ok(Token::QuotedString(s)) => Ok(Symbol::String(s.into_owned())),
            Ok(Token::Ident(s)) => Ok(Symbol::Ident(s.into_owned())),
            _ => Err(())
        }
    }
}

impl ToCss for Symbol {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Symbol::String(ref s) => serialize_string(s, dest),
            Symbol::Ident(ref s) => serialize_identifier(s, dest),
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
#[derive(Debug)]
pub struct Negative(pub Symbol, pub Option<Symbol>);

impl Parse for Negative {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        Ok(Negative(
            Symbol::parse(context, input)?,
            input.try(|input| Symbol::parse(context, input)).ok(),
        ))
    }
}

impl ToCss for Negative {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        self.0.to_css(dest)?;
        if let Some(ref symbol) = self.1 {
            dest.write_char(' ')?;
            symbol.to_css(dest)?
        }
        Ok(())
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-range
///
/// Empty Vec represents 'auto'
#[derive(Debug)]
pub struct Ranges(pub Vec<Range<Option<i32>>>);

impl Parse for Ranges {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
            Ok(Ranges(Vec::new()))
        } else {
            input.parse_comma_separated(|input| {
                let opt_start = parse_bound(input)?;
                let opt_end = parse_bound(input)?;
                if let (Some(start), Some(end)) = (opt_start, opt_end) {
                    if start > end {
                        return Err(())
                    }
                }
                Ok(opt_start..opt_end)
            }).map(Ranges)
        }
    }
}

fn parse_bound(input: &mut Parser) -> Result<Option<i32>, ()> {
    match input.next() {
        Ok(Token::Number(ref v)) if v.int_value.is_some() => Ok(Some(v.int_value.unwrap())),
        Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => Ok(None),
        _ => Err(())
    }
}

impl ToCss for Ranges {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        if let Some(first) = iter.next() {
            range_to_css(first, dest)?;
            for item in iter {
                dest.write_str(", ")?;
                range_to_css(item, dest)?;
            }
            Ok(())
        } else {
            dest.write_str("auto")
        }
    }
}

fn range_to_css<W>(range: &Range<Option<i32>>, dest: &mut W) -> fmt::Result
where W: fmt::Write {
    bound_to_css(range.start, dest)?;
    dest.write_char(' ')?;
    bound_to_css(range.end, dest)
}

fn bound_to_css<W>(range: Option<i32>, dest: &mut W) -> fmt::Result where W: fmt::Write {
    if let Some(finite) = range {
        write!(dest, "{}", finite)
    } else {
        dest.write_str("infinite")
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
#[derive(Debug)]
pub struct Pad(pub u32, pub Symbol);

impl Parse for Pad {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let pad_with = input.try(|input| Symbol::parse(context, input));
        let min_length = input.expect_integer()?;
        if min_length < 0 {
            return Err(())
        }
        let pad_with = pad_with.or_else(|()| Symbol::parse(context, input))?;
        Ok(Pad(min_length as u32, pad_with))
    }
}

impl ToCss for Pad {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)?;
        dest.write_char(' ')?;
        self.1.to_css(dest)
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
#[derive(Debug)]
pub struct Fallback(pub CustomIdent);

impl Parse for Fallback {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        parse_counter_style_name(input).map(Fallback)
    }
}

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

/// https://drafts.csswg.org/css-counter-styles/#counter-style-symbols
#[derive(Debug)]
pub struct Symbols(pub Vec<Symbol>);

impl Parse for Symbols {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let mut symbols = Vec::new();
        loop {
            if let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
                symbols.push(s)
            } else {
                if symbols.is_empty() {
                    return Err(())
                } else {
                    return Ok(Symbols(symbols))
                }
            }
        }
    }
}

impl ToCss for Symbols {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        let first = iter.next().expect("expected at least one symbol");
        first.to_css(dest)?;
        for item in iter {
            dest.write_char(' ')?;
            item.to_css(dest)?;
        }
        Ok(())
    }
}
<commit_msg>CSSOM requires @counter-style to keep track of which descriptors were specified<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/. */

//! The [`@counter-style`][counter-style] at-rule.
//!
//! [counter-style]: https://drafts.csswg.org/css-counter-styles/

use Atom;
use cssparser::{AtRuleParser, DeclarationListParser, DeclarationParser, Parser, Token};
use cssparser::{serialize_string, serialize_identifier};
#[cfg(feature = "gecko")] use gecko::rules::CounterStyleDescriptors;
#[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSCounterDesc;
use parser::{ParserContext, log_css_error, Parse};
use shared_lock::{SharedRwLockReadGuard, ToCssWithGuard};
use std::ascii::AsciiExt;
use std::borrow::Cow;
use std::fmt;
use std::ops::Range;
use style_traits::{ToCss, OneOrMoreCommaSeparated};
use values::CustomIdent;

/// Parse the prelude of an @counter-style rule
pub fn parse_counter_style_name(input: &mut Parser) -> Result<CustomIdent, ()> {
    CustomIdent::from_ident(input.expect_ident()?, &["decimal", "none"])
}

/// Parse the body (inside `{}`) of an @counter-style rule
pub fn parse_counter_style_body(name: CustomIdent, context: &ParserContext, input: &mut Parser)
                            -> Result<CounterStyleRule, ()> {
    let mut rule = CounterStyleRule::empty(name);
    {
        let parser = CounterStyleRuleParser {
            context: context,
            rule: &mut rule,
        };
        let mut iter = DeclarationListParser::new(input, parser);
        while let Some(declaration) = iter.next() {
            if let Err(range) = declaration {
                let pos = range.start;
                let message = format!("Unsupported @counter-style descriptor declaration: '{}'",
                                      iter.input.slice(range));
                log_css_error(iter.input, pos, &*message, context);
            }
        }
    }
    Ok(rule)
}

struct CounterStyleRuleParser<'a, 'b: 'a> {
    context: &'a ParserContext<'b>,
    rule: &'a mut CounterStyleRule,
}

/// Default methods reject all at rules.
impl<'a, 'b> AtRuleParser for CounterStyleRuleParser<'a, 'b> {
    type Prelude = ();
    type AtRule = ();
}

macro_rules! counter_style_descriptors {
    (
        $( #[$doc: meta] $name: tt $ident: ident / $gecko_ident: ident: $ty: ty = $initial: expr; )+
    ) => {
        /// An @counter-style rule
        #[derive(Debug)]
        pub struct CounterStyleRule {
            name: CustomIdent,
            $(
                #[$doc]
                $ident: Option<$ty>,
            )+
        }

        impl CounterStyleRule {
            fn empty(name: CustomIdent) -> Self {
                CounterStyleRule {
                    name: name,
                    $(
                        $ident: None,
                    )+
                }
            }

            $(
                #[$doc]
                pub fn $ident(&self) -> Cow<$ty> {
                    if let Some(ref value) = self.$ident {
                        Cow::Borrowed(value)
                    } else {
                        Cow::Owned($initial)
                    }
                }
            )+

            /// Convert to Gecko types
            #[cfg(feature = "gecko")]
            pub fn set_descriptors(&self, descriptors: &mut CounterStyleDescriptors) {
                $(
                    if let Some(ref value) = self.$ident {
                        descriptors[nsCSSCounterDesc::$gecko_ident as usize].set_from(value)
                    }
                )*
            }
        }

       impl<'a, 'b> DeclarationParser for CounterStyleRuleParser<'a, 'b> {
            type Declaration = ();

            fn parse_value(&mut self, name: &str, input: &mut Parser) -> Result<(), ()> {
                match_ignore_ascii_case! { name,
                    $(
                        $name => {
                            // DeclarationParser also calls parse_entirely
                            // so we’d normally not need to,
                            // but in this case we do because we set the value as a side effect
                            // rather than returning it.
                            let value = input.parse_entirely(|i| Parse::parse(self.context, i))?;
                            self.rule.$ident = Some(value)
                        }
                    )*
                    _ => return Err(())
                }
                Ok(())
            }
        }

        impl ToCssWithGuard for CounterStyleRule {
            fn to_css<W>(&self, _guard: &SharedRwLockReadGuard, dest: &mut W) -> fmt::Result
            where W: fmt::Write {
                dest.write_str("@counter-style ")?;
                self.name.to_css(dest)?;
                dest.write_str(" {\n")?;
                $(
                    if let Some(ref value) = self.$ident {
                        dest.write_str(concat!("  ", $name, ": "))?;
                        ToCss::to_css(value, dest)?;
                        dest.write_str(";\n")?;
                    }
                )+
                dest.write_str("}")
            }
        }
    }
}

counter_style_descriptors! {
    /// https://drafts.csswg.org/css-counter-styles/#counter-style-system
    "system" system / eCSSCounterDesc_System: System = System::Symbolic;

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
    "negative" negative / eCSSCounterDesc_Negative: Negative =
        Negative(Symbol::String("-".to_owned()), None);

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-prefix
    "prefix" prefix / eCSSCounterDesc_Prefix: Symbol = Symbol::String("".to_owned());

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-suffix
    "suffix" suffix / eCSSCounterDesc_Suffix: Symbol = Symbol::String(". ".to_owned());

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-range
    "range" range / eCSSCounterDesc_Range: Ranges =
        Ranges(Vec::new());  // Empty Vec represents 'auto'

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
    "pad" pad / eCSSCounterDesc_Pad: Pad = Pad(0, Symbol::String("".to_owned()));

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
    "fallback" fallback / eCSSCounterDesc_Fallback: Fallback =
        Fallback(CustomIdent(Atom::from("decimal")));

    /// https://drafts.csswg.org/css-counter-styles/#counter-style-symbols
    "symbols" symbols / eCSSCounterDesc_Symbols: Symbols = Symbols(Vec::new());
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-system
#[derive(Debug, Clone)]
pub enum System {
    /// 'cyclic'
    Cyclic,
    /// 'numeric'
    Numeric,
    /// 'alphabetic'
    Alphabetic,
    /// 'symbolic'
    Symbolic,
    /// 'additive'
    Additive,
    /// 'fixed <integer>?'
    Fixed {
        /// '<integer>?'
        first_symbol_value: Option<i32>
    },
    /// 'extends <counter-style-name>'
    Extends(CustomIdent),
}

impl Parse for System {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        match_ignore_ascii_case! { &input.expect_ident()?,
            "cyclic" => Ok(System::Cyclic),
            "numeric" => Ok(System::Numeric),
            "alphabetic" => Ok(System::Alphabetic),
            "symbolic" => Ok(System::Symbolic),
            "additive" => Ok(System::Additive),
            "fixed" => {
                let first_symbol_value = input.try(|i| i.expect_integer()).ok();
                Ok(System::Fixed { first_symbol_value: first_symbol_value })
            }
            "extends" => {
                let other = parse_counter_style_name(input)?;
                Ok(System::Extends(other))
            }
            _ => Err(())
        }
    }
}

impl ToCss for System {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            System::Cyclic => dest.write_str("cyclic"),
            System::Numeric => dest.write_str("numeric"),
            System::Alphabetic => dest.write_str("alphabetic"),
            System::Symbolic => dest.write_str("symbolic"),
            System::Additive => dest.write_str("additive"),
            System::Fixed { first_symbol_value } => {
                if let Some(value) = first_symbol_value {
                    write!(dest, "fixed {}", value)
                } else {
                    dest.write_str("fixed")
                }
            }
            System::Extends(ref other) => {
                dest.write_str("extends ")?;
                other.to_css(dest)
            }
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#typedef-symbol
#[derive(Debug, Clone)]
pub enum Symbol {
    /// <string>
    String(String),
    /// <ident>
    Ident(String),
    // Not implemented:
    // /// <image>
    // Image(Image),
}

impl Parse for Symbol {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        match input.next() {
            Ok(Token::QuotedString(s)) => Ok(Symbol::String(s.into_owned())),
            Ok(Token::Ident(s)) => Ok(Symbol::Ident(s.into_owned())),
            _ => Err(())
        }
    }
}

impl ToCss for Symbol {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        match *self {
            Symbol::String(ref s) => serialize_string(s, dest),
            Symbol::Ident(ref s) => serialize_identifier(s, dest),
        }
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-negative
#[derive(Debug, Clone)]
pub struct Negative(pub Symbol, pub Option<Symbol>);

impl Parse for Negative {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        Ok(Negative(
            Symbol::parse(context, input)?,
            input.try(|input| Symbol::parse(context, input)).ok(),
        ))
    }
}

impl ToCss for Negative {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        self.0.to_css(dest)?;
        if let Some(ref symbol) = self.1 {
            dest.write_char(' ')?;
            symbol.to_css(dest)?
        }
        Ok(())
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-range
///
/// Empty Vec represents 'auto'
#[derive(Debug, Clone)]
pub struct Ranges(pub Vec<Range<Option<i32>>>);

impl Parse for Ranges {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        if input.try(|input| input.expect_ident_matching("auto")).is_ok() {
            Ok(Ranges(Vec::new()))
        } else {
            input.parse_comma_separated(|input| {
                let opt_start = parse_bound(input)?;
                let opt_end = parse_bound(input)?;
                if let (Some(start), Some(end)) = (opt_start, opt_end) {
                    if start > end {
                        return Err(())
                    }
                }
                Ok(opt_start..opt_end)
            }).map(Ranges)
        }
    }
}

fn parse_bound(input: &mut Parser) -> Result<Option<i32>, ()> {
    match input.next() {
        Ok(Token::Number(ref v)) if v.int_value.is_some() => Ok(Some(v.int_value.unwrap())),
        Ok(Token::Ident(ref ident)) if ident.eq_ignore_ascii_case("infinite") => Ok(None),
        _ => Err(())
    }
}

impl ToCss for Ranges {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        if let Some(first) = iter.next() {
            range_to_css(first, dest)?;
            for item in iter {
                dest.write_str(", ")?;
                range_to_css(item, dest)?;
            }
            Ok(())
        } else {
            dest.write_str("auto")
        }
    }
}

fn range_to_css<W>(range: &Range<Option<i32>>, dest: &mut W) -> fmt::Result
where W: fmt::Write {
    bound_to_css(range.start, dest)?;
    dest.write_char(' ')?;
    bound_to_css(range.end, dest)
}

fn bound_to_css<W>(range: Option<i32>, dest: &mut W) -> fmt::Result where W: fmt::Write {
    if let Some(finite) = range {
        write!(dest, "{}", finite)
    } else {
        dest.write_str("infinite")
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-pad
#[derive(Debug, Clone)]
pub struct Pad(pub u32, pub Symbol);

impl Parse for Pad {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let pad_with = input.try(|input| Symbol::parse(context, input));
        let min_length = input.expect_integer()?;
        if min_length < 0 {
            return Err(())
        }
        let pad_with = pad_with.or_else(|()| Symbol::parse(context, input))?;
        Ok(Pad(min_length as u32, pad_with))
    }
}

impl ToCss for Pad {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        write!(dest, "{}", self.0)?;
        dest.write_char(' ')?;
        self.1.to_css(dest)
    }
}

/// https://drafts.csswg.org/css-counter-styles/#counter-style-fallback
#[derive(Debug, Clone)]
pub struct Fallback(pub CustomIdent);

impl Parse for Fallback {
    fn parse(_context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        parse_counter_style_name(input).map(Fallback)
    }
}

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

/// https://drafts.csswg.org/css-counter-styles/#counter-style-symbols
#[derive(Debug, Clone)]
pub struct Symbols(pub Vec<Symbol>);

impl Parse for Symbols {
    fn parse(context: &ParserContext, input: &mut Parser) -> Result<Self, ()> {
        let mut symbols = Vec::new();
        loop {
            if let Ok(s) = input.try(|input| Symbol::parse(context, input)) {
                symbols.push(s)
            } else {
                if symbols.is_empty() {
                    return Err(())
                } else {
                    return Ok(Symbols(symbols))
                }
            }
        }
    }
}

impl ToCss for Symbols {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        let mut iter = self.0.iter();
        let first = iter.next().expect("expected at least one symbol");
        first.to_css(dest)?;
        for item in iter {
            dest.write_char(' ')?;
            item.to_css(dest)?;
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright © 2015 by Michael Dilger (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
use std::string::FromUtf8Error;

use super::{httparse, hyper, mime_multipart};

/// An error type for the `formdata` crate.
pub enum Error {
    /// The Hyper request did not have a Content-Type header.
    NoRequestContentType,
    /// The Hyper request Content-Type top-level Mime was not `Multipart`.
    NotMultipart,
    /// The Hyper request Content-Type sub-level Mime was not `FormData`.
    NotFormData,
    /// The Content-Type header failed to specify boundary token.
    BoundaryNotSpecified,
    /// A multipart section contained only partial headers.
    PartialHeaders,
    /// A multipart section did not have the required Content-Disposition header.
    MissingDisposition,
    /// A multipart section did not have a valid corresponding Content-Disposition.
    InvalidDisposition,
    /// A multipart section Content-Disposition header failed to specify a name.
    NoName,
    /// The request body ended prior to reaching the expected terminating boundary.
    Eof,
    /// An HTTP parsing error from a multipart section.
    Httparse(httparse::Error),
    /// An I/O error.
    Io(io::Error),
    /// An error was returned from Hyper.
    Hyper(hyper::Error),
    /// An error occurred during UTF-8 processing.
    Utf8(FromUtf8Error),
    /// An error occurred during character decoding
    Decoding(Cow<'static, str>),
    /// A MIME multipart error
    Multipart(mime_multipart::Error),
    /// Filepart is not a file
    NotAFile,
}

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

impl From<httparse::Error> for Error {
    fn from(err: httparse::Error) -> Error {
        Error::Httparse(err)
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Error {
        Error::Hyper(err)
    }
}

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::Utf8(err)
    }
}

impl From<mime_multipart::Error> for Error {
    fn from(err: mime_multipart::Error) -> Error {
        Error::Multipart(err)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Httparse(ref e) =>
                format!("{}: {:?}", self.description(), e).fmt(f),
            Error::Io(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Hyper(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Utf8(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Decoding(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Multipart(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            _ => format!("{}", self.description()).fmt(f),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!( f.write_str(&*self.description()) );
        if self.cause().is_some() {
            try!( write!(f, ": {:?}", self.cause().unwrap()) ); // recurse
        }
        Ok(())
    }
}

impl StdError for Error {
    fn description(&self) -> &str{
        match *self {
            Error::NoRequestContentType => "The Hyper request did not have a Content-Type header.",
            Error::NotMultipart =>
                "The Hyper request Content-Type top-level Mime was not multipart.",
            Error::NotFormData =>
                "The Hyper request Content-Type sub-level Mime was not form-data.",
            Error::BoundaryNotSpecified =>
                "The Content-Type header failed to specify a boundary token.",
            Error::PartialHeaders => "A multipart section contained only partial headers.",
            Error::MissingDisposition =>
                "A multipart section did not have the required Content-Disposition header.",
            Error::InvalidDisposition =>
                "A multipart section did not have a valid corresponding Content-Disposition.",
            Error::NoName =>
                "A multipart section Content-Disposition header failed to specify a name.",
            Error::Eof =>
                "The request body ended prior to reaching the expected terminating boundary.",
            Error::Httparse(_) =>
                "A parse error occurred while parsing the headers of a multipart section.",
            Error::Io(_) => "An I/O error occurred.",
            Error::Hyper(_) => "A Hyper error occurred.",
            Error::Utf8(_) => "A UTF-8 error occurred.",
            Error::Decoding(_) => "A decoding error occurred.",
            Error::Multipart(_) => "A MIME multipart error occurred.",
            Error::NotAFile => "FilePart is not a file.",
        }
    }
}
<commit_msg>Update for std::Error deprecation in 1.33.0<commit_after>// Copyright © 2015 by Michael Dilger (of New Zealand)
// This code is licensed under the MIT license (see LICENSE-MIT for details)

use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt::{self, Display};
use std::io;
use std::string::FromUtf8Error;

use super::{httparse, hyper, mime_multipart};

/// An error type for the `formdata` crate.
pub enum Error {
    /// The Hyper request did not have a Content-Type header.
    NoRequestContentType,
    /// The Hyper request Content-Type top-level Mime was not `Multipart`.
    NotMultipart,
    /// The Hyper request Content-Type sub-level Mime was not `FormData`.
    NotFormData,
    /// The Content-Type header failed to specify boundary token.
    BoundaryNotSpecified,
    /// A multipart section contained only partial headers.
    PartialHeaders,
    /// A multipart section did not have the required Content-Disposition header.
    MissingDisposition,
    /// A multipart section did not have a valid corresponding Content-Disposition.
    InvalidDisposition,
    /// A multipart section Content-Disposition header failed to specify a name.
    NoName,
    /// The request body ended prior to reaching the expected terminating boundary.
    Eof,
    /// An HTTP parsing error from a multipart section.
    Httparse(httparse::Error),
    /// An I/O error.
    Io(io::Error),
    /// An error was returned from Hyper.
    Hyper(hyper::Error),
    /// An error occurred during UTF-8 processing.
    Utf8(FromUtf8Error),
    /// An error occurred during character decoding
    Decoding(Cow<'static, str>),
    /// A MIME multipart error
    Multipart(mime_multipart::Error),
    /// Filepart is not a file
    NotAFile,
}

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

impl From<httparse::Error> for Error {
    fn from(err: httparse::Error) -> Error {
        Error::Httparse(err)
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Error {
        Error::Hyper(err)
    }
}

impl From<FromUtf8Error> for Error {
    fn from(err: FromUtf8Error) -> Error {
        Error::Utf8(err)
    }
}

impl From<mime_multipart::Error> for Error {
    fn from(err: mime_multipart::Error) -> Error {
        Error::Multipart(err)
    }
}

impl Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Httparse(ref e) =>
                format!("{}: {:?}", self.description(), e).fmt(f),
            Error::Io(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Hyper(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Utf8(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Decoding(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            Error::Multipart(ref e) =>
                format!("{}: {}", self.description(), e).fmt(f),
            _ => format!("{}", self.description()).fmt(f),
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!( f.write_str(&*self.description()) );
        if self.source().is_some() {
            try!( write!(f, ": {:?}", self.source().unwrap()) ); // recurse
        }
        Ok(())
    }
}

impl StdError for Error {
    fn description(&self) -> &str{
        match *self {
            Error::NoRequestContentType => "The Hyper request did not have a Content-Type header.",
            Error::NotMultipart =>
                "The Hyper request Content-Type top-level Mime was not multipart.",
            Error::NotFormData =>
                "The Hyper request Content-Type sub-level Mime was not form-data.",
            Error::BoundaryNotSpecified =>
                "The Content-Type header failed to specify a boundary token.",
            Error::PartialHeaders => "A multipart section contained only partial headers.",
            Error::MissingDisposition =>
                "A multipart section did not have the required Content-Disposition header.",
            Error::InvalidDisposition =>
                "A multipart section did not have a valid corresponding Content-Disposition.",
            Error::NoName =>
                "A multipart section Content-Disposition header failed to specify a name.",
            Error::Eof =>
                "The request body ended prior to reaching the expected terminating boundary.",
            Error::Httparse(_) =>
                "A parse error occurred while parsing the headers of a multipart section.",
            Error::Io(_) => "An I/O error occurred.",
            Error::Hyper(_) => "A Hyper error occurred.",
            Error::Utf8(_) => "A UTF-8 error occurred.",
            Error::Decoding(_) => "A decoding error occurred.",
            Error::Multipart(_) => "A MIME multipart error occurred.",
            Error::NotAFile => "FilePart is not a file.",
        }
    }
}
<|endoftext|>


<commit_before>/// Error types

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {

        // Errors for tokenizer

        QueryParsingError(query: String) {
            description("parsing the query failed")
            display("Parsing the query '{}' failed", query)
        }

        EmptyQueryError {
            description("the query is empty")
            display("The query on the TOML is empty")
        }

        EmptyIdentifier {
            description("Query an empty identifier: ''")
            display("The passed query has an empty identifier")
        }

        ArrayAccessWithoutIndex {
            description("trying to access array without index")
            display("The passed query tries to access an array but does not specify the index")
        }

        ArrayAccessWithInvalidIndex {
            description("trying to pass an invalid index")
            display("The passed query tries to access an array but does not specify a valid index")
        }

        // Errors for Resolver

        IdentifierNotFoundInDocument(ident: String) {
            description("Identifier missing in document")
            display("The identfier '{}' is not present in the document", ident)
        }

        NoIndexInTable(i: usize) {
            description("Cannot deref index from table")
            display("Got an index query '[{}]' but have table", i)
        }

        NoIdentifierInArray(s: String) {
            description("Cannot query identifier in array")
            display("Got an identifier query '{}' but have array", s)
        }

        QueryingValueAsTable(s: String) {
            description("Querying a table where a value is")
            display("Got an identifier query '{}' but have value", s)
        }

        QueryingValueAsArray(i: usize) {
            description("Querying a table where a value is")
            display("Got an index query '{}' but have value", i)
        }

        CannotDeleteNonEmptyTable(tabname: String) {
            description("Cannot delete Table that is not empty")
            display("Cannot delete table '{}' which is not empty", tabname)
        }

        CannotDeleteNonEmptyArray(arrname: String) {
            description("Cannot delete Array that is not empty")
            display("Cannot delete array '{}' which is not empty", arrname)
        }

        CannotAccessBecauseTypeMismatch(expected: &'static str, actual: &'static str) {
            description("Cannot access value because of type mismatch")
            display("Cannot access {} because expected {}", actual, expected)
        }
    }
}
<commit_msg>Add error type for array index out of bounds<commit_after>/// Error types

error_chain! {
    types {
        Error, ErrorKind, ResultExt, Result;
    }

    errors {

        // Errors for tokenizer

        QueryParsingError(query: String) {
            description("parsing the query failed")
            display("Parsing the query '{}' failed", query)
        }

        EmptyQueryError {
            description("the query is empty")
            display("The query on the TOML is empty")
        }

        EmptyIdentifier {
            description("Query an empty identifier: ''")
            display("The passed query has an empty identifier")
        }

        ArrayAccessWithoutIndex {
            description("trying to access array without index")
            display("The passed query tries to access an array but does not specify the index")
        }

        ArrayAccessWithInvalidIndex {
            description("trying to pass an invalid index")
            display("The passed query tries to access an array but does not specify a valid index")
        }

        // Errors for Resolver

        IdentifierNotFoundInDocument(ident: String) {
            description("Identifier missing in document")
            display("The identfier '{}' is not present in the document", ident)
        }

        NoIndexInTable(i: usize) {
            description("Cannot deref index from table")
            display("Got an index query '[{}]' but have table", i)
        }

        NoIdentifierInArray(s: String) {
            description("Cannot query identifier in array")
            display("Got an identifier query '{}' but have array", s)
        }

        QueryingValueAsTable(s: String) {
            description("Querying a table where a value is")
            display("Got an identifier query '{}' but have value", s)
        }

        QueryingValueAsArray(i: usize) {
            description("Querying a table where a value is")
            display("Got an index query '{}' but have value", i)
        }

        CannotDeleteNonEmptyTable(tabname: String) {
            description("Cannot delete Table that is not empty")
            display("Cannot delete table '{}' which is not empty", tabname)
        }

        CannotDeleteNonEmptyArray(arrname: String) {
            description("Cannot delete Array that is not empty")
            display("Cannot delete array '{}' which is not empty", arrname)
        }

        CannotAccessBecauseTypeMismatch(expected: &'static str, actual: &'static str) {
            description("Cannot access value because of type mismatch")
            display("Cannot access {} because expected {}", actual, expected)
        }

        ArrayIndexOutOfBounds(idx: usize, arrlen: usize) {
            description("Delete index out of bounds")
            display("Cannot delete in array at {}, array has length {}", idx, arrlen)
        }

    }
}
<|endoftext|>


<commit_before>use std::intrinsics::TypeId;
use std::any::{ Any, AnyRefExt };

use input;

use {
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent,
    GenericEvent,
};

/// Contains the different game events.
#[deriving(Clone, PartialEq, Show)]
pub enum Event<I = input::InputEvent> {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Input event.
    Input(I),
}

impl<I: GenericEvent> GenericEvent for Event<I> {
    #[inline(always)]
    fn from_event(event_trait_id: TypeId, ev: &Any) -> Option<Event<I>> {
        let update = TypeId::of::<Box<UpdateEvent>>();
        let render = TypeId::of::<Box<RenderEvent>>();
        match event_trait_id {
            x if x == update => {
                match ev.downcast_ref::<UpdateArgs>() {
                    Some(args) => Some(Update(args.clone())),
                    None => fail!("Expected `UpdateArgs`")
                }
            }
            x if x == render => {
                match ev.downcast_ref::<RenderArgs>() {
                    Some(args) => Some(Render(args.clone())),
                    None => fail!("Expected no argument")
                }
            }
            _ => {
                let input: Option<I> = GenericEvent::from_event(
                    event_trait_id, ev
                );
                match input {
                    Some(input) => Some(Input(input)),
                    None => None
                }
            }
        }
    }

    #[inline(always)]
    fn with_event(&self, event_trait_id: TypeId, f: |&Any|) {
        let update = TypeId::of::<Box<UpdateEvent>>();
        let render = TypeId::of::<Box<RenderEvent>>();
        match event_trait_id {
            x if x == update => {
                match *self {
                    Update(ref args) => f(args as &Any),
                    _ => {}
                }
            }
            x if x == render => {
                match *self {
                    Render(ref args) => f(args as &Any),
                    _ => {}
                }
            }
            _ => {
                match *self {
                    Input(ref input) => input.with_event(event_trait_id, f),
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_event() {
    use assert_event_trait;
    use MouseCursorEvent;

    // Update.
    let ref e = UpdateEvent::from_update_args(&UpdateArgs { dt: 1.0 }).unwrap();
    assert_event_trait::<Event, Box<UpdateEvent>>(e);

    // Render.
    let ref e = RenderEvent::from_render_args(
        &RenderArgs { ext_dt: 1.0, width: 0, height: 0 }
    ).unwrap();
    assert_event_trait::<Event, Box<RenderEvent>>(e);

    let ref e = MouseCursorEvent::from_xy(1.0, 0.0).unwrap();
    assert_event_trait::<Event, Box<MouseCursorEvent>>(e);
}
<commit_msg>Fixed error message<commit_after>use std::intrinsics::TypeId;
use std::any::{ Any, AnyRefExt };

use input;

use {
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent,
    GenericEvent,
};

/// Contains the different game events.
#[deriving(Clone, PartialEq, Show)]
pub enum Event<I = input::InputEvent> {
    /// Render graphics.
    Render(RenderArgs),
    /// Update physical state of the game.
    Update(UpdateArgs),
    /// Input event.
    Input(I),
}

impl<I: GenericEvent> GenericEvent for Event<I> {
    #[inline(always)]
    fn from_event(event_trait_id: TypeId, ev: &Any) -> Option<Event<I>> {
        let update = TypeId::of::<Box<UpdateEvent>>();
        let render = TypeId::of::<Box<RenderEvent>>();
        match event_trait_id {
            x if x == update => {
                match ev.downcast_ref::<UpdateArgs>() {
                    Some(args) => Some(Update(args.clone())),
                    None => fail!("Expected `UpdateArgs`")
                }
            }
            x if x == render => {
                match ev.downcast_ref::<RenderArgs>() {
                    Some(args) => Some(Render(args.clone())),
                    None => fail!("Expected `RenderArgs`")
                }
            }
            _ => {
                let input: Option<I> = GenericEvent::from_event(
                    event_trait_id, ev
                );
                match input {
                    Some(input) => Some(Input(input)),
                    None => None
                }
            }
        }
    }

    #[inline(always)]
    fn with_event(&self, event_trait_id: TypeId, f: |&Any|) {
        let update = TypeId::of::<Box<UpdateEvent>>();
        let render = TypeId::of::<Box<RenderEvent>>();
        match event_trait_id {
            x if x == update => {
                match *self {
                    Update(ref args) => f(args as &Any),
                    _ => {}
                }
            }
            x if x == render => {
                match *self {
                    Render(ref args) => f(args as &Any),
                    _ => {}
                }
            }
            _ => {
                match *self {
                    Input(ref input) => input.with_event(event_trait_id, f),
                    _ => {}
                }
            }
        }
    }
}

#[test]
fn test_event() {
    use assert_event_trait;
    use MouseCursorEvent;

    // Update.
    let ref e = UpdateEvent::from_update_args(&UpdateArgs { dt: 1.0 }).unwrap();
    assert_event_trait::<Event, Box<UpdateEvent>>(e);

    // Render.
    let ref e = RenderEvent::from_render_args(
        &RenderArgs { ext_dt: 1.0, width: 0, height: 0 }
    ).unwrap();
    assert_event_trait::<Event, Box<RenderEvent>>(e);

    let ref e = MouseCursorEvent::from_xy(1.0, 0.0).unwrap();
    assert_event_trait::<Event, Box<MouseCursorEvent>>(e);
}
<|endoftext|>


<commit_before>//! User-input events and their effects.

use std::fmt;
use std::rc::Rc;

use ncurses;

use ::Cursive;

/// Callback is a function that can be triggered by an event.
/// It has a mutable access to the cursive root.
pub type Callback = Box<Fn(&mut Cursive)>;

/// Answer to an event notification.
/// The event can be consumed or ignored.
pub enum EventResult {
    /// The event was ignored. The parent can keep handling it.
    Ignored,
    /// The event was consumed. An optionnal callback to run is attached.
    Consumed(Option<Rc<Callback>>),
}

#[derive(PartialEq,Eq,Clone,Copy,Hash)]
pub enum Key {
    /// Both Enter and numpad Enter
    Enter,
    /// Tabulation key
    Tab,
    ShiftTab,
    Left,
    Right,
    Up,
    Down,
    PageUp,
    PageDown,
    Backspace,
    Home,
    End,
    /// Delete key
    Del,
    /// Insert key
    Ins,
    /// Escape key. Often buffered by the terminal,
    /// may appear with a delay or along with the next key.
    Esc,
    // Left arrow while shift is pressed
    CtrlPageUp,
    CtrlPageDown,
    ShiftLeft,
    ShiftRight,
    ShiftCtrlLeft,
    ShiftCtrlRight,
    CtrlLeft,
    CtrlRight,
    CtrlUp,
    CtrlDown,
    CtrlDel,
    F(u8),
    CtrlChar(char),
    Unknown(i32),
}

impl Key {
    pub fn from_ncurses(ch: i32) -> Self {
        match ch {
            9 => Key::Tab,
            // Treat Return and the numpad Enter the same
            10 | ncurses::KEY_ENTER => Key::Enter,
            27 => Key::Esc,
            127 | ncurses::KEY_BACKSPACE => Key::Backspace,
            330 => Key::Del,
            331 => Key::Ins,
            // Those keys don't seem to be documented...
            515 => Key::CtrlDel,
            521 => Key::CtrlDown,
            541 => Key::CtrlLeft,
            542 => Key::ShiftCtrlLeft,
            546 => Key::CtrlPageDown,
            551 => Key::CtrlPageUp,
            556 => Key::CtrlRight,
            557 => Key::ShiftCtrlRight,
            562 => Key::CtrlUp,
            ncurses::KEY_BTAB => Key::ShiftTab,
            ncurses::KEY_SLEFT => Key::ShiftLeft,
            ncurses::KEY_SRIGHT => Key::ShiftRight,
            ncurses::KEY_LEFT => Key::Left,
            ncurses::KEY_RIGHT => Key::Right,
            ncurses::KEY_UP => Key::Up,
            ncurses::KEY_DOWN => Key::Down,
            ncurses::KEY_PPAGE => Key::PageUp,
            ncurses::KEY_NPAGE => Key::PageDown,
            ncurses::KEY_HOME => Key::Home,
            ncurses::KEY_END => Key::End,
            f @ ncurses::KEY_F1 ... ncurses::KEY_F15 => Key::F((f - ncurses::KEY_F0) as u8),
            // Avoids 8-10 (H,I,J), they are used by other commands.
            c @ 1 ... 7 | c @ 11 ... 25 => Key::CtrlChar(('a' as u8 + (c-1) as u8) as char),
            _ => Key::Unknown(ch),
        }
    }
}

impl fmt::Display for Key {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Key::Unknown(ch) => write!(f, "Unknown: {}", ch),
            Key::CtrlChar(ch) => write!(f, "Ctrl-{}", ch),
            Key::F(n) => write!(f, "F{}", n),
            key => write!(f, "{}", match key {
                Key::Left => "Left",
                Key::Right => "Right",
                Key::Down => "Down",
                Key::Up => "Up",
                Key::ShiftCtrlLeft => "Shift-Ctrl-Left",
                Key::ShiftCtrlRight => "Shift-Ctrl-Right",
                Key::ShiftLeft => "Shift-Left",
                Key::ShiftRight => "Shift-Right",
                Key::CtrlDel => "Ctrl-Del",
                Key::CtrlLeft => "Ctrl-Left",
                Key::CtrlRight => "Ctrl-Right",
                Key::CtrlUp => "Ctrl-Up",
                Key::CtrlDown => "Ctrl-Down",
                Key::CtrlPageUp => "Ctrl-PageUp",
                Key::CtrlPageDown => "Ctrl-PageDown",
                Key::PageUp => "PageUp",
                Key::PageDown => "PageDown",
                Key::Home => "Home",
                Key::End => "End",
                Key::Backspace => "Backspace",
                Key::Del => "Del",
                Key::Enter => "Enter",
                Key::ShiftTab => "Shift-Tab",
                Key::Tab => "Tab",
                Key::Ins => "Ins",
                Key::Esc => "Esc",
                _ => "",
            }),
        }
    }
}

#[derive(PartialEq,Eq,Clone,Copy,Hash)]
pub enum Event {
    CharEvent(char),
    KeyEvent(Key),
}

pub trait ToEvent {
    fn to_event(self) -> Event;
}

impl ToEvent for char {
    fn to_event(self) -> Event {
        Event::CharEvent(self)
    }
}

impl ToEvent for Key {
    fn to_event(self) -> Event {
        Event::KeyEvent(self)
    }
}

impl ToEvent for Event {
    fn to_event(self) -> Event {
        self
    }
}
<commit_msg>Add input support for more keys<commit_after>//! User-input events and their effects.

use std::fmt;
use std::rc::Rc;

use ncurses;

use ::Cursive;

/// Callback is a function that can be triggered by an event.
/// It has a mutable access to the cursive root.
pub type Callback = Box<Fn(&mut Cursive)>;

/// Answer to an event notification.
/// The event can be consumed or ignored.
pub enum EventResult {
    /// The event was ignored. The parent can keep handling it.
    Ignored,
    /// The event was consumed. An optionnal callback to run is attached.
    Consumed(Option<Rc<Callback>>),
}

#[derive(PartialEq,Eq,Clone,Copy,Hash)]
pub enum Key {
    /// Both Enter and numpad Enter
    Enter,
    /// Tabulation key
    Tab,
    ShiftTab,
    Left,
    Right,
    Up,
    Down,
    PageUp,
    PageDown,
    Backspace,
    Home,
    End,
    /// Delete key
    Del,
    ShiftDel,
    CtrlDel,
    /// Insert key
    Ins,
    /// Escape key. Often buffered by the terminal,
    /// may appear with a delay or along with the next key.
    Esc,
    // Left arrow while shift is pressed
    CtrlPageUp,
    CtrlPageDown,
    ShiftLeft,
    ShiftRight,
    CtrlShiftLeft,
    CtrlShiftRight,
    CtrlLeft,
    CtrlRight,
    CtrlUp,
    CtrlDown,
    F(u8),
    CtrlF(u8),
    ShiftF(u8),
    CtrlShiftF(u8),
    CtrlChar(char),
    Unknown(i32),
}

impl Key {
    pub fn from_ncurses(ch: i32) -> Self {
        match ch {
            // Tab is '\t'
            9 => Key::Tab,
            // Treat '\n' and the numpad Enter the same
            10 | ncurses::KEY_ENTER => Key::Enter,
            // This is the escape key when pressed by itself.
            // When used for control sequences, it should have been caught earlier.
            27 => Key::Esc,
            // `Backspace` sends 127, but Ctrl-H sends `Backspace`
            127 | ncurses::KEY_BACKSPACE => Key::Backspace,
            // Del and Ins currently use a bad value in ncurses-rs
            330 => Key::Del,
            331 => Key::Ins,
            // Those keys don't seem to be documented...
            515 => Key::CtrlDel,
            521 => Key::CtrlDown,
            541 => Key::CtrlLeft,
            542 => Key::CtrlShiftLeft,
            546 => Key::CtrlPageDown,
            551 => Key::CtrlPageUp,
            556 => Key::CtrlRight,
            557 => Key::CtrlShiftRight,
            562 => Key::CtrlUp,
            ncurses::KEY_BTAB => Key::ShiftTab,
            ncurses::KEY_SLEFT => Key::ShiftLeft,
            ncurses::KEY_SRIGHT => Key::ShiftRight,
            ncurses::KEY_LEFT => Key::Left,
            ncurses::KEY_RIGHT => Key::Right,
            ncurses::KEY_UP => Key::Up,
            ncurses::KEY_DOWN => Key::Down,
            ncurses::KEY_PPAGE => Key::PageUp,
            ncurses::KEY_NPAGE => Key::PageDown,
            ncurses::KEY_HOME => Key::Home,
            ncurses::KEY_END => Key::End,
            ncurses::KEY_SDC => Key::ShiftDel,
            // All Fn keys use the same enum with associated number
            f @ ncurses::KEY_F1 ... ncurses::KEY_F15 => Key::F((f - ncurses::KEY_F0) as u8),
            f @ 281 ... 291 => Key::ShiftF((f - 281 + 5) as u8),
            f @ 293 ... 303 => Key::CtrlF((f - 293 + 5) as u8),
            f @ 305 ... 315 => Key::CtrlShiftF((f - 305 + 5) as u8),
            // Shift and Ctrl F{1-4} need escape sequences...

            // TODO: shift and ctrl Fn keys
            // Avoids 8-10 (H,I,J), they are used by other commands.
            c @ 1 ... 7 | c @ 11 ... 25 => Key::CtrlChar(('a' as u8 + (c-1) as u8) as char),
            _ => Key::Unknown(ch),
        }
    }
}

impl fmt::Display for Key {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Key::Unknown(ch) => write!(f, "Unknown: {}", ch),
            Key::CtrlChar(ch) => write!(f, "Ctrl-{}", ch),
            Key::F(n) => write!(f, "F{}", n),
            Key::ShiftF(n) => write!(f, "Shift-F{}", n),
            Key::CtrlF(n) => write!(f, "Ctrl-F{}", n),
            Key::CtrlShiftF(n) => write!(f, "Ctrl-Shift-F{}", n),
            key => write!(f, "{}", match key {
                Key::Left => "Left",
                Key::Right => "Right",
                Key::Down => "Down",
                Key::Up => "Up",
                Key::CtrlShiftLeft => "Ctrl-Shift-Left",
                Key::CtrlShiftRight => "Ctrl-Shift-Right",
                Key::ShiftLeft => "Shift-Left",
                Key::ShiftRight => "Shift-Right",
                Key::ShiftDel => "Shift-Del",
                Key::CtrlDel => "Ctrl-Del",
                Key::CtrlLeft => "Ctrl-Left",
                Key::CtrlRight => "Ctrl-Right",
                Key::CtrlUp => "Ctrl-Up",
                Key::CtrlDown => "Ctrl-Down",
                Key::CtrlPageUp => "Ctrl-PageUp",
                Key::CtrlPageDown => "Ctrl-PageDown",
                Key::PageUp => "PageUp",
                Key::PageDown => "PageDown",
                Key::Home => "Home",
                Key::End => "End",
                Key::Backspace => "Backspace",
                Key::Del => "Del",
                Key::Enter => "Enter",
                Key::ShiftTab => "Shift-Tab",
                Key::Tab => "Tab",
                Key::Ins => "Ins",
                Key::Esc => "Esc",
                _ => "Missing key label",
            }),
        }
    }
}

#[derive(PartialEq,Eq,Clone,Copy,Hash)]
pub enum Event {
    CharEvent(char),
    KeyEvent(Key),
}

pub trait ToEvent {
    fn to_event(self) -> Event;
}

impl ToEvent for char {
    fn to_event(self) -> Event {
        Event::CharEvent(self)
    }
}

impl ToEvent for Key {
    fn to_event(self) -> Event {
        Event::KeyEvent(self)
    }
}

impl ToEvent for Event {
    fn to_event(self) -> Event {
        self
    }
}
<|endoftext|>


<commit_before>use index::btree::node::read_node;
use index::btree::node::write_node;
use index::btree::node::write_targeted;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::remove::scatter_nodes;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use std::fmt::Debug;

pub fn mid_key<KS, PS>(node_ref: &NodeCellRef) -> Option<EntryKey>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("Searching for mid key for split");
    read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        match node {
            &NodeData::External(ref n) => Some(n.keys.as_slice_immute()[n.len / 2].clone()),
            &NodeData::Internal(ref n) => mid_key::<KS, PS>(&n.ptrs.as_slice_immute()[n.len / 2]),
            &NodeData::Empty(ref n) => mid_key::<KS, PS>(&n.right),
            &NodeData::None => None,
        }
    })
}

pub fn remove_to_right<KS, PS>(node_ref: &NodeCellRef, start_key: &EntryKey) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut removed_nodes = 0;
    match mut_search::<KS, PS>(node_ref, start_key) {
        MutSearchResult::Internal(ref next_level_node) => {
            {
                removed_nodes = remove_to_right::<KS, PS>(next_level_node, start_key);
                let mut pivot_node = write_targeted::<KS, PS>(write_node(node_ref), start_key);
                if pivot_node.is_none() {
                    // terminate when at and of nodes of this level
                    return removed_nodes;
                }
                debug_assert!(!pivot_node.is_empty_node());
                debug_assert!(!pivot_node.is_ext());
                let mut innode = pivot_node.innode_mut();
                let right_pos = innode.keys.as_slice_immute()[..innode.len]
                    .binary_search(start_key)
                    .unwrap_or_else(|x| x);
                debug_assert!(
                    right_pos <= innode.len,
                    "pos {} >= len {}",
                    right_pos,
                    innode.len
                );
                for i in right_pos..innode.len {
                    innode.ptrs.as_slice()[i + 1] = NodeCellRef::new_none::<KS, PS>();
                }
                scatter_nodes::<KS, PS>(&innode.right);
                innode.right = NodeCellRef::new_none::<KS, PS>();
                innode.len = right_pos;
            }
        }
        MutSearchResult::External => {
            let mut pivot_node = write_targeted::<KS, PS>(write_node(node_ref), start_key);
            if pivot_node.is_none() {
                // terminate when at and of nodes of this level
                return removed_nodes;
            }
            debug_assert!(!pivot_node.is_empty_node());
            debug_assert!(pivot_node.is_ext());
            let mut extnode = pivot_node.extnode_mut();
            let len = extnode.len;
            let new_len = extnode.keys.as_slice_immute()[..extnode.len]
                .binary_search(start_key)
                .unwrap_or_else(|x| x);
            extnode.len = new_len;
            let node_key_removed = len - new_len;
            removed_nodes = scatter_nodes::<KS, PS>(&extnode.next) + node_key_removed;
        }
    }
    removed_nodes
}
<commit_msg>get of cascading read node<commit_after>use index::btree::node::read_node;
use index::btree::node::write_node;
use index::btree::node::write_targeted;
use index::btree::node::NodeData;
use index::btree::node::NodeReadHandler;
use index::btree::remove::scatter_nodes;
use index::btree::search::mut_search;
use index::btree::search::MutSearchResult;
use index::btree::NodeCellRef;
use index::EntryKey;
use index::Slice;
use std::fmt::Debug;

pub fn mid_key<KS, PS>(node_ref: &NodeCellRef) -> Option<EntryKey>
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    debug!("Searching for mid key for split");
    enum R {
        Result(Option<EntryKey>),
        SubSearch(NodeCellRef),
        None
    }
    let r = read_node(node_ref, |node_handler: &NodeReadHandler<KS, PS>| {
        let node = &**node_handler;
        match node {
            &NodeData::External(ref n) => R::Result(Some(n.keys.as_slice_immute()[n.len / 2].clone())),
            &NodeData::Internal(ref n) => R::SubSearch(n.ptrs.as_slice_immute()[n.len / 2].clone()) ,
            &NodeData::Empty(ref n) => R::SubSearch(n.right.clone()),
            &NodeData::None => R::None,
        }
    });
    match r {
        R::Result(r) => r,
        R::SubSearch(r) =>  mid_key::<KS, PS>(&r),
        R::None => None
    }
}

pub fn remove_to_right<KS, PS>(node_ref: &NodeCellRef, start_key: &EntryKey) -> usize
where
    KS: Slice<EntryKey> + Debug + 'static,
    PS: Slice<NodeCellRef> + 'static,
{
    let mut removed_nodes = 0;
    match mut_search::<KS, PS>(node_ref, start_key) {
        MutSearchResult::Internal(ref next_level_node) => {
            {
                removed_nodes = remove_to_right::<KS, PS>(next_level_node, start_key);
                let mut pivot_node = write_targeted::<KS, PS>(write_node(node_ref), start_key);
                if pivot_node.is_none() {
                    // terminate when at and of nodes of this level
                    return removed_nodes;
                }
                debug_assert!(!pivot_node.is_empty_node());
                debug_assert!(!pivot_node.is_ext());
                let mut innode = pivot_node.innode_mut();
                let right_pos = innode.keys.as_slice_immute()[..innode.len]
                    .binary_search(start_key)
                    .unwrap_or_else(|x| x);
                debug_assert!(
                    right_pos <= innode.len,
                    "pos {} >= len {}",
                    right_pos,
                    innode.len
                );
                for i in right_pos..innode.len {
                    innode.ptrs.as_slice()[i + 1] = NodeCellRef::new_none::<KS, PS>();
                }
                scatter_nodes::<KS, PS>(&innode.right);
                innode.right = NodeCellRef::new_none::<KS, PS>();
                innode.len = right_pos;
            }
        }
        MutSearchResult::External => {
            let mut pivot_node = write_targeted::<KS, PS>(write_node(node_ref), start_key);
            if pivot_node.is_none() {
                // terminate when at and of nodes of this level
                return removed_nodes;
            }
            debug_assert!(!pivot_node.is_empty_node());
            debug_assert!(pivot_node.is_ext());
            let mut extnode = pivot_node.extnode_mut();
            let len = extnode.len;
            let new_len = extnode.keys.as_slice_immute()[..extnode.len]
                .binary_search(start_key)
                .unwrap_or_else(|x| x);
            extnode.len = new_len;
            let node_key_removed = len - new_len;
            removed_nodes = scatter_nodes::<KS, PS>(&extnode.next) + node_key_removed;
        }
    }
    removed_nodes
}
<|endoftext|>


<commit_before>use std::str::Chars;
use std::iter::Peekable;
use std::iter::Enumerate;

#[derive(Debug)]
pub enum Token {
    Ident(String),

    OpenBrace,
    CloseBrace,
    OpenParen,
    CloseParen,
    OpenSquare,
    CloseSquare,

    Newline,
    Whitespace,

    Underscore,
    Dash,

    SingleQuote,
    DoubleQuote,
    Comma,
    SemiColon,
    Colon,
    ForwardSlash,
    Pipe,
    Dot,
    Equal,
    Bang,
    Greater,
    Less,
    Hash,
    Dollar,
    Amp,
    Asterisk,
    At,
    SingleLineComment(String),
    Attribute(String),
    String(String),
}

impl Token {
    pub fn as_char(&self) -> Option<char> {
        match *self {
            Token::OpenBrace => Some('{'),
            Token::CloseBrace => Some('}'),
            Token::OpenParen => Some('('),
            Token::CloseParen => Some(')'),
            Token::OpenSquare => Some('['),
            Token::CloseSquare => Some(']'),
            Token::Newline => Some('\n'),
            Token::Whitespace => Some(' '),
            Token::Underscore => Some('_'),
            Token::Dash => Some('-'),
            Token::SingleQuote => Some('\''),
            Token::DoubleQuote => Some('"'),
            Token::Comma => Some(','),
            Token::SemiColon => Some(';'),
            Token::Colon => Some(':'),
            Token::ForwardSlash => Some('/'),
            Token::Pipe => Some('|'),
            Token::Dot => Some('.'),
            Token::Equal => Some('='),
            Token::Bang => Some('!'),
            Token::Greater => Some('>'),
            Token::Less => Some('<'),
            Token::Hash => Some('#'),
            Token::Dollar => Some('$'),
            Token::Amp => Some('&'),
            Token::Asterisk => Some('*'),
            Token::At => Some('@'),

            _ => None,
        }
    }
}

pub trait Lexer {
    fn handle_ident(&self, ch: char, iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize) -> Span;
    fn handle_char(&self, ch: char, mut iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize, idx: usize) -> Option<Span>;

    fn is_ident(&self, ch: Option<&(usize, char)>) -> bool {
        if let Some(&(idx, c)) = ch {
            c.is_alphabetic() || c == '_'
        } else {
            false
        }
    }

    fn get_stream(&self, input: &str) -> Vec<Span> {
        let mut tokens = Vec::new();
        let mut y_pos = 0;

        let mut chars = input.chars().enumerate().peekable();
        while let Some((idx, c)) = chars.next() {
            match self.handle_char(c, &mut chars, y_pos, idx) {
                Some(span) => tokens.push(span),
                None => {
                    match c {
                        ' ' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Whitespace}),
                        '\n' => {
                            y_pos += 1;
                            tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Newline})
                        }
                        '{' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenBrace}),
                        '}' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseBrace}),
                        '(' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenParen}),
                        ')' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseParen}),
                        '[' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenSquare}),
                        ']' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseSquare}),
                        '\'' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::SingleQuote}),
                        '"' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::DoubleQuote}),
                        ',' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Comma}),
                        ';' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::SemiColon}),
                        ':' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Colon}),
                        '/' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::ForwardSlash}),
                        '|' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Pipe}),
                        '.' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dot}),
                        '=' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Equal}),
                        '!' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Bang}),
                        '>' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Greater}),
                        '<' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Less}),
                        '-' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dash}),
                        '#' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Hash}),
                        '$' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dollar}),
                        '&' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Amp}),
                        '*' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Asterisk}),
                        '@' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::At}),
                        _ => {
                            let ident = self.handle_ident(c, &mut chars, y_pos);
                            tokens.push(ident);
                        }
                    }
                }
            }
        }

        tokens
    }
}

#[derive(Debug)]
pub struct Span {
    pub start: usize,
    pub end: usize,
    pub token: Token,
    pub y_pos: usize,
}

pub struct SyntaxInstance {
    pub lexer: Box<Lexer>,
    pub file_extensions: Vec<String>,
    pub keywords: Vec<String>,
    pub types: Vec<String>,
}

impl SyntaxInstance {
    pub fn is_keyword(&self, s: &str) -> bool {
        self.keywords.contains(&s.into())
    }

    pub fn is_type(&self, s: &str) -> bool {
        self.types.contains(&s.into())
    }

    pub fn get_stream(&self, text: &str) -> Vec<Span> {
        self.lexer.get_stream(text)
    }
}

macro_rules! define_lang {
    (
        $name:ident,
        $lexer_ty:ident,
        $lexer:expr,
        extensions=[$($exts:expr),+],
        keywords=[$($keys:expr),*],
        types=[$($types:expr),*]
    ) => {
        use ::syntax::langs::$lexer_ty;

        impl SyntaxInstance {
            pub fn $name() -> SyntaxInstance {
                let mut extensions = Vec::new();
                $(
                    extensions.push($exts.into());
                )*
                let mut keywords = Vec::new();
                $(
                    keywords.push($keys.into());
                )*
                let mut types = Vec::new();
                $(
                    types.push($types.into());
                )*

                SyntaxInstance {
                    lexer: Box::new($lexer),
                    file_extensions: extensions,
                    keywords: keywords,
                    types: types,
                }
            }
        }
    };
}

define_lang!(rust, RustSyntax, RustSyntax,
             extensions=["rs"],
             keywords=["fn", "let", "struct", "pub", "use", "impl"],
             types=["usize", "u32", "i32", "String", "mut", "Buffer", "Option"]
);

define_lang!(python, PythonSyntax, PythonSyntax,
             extensions=["py"],
             keywords=["def", "for", "while", "if", "class", "import", "return", "from", "not", "in", "and", "else", "try", "except"],
             types=["int", "dict", "list", "typle", "type", "Exception"]
);

<commit_msg>Remove file_extensions from SyntaxInstance<commit_after>use std::str::Chars;
use std::iter::Peekable;
use std::iter::Enumerate;

#[derive(Debug)]
pub enum Token {
    Ident(String),

    OpenBrace,
    CloseBrace,
    OpenParen,
    CloseParen,
    OpenSquare,
    CloseSquare,

    Newline,
    Whitespace,

    Underscore,
    Dash,

    SingleQuote,
    DoubleQuote,
    Comma,
    SemiColon,
    Colon,
    ForwardSlash,
    Pipe,
    Dot,
    Equal,
    Bang,
    Greater,
    Less,
    Hash,
    Dollar,
    Amp,
    Asterisk,
    At,
    SingleLineComment(String),
    Attribute(String),
    String(String),
}

impl Token {
    pub fn as_char(&self) -> Option<char> {
        match *self {
            Token::OpenBrace => Some('{'),
            Token::CloseBrace => Some('}'),
            Token::OpenParen => Some('('),
            Token::CloseParen => Some(')'),
            Token::OpenSquare => Some('['),
            Token::CloseSquare => Some(']'),
            Token::Newline => Some('\n'),
            Token::Whitespace => Some(' '),
            Token::Underscore => Some('_'),
            Token::Dash => Some('-'),
            Token::SingleQuote => Some('\''),
            Token::DoubleQuote => Some('"'),
            Token::Comma => Some(','),
            Token::SemiColon => Some(';'),
            Token::Colon => Some(':'),
            Token::ForwardSlash => Some('/'),
            Token::Pipe => Some('|'),
            Token::Dot => Some('.'),
            Token::Equal => Some('='),
            Token::Bang => Some('!'),
            Token::Greater => Some('>'),
            Token::Less => Some('<'),
            Token::Hash => Some('#'),
            Token::Dollar => Some('$'),
            Token::Amp => Some('&'),
            Token::Asterisk => Some('*'),
            Token::At => Some('@'),

            _ => None,
        }
    }
}

pub trait Lexer {
    fn handle_ident(&self, ch: char, iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize) -> Span;
    fn handle_char(&self, ch: char, mut iter: &mut Peekable<Enumerate<Chars>>, y_pos: usize, idx: usize) -> Option<Span>;

    fn is_ident(&self, ch: Option<&(usize, char)>) -> bool {
        if let Some(&(idx, c)) = ch {
            c.is_alphabetic() || c == '_'
        } else {
            false
        }
    }

    fn get_stream(&self, input: &str) -> Vec<Span> {
        let mut tokens = Vec::new();
        let mut y_pos = 0;

        let mut chars = input.chars().enumerate().peekable();
        while let Some((idx, c)) = chars.next() {
            match self.handle_char(c, &mut chars, y_pos, idx) {
                Some(span) => tokens.push(span),
                None => {
                    match c {
                        ' ' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Whitespace}),
                        '\n' => {
                            y_pos += 1;
                            tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Newline})
                        }
                        '{' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenBrace}),
                        '}' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseBrace}),
                        '(' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenParen}),
                        ')' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseParen}),
                        '[' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::OpenSquare}),
                        ']' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::CloseSquare}),
                        '\'' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::SingleQuote}),
                        '"' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::DoubleQuote}),
                        ',' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Comma}),
                        ';' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::SemiColon}),
                        ':' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Colon}),
                        '/' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::ForwardSlash}),
                        '|' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Pipe}),
                        '.' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dot}),
                        '=' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Equal}),
                        '!' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Bang}),
                        '>' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Greater}),
                        '<' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Less}),
                        '-' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dash}),
                        '#' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Hash}),
                        '$' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Dollar}),
                        '&' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Amp}),
                        '*' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::Asterisk}),
                        '@' => tokens.push(Span{y_pos: y_pos, start: idx, end: idx + 1, token: Token::At}),
                        _ => {
                            let ident = self.handle_ident(c, &mut chars, y_pos);
                            tokens.push(ident);
                        }
                    }
                }
            }
        }

        tokens
    }
}

#[derive(Debug)]
pub struct Span {
    pub start: usize,
    pub end: usize,
    pub token: Token,
    pub y_pos: usize,
}

pub struct SyntaxInstance {
    pub lexer: Box<Lexer>,
    pub keywords: Vec<String>,
    pub types: Vec<String>,
}

impl SyntaxInstance {
    pub fn is_keyword(&self, s: &str) -> bool {
        self.keywords.contains(&s.into())
    }

    pub fn is_type(&self, s: &str) -> bool {
        self.types.contains(&s.into())
    }

    pub fn get_stream(&self, text: &str) -> Vec<Span> {
        self.lexer.get_stream(text)
    }
}

macro_rules! define_lang {
    (
        $name:ident,
        $lexer_ty:ident,
        $lexer:expr,
        keywords=[$($keys:expr),*],
        types=[$($types:expr),*]
    ) => {
        use ::syntax::langs::$lexer_ty;

        impl SyntaxInstance {
            pub fn $name() -> SyntaxInstance {
                let mut keywords = Vec::new();
                $(
                    keywords.push($keys.into());
                )*
                let mut types = Vec::new();
                $(
                    types.push($types.into());
                )*

                SyntaxInstance {
                    lexer: Box::new($lexer),
                    keywords: keywords,
                    types: types,
                }
            }
        }
    };
}

define_lang!(rust, RustSyntax, RustSyntax,
             keywords=["fn", "let", "struct", "pub", "use", "impl"],
             types=["usize", "u32", "i32", "String", "mut", "Buffer", "Option"]
);

define_lang!(python, PythonSyntax, PythonSyntax,
             keywords=["def", "for", "while", "if", "class", "import", "return", "from", "not", "in", "and", "else", "try", "except"],
             types=["int", "dict", "list", "typle", "type", "Exception"]
);

<|endoftext|>


<commit_before>pub fn is_leap_year(x:i16) -> bool {
   check_divisibility(x,4) ^ (
       check_divisibility(x,100) ^ check_divisibility(x,400)
       )
}

fn check_divisibility(number:i16,divisor:i16) ->bool{
  number%divisor == 0
}<commit_msg>use closure instead of function<commit_after>pub fn is_leap_year(number:i16) -> bool {
    let divisible_by = |divisor:i16|  number%divisor == 0 ;
   divisible_by(4) ^  divisible_by(100) ^ divisible_by(400)
}<|endoftext|>