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

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq, Hash)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = input.try(|input| {
            let mut state = ElementState::empty();

            while !input.is_exhausted() {
                input.expect_colon()?;
                let ident = input.expect_ident()?;
                let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                if !pseudo_class.is_safe_user_action_state() {
                    return Err(())
                }
                state.insert(pseudo_class.state_flag());
            }

            Ok(state)
        });

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<commit_msg>Bug 1364412: Properly reject to parse pseudo-element states that don't support state. r=bholley<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/. */

//! Gecko-specific bits for selector-parsing.

use cssparser::{Parser, ToCss};
use element_state::{IN_ACTIVE_STATE, IN_FOCUS_STATE, IN_HOVER_STATE};
use element_state::ElementState;
use gecko_bindings::structs::CSSPseudoClassType;
use selector_parser::{SelectorParser, PseudoElementCascadeType};
use selectors::parser::{ComplexSelector, SelectorMethods};
use selectors::visitor::SelectorVisitor;
use std::borrow::Cow;
use std::fmt;
use string_cache::{Atom, Namespace, WeakAtom, WeakNamespace};

pub use gecko::pseudo_element::{PseudoElement, EAGER_PSEUDOS, EAGER_PSEUDO_COUNT};
pub use gecko::snapshot::SnapshotMap;

bitflags! {
    flags NonTSPseudoClassFlag: u8 {
        // See NonTSPseudoClass::is_internal()
        const PSEUDO_CLASS_INTERNAL = 0x01,
    }
}

macro_rules! pseudo_class_name {
    (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
     string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
        #[doc = "Our representation of a non tree-structural pseudo-class."]
        #[derive(Clone, Debug, PartialEq, Eq, Hash)]
        pub enum NonTSPseudoClass {
            $(
                #[doc = $css]
                $name,
            )*
            $(
                #[doc = $s_css]
                $s_name(Box<[u16]>),
            )*
            /// The non-standard `:-moz-any` pseudo-class.
            ///
            /// TODO(emilio): We disallow combinators and pseudos here, so we
            /// should use SimpleSelector instead
            MozAny(Box<[ComplexSelector<SelectorImpl>]>),
        }
    }
}
apply_non_ts_list!(pseudo_class_name);

impl ToCss for NonTSPseudoClass {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
        use cssparser::CssStringWriter;
        use fmt::Write;
        macro_rules! pseudo_class_serialize {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => concat!(":", $css),)*
                    $(NonTSPseudoClass::$s_name(ref s) => {
                        write!(dest, ":{}(", $s_css)?;
                        {
                            // FIXME(emilio): Avoid the extra allocation!
                            let mut css = CssStringWriter::new(dest);

                            // Discount the null char in the end from the
                            // string.
                            css.write_str(&String::from_utf16(&s[..s.len() - 1]).unwrap())?;
                        }
                        return dest.write_str(")")
                    }, )*
                    NonTSPseudoClass::MozAny(ref selectors) => {
                        dest.write_str(":-moz-any(")?;
                        let mut iter = selectors.iter();
                        let first = iter.next().expect(":-moz-any must have at least 1 selector");
                        first.to_css(dest)?;
                        for selector in iter {
                            dest.write_str(", ")?;
                            selector.to_css(dest)?;
                        }
                        return dest.write_str(")")
                    }
                }
            }
        }
        let ser = apply_non_ts_list!(pseudo_class_serialize);
        dest.write_str(ser)
    }
}

impl SelectorMethods for NonTSPseudoClass {
    type Impl = SelectorImpl;

    fn visit<V>(&self, visitor: &mut V) -> bool
        where V: SelectorVisitor<Impl = Self::Impl>,
    {
        if let NonTSPseudoClass::MozAny(ref selectors) = *self {
            for selector in selectors.iter() {
                if !selector.visit(visitor) {
                    return false;
                }
            }
        }

        true
    }
}

impl NonTSPseudoClass {
    /// A pseudo-class is internal if it can only be used inside
    /// user agent style sheets.
    pub fn is_internal(&self) -> bool {
        macro_rules! check_flag {
            (_) => (false);
            ($flags:expr) => ($flags.contains(PSEUDO_CLASS_INTERNAL));
        }
        macro_rules! pseudo_class_check_internal {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => check_flag!($flags),)*
                    $(NonTSPseudoClass::$s_name(..) => check_flag!($s_flags),)*
                    NonTSPseudoClass::MozAny(_) => false,
                }
            }
        }
        apply_non_ts_list!(pseudo_class_check_internal)
    }

    /// https://drafts.csswg.org/selectors-4/#useraction-pseudos
    ///
    /// We intentionally skip the link-related ones.
    fn is_safe_user_action_state(&self) -> bool {
        matches!(*self, NonTSPseudoClass::Hover |
                        NonTSPseudoClass::Active |
                        NonTSPseudoClass::Focus)
    }

    /// Get the state flag associated with a pseudo-class, if any.
    pub fn state_flag(&self) -> ElementState {
        macro_rules! flag {
            (_) => (ElementState::empty());
            ($state:ident) => (::element_state::$state);
        }
        macro_rules! pseudo_class_state {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => flag!($state),)*
                    $(NonTSPseudoClass::$s_name(..) => flag!($s_state),)*
                    NonTSPseudoClass::MozAny(..) => ElementState::empty(),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_state)
    }

    /// Returns true if the given pseudoclass should trigger style sharing cache revalidation.
    pub fn needs_cache_revalidation(&self) -> bool {
        self.state_flag().is_empty() &&
        !matches!(*self, NonTSPseudoClass::MozAny(_))
    }

    /// Convert NonTSPseudoClass to Gecko's CSSPseudoClassType.
    pub fn to_gecko_pseudoclasstype(&self) -> Option<CSSPseudoClassType> {
        macro_rules! gecko_type {
            (_) => (None);
            ($gecko_type:ident) =>
                (Some(::gecko_bindings::structs::CSSPseudoClassType::$gecko_type));
        }
        macro_rules! pseudo_class_geckotype {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
            string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match *self {
                    $(NonTSPseudoClass::$name => gecko_type!($gecko_type),)*
                    $(NonTSPseudoClass::$s_name(..) => gecko_type!($s_gecko_type),)*
                    NonTSPseudoClass::MozAny(_) => gecko_type!(any),
                }
            }
        }
        apply_non_ts_list!(pseudo_class_geckotype)
    }
}

/// The dummy struct we use to implement our selector parsing.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SelectorImpl;

/// Some subset of pseudo-elements in Gecko are sensitive to some state
/// selectors.
///
/// We store the sensitive states in this struct in order to properly handle
/// these.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct PseudoElementSelector {
    pseudo: PseudoElement,
    state: ElementState,
}

impl PseudoElementSelector {
    /// Returns the pseudo-element this selector represents.
    pub fn pseudo_element(&self) -> &PseudoElement {
        &self.pseudo
    }

    /// Returns the pseudo-element selector state.
    pub fn state(&self) -> ElementState {
        self.state
    }
}

impl ToCss for PseudoElementSelector {
    fn to_css<W>(&self, dest: &mut W) -> fmt::Result
        where W: fmt::Write,
    {
        if cfg!(debug_assertions) {
            let mut state = self.state;
            state.remove(IN_HOVER_STATE | IN_ACTIVE_STATE | IN_FOCUS_STATE);
            assert_eq!(state, ElementState::empty(),
                       "Unhandled pseudo-element state selector?");
        }

        self.pseudo.to_css(dest)?;

        if self.state.contains(IN_HOVER_STATE) {
            dest.write_str(":hover")?
        }

        if self.state.contains(IN_ACTIVE_STATE) {
            dest.write_str(":active")?
        }

        if self.state.contains(IN_FOCUS_STATE) {
            dest.write_str(":focus")?
        }

        Ok(())
    }
}

impl ::selectors::SelectorImpl for SelectorImpl {
    type AttrValue = Atom;
    type Identifier = Atom;
    type ClassName = Atom;
    type LocalName = Atom;
    type NamespacePrefix = Atom;
    type NamespaceUrl = Namespace;
    type BorrowedNamespaceUrl = WeakNamespace;
    type BorrowedLocalName = WeakAtom;

    type PseudoElementSelector = PseudoElementSelector;
    type NonTSPseudoClass = NonTSPseudoClass;
}

impl<'a> ::selectors::Parser for SelectorParser<'a> {
    type Impl = SelectorImpl;

    fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($css => NonTSPseudoClass::$name,)*
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_non_ts_functional_pseudo_class(&self,
                                            name: Cow<str>,
                                            parser: &mut Parser)
                                            -> Result<NonTSPseudoClass, ()> {
        macro_rules! pseudo_class_string_parse {
            (bare: [$(($css:expr, $name:ident, $gecko_type:tt, $state:tt, $flags:tt),)*],
             string: [$(($s_css:expr, $s_name:ident, $s_gecko_type:tt, $s_state:tt, $s_flags:tt),)*]) => {
                match_ignore_ascii_case! { &name,
                    $($s_css => {
                        let name = parser.expect_ident_or_string()?;
                        // convert to null terminated utf16 string
                        // since that's what Gecko deals with
                        let utf16: Vec<u16> = name.encode_utf16().chain(Some(0u16)).collect();
                        NonTSPseudoClass::$s_name(utf16.into_boxed_slice())
                    }, )*
                    "-moz-any" => {
                        let selectors = parser.parse_comma_separated(|input| {
                            ComplexSelector::parse(self, input)
                        })?;
                        // Selectors inside `:-moz-any` may not include combinators.
                        if selectors.iter().flat_map(|x| x.iter_raw()).any(|s| s.is_combinator()) {
                            return Err(())
                        }
                        NonTSPseudoClass::MozAny(selectors.into_boxed_slice())
                    }
                    _ => return Err(())
                }
            }
        }
        let pseudo_class = apply_non_ts_list!(pseudo_class_string_parse);
        if !pseudo_class.is_internal() || self.in_user_agent_stylesheet() {
            Ok(pseudo_class)
        } else {
            Err(())
        }
    }

    fn parse_pseudo_element(&self, name: Cow<str>, input: &mut Parser) -> Result<PseudoElementSelector, ()> {
        let pseudo =
            match PseudoElement::from_slice(&name, self.in_user_agent_stylesheet()) {
                Some(pseudo) => pseudo,
                None => return Err(()),
            };

        let state = if pseudo.supports_user_action_state() {
            input.try(|input| {
                let mut state = ElementState::empty();

                while !input.is_exhausted() {
                    input.expect_colon()?;
                    let ident = input.expect_ident()?;
                    let pseudo_class = self.parse_non_ts_pseudo_class(ident)?;

                    if !pseudo_class.is_safe_user_action_state() {
                        return Err(())
                    }
                    state.insert(pseudo_class.state_flag());
                }

                Ok(state)
            }).ok()
        } else {
            None
        };

        Ok(PseudoElementSelector {
            pseudo: pseudo,
            state: state.unwrap_or(ElementState::empty()),
        })
    }

    fn default_namespace(&self) -> Option<Namespace> {
        self.namespaces.default.clone()
    }

    fn namespace_for_prefix(&self, prefix: &Atom) -> Option<Namespace> {
        self.namespaces.prefixes.get(prefix).cloned()
    }
}

impl SelectorImpl {
    #[inline]
    /// Legacy alias for PseudoElement::cascade_type.
    pub fn pseudo_element_cascade_type(pseudo: &PseudoElement) -> PseudoElementCascadeType {
        pseudo.cascade_type()
    }

    /// A helper to traverse each eagerly cascaded pseudo-element, executing
    /// `fun` on it.
    #[inline]
    pub fn each_eagerly_cascaded_pseudo_element<F>(mut fun: F)
        where F: FnMut(PseudoElement),
    {
        for pseudo in &EAGER_PSEUDOS {
            fun(pseudo.clone())
        }
    }

    #[inline]
    /// Executes a function for each pseudo-element.
    pub fn each_pseudo_element<F>(fun: F)
        where F: FnMut(PseudoElement),
    {
        PseudoElement::each(fun)
    }

    #[inline]
    /// Returns the relevant state flag for a given non-tree-structural
    /// pseudo-class.
    pub fn pseudo_class_state_flag(pc: &NonTSPseudoClass) -> ElementState {
        pc.state_flag()
    }
}
<|endoftext|>


<commit_before>use clap::Parser;

#[test]
fn test_safely_nest_parser() {
    #[derive(Parser, Debug, PartialEq)]
    struct Opt {
        #[command(flatten)]
        foo: Foo,
    }

    #[derive(Parser, Debug, PartialEq)]
    struct Foo {
        #[arg(long)]
        foo: bool,
    }

    assert_eq!(
        Opt {
            foo: Foo { foo: true }
        },
        Opt::try_parse_from(&["test", "--foo"]).unwrap()
    );
}
<commit_msg>test(derive): Show failure in #4279<commit_after>use clap::Parser;

#[test]
fn test_safely_nest_parser() {
    #[derive(Parser, Debug, PartialEq)]
    struct Opt {
        #[command(flatten)]
        foo: Foo,
    }

    #[derive(Parser, Debug, PartialEq)]
    struct Foo {
        #[arg(long)]
        foo: bool,
    }

    assert_eq!(
        Opt {
            foo: Foo { foo: true }
        },
        Opt::try_parse_from(&["test", "--foo"]).unwrap()
    );
}

#[test]
#[should_panic = "'Compose' is already in use"]
fn skip_group_avoids_duplicate_ids() {
    #[derive(Parser, Debug)]
    struct Opt {
        #[command(flatten)]
        first: Compose<Empty, Empty>,
        #[command(flatten)]
        second: Compose<Empty, Empty>,
    }

    #[derive(clap::Args, Debug)]
    pub struct Compose<L: clap::Args, R: clap::Args> {
        #[clap(flatten)]
        pub left: L,
        #[clap(flatten)]
        pub right: R,
    }

    #[derive(clap::Args, Clone, Copy, Debug)]
    pub struct Empty;

    use clap::CommandFactory;
    Opt::command().debug_assert();
}
<|endoftext|>


<commit_before>//! Builtin macro
use crate::db::AstDatabase;
use crate::{
    ast::{self, AstNode},
    name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
    TextUnit,
};

use crate::quote;

macro_rules! register_builtin {
    ( $(($name:ident, $kind: ident) => $expand:ident),* ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub enum BuiltinFnLikeExpander {
            $($kind),*
        }

        impl BuiltinFnLikeExpander {
            pub fn expand(
                &self,
                db: &dyn AstDatabase,
                id: MacroCallId,
                tt: &tt::Subtree,
            ) -> Result<tt::Subtree, mbe::ExpandError> {
                let expander = match *self {
                    $( BuiltinFnLikeExpander::$kind => $expand, )*
                };
                expander(db, id, tt)
            }
        }

        pub fn find_builtin_macro(
            ident: &name::Name,
            krate: CrateId,
            ast_id: AstId<ast::MacroCall>,
        ) -> Option<MacroDefId> {
            let kind = match ident {
                 $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )*
                 _ => return None,
            };

            Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) })
        }
    };
}

register_builtin! {
    (COLUMN_MACRO, Column) => column_expand,
    (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand,
    (FILE_MACRO, File) => file_expand,
    (LINE_MACRO, Line) => line_expand,
    (STRINGIFY_MACRO, Stringify) => stringify_expand,
    (FORMAT_ARGS_MACRO, FormatArgs) => format_args_expand,
    // format_args_nl only differs in that it adds a newline in the end,
    // so we use the same stub expansion for now
    (FORMAT_ARGS_NL_MACRO, FormatArgsNl) => format_args_expand
}

fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
    // FIXME: Use expansion info
    let file_id = file.original_file(db);
    let text = db.file_text(file_id);
    let mut line_num = 1;

    let pos = pos.to_usize();
    if pos > text.len() {
        // FIXME: `pos` at the moment could be an offset inside the "wrong" file
        // in this case, when we know it's wrong, we return a dummy value
        return 0;
    }
    // Count line end
    for (i, c) in text.chars().enumerate() {
        if i == pos {
            break;
        }
        if c == '\n' {
            line_num += 1;
        }
    }
    line_num
}

fn line_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);

    let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
    let arg_start = arg.text_range().start();

    let file = id.as_file(MacroFileKind::Expr);
    let line_num = to_line_number(db, file, arg_start);

    let expanded = quote! {
        #line_num
    };

    Ok(expanded)
}

fn stringify_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);

    let macro_content = {
        let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
        let macro_args = arg.clone();
        let text = macro_args.text();
        let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
        text.slice(without_parens).to_string()
    };

    let expanded = quote! {
        #macro_content
    };

    Ok(expanded)
}

fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
    // FIXME: Use expansion info
    let file_id = file.original_file(db);
    let text = db.file_text(file_id);

    let pos = pos.to_usize();
    if pos > text.len() {
        // FIXME: `pos` at the moment could be an offset inside the "wrong" file
        // in this case we return a dummy value so that we don't `panic!`
        return 0;
    }

    let mut col_num = 1;
    for c in text[..pos].chars().rev() {
        if c == '\n' {
            break;
        }
        col_num = col_num + 1;
    }
    col_num
}

fn column_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);
    let macro_call = match loc.kind {
        crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db),
        _ => panic!("column macro called as attr"),
    };

    let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
    let col_start = macro_call.syntax().text_range().start();

    let file = id.as_file(MacroFileKind::Expr);
    let col_num = to_col_number(db, file, col_start);

    let expanded = quote! {
        #col_num
    };

    Ok(expanded)
}

fn file_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    // FIXME: RA purposefully lacks knowledge of absolute file names
    // so just return "".
    let file_name = "";

    let expanded = quote! {
        #file_name
    };

    Ok(expanded)
}

fn compile_error_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    if tt.count() == 1 {
        match &tt.token_trees[0] {
            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
                let s = it.text.as_str();
                if s.contains(r#"""#) {
                    return Ok(quote! { loop { #it }});
                }
            }
            _ => {}
        };
    }

    Err(mbe::ExpandError::BindingError("Must be a string".into()))
}

fn format_args_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    // FIXME this is just a stub to make format macros type-check without mismatches
    // We should make this at least insert the arguments, so that go to def etc. work within format macros
    let expanded = quote! {
        std::fmt::Arguments::new_v1(&[], &[])
    };
    Ok(expanded)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{test_db::TestDB, MacroCallKind, MacroCallLoc};
    use ra_db::{fixture::WithFixture, SourceDatabase};

    fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
        let (db, file_id) = TestDB::with_single_file(&s);
        let parsed = db.parse(file_id);
        let macro_calls: Vec<_> =
            parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect();

        let ast_id_map = db.ast_id_map(file_id.into());

        // the first one should be a macro_rules
        let def = MacroDefId {
            krate: Some(CrateId(0)),
            ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
            kind: MacroDefKind::BuiltIn(expander),
        };

        let loc = MacroCallLoc {
            def,
            kind: MacroCallKind::FnLike(AstId::new(
                file_id.into(),
                ast_id_map.ast_id(&macro_calls[1]),
            )),
        };

        let id = db.intern_macro(loc);
        let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap();

        parsed.text().to_string()
    }

    #[test]
    fn test_column_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! column {() => {}}
        column!()
"#,
            BuiltinFnLikeExpander::Column,
        );

        assert_eq!(expanded, "9");
    }

    #[test]
    fn test_line_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! line {() => {}}
        line!()
"#,
            BuiltinFnLikeExpander::Line,
        );

        assert_eq!(expanded, "4");
    }

    #[test]
    fn test_stringify_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! stringify {() => {}}
        stringify!(a b c)
"#,
            BuiltinFnLikeExpander::Stringify,
        );

        assert_eq!(expanded, "\"a b c\"");
    }

    #[test]
    fn test_file_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! file {() => {}}
        file!()
"#,
            BuiltinFnLikeExpander::File,
        );

        assert_eq!(expanded, "\"\"");
    }

    #[test]
    fn test_compile_error_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! compile_error {
            ($msg:expr) => ({ /* compiler built-in */ });
            ($msg:expr,) => ({ /* compiler built-in */ })
        }
        compile_error!("error!");
"#,
            BuiltinFnLikeExpander::CompileError,
        );

        assert_eq!(expanded, r#"loop{"error!"}"#);
    }
}
<commit_msg>Implement format_args more properly<commit_after>//! Builtin macro
use crate::db::AstDatabase;
use crate::{
    ast::{self, AstNode},
    name, AstId, CrateId, HirFileId, MacroCallId, MacroDefId, MacroDefKind, MacroFileKind,
    TextUnit,
};

use crate::quote;

macro_rules! register_builtin {
    ( $(($name:ident, $kind: ident) => $expand:ident),* ) => {
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
        pub enum BuiltinFnLikeExpander {
            $($kind),*
        }

        impl BuiltinFnLikeExpander {
            pub fn expand(
                &self,
                db: &dyn AstDatabase,
                id: MacroCallId,
                tt: &tt::Subtree,
            ) -> Result<tt::Subtree, mbe::ExpandError> {
                let expander = match *self {
                    $( BuiltinFnLikeExpander::$kind => $expand, )*
                };
                expander(db, id, tt)
            }
        }

        pub fn find_builtin_macro(
            ident: &name::Name,
            krate: CrateId,
            ast_id: AstId<ast::MacroCall>,
        ) -> Option<MacroDefId> {
            let kind = match ident {
                 $( id if id == &name::$name => BuiltinFnLikeExpander::$kind, )*
                 _ => return None,
            };

            Some(MacroDefId { krate: Some(krate), ast_id: Some(ast_id), kind: MacroDefKind::BuiltIn(kind) })
        }
    };
}

register_builtin! {
    (COLUMN_MACRO, Column) => column_expand,
    (COMPILE_ERROR_MACRO, CompileError) => compile_error_expand,
    (FILE_MACRO, File) => file_expand,
    (LINE_MACRO, Line) => line_expand,
    (STRINGIFY_MACRO, Stringify) => stringify_expand,
    (FORMAT_ARGS_MACRO, FormatArgs) => format_args_expand,
    // format_args_nl only differs in that it adds a newline in the end,
    // so we use the same stub expansion for now
    (FORMAT_ARGS_NL_MACRO, FormatArgsNl) => format_args_expand
}

fn to_line_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
    // FIXME: Use expansion info
    let file_id = file.original_file(db);
    let text = db.file_text(file_id);
    let mut line_num = 1;

    let pos = pos.to_usize();
    if pos > text.len() {
        // FIXME: `pos` at the moment could be an offset inside the "wrong" file
        // in this case, when we know it's wrong, we return a dummy value
        return 0;
    }
    // Count line end
    for (i, c) in text.chars().enumerate() {
        if i == pos {
            break;
        }
        if c == '\n' {
            line_num += 1;
        }
    }
    line_num
}

fn line_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);

    let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
    let arg_start = arg.text_range().start();

    let file = id.as_file(MacroFileKind::Expr);
    let line_num = to_line_number(db, file, arg_start);

    let expanded = quote! {
        #line_num
    };

    Ok(expanded)
}

fn stringify_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);

    let macro_content = {
        let arg = loc.kind.arg(db).ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
        let macro_args = arg.clone();
        let text = macro_args.text();
        let without_parens = TextUnit::of_char('(')..text.len() - TextUnit::of_char(')');
        text.slice(without_parens).to_string()
    };

    let expanded = quote! {
        #macro_content
    };

    Ok(expanded)
}

fn to_col_number(db: &dyn AstDatabase, file: HirFileId, pos: TextUnit) -> usize {
    // FIXME: Use expansion info
    let file_id = file.original_file(db);
    let text = db.file_text(file_id);

    let pos = pos.to_usize();
    if pos > text.len() {
        // FIXME: `pos` at the moment could be an offset inside the "wrong" file
        // in this case we return a dummy value so that we don't `panic!`
        return 0;
    }

    let mut col_num = 1;
    for c in text[..pos].chars().rev() {
        if c == '\n' {
            break;
        }
        col_num = col_num + 1;
    }
    col_num
}

fn column_expand(
    db: &dyn AstDatabase,
    id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    let loc = db.lookup_intern_macro(id);
    let macro_call = match loc.kind {
        crate::MacroCallKind::FnLike(ast_id) => ast_id.to_node(db),
        _ => panic!("column macro called as attr"),
    };

    let _arg = macro_call.token_tree().ok_or_else(|| mbe::ExpandError::UnexpectedToken)?;
    let col_start = macro_call.syntax().text_range().start();

    let file = id.as_file(MacroFileKind::Expr);
    let col_num = to_col_number(db, file, col_start);

    let expanded = quote! {
        #col_num
    };

    Ok(expanded)
}

fn file_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    _tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    // FIXME: RA purposefully lacks knowledge of absolute file names
    // so just return "".
    let file_name = "";

    let expanded = quote! {
        #file_name
    };

    Ok(expanded)
}

fn compile_error_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    if tt.count() == 1 {
        match &tt.token_trees[0] {
            tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
                let s = it.text.as_str();
                if s.contains(r#"""#) {
                    return Ok(quote! { loop { #it }});
                }
            }
            _ => {}
        };
    }

    Err(mbe::ExpandError::BindingError("Must be a string".into()))
}

fn format_args_expand(
    _db: &dyn AstDatabase,
    _id: MacroCallId,
    tt: &tt::Subtree,
) -> Result<tt::Subtree, mbe::ExpandError> {
    // We expand `format_args!("", arg1, arg2)` to
    // `std::fmt::Arguments::new_v1(&[], &[&arg1, &arg2])`,
    // which is still not really correct, but close enough for now
    let mut args = Vec::new();
    let mut current = Vec::new();
    for tt in tt.token_trees.iter().cloned() {
        match tt {
            tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
                args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current });
                current = Vec::new();
            }
            _ => {
                current.push(tt);
            }
        }
    }
    if !current.is_empty() {
        args.push(tt::Subtree { delimiter: tt::Delimiter::None, token_trees: current });
    }
    if args.is_empty() {
        return Err(mbe::ExpandError::NoMatchingRule);
    }
    let _format_string = args.remove(0);
    let arg_tts = args.into_iter().flat_map(|arg| (quote! { & #arg , }).token_trees);
    let expanded = quote! {
        std::fmt::Arguments::new_v1(&[], &[##arg_tts])
    };
    Ok(expanded)
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::{test_db::TestDB, MacroCallKind, MacroCallLoc};
    use ra_db::{fixture::WithFixture, SourceDatabase};

    fn expand_builtin_macro(s: &str, expander: BuiltinFnLikeExpander) -> String {
        let (db, file_id) = TestDB::with_single_file(&s);
        let parsed = db.parse(file_id);
        let macro_calls: Vec<_> =
            parsed.syntax_node().descendants().filter_map(|it| ast::MacroCall::cast(it)).collect();

        let ast_id_map = db.ast_id_map(file_id.into());

        // the first one should be a macro_rules
        let def = MacroDefId {
            krate: Some(CrateId(0)),
            ast_id: Some(AstId::new(file_id.into(), ast_id_map.ast_id(&macro_calls[0]))),
            kind: MacroDefKind::BuiltIn(expander),
        };

        let loc = MacroCallLoc {
            def,
            kind: MacroCallKind::FnLike(AstId::new(
                file_id.into(),
                ast_id_map.ast_id(&macro_calls[1]),
            )),
        };

        let id = db.intern_macro(loc);
        let parsed = db.parse_or_expand(id.as_file(MacroFileKind::Expr)).unwrap();

        parsed.text().to_string()
    }

    #[test]
    fn test_column_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! column {() => {}}
        column!()
"#,
            BuiltinFnLikeExpander::Column,
        );

        assert_eq!(expanded, "9");
    }

    #[test]
    fn test_line_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! line {() => {}}
        line!()
"#,
            BuiltinFnLikeExpander::Line,
        );

        assert_eq!(expanded, "4");
    }

    #[test]
    fn test_stringify_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! stringify {() => {}}
        stringify!(a b c)
"#,
            BuiltinFnLikeExpander::Stringify,
        );

        assert_eq!(expanded, "\"a b c\"");
    }

    #[test]
    fn test_file_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! file {() => {}}
        file!()
"#,
            BuiltinFnLikeExpander::File,
        );

        assert_eq!(expanded, "\"\"");
    }

    #[test]
    fn test_compile_error_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! compile_error {
            ($msg:expr) => ({ /* compiler built-in */ });
            ($msg:expr,) => ({ /* compiler built-in */ })
        }
        compile_error!("error!");
"#,
            BuiltinFnLikeExpander::CompileError,
        );

        assert_eq!(expanded, r#"loop{"error!"}"#);
    }

    #[test]
    fn test_format_args_expand() {
        let expanded = expand_builtin_macro(
            r#"
        #[rustc_builtin_macro]
        macro_rules! format_args {
            ($fmt:expr) => ({ /* compiler built-in */ });
            ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
        }
        format_args!("{} {:?}", arg1(a, b, c), arg2);
"#,
            BuiltinFnLikeExpander::FormatArgs,
        );

        assert_eq!(expanded, r#"std::fmt::Arguments::new_v1(&[] ,&[&arg1(a,b,c),&arg2,])"#);
    }
}
<|endoftext|>


<commit_before>//! Tests for scale-generator
//!
//! Generated by [script][script] using [canonical data][canonical-data]
//!
//! [script]: https://github.com/exercism/rust/blob/main/bin/init_exercise.py
//! [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/scale-generator/canonical_data.json

use scale_generator::*;

/// Process a single test case for the property `chromatic`
///
/// All cases for the `chromatic` property are implemented
/// in terms of this function.
fn process_chromatic_case(tonic: &str, expected: &[&str]) {
    let s = Scale::chromatic(tonic).unwrap();
    assert_eq!(s.enumerate(), expected);
}

/// Process a single test case for the property `interval`
///
/// All cases for the `interval` property are implemented
/// in terms of this function.
fn process_interval_case(tonic: &str, intervals: &str, expected: &[&str]) {
    let s = Scale::new(tonic, intervals).unwrap();
    assert_eq!(s.enumerate(), expected);
}

// Chromatic scales
// These tests have no interval.
// The chromatic scale is considered the default scale

#[test]
/// Chromatic scale with sharps
fn test_chromatic_scale_with_sharps() {
    process_chromatic_case(
        "C",
        &[
            "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
        ],
    );
}

#[test]
#[ignore]
/// Chromatic scale with flats
fn test_chromatic_scale_with_flats() {
    process_chromatic_case(
        "F",
        &[
            "F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E",
        ],
    );
}

// Scales with specified intervals
// These tests all have intervals and are explorations of different
// traversals of the scale.

#[test]
#[ignore]
/// Simple major scale
///
/// The simplest major scale, with no sharps or flats.
fn test_simple_major_scale() {
    process_interval_case("C", "MMmMMMm", &["C", "D", "E", "F", "G", "A", "B"]);
}

#[test]
#[ignore]
/// Major scale with sharps
fn test_major_scale_with_sharps() {
    process_interval_case("G", "MMmMMMm", &["G", "A", "B", "C", "D", "E", "F#"]);
}

#[test]
#[ignore]
/// Major scale with flats
fn test_major_scale_with_flats() {
    process_interval_case("F", "MMmMMMm", &["F", "G", "A", "Bb", "C", "D", "E"]);
}

#[test]
#[ignore]
/// Minor scale with sharps
fn test_minor_scale_with_sharps() {
    process_interval_case("f#", "MmMMmMM", &["F#", "G#", "A", "B", "C#", "D", "E"]);
}

#[test]
#[ignore]
/// Minor scale with flats
fn test_minor_scale_with_flats() {
    process_interval_case("bb", "MmMMmMM", &["Bb", "C", "Db", "Eb", "F", "Gb", "Ab"]);
}

#[test]
#[ignore]
/// Dorian mode
fn test_dorian_mode() {
    process_interval_case("d", "MmMMMmM", &["D", "E", "F", "G", "A", "B", "C"]);
}

#[test]
#[ignore]
/// Mixolydian mode
fn test_mixolydian_mode() {
    process_interval_case("Eb", "MMmMMmM", &["Eb", "F", "G", "Ab", "Bb", "C", "Db"]);
}

#[test]
#[ignore]
/// Lydian mode
fn test_lydian_mode() {
    process_interval_case("a", "MMMmMMm", &["A", "B", "C#", "D#", "E", "F#", "G#"]);
}

#[test]
#[ignore]
/// Phrygian mode
fn test_phrygian_mode() {
    process_interval_case("e", "mMMMmMM", &["E", "F", "G", "A", "B", "C", "D"]);
}

#[test]
#[ignore]
/// Locrian mode
fn test_locrian_mode() {
    process_interval_case("g", "mMMmMMM", &["G", "Ab", "Bb", "C", "Db", "Eb", "F"]);
}

#[test]
#[ignore]
/// Harmonic minor
///
/// Note that this case introduces the accidental interval (A)
fn test_harmonic_minor() {
    process_interval_case("d", "MmMMmAm", &["D", "E", "F", "G", "A", "Bb", "Db"]);
}

#[test]
#[ignore]
/// Octatonic
fn test_octatonic() {
    process_interval_case(
        "C",
        "MmMmMmMm",
        &["C", "D", "D#", "F", "F#", "G#", "A", "B"],
    );
}

#[test]
#[ignore]
/// Hexatonic
fn test_hexatonic() {
    process_interval_case("Db", "MMMMMM", &["Db", "Eb", "F", "G", "A", "B"]);
}

#[test]
#[ignore]
/// Pentatonic
fn test_pentatonic() {
    process_interval_case("A", "MMAMA", &["A", "B", "C#", "E", "F#"]);
}

#[test]
#[ignore]
/// Enigmatic
fn test_enigmatic() {
    process_interval_case("G", "mAMMMmm", &["G", "G#", "B", "C#", "D#", "F", "F#"]);
}
<commit_msg>scale-generator: Correct name of `A` interval<commit_after>//! Tests for scale-generator
//!
//! Generated by [script][script] using [canonical data][canonical-data]
//!
//! [script]: https://github.com/exercism/rust/blob/main/bin/init_exercise.py
//! [canonical-data]: https://raw.githubusercontent.com/exercism/problem-specifications/main/exercises/scale-generator/canonical_data.json

use scale_generator::*;

/// Process a single test case for the property `chromatic`
///
/// All cases for the `chromatic` property are implemented
/// in terms of this function.
fn process_chromatic_case(tonic: &str, expected: &[&str]) {
    let s = Scale::chromatic(tonic).unwrap();
    assert_eq!(s.enumerate(), expected);
}

/// Process a single test case for the property `interval`
///
/// All cases for the `interval` property are implemented
/// in terms of this function.
fn process_interval_case(tonic: &str, intervals: &str, expected: &[&str]) {
    let s = Scale::new(tonic, intervals).unwrap();
    assert_eq!(s.enumerate(), expected);
}

// Chromatic scales
// These tests have no interval.
// The chromatic scale is considered the default scale

#[test]
/// Chromatic scale with sharps
fn test_chromatic_scale_with_sharps() {
    process_chromatic_case(
        "C",
        &[
            "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B",
        ],
    );
}

#[test]
#[ignore]
/// Chromatic scale with flats
fn test_chromatic_scale_with_flats() {
    process_chromatic_case(
        "F",
        &[
            "F", "Gb", "G", "Ab", "A", "Bb", "B", "C", "Db", "D", "Eb", "E",
        ],
    );
}

// Scales with specified intervals
// These tests all have intervals and are explorations of different
// traversals of the scale.

#[test]
#[ignore]
/// Simple major scale
///
/// The simplest major scale, with no sharps or flats.
fn test_simple_major_scale() {
    process_interval_case("C", "MMmMMMm", &["C", "D", "E", "F", "G", "A", "B"]);
}

#[test]
#[ignore]
/// Major scale with sharps
fn test_major_scale_with_sharps() {
    process_interval_case("G", "MMmMMMm", &["G", "A", "B", "C", "D", "E", "F#"]);
}

#[test]
#[ignore]
/// Major scale with flats
fn test_major_scale_with_flats() {
    process_interval_case("F", "MMmMMMm", &["F", "G", "A", "Bb", "C", "D", "E"]);
}

#[test]
#[ignore]
/// Minor scale with sharps
fn test_minor_scale_with_sharps() {
    process_interval_case("f#", "MmMMmMM", &["F#", "G#", "A", "B", "C#", "D", "E"]);
}

#[test]
#[ignore]
/// Minor scale with flats
fn test_minor_scale_with_flats() {
    process_interval_case("bb", "MmMMmMM", &["Bb", "C", "Db", "Eb", "F", "Gb", "Ab"]);
}

#[test]
#[ignore]
/// Dorian mode
fn test_dorian_mode() {
    process_interval_case("d", "MmMMMmM", &["D", "E", "F", "G", "A", "B", "C"]);
}

#[test]
#[ignore]
/// Mixolydian mode
fn test_mixolydian_mode() {
    process_interval_case("Eb", "MMmMMmM", &["Eb", "F", "G", "Ab", "Bb", "C", "Db"]);
}

#[test]
#[ignore]
/// Lydian mode
fn test_lydian_mode() {
    process_interval_case("a", "MMMmMMm", &["A", "B", "C#", "D#", "E", "F#", "G#"]);
}

#[test]
#[ignore]
/// Phrygian mode
fn test_phrygian_mode() {
    process_interval_case("e", "mMMMmMM", &["E", "F", "G", "A", "B", "C", "D"]);
}

#[test]
#[ignore]
/// Locrian mode
fn test_locrian_mode() {
    process_interval_case("g", "mMMmMMM", &["G", "Ab", "Bb", "C", "Db", "Eb", "F"]);
}

#[test]
#[ignore]
/// Harmonic minor
///
/// Note that this case introduces the augmented second interval (A)
fn test_harmonic_minor() {
    process_interval_case("d", "MmMMmAm", &["D", "E", "F", "G", "A", "Bb", "Db"]);
}

#[test]
#[ignore]
/// Octatonic
fn test_octatonic() {
    process_interval_case(
        "C",
        "MmMmMmMm",
        &["C", "D", "D#", "F", "F#", "G#", "A", "B"],
    );
}

#[test]
#[ignore]
/// Hexatonic
fn test_hexatonic() {
    process_interval_case("Db", "MMMMMM", &["Db", "Eb", "F", "G", "A", "B"]);
}

#[test]
#[ignore]
/// Pentatonic
fn test_pentatonic() {
    process_interval_case("A", "MMAMA", &["A", "B", "C#", "E", "F#"]);
}

#[test]
#[ignore]
/// Enigmatic
fn test_enigmatic() {
    process_interval_case("G", "mAMMMmm", &["G", "G#", "B", "C#", "D#", "F", "F#"]);
}
<|endoftext|>


<commit_before>use std::env;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib: Vec<u8> = args[1].bytes().collect();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    attack_cipher(ciphertext, crib);
}

fn attack_cipher(ciphertext: Vec<u8>, crib: Vec<u8>) {
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i+j) < ciphertext.len() {
            let key = ciphertext[i+j] ^ crib[j];
            if key != old_key { break; }
            old_key = key;
            j += 1;
            if j == crib.len() {
                println!("Found text at 0x{:x} (XOR key 0x{:02x})", i, key);
                print!("  preview: ");
                let preview: String = ciphertext[i..i+50]
                    .iter()
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 {x as char} else {'.'})
                    .collect();
                println!("{}", preview);
            }
        }
    }
}
<commit_msg>Return first match using Option type<commit_after>use std::env;
use std::error::Error;
use std::fs::File;
use std::io::prelude::*;
use std::io::stderr;
use std::path::Path;
use std::process;

struct Match {
    offset: usize,
    key: u8,
    preview: String,
}

fn main() {
    let args: Vec<String> = env::args().skip(1).collect();
    if args.len() < 2 {
        writeln!(stderr(), "usage: roxor <file> <crib>").unwrap();
        process::exit(1);
    }

    let path = Path::new(&args[0]);
    let crib: Vec<u8> = args[1].bytes().collect();

    if crib.len() < 2 {
        writeln!(stderr(), "error: crib too short").unwrap();
        process::exit(1);
    }

    let mut file = match File::open(&path) {
        Err(why) => panic!("couldn't open {}: {}", path.display(), Error::description(&why)),
        Ok(file) => file,
    };

    let mut ciphertext: Vec<u8> = Vec::new();
    file.read_to_end(&mut ciphertext).unwrap();

    if ciphertext.len() < crib.len() {
        writeln!(stderr(), "error: ciphertext too short").unwrap();
        process::exit(1);
    }

    match attack_cipher(ciphertext, crib) {
        Some(m) => {
            println!("Found text at 0x{:x} (XOR key 0x{:02x})", m.offset, m.key);
            println!("  preview: {}", m.preview);
        },
        None => {},
    };
}

fn attack_cipher(ciphertext: Vec<u8>, crib: Vec<u8>) -> Option<Match> {
    for (i, x) in ciphertext.iter().enumerate() {
        let mut old_key = x ^ crib[0];
        let mut j = 1;
        while j < crib.len() && (i+j) < ciphertext.len() {
            let key = ciphertext[i+j] ^ crib[j];
            if key != old_key { break; }
            old_key = key;
            j += 1;
            if j == crib.len() {
                let preview: String = ciphertext[i..i+50]
                    .iter()
                    .map(|x| x ^ key)
                    .map(|x| if x >= 32 && x <= 126 {x as char} else {'.'})
                    .collect();
                return Some(Match { offset: i, key: key, preview: preview })
            }
        }
    }
    return None
}
<|endoftext|>


<commit_before>extern crate bgfx;
extern crate glfw;
extern crate libc;

use std::fs::File;
use std::io::Read;
use std::ptr;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc;

use glfw::{Context, Glfw, Window, WindowEvent};

/// Events received by the main thread, sent by the render thread.
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum Event {
    /// Window close event.
    Close,

    /// Window size event.
    Size(u16, u16),
}

/// Example application.
pub struct Example {
    /// Receiver for events from the render thread.
    event_rx: Receiver<Event>,
}

impl Example {

    /// Handles events received from the render thread. If there are no events to process, returns
    /// instantly.
    ///
    /// Returns `true` if the app should exit.
    pub fn handle_events(&self, bgfx: &bgfx::MainContext, width: &mut u16, height: &mut u16, reset: bgfx::ResetFlags) -> bool {
        let mut close = false;

        loop {
            let result = self.event_rx.try_recv();

            if !result.is_ok() {
                break;
            }

            match result.ok().unwrap() {
                Event::Close => close = true,
                Event::Size(w, h) => {
                    *width = w;
                    *height = h;
                    bgfx.reset(w, h, reset);
                },
            }
        }

        close
    }

}

/// Example data used by the render thread.
struct ExampleData {
    /// The 'Glfw' object.
    glfw: Glfw,

    /// Receiver of window events from GLFW.
    events: Receiver<(f64, WindowEvent)>,

    /// The GLFW window object.
    window: Window,

    /// Sender of events to the main thread.
    event_tx: Sender<Event>,
}

impl ExampleData {

    /// Process GLFW events, and potentially forward them to the main thread.
    ///
    /// Returns `true` if the example should exit.
    fn process_events(&mut self) -> bool {
        self.glfw.poll_events();

        for (_, event) in glfw::flush_messages(&self.events) {
            match event {
                WindowEvent::Close => {
                    self.event_tx.send(Event::Close).unwrap();
                }
                WindowEvent::Size(w, h) => {
                    self.event_tx.send(Event::Size(w as u16, h as u16)).unwrap();
                }
                ref e => {
                    panic!(format!("Unhandled event: {:?}", e))
                }
            }
        }

        self.window.should_close()
    }

}

/// Loads the contents of a file and returns it.
pub fn load_file(name: &str) -> Vec<u8> {
    let mut data = Vec::new();
    let mut file = File::open(name).unwrap();
    file.read_to_end(&mut data).unwrap();
    data
}

/// Loads the two given shaders from disk, and creates a program using the new
/// shaders.
///
/// # Arguments
///
/// * ``
pub fn load_program<'a, 'b>(bgfx: &'a bgfx::MainContext,
                            vsh_name: &'b str,
                            fsh_name: &'b str)
                            -> bgfx::Program<'a> {
    let renderer = bgfx.get_renderer_type();
    let vsh_path = format!("{:?}/{}.bin", renderer, vsh_name);
    let fsh_path = format!("{:?}/{}.bin", renderer, fsh_name);
    let vsh_mem = bgfx::Memory::copy(&load_file(&vsh_path));
    let fsh_mem = bgfx::Memory::copy(&load_file(&fsh_path));
    let vsh = bgfx::Shader::new(bgfx, vsh_mem);
    let fsh = bgfx::Shader::new(bgfx, fsh_mem);

    bgfx::Program::new(vsh, fsh)
}

/// Returns a new `bgfx::Application`.
///
/// # Arguments
///
/// * `glfw` - Reference to the `Glfw` object.
/// * `window` - Reference to the GLFW window object.
#[cfg(target_os = "linux")]
fn create_bgfx_app(glfw: &Glfw, window: &Window) -> bgfx::Application {
    bgfx::create(glfw.get_x11_display(), window.get_x11_window(), window.get_glx_context())
}

#[cfg(target_os = "windows")]
fn create_bgfx_app(_: &Glfw, window: &Window) -> bgfx::Application {
    bgfx::create(ptr::null_mut(), window.get_win32_window(), ptr::null_mut())
}

/// Determines the renderer to use based on platform.
///
/// The sole reason for using this instead of using `bgfx::RendererType::Default` is cause
/// `Direct3D12` - which is the default under Windows 10 - currently (2015-10-08) crashes when
/// compiled with MinGW. This is true with the examples in the C++ version of bgfx as well, and
/// not exlusive to Rust.
pub fn get_renderer_type() -> Option<bgfx::RendererType> {
    if cfg!(windows) && cfg!(target_env = "gnu") {
        Some(bgfx::RendererType::Direct3D11)
    } else {
        None
    }
}

/// Runs an example.
///
/// # Arguments
///
/// * `width` - Initial width of the window, in pixels.
/// * `height` - Initial height of the window, in pixels.
/// * `main` - Function to act as the entry point for the example.
pub fn run_example<M>(width: u32, height: u32, main: M)
    where M: Send + 'static + FnOnce(&mut bgfx::MainContext, &Example)
{
    // Initialize GLFW and create the window.
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    let (mut window, events) = glfw.create_window(width,
                                                  height,
                                                  "BGFX",
                                                  glfw::WindowMode::Windowed)
                                   .expect("Failed to create GLFW window.");

    window.set_close_polling(true);
    window.set_size_polling(true);
    window.make_current();

    // Create the channel used for communication between the main and render threads.
    let (event_tx, event_rx) = mpsc::channel::<Event>();

    // Initialize the example.
    let mut data = ExampleData {
        glfw: glfw,
        events: events,
        window: window,
        event_tx: event_tx,
    };

    let bgfx_app = create_bgfx_app(&data.glfw, &data.window);

    // Main thread implementation.
    let main_thread = move |bgfx: &mut bgfx::MainContext| {
        let example = Example { event_rx: event_rx };
        main(bgfx, &example);
    };

    // Render thread implementation.
    let render_thread = |bgfx: &bgfx::RenderContext| {
        while !data.process_events() {
            bgfx.render_frame();
        }
    };

    // Run the application
    bgfx_app.run(main_thread, render_thread);
}
<commit_msg>Formatting<commit_after>extern crate bgfx;
extern crate glfw;
extern crate libc;

use std::fs::File;
use std::io::Read;
use std::ptr;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::mpsc;

use glfw::{Context, Glfw, Window, WindowEvent};

/// Events received by the main thread, sent by the render thread.
#[derive(PartialEq, Eq, Hash, Debug)]
pub enum Event {
    /// Window close event.
    Close,

    /// Window size event.
    Size(u16, u16),
}

/// Example application.
pub struct Example {
    /// Receiver for events from the render thread.
    event_rx: Receiver<Event>,
}

impl Example {

    /// Handles events received from the render thread. If there are no events to process, returns
    /// instantly.
    ///
    /// Returns `true` if the app should exit.
    pub fn handle_events(&self,
                         bgfx: &bgfx::MainContext,
                         width: &mut u16,
                         height: &mut u16,
                         reset: bgfx::ResetFlags)
                         -> bool {
        let mut close = false;

        loop {
            let result = self.event_rx.try_recv();

            if !result.is_ok() {
                break;
            }

            match result.ok().unwrap() {
                Event::Close => close = true,
                Event::Size(w, h) => {
                    *width = w;
                    *height = h;
                    bgfx.reset(w, h, reset);
                }
            }
        }

        close
    }

}

/// Example data used by the render thread.
struct ExampleData {
    /// The 'Glfw' object.
    glfw: Glfw,

    /// Receiver of window events from GLFW.
    events: Receiver<(f64, WindowEvent)>,

    /// The GLFW window object.
    window: Window,

    /// Sender of events to the main thread.
    event_tx: Sender<Event>,
}

impl ExampleData {

    /// Process GLFW events, and potentially forward them to the main thread.
    ///
    /// Returns `true` if the example should exit.
    fn process_events(&mut self) -> bool {
        self.glfw.poll_events();

        for (_, event) in glfw::flush_messages(&self.events) {
            match event {
                WindowEvent::Close => {
                    self.event_tx.send(Event::Close).unwrap();
                }
                WindowEvent::Size(w, h) => {
                    self.event_tx.send(Event::Size(w as u16, h as u16)).unwrap();
                }
                ref e => {
                    panic!(format!("Unhandled event: {:?}", e))
                }
            }
        }

        self.window.should_close()
    }

}

/// Loads the contents of a file and returns it.
pub fn load_file(name: &str) -> Vec<u8> {
    let mut data = Vec::new();
    let mut file = File::open(name).unwrap();
    file.read_to_end(&mut data).unwrap();
    data
}

/// Loads the two given shaders from disk, and creates a program using the new
/// shaders.
///
/// # Arguments
///
/// * ``
pub fn load_program<'a, 'b>(bgfx: &'a bgfx::MainContext,
                            vsh_name: &'b str,
                            fsh_name: &'b str)
                            -> bgfx::Program<'a> {
    let renderer = bgfx.get_renderer_type();
    let vsh_path = format!("{:?}/{}.bin", renderer, vsh_name);
    let fsh_path = format!("{:?}/{}.bin", renderer, fsh_name);
    let vsh_mem = bgfx::Memory::copy(&load_file(&vsh_path));
    let fsh_mem = bgfx::Memory::copy(&load_file(&fsh_path));
    let vsh = bgfx::Shader::new(bgfx, vsh_mem);
    let fsh = bgfx::Shader::new(bgfx, fsh_mem);

    bgfx::Program::new(vsh, fsh)
}

/// Returns a new `bgfx::Application`.
///
/// # Arguments
///
/// * `glfw` - Reference to the `Glfw` object.
/// * `window` - Reference to the GLFW window object.
#[cfg(target_os = "linux")]
fn create_bgfx_app(glfw: &Glfw, window: &Window) -> bgfx::Application {
    bgfx::create(glfw.get_x11_display(), window.get_x11_window(), window.get_glx_context())
}

#[cfg(target_os = "windows")]
fn create_bgfx_app(_: &Glfw, window: &Window) -> bgfx::Application {
    bgfx::create(ptr::null_mut(), window.get_win32_window(), ptr::null_mut())
}

/// Determines the renderer to use based on platform.
///
/// The sole reason for using this instead of using `bgfx::RendererType::Default` is cause
/// `Direct3D12` - which is the default under Windows 10 - currently (2015-10-08) crashes when
/// compiled with MinGW. This is true with the examples in the C++ version of bgfx as well, and
/// not exlusive to Rust.
pub fn get_renderer_type() -> Option<bgfx::RendererType> {
    if cfg!(windows) && cfg!(target_env = "gnu") {
        Some(bgfx::RendererType::Direct3D11)
    } else {
        None
    }
}

/// Runs an example.
///
/// # Arguments
///
/// * `width` - Initial width of the window, in pixels.
/// * `height` - Initial height of the window, in pixels.
/// * `main` - Function to act as the entry point for the example.
pub fn run_example<M>(width: u32, height: u32, main: M)
    where M: Send + 'static + FnOnce(&mut bgfx::MainContext, &Example)
{
    // Initialize GLFW and create the window.
    let glfw = glfw::init(glfw::FAIL_ON_ERRORS).unwrap();

    let (mut window, events) = glfw.create_window(width,
                                                  height,
                                                  "BGFX",
                                                  glfw::WindowMode::Windowed)
                                   .expect("Failed to create GLFW window.");

    window.set_close_polling(true);
    window.set_size_polling(true);
    window.make_current();

    // Create the channel used for communication between the main and render threads.
    let (event_tx, event_rx) = mpsc::channel::<Event>();

    // Initialize the example.
    let mut data = ExampleData {
        glfw: glfw,
        events: events,
        window: window,
        event_tx: event_tx,
    };

    let bgfx_app = create_bgfx_app(&data.glfw, &data.window);

    // Main thread implementation.
    let main_thread = move |bgfx: &mut bgfx::MainContext| {
        let example = Example { event_rx: event_rx };
        main(bgfx, &example);
    };

    // Render thread implementation.
    let render_thread = |bgfx: &bgfx::RenderContext| {
        while !data.process_events() {
            bgfx.render_frame();
        }
    };

    // Run the application
    bgfx_app.run(main_thread, render_thread);
}
<|endoftext|>


<commit_before>extern crate rl_sys;
extern crate nix;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::rc::Rc;
use rl_sys::readline;

use nix::libc;

mod directive;
use InputType::*;

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Continue(String),
  Quit
}

pub struct Repl {
  pub sout: Rc<RefCell<io::Write>>, // stream out,
  pub serr: Rc<RefCell<io::Write>>, // stream err,
}

impl Repl {
  pub fn new(sout: Rc<RefCell<io::Write>>, serr: Rc<RefCell<io::Write>>) -> Repl {
    Repl {
      sout: sout,
      serr: serr,
    }
  }

  pub fn write_out(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_out(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn write_err(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_err(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn run(&self) {
    self.write_out(b"Welcome to Flang version 0.1. (Type :help for assistance.)\n");
    self.flush_out();

    let mut state = ReplState::new();

    let mut prompt: String = "\x1b[33mflang> \x1b[0m".to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done => {}
            ReplAction::Continue(p) => prompt = p,
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.write_err(format!("ERROR: {}", msg).as_bytes());
        }
      }
    }
  }

  fn handle_line(&self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      Directive(d, args) => self.exec_directive(d, args),
      ExecuteSource(line) => self.exec_line(line),
      ExternalComamnd(command) => self.exec_external_program(command),
      Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => Directive(&first[1..], tokens.collect::<Vec<_>>()),
        x if x.starts_with('!') => ExternalComamnd(&line[1..]),
        _ => ExecuteSource(line)
      },
      None => Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_line(&self, line: &str) -> ReplAction {
    ReplAction::Done
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

pub struct ReplState;

impl ReplState {
  pub fn new() -> ReplState {
    ReplState
  }
}

pub struct Input;

pub struct Output;

pub struct CompilerInstance;

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
  }
}

fn exec_line(src_file: &mut SourceFile, line: &str) {
  // append source
  // generate mir
  // check ast error
  // add source which is valid
  // skip if the source doesn't need to run
  // generate llvm ir and module
  // reorganize llvm module
}

<commit_msg>Use another prompt when statement is still incomplete<commit_after>extern crate rl_sys;
extern crate nix;

use std::cell::RefCell;
use std::collections::HashMap;
use std::ffi::CString;
use std::io;
use std::rc::Rc;
use rl_sys::readline;

use nix::libc;

mod directive;
use InputType::*;

static DEFAULT_PROMPT: &'static str = "\x1b[33mflang> \x1b[0m";

pub enum InputType<'a> {
  Directive(&'a str, Vec<&'a str>),
  ExternalComamnd(&'a str),
  ExecuteSource(&'a str),
  Empty,
}

pub enum ReplAction {
  Done,
  Continue(String),
  Quit
}

pub struct Repl {
  source: String,
  pub sout: Rc<RefCell<io::Write>>, // stream out,
  pub serr: Rc<RefCell<io::Write>>, // stream err,
}

impl Repl {
  pub fn new(sout: Rc<RefCell<io::Write>>, serr: Rc<RefCell<io::Write>>) -> Repl {
    Repl {
      source: String::new(),
      sout: sout,
      serr: serr,
    }
  }

  pub fn write_out(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_out(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn write_err(&self, msg: &[u8]) {
    self.sout.borrow_mut().write(msg).ok();
  }

  pub fn flush_err(&self) {
    self.sout.borrow_mut().flush().ok();
  }

  pub fn run(&mut self) {
    self.write_out(b"Welcome to Flang version 0.1. (Type :help for assistance.)\n");
    self.flush_out();

    let mut state = ReplState::new();

    let mut prompt: String = DEFAULT_PROMPT.to_string();

    loop {
      match readline::readline(&prompt) {
        Ok(Some(line)) => {
          match self.handle_line(&line) {
            ReplAction::Done => { prompt = DEFAULT_PROMPT.to_string(); }
            ReplAction::Continue(p) => prompt = p,
            ReplAction::Quit => break
          }
        }
        Ok(None) => break, // eof
        Err(msg) => {
          self.write_err(format!("ERROR: {}", msg).as_bytes());
        }
      }
    }
  }

  fn handle_line(&mut self, line: &str) -> ReplAction {
    match self.parse_input(line) {
      Directive(d, args) => self.exec_directive(d, args),
      ExecuteSource(line) => self.exec_line(line),
      ExternalComamnd(command) => self.exec_external_program(command),
      Empty => ReplAction::Done // just enter without any type
    }
  }

  fn parse_input<'a>(&self, line: &'a str) -> InputType<'a> {
    let mut tokens = line.split_whitespace();
    let first_token = tokens.next();

    match first_token {
      Some(first) => match first {
        x if x.starts_with(':') => Directive(&first[1..], tokens.collect::<Vec<_>>()),
        x if x.starts_with('!') => ExternalComamnd(&line[1..]),
        _ => ExecuteSource(line)
      },
      None => Empty
    }
  }

  pub fn exec_directive(&self, directive: &str, args: Vec<&str>) -> ReplAction {
    match directive {
      "dump" => {
        self.write_out(self.source.as_bytes());
        ReplAction::Done
      }
      "quit" => ReplAction::Quit,
      _ => ReplAction::Done
    }
  }

  pub fn exec_line(&mut self, line: &str) -> ReplAction {
    self.source.push_str(line);
    ReplAction::Done
  }

  pub fn exec_external_program(&self, command: &str) -> ReplAction {
    let c_to_print = CString::new(command).unwrap();
    unsafe { libc::system(c_to_print.as_ptr()); }
    ReplAction::Done
  }
}

pub struct ReplState;

impl ReplState {
  pub fn new() -> ReplState {
    ReplState
  }
}

pub struct Input;

pub struct Output;

pub struct CompilerInstance;

pub struct SourceFile {
  source: String
}

impl SourceFile {
  pub fn add_line(&mut self, line: &str) {
    self.source.push_str(line);
  }
}

fn exec_line(src_file: &mut SourceFile, line: &str) {
  // append source
  // generate mir
  // check ast error
  // add source which is valid
  // skip if the source doesn't need to run
  // generate llvm ir and module
  // reorganize llvm module
}

<|endoftext|>


<commit_before>// Copyright 2014-2015 Johannes Köster, Vadim Nazarov.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Various alignment algorithms.

pub mod pairwise;
pub mod distance;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, the length
/// of sequence x and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a String.
    ///
    /// # Example
    ///
    /// ```
    /// use bio::alignment::pairwise::*;
    ///
    /// let x = b"CCGTCCGGCAA";
    /// let y = b"AAAAACCGTTGACGCAA";
    /// let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
    ///
    /// // ------CCGTCCGGCAA
    /// //       |  |   ||||
    /// // AAAAACCGTTGACGCAA
    /// let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
    /// let alignment = aligner.semiglobal(x, y);
    /// println!("Semiglobal: \n{}\n", alignment.pretty(x, y));
    ///
    /// // -----CCGTCCGGCAA-
    /// //      ||||
    /// // AAAAACCGTTGACGCAA
    /// // You can also call easy-to-use functions for perform alignment...
    /// println!("Local: \n{}\n", align_local(x, y, -5, -1, &score).pretty(x, y));
    ///
    /// // ------CCGTCCGGCAA
    /// //       |  |   ||||
    /// // AAAAACCGTTGACGCAA
    /// // ... or to perform alignment and pretty formatting sequentially.
    /// println!("Global: \n{}\n", pretty_global(x, y, -5, -1, &score));
    /// ```
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_i : usize = self.xstart;
            let mut y_i : usize = self.ystart;

            // Add '-' before aligned subsequences and un-aligned 5' substrings of sequences.
            if x_i > y_i {
                let diff = x_i - y_i;
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                for _ in 0..diff {
                    y_pretty.push('-');
                }
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..y_i])));
            } else if x_i < y_i {
                let diff = y_i - x_i;
                for _ in 0..diff {
                    x_pretty.push('-');
                }
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            } else {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            }
            for _ in 0..x_pretty.len() {
                inb_pretty.push(' ');
            }

            // Process the alignment.
            for i in 0..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push_str("|");

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Subst => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Del => {
                        x_pretty.push('-');

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Ins => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push('-');
                    },
                }
            }

            // Add un-aligned 3' substrings of sequences.
            for i in x_i..x.len() {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
            }
            for i in y_i..y.len() {
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
            }

            // Add trailing '-'.
            if x_pretty.len() > y_pretty.len() {
                for _ in y_pretty.len()..x_pretty.len() {
                    y_pretty.push('-');
                }
            } else {
                for _ in x_pretty.len()..y_pretty.len() {
                    x_pretty.push('-');
                }
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

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

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }
}
<commit_msg>update the description a bit<commit_after>// Copyright 2014-2015 Johannes Köster, Vadim Nazarov.
// Licensed under the MIT license (http://opensource.org/licenses/MIT)
// This file may not be copied, modified, or distributed
// except according to those terms.

//! Various alignment and distance computing algorithms.

pub mod pairwise;
pub mod distance;

/// Alignment operations (Match, Subst, Del and Ins).
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum AlignmentOperation {
    Match,
    Subst,
    Del,
    Ins
}

/// An alignment, consisting of a score, a start in sequence y, a start in sequence x, the length
/// of sequence x and its edit operations (see alignment::pairwise for meaning of x and y).
#[derive(Debug)]
pub struct Alignment {
    pub score: i32,
    pub ystart: usize,
    pub xstart: usize,
    pub xlen: usize,
    pub operations: Vec<AlignmentOperation>
}

impl Alignment {
    /// Calculate the cigar string.
    pub fn cigar(&self, hard_clip: bool) -> String {
        let add_op = |op, k, cigar: &mut String| {
            cigar.push_str(&format!("{}{}", k, match op {
                AlignmentOperation::Match => "=",
                AlignmentOperation::Subst => "X",
                AlignmentOperation::Del => "D",
                AlignmentOperation::Ins => "I",
            }));
        };

        let op_len = |op: AlignmentOperation| {
            (op == AlignmentOperation::Match || op == AlignmentOperation::Subst || op == AlignmentOperation::Ins) as usize
        };

        let clip_str = if hard_clip {"H"} else {"S"};

        let mut cigar = String::new();

        if !self.operations.is_empty() {
            if self.xstart > 0 {
                cigar.push_str(&format!("{}{}", self.xstart, clip_str));
            }

            let mut last = self.operations[0];
            let mut k = 1;
            let mut alen = op_len(last);
            for &op in self.operations[1..].iter() {
                if op == last {
                    k += 1;
                } else {
                    add_op(last, k, &mut cigar);
                    k = 1;
                }
                last = op;
                alen += op_len(op);
            }
            add_op(last, k, &mut cigar);

            let clip = self.xlen - alen;
            if clip > 0 {
                cigar.push_str(&format!("{}{}", clip, clip_str));
            }
        }
        else {
            cigar.push_str(&format!("{}{}", self.xlen, clip_str));
        }

        cigar
    }

    /// Return the pretty formatted alignment as a String.
    ///
    /// # Example
    ///
    /// ```
    /// use bio::alignment::pairwise::*;
    ///
    /// let x = b"CCGTCCGGCAA";
    /// let y = b"AAAAACCGTTGACGCAA";
    /// let score = |a: u8, b: u8| if a == b {1i32} else {-1i32};
    ///
    /// // ------CCGTCCGGCAA
    /// //       |  |   ||||
    /// // AAAAACCGTTGACGCAA
    /// let mut aligner = Aligner::with_capacity(x.len(), y.len(), -5, -1, &score);
    /// let alignment = aligner.semiglobal(x, y);
    /// println!("Semiglobal: \n{}\n", alignment.pretty(x, y));
    ///
    /// // -----CCGTCCGGCAA-
    /// //      ||||
    /// // AAAAACCGTTGACGCAA
    /// // You can also call easy-to-use functions for perform alignment...
    /// println!("Local: \n{}\n", align_local(x, y, -5, -1, &score).pretty(x, y));
    ///
    /// // ------CCGTCCGGCAA
    /// //       |  |   ||||
    /// // AAAAACCGTTGACGCAA
    /// // ... or to perform alignment and pretty formatting sequentially.
    /// println!("Global: \n{}\n", pretty_global(x, y, -5, -1, &score));
    /// ```
    pub fn pretty(&self, x: &[u8], y: &[u8]) -> String {
        let mut x_pretty = String::new();
        let mut y_pretty = String::new();
        let mut inb_pretty = String::new();

        if !self.operations.is_empty() {
            let mut x_i : usize = self.xstart;
            let mut y_i : usize = self.ystart;

            // Add '-' before aligned subsequences and un-aligned 5' substrings of sequences.
            if x_i > y_i {
                let diff = x_i - y_i;
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                for _ in 0..diff {
                    y_pretty.push('-');
                }
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..y_i])));
            } else if x_i < y_i {
                let diff = y_i - x_i;
                for _ in 0..diff {
                    x_pretty.push('-');
                }
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            } else {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&x[0..x_i])));
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&y[0..y_i])));
            }
            for _ in 0..x_pretty.len() {
                inb_pretty.push(' ');
            }

            // Process the alignment.
            for i in 0..self.operations.len() {
                match self.operations[i] {
                    AlignmentOperation::Match => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push_str("|");

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Subst => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Del => {
                        x_pretty.push('-');

                        inb_pretty.push(' ');

                        y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[y_i]])));
                        y_i += 1;
                    },
                    AlignmentOperation::Ins => {
                        x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[x_i]])));
                        x_i += 1;

                        inb_pretty.push(' ');

                        y_pretty.push('-');
                    },
                }
            }

            // Add un-aligned 3' substrings of sequences.
            for i in x_i..x.len() {
                x_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[x[i]])));
            }
            for i in y_i..y.len() {
                y_pretty.push_str(&format!("{}", String::from_utf8_lossy(&[y[i]])));
            }

            // Add trailing '-'.
            if x_pretty.len() > y_pretty.len() {
                for _ in y_pretty.len()..x_pretty.len() {
                    y_pretty.push('-');
                }
            } else {
                for _ in x_pretty.len()..y_pretty.len() {
                    x_pretty.push('-');
                }
            }
        }

        format!("{}\n{}\n{}", x_pretty, inb_pretty, y_pretty)
    }
}

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

    #[test]
    fn test_cigar() {
        let alignment = Alignment { score: 5, xstart: 3, ystart: 0, xlen: 10, operations: vec![Match, Match, Match, Subst, Ins, Ins, Del, Del] };
        assert_eq!(alignment.cigar(false), "3S3=1X2I2D4S");
    }
}
<|endoftext|>