<commit_before>macro_rules! impl_both_mutability {
    ($module:ident, $slice:ty,
                    $cursor:ty,
                    $cursor_elem:ty,
                    $cursors_to_haystack:expr,
                    $haystack_to_cursors:expr) => {
        pub mod $module {
            use core_traits::*;

            #[derive(Copy, Clone)]
            struct Iter<'a> {
                haystack: ($cursor, $cursor),
                start: $cursor,
                end: $cursor,
                _marker: ::std::marker::PhantomData<$slice>
            }

            impl<'a> Iter<'a> {
                #[inline]
                fn new(haystack: $slice) -> Self {
                    let (start, end) = $haystack_to_cursors(haystack);
                    Iter {
                        haystack: (start, end),
                        start: start,
                        end: end,
                        _marker: ::std::marker::PhantomData,
                    }
                }

                #[inline]
                fn next(&mut self) -> Option<$cursor_elem> {
                    while self.start != self.end {
                        unsafe {
                            let b = *self.start;
                            self.start = self.start.offset(1);
                            return Some(b);
                        }
                    }
                    None
                }

                #[inline]
                fn next_back(&mut self) -> Option<$cursor_elem> {
                    while self.start != self.end {
                        unsafe {
                            self.end = self.end.offset(-1);
                            let b = *self.end;
                            return Some(b);
                        }
                    }
                    None
                }
            }

            impl<'a> SearchCursors for $slice {
                type Haystack = ($cursor, $cursor);
                type Cursor = $cursor;

                fn into_haystack(self) -> Self::Haystack {
                    $haystack_to_cursors(self)
                }

                fn offset_from_front(haystack: Self::Haystack,
                                     begin: Self::Cursor) -> usize {
                    begin as usize - haystack.0 as usize
                }

                unsafe fn range_to_self(_: Self::Haystack,
                                        start: Self::Cursor,
                                        end: Self::Cursor) -> Self {
                    ($cursors_to_haystack)(start, end)
                }
                fn cursor_at_front(hs: Self::Haystack) -> Self::Cursor {
                    hs.0
                }
                fn cursor_at_back(hs: Self::Haystack) -> Self::Cursor {
                    hs.1
                }
            }

            //////////////////////////////////////////////////////////////////
            // Impl for a CharEq wrapper
            //////////////////////////////////////////////////////////////////

            use utf8::{self, CharEq, CharEqPattern};

            #[derive(Clone)]
            pub struct CharEqSearcher<'a, C: CharEq> {
                char_eq: C,
                iter: Iter<'a>,
                ascii_only: bool,
            }

            impl<'a, C: CharEq> Pattern<$slice> for CharEqPattern<C> {
                type Searcher = CharEqSearcher<'a, C>;

                #[inline]
                fn into_searcher(self, haystack: $slice) -> CharEqSearcher<'a, C> {
                    CharEqSearcher {
                        ascii_only: self.0.only_ascii(),
                        char_eq: self.0,
                        iter: Iter::new(haystack),
                    }
                }

                fn is_prefix_of(mut self, haystack: $slice) -> bool {
                    haystack.chars()
                            .next()
                            .map(|c| self.0.matches(c))
                            .unwrap_or(false)
                }

                fn is_suffix_of(mut self, haystack: $slice) -> bool
                    where Self::Searcher: ReverseSearcher<$slice>
                {
                    haystack.chars()
                            .next_back()
                            .map(|c| self.0.matches(c))
                            .unwrap_or(false)
                }
            }

            unsafe impl<'a, C: CharEq> Searcher<$slice> for CharEqSearcher<'a, C> {
                #[inline]
                fn haystack(&self) -> ($cursor, $cursor) {
                    self.iter.haystack
                }

                #[inline]
                fn next_match(&mut self) -> Option<($cursor, $cursor)> {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next() {
                            if self.char_eq.matches(b as char) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-1),
                                     self.iter.start)
                                })
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point(|| self.iter.next()) {
                            if self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-(c.len_utf8() as isize)),
                                     self.iter.start)
                                })
                            }
                        }
                    }
                    None
                }

                #[inline]
                fn next_reject(&mut self) -> Option<($cursor, $cursor)> {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next() {
                            if !self.char_eq.matches(b as char) {
                                unsafe {
                                    let reject_start = self.iter.start.offset(-1);
                                    while !utf8::byte_is_char_boundary(
                                            *self.iter.start) {
                                        self.iter.start = self.iter.start.offset(1);
                                    }
                                    return Some((reject_start, self.iter.start))
                                }
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point(|| self.iter.next()) {
                            if !self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-(c.len_utf8() as isize)),
                                     self.iter.start)
                                })
                            }
                        }
                    }
                    None
                }
            }

            unsafe impl<'a, C: CharEq> ReverseSearcher<$slice> for CharEqSearcher<'a, C> {
                #[inline]
                fn next_match_back(&mut self) -> Option<($cursor, $cursor)>  {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next_back() {
                            if self.char_eq.matches(b as char) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(1))
                                })
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point_reverse(|| self.iter.next_back()) {
                            if self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(c.len_utf8() as isize))
                                })
                            }
                        }
                    }
                    None
                }

                #[inline]
                fn next_reject_back(&mut self) -> Option<($cursor, $cursor)>  {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next_back() {
                            if !self.char_eq.matches(b as char) {
                                unsafe {
                                    let reject_end = self.iter.end.offset(1);
                                    while !utf8::byte_is_char_boundary(
                                            *self.iter.end) {
                                        self.iter.end = self.iter.end.offset(-1);
                                    }
                                    return Some((self.iter.end, reject_end))
                                }
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point_reverse(|| self.iter.next_back()) {
                            if !self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(c.len_utf8() as isize))
                                })
                            }
                        }
                    }
                    None
                }
            }

            impl<'a, C: CharEq> DoubleEndedSearcher<$slice> for CharEqSearcher<'a, C> {}

            /////////////////////////////////////////////////////////////////////////////
            // Impl for char
            /////////////////////////////////////////////////////////////////////////////

            /// Associated type for `<char as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharSearcher<'a>(CharEqSearcher<'a, char>);

            unsafe impl<'a> Searcher<$slice> for CharSearcher<'a> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a> ReverseSearcher<$slice> for CharSearcher<'a> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a> DoubleEndedSearcher<$slice> for CharSearcher<'a> {}

            /// Searches for chars that are equal to a given char
            impl<'a> Pattern<$slice> for char {
                pattern_methods!(CharSearcher<'a>, CharEqPattern, CharSearcher, $slice);
            }

            /////////////////////////////////////////////////////////////////////////////
            // Impl for &[char]
            /////////////////////////////////////////////////////////////////////////////

            // Todo: Change / Remove due to ambiguity in meaning.

            /// Associated type for `<&[char] as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharSliceSearcher<'a, 'b>(CharEqSearcher<'a, &'b [char]>);

            unsafe impl<'a, 'b> Searcher<$slice> for CharSliceSearcher<'a, 'b> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, 'b> ReverseSearcher<$slice> for CharSliceSearcher<'a, 'b> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a, 'b> DoubleEndedSearcher<$slice> for CharSliceSearcher<'a, 'b> {}

            /// Searches for chars that are equal to any of the chars in the array
            impl<'a, 'b> Pattern<$slice> for &'b [char] {
                pattern_methods!(CharSliceSearcher<'a, 'b>, CharEqPattern, CharSliceSearcher, $slice);
            }

            /////////////////////////////////////////////////////////////////////////////
            // Impl for F: FnMut(char) -> bool
            /////////////////////////////////////////////////////////////////////////////

            /// Associated type for `<F as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharPredicateSearcher<'a, F>(CharEqSearcher<'a, F>)
                where F: FnMut(char) -> bool;

            unsafe impl<'a, F> Searcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool
            {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, F> ReverseSearcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool
            {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a, F> DoubleEndedSearcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool {}

            /// Searches for chars that match the given predicate
            impl<'a, F> Pattern<$slice> for F where F: FnMut(char) -> bool {
                pattern_methods!(CharPredicateSearcher<'a, F>, CharEqPattern, CharPredicateSearcher, $slice);
            }

            ////////////////////////////////////////////////////////////////////
            // Impl for &str
            ////////////////////////////////////////////////////////////////////

            use fast_sequence_search::{OrdSlice, OrdSlicePattern, OrdSeqSearcher};
            use fast_sequence_search::ByteOptimization;

            pub struct StrSearcher<'a, 'b>(OrdSeqSearcher<'b, $slice>);

            impl<'a> OrdSlice for $slice {
                type NeedleElement = u8;
                type FastSkipOptimization = ByteOptimization;

                fn next_valid_pos(hs: &Self::Haystack, pos: usize) -> Option<usize> {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s[pos..].chars().next().map(|c| pos + c.len_utf8())
                }

                fn next_valid_pos_back(hs: &Self::Haystack, pos: usize) -> Option<usize> {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s[..pos].chars().next_back().map(|c| pos - c.len_utf8())
                }

                fn haystack_as_slice<'t>(hs: &'t Self::Haystack) -> &'t [Self::NeedleElement] {
                    unsafe {
                        ::std::slice::from_raw_parts(hs.0, hs.1 as usize - hs.0 as usize)
                    }
                }

                fn pos_is_valid(hs: &Self::Haystack, pos: usize) -> bool {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s.is_char_boundary(pos)
                }

                unsafe fn cursor_at_offset(hs: Self::Haystack, offset: usize) -> Self::Cursor {
                    hs.0.offset(offset as isize)
                }
            }

            /// Non-allocating substring search.
            ///
            /// Will handle the pattern `""` as returning empty matches at each character
            /// boundary.
            impl<'a, 'b> Pattern<$slice> for &'b str {
                pattern_methods!(StrSearcher<'a, 'b>,
                                |s: &'b str| OrdSlicePattern(s.as_bytes()),
                                StrSearcher,
                                $slice);
            }

            unsafe impl<'a, 'b> Searcher<$slice> for StrSearcher<'a, 'b> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, 'b> ReverseSearcher<$slice> for StrSearcher<'a, 'b> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

        }
    }
}

impl_both_mutability!(shared, &'a str, *const u8, u8, |start, end| {
    let slice = ::std::slice::from_raw_parts(start,
        end as usize - start as usize);
    ::std::str::from_utf8_unchecked(slice)
}, |haystack: &str| {
    let begin = haystack.as_ptr();
    let end = unsafe {
        begin.offset(haystack.len() as isize)
    };
    (begin, end)
});

impl_both_mutability!(mutable, &'a mut str, *mut u8, u8, |start, end| {
    let slice = ::std::slice::from_raw_parts_mut(start,
        end as usize - start as usize);

    // TODO: This should probably be just library support in std
    ::std::mem::transmute::<&mut [u8], &mut str>(slice)
}, |haystack: &mut str| {
    let begin = haystack.as_ptr() as *mut u8;
    let end = unsafe {
        begin.offset(haystack.len() as isize)
    };
    (begin, end)
});
<commit_msg>Prevented char_eq from being called with non-ascii chars in the ascii case<commit_after>macro_rules! impl_both_mutability {
    ($module:ident, $slice:ty,
                    $cursor:ty,
                    $cursor_elem:ty,
                    $cursors_to_haystack:expr,
                    $haystack_to_cursors:expr) => {
        pub mod $module {
            use core_traits::*;

            #[derive(Copy, Clone)]
            struct Iter<'a> {
                haystack: ($cursor, $cursor),
                start: $cursor,
                end: $cursor,
                _marker: ::std::marker::PhantomData<$slice>
            }

            impl<'a> Iter<'a> {
                #[inline]
                fn new(haystack: $slice) -> Self {
                    let (start, end) = $haystack_to_cursors(haystack);
                    Iter {
                        haystack: (start, end),
                        start: start,
                        end: end,
                        _marker: ::std::marker::PhantomData,
                    }
                }

                #[inline]
                fn next(&mut self) -> Option<$cursor_elem> {
                    while self.start != self.end {
                        unsafe {
                            let b = *self.start;
                            self.start = self.start.offset(1);
                            return Some(b);
                        }
                    }
                    None
                }

                #[inline]
                fn next_back(&mut self) -> Option<$cursor_elem> {
                    while self.start != self.end {
                        unsafe {
                            self.end = self.end.offset(-1);
                            let b = *self.end;
                            return Some(b);
                        }
                    }
                    None
                }
            }

            impl<'a> SearchCursors for $slice {
                type Haystack = ($cursor, $cursor);
                type Cursor = $cursor;

                fn into_haystack(self) -> Self::Haystack {
                    $haystack_to_cursors(self)
                }

                fn offset_from_front(haystack: Self::Haystack,
                                     begin: Self::Cursor) -> usize {
                    begin as usize - haystack.0 as usize
                }

                unsafe fn range_to_self(_: Self::Haystack,
                                        start: Self::Cursor,
                                        end: Self::Cursor) -> Self {
                    ($cursors_to_haystack)(start, end)
                }
                fn cursor_at_front(hs: Self::Haystack) -> Self::Cursor {
                    hs.0
                }
                fn cursor_at_back(hs: Self::Haystack) -> Self::Cursor {
                    hs.1
                }
            }

            //////////////////////////////////////////////////////////////////
            // Impl for a CharEq wrapper
            //////////////////////////////////////////////////////////////////

            use utf8::{self, CharEq, CharEqPattern};

            #[derive(Clone)]
            pub struct CharEqSearcher<'a, C: CharEq> {
                char_eq: C,
                iter: Iter<'a>,
                ascii_only: bool,
            }

            impl<'a, C: CharEq> Pattern<$slice> for CharEqPattern<C> {
                type Searcher = CharEqSearcher<'a, C>;

                #[inline]
                fn into_searcher(self, haystack: $slice) -> CharEqSearcher<'a, C> {
                    CharEqSearcher {
                        ascii_only: self.0.only_ascii(),
                        char_eq: self.0,
                        iter: Iter::new(haystack),
                    }
                }

                fn is_prefix_of(mut self, haystack: $slice) -> bool {
                    haystack.chars()
                            .next()
                            .map(|c| self.0.matches(c))
                            .unwrap_or(false)
                }

                fn is_suffix_of(mut self, haystack: $slice) -> bool
                    where Self::Searcher: ReverseSearcher<$slice>
                {
                    haystack.chars()
                            .next_back()
                            .map(|c| self.0.matches(c))
                            .unwrap_or(false)
                }
            }

            unsafe impl<'a, C: CharEq> Searcher<$slice> for CharEqSearcher<'a, C> {
                #[inline]
                fn haystack(&self) -> ($cursor, $cursor) {
                    self.iter.haystack
                }

                #[inline]
                fn next_match(&mut self) -> Option<($cursor, $cursor)> {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next() {
                            if b < 128 && self.char_eq.matches(b as char) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-1),
                                     self.iter.start)
                                })
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point(|| self.iter.next()) {
                            if self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-(c.len_utf8() as isize)),
                                     self.iter.start)
                                })
                            }
                        }
                    }
                    None
                }

                #[inline]
                fn next_reject(&mut self) -> Option<($cursor, $cursor)> {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next() {
                            if b > 127 || !self.char_eq.matches(b as char) {
                                unsafe {
                                    let reject_start = self.iter.start.offset(-1);
                                    while !utf8::byte_is_char_boundary(
                                            *self.iter.start) {
                                        self.iter.start = self.iter.start.offset(1);
                                    }
                                    return Some((reject_start, self.iter.start))
                                }
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point(|| self.iter.next()) {
                            if !self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.start.offset(-(c.len_utf8() as isize)),
                                     self.iter.start)
                                })
                            }
                        }
                    }
                    None
                }
            }

            unsafe impl<'a, C: CharEq> ReverseSearcher<$slice> for CharEqSearcher<'a, C> {
                #[inline]
                fn next_match_back(&mut self) -> Option<($cursor, $cursor)>  {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next_back() {
                            if b < 128 && self.char_eq.matches(b as char) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(1))
                                })
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point_reverse(|| self.iter.next_back()) {
                            if self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(c.len_utf8() as isize))
                                })
                            }
                        }
                    }
                    None
                }

                #[inline]
                fn next_reject_back(&mut self) -> Option<($cursor, $cursor)>  {
                    if self.ascii_only {
                        while let Some(b) = self.iter.next_back() {
                            if b > 127 || !self.char_eq.matches(b as char) {
                                unsafe {
                                    let reject_end = self.iter.end.offset(1);
                                    while !utf8::byte_is_char_boundary(
                                            *self.iter.end) {
                                        self.iter.end = self.iter.end.offset(-1);
                                    }
                                    return Some((self.iter.end, reject_end))
                                }
                            }
                        }
                    } else {
                        while let Some(c) = utf8::next_code_point_reverse(|| self.iter.next_back()) {
                            if !self.char_eq.matches(c) {
                                return Some(unsafe {
                                    (self.iter.end,
                                     self.iter.end.offset(c.len_utf8() as isize))
                                })
                            }
                        }
                    }
                    None
                }
            }

            impl<'a, C: CharEq> DoubleEndedSearcher<$slice> for CharEqSearcher<'a, C> {}

            /////////////////////////////////////////////////////////////////////////////
            // Impl for char
            /////////////////////////////////////////////////////////////////////////////

            /// Associated type for `<char as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharSearcher<'a>(CharEqSearcher<'a, char>);

            unsafe impl<'a> Searcher<$slice> for CharSearcher<'a> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a> ReverseSearcher<$slice> for CharSearcher<'a> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a> DoubleEndedSearcher<$slice> for CharSearcher<'a> {}

            /// Searches for chars that are equal to a given char
            impl<'a> Pattern<$slice> for char {
                pattern_methods!(CharSearcher<'a>, CharEqPattern, CharSearcher, $slice);
            }

            /////////////////////////////////////////////////////////////////////////////
            // Impl for &[char]
            /////////////////////////////////////////////////////////////////////////////

            // Todo: Change / Remove due to ambiguity in meaning.

            /// Associated type for `<&[char] as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharSliceSearcher<'a, 'b>(CharEqSearcher<'a, &'b [char]>);

            unsafe impl<'a, 'b> Searcher<$slice> for CharSliceSearcher<'a, 'b> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, 'b> ReverseSearcher<$slice> for CharSliceSearcher<'a, 'b> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a, 'b> DoubleEndedSearcher<$slice> for CharSliceSearcher<'a, 'b> {}

            /// Searches for chars that are equal to any of the chars in the array
            impl<'a, 'b> Pattern<$slice> for &'b [char] {
                pattern_methods!(CharSliceSearcher<'a, 'b>, CharEqPattern, CharSliceSearcher, $slice);
            }

            /////////////////////////////////////////////////////////////////////////////
            // Impl for F: FnMut(char) -> bool
            /////////////////////////////////////////////////////////////////////////////

            /// Associated type for `<F as Pattern<&'a str>>::Searcher`.
            #[derive(Clone)]
            pub struct CharPredicateSearcher<'a, F>(CharEqSearcher<'a, F>)
                where F: FnMut(char) -> bool;

            unsafe impl<'a, F> Searcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool
            {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, F> ReverseSearcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool
            {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

            impl<'a, F> DoubleEndedSearcher<$slice> for CharPredicateSearcher<'a, F>
                where F: FnMut(char) -> bool {}

            /// Searches for chars that match the given predicate
            impl<'a, F> Pattern<$slice> for F where F: FnMut(char) -> bool {
                pattern_methods!(CharPredicateSearcher<'a, F>, CharEqPattern, CharPredicateSearcher, $slice);
            }

            ////////////////////////////////////////////////////////////////////
            // Impl for &str
            ////////////////////////////////////////////////////////////////////

            use fast_sequence_search::{OrdSlice, OrdSlicePattern, OrdSeqSearcher};
            use fast_sequence_search::ByteOptimization;

            pub struct StrSearcher<'a, 'b>(OrdSeqSearcher<'b, $slice>);

            impl<'a> OrdSlice for $slice {
                type NeedleElement = u8;
                type FastSkipOptimization = ByteOptimization;

                fn next_valid_pos(hs: &Self::Haystack, pos: usize) -> Option<usize> {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s[pos..].chars().next().map(|c| pos + c.len_utf8())
                }

                fn next_valid_pos_back(hs: &Self::Haystack, pos: usize) -> Option<usize> {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s[..pos].chars().next_back().map(|c| pos - c.len_utf8())
                }

                fn haystack_as_slice<'t>(hs: &'t Self::Haystack) -> &'t [Self::NeedleElement] {
                    unsafe {
                        ::std::slice::from_raw_parts(hs.0, hs.1 as usize - hs.0 as usize)
                    }
                }

                fn pos_is_valid(hs: &Self::Haystack, pos: usize) -> bool {
                    let s = unsafe {
                        ::std::str::from_utf8_unchecked(Self::haystack_as_slice(hs))
                    };
                    s.is_char_boundary(pos)
                }

                unsafe fn cursor_at_offset(hs: Self::Haystack, offset: usize) -> Self::Cursor {
                    hs.0.offset(offset as isize)
                }
            }

            /// Non-allocating substring search.
            ///
            /// Will handle the pattern `""` as returning empty matches at each character
            /// boundary.
            impl<'a, 'b> Pattern<$slice> for &'b str {
                pattern_methods!(StrSearcher<'a, 'b>,
                                |s: &'b str| OrdSlicePattern(s.as_bytes()),
                                StrSearcher,
                                $slice);
            }

            unsafe impl<'a, 'b> Searcher<$slice> for StrSearcher<'a, 'b> {
                searcher_methods!(forward, s, s.0, $cursor);
            }

            unsafe impl<'a, 'b> ReverseSearcher<$slice> for StrSearcher<'a, 'b> {
                searcher_methods!(reverse, s, s.0, $cursor);
            }

        }
    }
}

impl_both_mutability!(shared, &'a str, *const u8, u8, |start, end| {
    let slice = ::std::slice::from_raw_parts(start,
        end as usize - start as usize);
    ::std::str::from_utf8_unchecked(slice)
}, |haystack: &str| {
    let begin = haystack.as_ptr();
    let end = unsafe {
        begin.offset(haystack.len() as isize)
    };
    (begin, end)
});

impl_both_mutability!(mutable, &'a mut str, *mut u8, u8, |start, end| {
    let slice = ::std::slice::from_raw_parts_mut(start,
        end as usize - start as usize);

    // TODO: This should probably be just library support in std
    ::std::mem::transmute::<&mut [u8], &mut str>(slice)
}, |haystack: &mut str| {
    let begin = haystack.as_ptr() as *mut u8;
    let end = unsafe {
        begin.offset(haystack.len() as isize)
    };
    (begin, end)
});
<|endoftext|>


<commit_before>use std::collections::{VecDeque,HashMap};
use std::iter::Take;
use std::slice::Iter;
use std::error::Error;
use std::string::FromUtf8Error;

use super::record_type::RecordType;
use super::dns_class::DNSClass;

// labels          63 octets or less
// names           255 octets or less
// TTL             positive values of a signed 32 bit number.
// UDP messages    512 octets or less
pub struct Record {
  rr_type: RecordType,
  dns_class: DNSClass,
  name_labels: VecDeque<String>,
}

impl Record {

  /// parse a resource record line example:
  ///
  ///
  //fn parse(line: &str) -> Record {

    // tokenize the string
    //let mut tokens = line.words();
  //}

  /// parses the chain of labels
  ///  this has a max of 255 octets, with each label being less than 63.
  ///  all names will be stored lowercase internally.
  fn parse_labels(slice: &[u8]) -> Result<VecDeque<String>, FromUtf8Error> {
    let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
    let mut labels: VecDeque<String> = VecDeque::with_capacity(3); // most labels will be around three, e.g. www.example.com
//    let mut label_map: HashMap<u8, usize>;  // temporary map for pointers, offset in slice, start location in labels

    let mut cur_slice = slice;

    // assume all chars are utf-8. We're doing byte-by-byte operations, no endianess issues...
    // reserved: (1000 0000 aka 0800) && (0100 0000 aka 0400)
    // pointer: (slice == 1100 0000 aka C0) & C0 == true, then 03FF & slice = offset
    // label: 03FF & slice = length; slice.next(length) = label
    // root: 0000
    loop {
      state = match state {
        LabelParseState::LabelLengthOrPointer => {
          //let byte: u8 = *iter.take(1).next().unwrap(); // could default to zero, but it's an error if this doesn't exist, perhaps try! instead
          let (first, cur_slice) = cur_slice.split_at(1);

          match first.first() {
            Some(&0) | None => LabelParseState::Root,
            Some(&byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
            Some(&byte) if byte <= 0x3F        => LabelParseState::Label(byte),
            _ => unimplemented!(),
          }
          },
        LabelParseState::Label(count) => {
          //let label_iter: Take<Iter<u8>> = iter.take(count as usize);
          //let arr: Vec<&u8> = label_iter.collect();
          //let arr2: Vec<u8> = arr.to_vec();
          let (label_slice, cur_slice) = cur_slice.split_at(count as usize);

          // using lossy, this is safe, but can end up with junk in the name...
          // TODO other option
          let label = try!(String::from_utf8(label_slice.into()));
          labels.push_back(label);

          // reset to collect more data
          LabelParseState::LabelLengthOrPointer
          },
        LabelParseState::Pointer(offset) => {
          // lookup in the hashmap the label to use
          unimplemented!()
        },
        LabelParseState::Root => {
          // technically could return here...
          break;
        }
      }
    }

    return Ok(labels);
  }
}

/// This is the list of states for the state machine
enum LabelParseState {
  LabelLengthOrPointer, // basically the start of the FSM
  Label(u8),   // storing length of the label, must be < 63
  Pointer(u8), // location of pointer in slice,
  Root,        // root is the end of the labels list, aka null
}
<commit_msg>trying to get parsing tests going<commit_after>//use std::collections::HashMap;
//use std::error::Error;
use std::string::FromUtf8Error;

use super::record_type::RecordType;
use super::dns_class::DNSClass;

// labels          63 octets or less
// names           255 octets or less
// TTL             positive values of a signed 32 bit number.
// UDP messages    512 octets or less
pub struct Record {
  rr_type: RecordType,
  dns_class: DNSClass,
  name_labels: Vec<String>,
}

impl Record {

  /// parse a resource record line example:
  ///
  ///
  //fn parse(line: &str) -> Record {

    // tokenize the string
    //let mut tokens = line.words();
  //}

  /// parses the chain of labels
  ///  this has a max of 255 octets, with each label being less than 63.
  ///  all names will be stored lowercase internally.
  fn parse_labels(slice: &[u8]) -> Result<Vec<String>, FromUtf8Error> {
    let mut state: LabelParseState = LabelParseState::LabelLengthOrPointer;
    let mut labels: Vec<String> = Vec::with_capacity(3); // most labels will be around three, e.g. www.example.com

    let mut cur_slice = slice;

    // assume all chars are utf-8. We're doing byte-by-byte operations, no endianess issues...
    // reserved: (1000 0000 aka 0800) && (0100 0000 aka 0400)
    // pointer: (slice == 1100 0000 aka C0) & C0 == true, then 03FF & slice = offset
    // label: 03FF & slice = length; slice.next(length) = label
    // root: 0000
    loop {
      state = match state {
        LabelParseState::LabelLengthOrPointer => {
          //let byte: u8 = *iter.take(1).next().unwrap(); // could default to zero, but it's an error if this doesn't exist, perhaps try! instead
          let (first, tmp_slice) = cur_slice.split_at(1);
          cur_slice = tmp_slice;

          match first.first() {
            Some(&0) | None => LabelParseState::Root,
            Some(&byte) if byte & 0xC0 == 0xC0 => LabelParseState::Pointer(byte & 0x3F),
            Some(&byte) if byte <= 0x3F        => LabelParseState::Label(byte),
            _ => unimplemented!(),
          }
          },
        LabelParseState::Label(count) => {
          //let label_iter: Take<Iter<u8>> = iter.take(count as usize);
          //let arr: Vec<&u8> = label_iter.collect();
          //let arr2: Vec<u8> = arr.to_vec();
          let (label_slice, tmp_slice) = cur_slice.split_at(count as usize);
          cur_slice = tmp_slice;

          // using lossy, this is safe, but can end up with junk in the name...
          // TODO other option
          let label = try!(String::from_utf8(label_slice.into()));
          labels.push(label);

          // reset to collect more data
          LabelParseState::LabelLengthOrPointer
          },
        LabelParseState::Pointer(offset) => {
          // lookup in the hashmap the label to use
          unimplemented!()
        },
        LabelParseState::Root => {
          // technically could return here...
          break;
        }
      }
    }

    return Ok(labels);
  }
}

/// This is the list of states for the state machine
enum LabelParseState {
  LabelLengthOrPointer, // basically the start of the FSM
  Label(u8),   // storing length of the label, must be < 63
  Pointer(u8), // location of pointer in slice,
  Root,        // root is the end of the labels list, aka null
}

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

  #[test]
  fn parse_labels() {
    let data: Vec<(Vec<u8>, Vec<String>)> = vec![
      (vec![0 as u8], vec![String::new()]), // base case, only the root
    ];

    for (binary, result) in data {
      assert_eq!(Record::parse_labels(binary.as_slice()).ok().unwrap(), result);
    }
  }
}
<|endoftext|>


<commit_before>#![allow(unused_variables, unused_imports)]
use tdo_core::*;
use tdo_export;
use std::fs::File;
use std::io::{Write, Read, stdin, stdout};
use std::io::prelude::*;
use super::exit;
use super::colored::*;

use filesystem;

pub fn print_out(tdo: &super::tdo_core::tdo::Tdo, all: bool) {
    match tdo_export::render_terminal_output(tdo, all) {
        Some(printlines) => {
            for item in printlines.iter() {
                println!("{}", item);
            }
        }
        None => {
            if tdo.get_highest_id() > 0 {
                println!("No undone todos");
            } else {
                println!("No todos yet");
            }
        }
    }
}

pub fn add(tdo: &mut tdo::Tdo, new_todo: &str, in_list: Option<&str>) {
    let todo = todo::Todo::new(tdo.get_highest_id() + 1, new_todo);
    match tdo.add_todo(in_list, todo) {
        Err(e) => errorprint!(e.description()),
        _ => {}
    }
}

pub fn edit(tdo: &mut tdo::Tdo, id: u32) {
    let list = match tdo.find_id(id) {
        Ok(list_id) => list_id,
        Err(e) => {
            errorprint!(e.description());
            exit(1);
        }
    };
    let position = match tdo.lists[list].contains_id(id) {
        Ok(position) => position,
        Err(e) => {
            errorprint!(e.description());
            exit(1);
        }
    };
    println!("Task #{}: {}", id, tdo.lists[list].list[position].name);
    println!("Enter your new task description (leave blank for abort)");
    let mut new_task = String::new();
    stdin().read_line(&mut new_task).unwrap();
    match new_task.trim() {
        "" => {},
        _ => tdo.lists[list].list[position].edit(&new_task.trim()),
    };
}

pub fn done(tdo: &mut tdo::Tdo, id: u32) {
    match tdo.done_id(id) {
        Ok(()) => {}
        Err(e) => errorprint!(e.description()),
    }
}

pub fn newlist(tdo: &mut tdo::Tdo, new_list: &str) {
    match tdo.add_list(list::TodoList::new(new_list)) {
        Ok(()) => { println!("Created a new list named '{}'", new_list); }
        Err(e) => errorprint!(e.description()),
    }
}

pub fn remove(tdo: &mut tdo::Tdo, list_name: &str) {
    print!("{}",
             format!("[WARNING] Are you sure you want to delete \"{}\" ans all todos in it? [y/N] ", &list_name).red());
    stdout().flush().ok().expect("Could not flush stdout!");
    let mut answer = String::new();
    stdin().read_line(&mut answer).unwrap();
    let should_delete = match answer.to_lowercase().trim() {
        "y" | "yes" => true,
        "n" | "no" | "" => false,
        _ => {
            errorprint!("No valid answer given. Defaulting to \"no\"");
            false
        }
    };
    if should_delete {
        match tdo.remove_list(list_name) {
            Ok(()) => println!("Removed the list '{}'", list_name),
            Err(e) => errorprint!(e),
        }
    }

}

pub fn clean(tdo: &mut tdo::Tdo, list_name: Option<&str>) {
    match list_name {
        Some(name) => match tdo.clean_list(name) {
            Err(e) => errorprint!(e.description()),
            _ => {},
        },
        None => tdo.clean_lists(),
    }
}

pub fn lists(tdo: &tdo::Tdo) {
    println!("Your todo collection currently encompasses the following lists:\n");

    for list in tdo.lists.iter() {
        let done = list.list
            .iter()
            .fold(0, |acc, &ref item| if item.done { acc + 1 } else { acc });
        let undone: u64 = (list.list.len() - done) as u64;
        println!("    [{undone}|{done}] {name}",
                 undone = undone.to_string().red(),
                 done = (done as u64).to_string().green(),
                 name = &list.name);
    }
}

pub fn export(tdo: &tdo::Tdo, destination: &str, undone: bool) {
    let target = match filesystem::validate_target_file(destination) {
        Ok(path) => path,
        Err(e) => {
            errorprint!(e);
            return;
        }
    };
    match File::create(target) {
        Ok(mut file) => {
            file.write(&tdo_export::gen_tasks_md(tdo, true).unwrap().into_bytes())
                .unwrap();
        }
        Err(e) => errorprint!(e),
    }
}

pub fn reset(tdo: &mut tdo::Tdo) -> Option<tdo::Tdo> {
    println!("{}",
             "[WARNING] Are you sure you want to delete all todos and lists? [y/N] ".red());
    let mut answer = String::new();
    stdin().read_line(&mut answer).unwrap();
    let should_delete = match answer.to_lowercase().trim() {
        "y" | "yes" => true,
        "n" | "no" | "" => false,
        _ => {
            errorprint!("No valid answer given. Defaulting to \"no\"");
            false
        }
    };
    if should_delete {
        Some(tdo::Tdo::new())
    } else {
        None
    }
}
<commit_msg>Turn reset println! into print!<commit_after>#![allow(unused_variables, unused_imports)]
use tdo_core::*;
use tdo_export;
use std::fs::File;
use std::io::{Write, Read, stdin, stdout};
use std::io::prelude::*;
use super::exit;
use super::colored::*;

use filesystem;

pub fn print_out(tdo: &super::tdo_core::tdo::Tdo, all: bool) {
    match tdo_export::render_terminal_output(tdo, all) {
        Some(printlines) => {
            for item in printlines.iter() {
                println!("{}", item);
            }
        }
        None => {
            if tdo.get_highest_id() > 0 {
                println!("No undone todos");
            } else {
                println!("No todos yet");
            }
        }
    }
}

pub fn add(tdo: &mut tdo::Tdo, new_todo: &str, in_list: Option<&str>) {
    let todo = todo::Todo::new(tdo.get_highest_id() + 1, new_todo);
    match tdo.add_todo(in_list, todo) {
        Err(e) => errorprint!(e.description()),
        _ => {}
    }
}

pub fn edit(tdo: &mut tdo::Tdo, id: u32) {
    let list = match tdo.find_id(id) {
        Ok(list_id) => list_id,
        Err(e) => {
            errorprint!(e.description());
            exit(1);
        }
    };
    let position = match tdo.lists[list].contains_id(id) {
        Ok(position) => position,
        Err(e) => {
            errorprint!(e.description());
            exit(1);
        }
    };
    println!("Task #{}: {}", id, tdo.lists[list].list[position].name);
    println!("Enter your new task description (leave blank for abort)");
    let mut new_task = String::new();
    stdin().read_line(&mut new_task).unwrap();
    match new_task.trim() {
        "" => {}
        _ => tdo.lists[list].list[position].edit(&new_task.trim()),
    };
}

pub fn done(tdo: &mut tdo::Tdo, id: u32) {
    match tdo.done_id(id) {
        Ok(()) => {}
        Err(e) => errorprint!(e.description()),
    }
}

pub fn newlist(tdo: &mut tdo::Tdo, new_list: &str) {
    match tdo.add_list(list::TodoList::new(new_list)) {
        Ok(()) => { println!("Created a new list named '{}'", new_list); }
        Err(e) => errorprint!(e.description()),
    }
}

pub fn remove(tdo: &mut tdo::Tdo, list_name: &str) {
    print!("{}",
           format!("[WARNING] Are you sure you want to delete \"{}\" ans all todos in it? [y/N] ",
                   &list_name)
                   .red());
    stdout().flush().ok().expect("Could not flush stdout!");
    let mut answer = String::new();
    stdin().read_line(&mut answer).unwrap();
    let should_delete = match answer.to_lowercase().trim() {
        "y" | "yes" => true,
        "n" | "no" | "" => false,
        _ => {
            errorprint!("No valid answer given. Defaulting to \"no\"");
            false
        }
    };
    if should_delete {
        match tdo.remove_list(list_name) {
            Ok(()) => println!("Removed the list '{}'", list_name),
            Err(e) => errorprint!(e),
        }
    }

}

pub fn clean(tdo: &mut tdo::Tdo, list_name: Option<&str>) {
    match list_name {
        Some(name) => {
            match tdo.clean_list(name) {
                Err(e) => errorprint!(e.description()),
                _ => {}
            }
        }
        None => tdo.clean_lists(),
    }
}

pub fn lists(tdo: &tdo::Tdo) {
    println!("Your todo collection currently encompasses the following lists:\n");

    for list in tdo.lists.iter() {
        let done = list.list
            .iter()
            .fold(0, |acc, &ref item| if item.done { acc + 1 } else { acc });
        let undone: u64 = (list.list.len() - done) as u64;
        println!("    [{undone}|{done}] {name}",
                 undone = undone.to_string().red(),
                 done = (done as u64).to_string().green(),
                 name = &list.name);
    }
}

pub fn export(tdo: &tdo::Tdo, destination: &str, undone: bool) {
    let target = match filesystem::validate_target_file(destination) {
        Ok(path) => path,
        Err(e) => {
            errorprint!(e);
            return;
        }
    };
    match File::create(target) {
        Ok(mut file) => {
            file.write(&tdo_export::gen_tasks_md(tdo, true).unwrap().into_bytes())
                .unwrap();
        }
        Err(e) => errorprint!(e),
    }
}

pub fn reset(tdo: &mut tdo::Tdo) -> Option<tdo::Tdo> {
    print!("{}",
           "[WARNING] Are you sure you want to delete all todos and lists? [y/N] ".red());
    stdout().flush().ok().expect("Could not flush stdout!");
    let mut answer = String::new();
    stdin().read_line(&mut answer).unwrap();
    let should_delete = match answer.to_lowercase().trim() {
        "y" | "yes" => true,
        "n" | "no" | "" => false,
        _ => {
            errorprint!("No valid answer given. Defaulting to \"no\"");
            false
        }
    };
    if should_delete {
        Some(tdo::Tdo::new())
    } else {
        None
    }
}
<|endoftext|>


<commit_before>/*! Functions for changing world and entity state. */

use std::path::Path;
use std::fs::{self, File};
use rand::Rng;
use calx::{Dijkstra, Dir6, HexFov, HexGeom, RngExt};
use calx_ecs::Entity;
use world::World;
use area;
use location::Location;
use item::Slot;
use components::{CompositeStats, BrainState};
use query::{self, ControlState};
use msg;

/// Player input action.
#[derive(Copy, Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum Input {
    /// Take a step in the given direction.
    Step(Dir6),
    /// Melee attack in the given direction.
    Melee(Dir6),
    /// Shoot in the given direction.
    Shoot(Dir6),
    /// Do nothing for a turn.
    Pass,
}

/// Top-level game state update function. Only valid to call if
/// control_state() returned ReadyToUpdate.
pub fn update(w: &mut World) {
    assert!(query::control_state(w) == ControlState::ReadyToUpdate);

    clean_dead(w);
    ai_main(w);

    w.flags.tick += 1;
    w.flags.player_acted = false;
}

/// Run AI for all autonomous mobs.
fn ai_main(w: &mut World) {
    let actives: Vec<Entity> = w.ecs.brain.iter().map(|(&e, _)| e).collect();
    for e in actives.into_iter() {
        update_entity(w, e);
    }
}

fn clean_dead(w: &mut World) {
    let dead: Vec<Entity> = w.ecs.iter().filter(|&&e| !query::is_alive(w, e)).map(|&e| e).collect();
    for e in dead.into_iter() {
        w.ecs.remove(e);
    }
}

pub fn update_entity(w: &mut World, e: Entity) {
    if query::is_mob(w, e) && !query::is_player(w, e) && query::ticks_this_frame(w, e) {
        mob_ai(w, e);
    }
}

pub fn mob_ai(w: &mut World, e: Entity) {
    assert!(query::is_mob(w, e));
    assert!(!query::is_player(w, e));
    assert!(query::ticks_this_frame(w, e));

    if query::brain_state(w, e) == Some(BrainState::Asleep) {
        let self_loc = query::location(w, e).unwrap();
        // Mob is inactive, but may wake up if it spots the player.
        if let Some(player_loc) = query::player(w).and_then(|e| query::location(w, e)) {
            // TODO: Line-of-sight, stealth concerns, other enemies than
            // player etc.
            if let Some(d) = player_loc.distance_from(self_loc) {
                if d < 6 {
                    wake_up(w, e);
                }
            }
        }

        return;
    }

    if let Some(p) = query::player(w) {
        let loc = query::location(w, e).expect("no location");

        let vec_to_enemy = loc.v2_at(query::location(w, p).expect("no location"));
        if let Some(v) = vec_to_enemy {
            if v.hex_dist() == 1 {
                // Melee range, hit.
                melee(w, e, Dir6::from_v2(v));
            } else {
                // Walk towards.
                let pathing_depth = 16;
                let pathing = Dijkstra::new(vec![query::location(w, p).expect("no location")],
                                            |&loc| !query::blocks_walk(w, loc),
                                            pathing_depth);

                let steps = pathing.sorted_neighbors(&loc);
                if steps.len() > 0 {
                    step(w,
                         e,
                         loc.dir6_towards(steps[0]).expect("No loc pair orientation"));
                } else {
                    let dir = w.flags.rng.gen();
                    step(w, e, dir);
                    // TODO: Fall asleep if things get boring.
                }
            }
        }
    }
}

/// Give player input. Only valid to call if control_state() returned
/// AwaitingInput.
pub fn input(w: &mut World, input: Input) {
    assert!(query::control_state(w) == ControlState::AwaitingInput);
    let p = query::player(w).expect("No player to receive input");
    match input {
        Input::Step(d) => {
            step(w, p, d);
        }
        Input::Melee(d) => {
            melee(w, p, d);
        }
        Input::Shoot(d) => {
            shoot(w, p, d);
        }
        Input::Pass => {}
    }
    w.flags.player_acted = true;

    // Run one world update cycle right away, so that we don't get awkward
    // single frames rendered where the player has acted and the rest of the
    // world hasn't.
    if query::control_state(w) == ControlState::ReadyToUpdate {
        update(w);
    }
}

/// Try to move the entity in direction.
pub fn step(w: &mut World, e: Entity, dir: Dir6) {
    let target_loc = query::location(w, e).unwrap() + dir.to_v2();
    if query::can_enter(w, e, target_loc) {
        place_entity(w, e, target_loc);
    }
}

/// Fight in a direction.
pub fn melee(w: &mut World, e: Entity, dir: Dir6) {
    let loc = query::location(w, e).expect("no location") + dir.to_v2();
    if let Some(e) = query::mob_at(w, loc) {
        let us = query::stats(w, e);
        damage(w, e, us.power + us.attack);
    }
}

pub fn shoot(w: &mut World, e: Entity, dir: Dir6) {
    unimplemented!();
    /*
    let stats = self.stats();

    if stats.ranged_range > 0 {
        action::shoot(self.location().unwrap(), dir, stats.ranged_range, stats.ranged_power);
    }
    */
}

pub fn pick_up(w: &mut World, picker: Entity, item: Entity) -> bool {
    if !query::can_be_picked_up(w, item) {
        return false;
    }

    match query::free_bag_slot(w, picker) {
        Some(slot) => {
            equip(w, item, picker, slot);
            return true;
        }
        // Inventory full.
        None => {
            return false;
        }
    }
}

/// Equip an item to a slot. Slot must be empty.
pub fn equip(w: &mut World, item: Entity, e: Entity, slot: Slot) {
    w.spatial.equip(item, e, slot);
    recompose_stats(w, e)
}

/// Generate composed stats from base stats and the stats of equipped items.
/// This function must be called after any operation that changes the composed
/// stats affecting state of an entity.
pub fn recompose_stats(w: &mut World, e: Entity) {
    let mut stats = query::base_stats(w, e);
    for &slot in [Slot::Body,
                  Slot::Feet,
                  Slot::Head,
                  Slot::Melee,
                  Slot::Ranged,
                  Slot::TrinketF,
                  Slot::TrinketG,
                  Slot::TrinketH,
                  Slot::TrinketI]
                     .iter() {
        if let Some(item) = w.spatial.entity_equipped(e, slot) {
            stats = stats + query::stats(w, item);
        }
    }

    w.ecs.composite_stats.insert(e, CompositeStats(stats));
}

pub fn place_entity(w: &mut World, e: Entity, loc: Location) {
    w.spatial.insert_at(e, loc);
    after_entity_move(w, e);
}

/// Clear map memory of an entity.
pub fn forget_map(w: &mut World, e: Entity) {
    w.ecs.map_memory.get_mut(e).map(|mm| {
        mm.seen.clear();
        mm.remembered.clear();
    });
}

/// Callback when entity moves to a new location.
fn after_entity_move(w: &mut World, e: Entity) {
    let loc = query::location(w, e).expect("Entity must have location for callback");

    do_fov(w, e);

    for item in w.spatial.entities_at(loc).into_iter() {
        if item != e {
            on_step_on(w, e, item);
        }
    }

    if query::is_player(w, e) {
        w.flags.camera = loc;

        if query::terrain(w, loc).is_exit() {
            area::next_level(w);
        }
    }
}

pub fn do_fov(w: &mut World, e: Entity) {
    if !w.ecs.map_memory.contains(e) {
        return;
    }

    if let Some(loc) = query::location(w, e) {
        let sight_range = 12;
        let seen_locs: Vec<Location> = HexFov::new(|pt| query::blocks_sight(w, (loc + pt)),
                                                   sight_range)
                                           .fake_isometric()
                                           .map(|pt| loc + pt)
                                           .collect();
        let mut mm = &mut w.ecs.map_memory[e];
        mm.seen.clear();
        mm.seen.extend(seen_locs.clone().into_iter());
        mm.remembered.extend(seen_locs.into_iter());
    }
}

pub fn on_step_on(w: &mut World, stepper: Entity, item: Entity) {
    if query::is_instant_item(w, item) && query::is_player(w, stepper) {
        unimplemented!();
    }
}

pub fn set_brain_state(w: &mut World, e: Entity, state: BrainState) {
    w.ecs.brain.get_mut(e).map(|b| b.state = state);
}

pub fn wake_up(w: &mut World, e: Entity) {
    if query::brain_state(w, e) == Some(BrainState::Asleep) {
        set_brain_state(w, e, BrainState::Hunting);
    }
}

/// Apply damage to entity, subject to damage reduction.
pub fn damage(w: &mut World, e: Entity, mut power: i32) {
    let stats = query::stats(w, e);
    power -= stats.protection;

    if power < 1 {
        // Give damage a bit under the reduction an off-chance to hit.
        // Power that's too low can't do anything though.
        if power >= -5 {
            power = 1;
        } else {
            return;
        }
    }

    // Every five points of power is one certain hit.
    let full = power / 5;
    // The fractional points are one probabilistic hit.
    let partial = (power % 5) as f32 / 5.0;

    let damage = full +
                 if w.flags.rng.with_chance(partial) {
        1
    } else {
        0
    };
    apply_damage(w, e, damage);
}

/// Actually subtract points from the entity's hit points. Called from
/// damage method.
fn apply_damage(w: &mut World, e: Entity, amount: i32) {
    if amount <= 0 {
        return;
    }
    let max_hp = query::max_hp(w, e);

    let is_killed = w.ecs.health.get_mut(e).map_or(false, |h| {
        h.wounds += amount;
        h.wounds >= max_hp
    });

    msg::push(::Msg::Damage(e));

    if is_killed {
        kill(w, e);
    }
}

pub fn kill(w: &mut World, e: Entity) {
    // We use not being present in the spatial index as a shorthand for being
    // dead.
    w.spatial.remove(e);
}

////////////////////////////////////////////////////////////////////////

static SAVE_FILENAME: &'static str = "magog_save.dat";

pub fn save_game(w: &World) {
    // Only save if there's still a living player around.
    if query::game_over(w) {
        return;
    }

    let mut file = File::create(SAVE_FILENAME).expect("Opening save file failed");
    w.save(&mut file).expect("Saving game failed");
}

pub fn load_game() -> Result<World, ()> {
    if !save_exists() {
        return Err(());
    }
    let path = Path::new(SAVE_FILENAME);
    let mut file = File::open(&path).expect("Opening save file failed");
    // TODO: Use separate error code instead of panic.
    Ok(World::load(&mut file).expect("Invalid save file"))
}

pub fn _delete_save() {
    let _ = fs::remove_file(SAVE_FILENAME);
}

pub fn save_exists() -> bool {
    fs::metadata(SAVE_FILENAME).is_ok()
}
<commit_msg>Reactivate gib events<commit_after>/*! Functions for changing world and entity state. */

use std::path::Path;
use std::fs::{self, File};
use rand::Rng;
use calx::{Dijkstra, Dir6, HexFov, HexGeom, RngExt};
use calx_ecs::Entity;
use world::World;
use area;
use location::Location;
use item::Slot;
use components::{CompositeStats, BrainState};
use query::{self, ControlState};
use msg;

/// Player input action.
#[derive(Copy, Eq, PartialEq, Clone, Debug, Serialize, Deserialize)]
pub enum Input {
    /// Take a step in the given direction.
    Step(Dir6),
    /// Melee attack in the given direction.
    Melee(Dir6),
    /// Shoot in the given direction.
    Shoot(Dir6),
    /// Do nothing for a turn.
    Pass,
}

/// Top-level game state update function. Only valid to call if
/// control_state() returned ReadyToUpdate.
pub fn update(w: &mut World) {
    assert!(query::control_state(w) == ControlState::ReadyToUpdate);

    clean_dead(w);
    ai_main(w);

    w.flags.tick += 1;
    w.flags.player_acted = false;
}

/// Run AI for all autonomous mobs.
fn ai_main(w: &mut World) {
    let actives: Vec<Entity> = w.ecs.brain.iter().map(|(&e, _)| e).collect();
    for e in actives.into_iter() {
        update_entity(w, e);
    }
}

fn clean_dead(w: &mut World) {
    let dead: Vec<Entity> = w.ecs.iter().filter(|&&e| !query::is_alive(w, e)).map(|&e| e).collect();
    for e in dead.into_iter() {
        w.ecs.remove(e);
    }
}

pub fn update_entity(w: &mut World, e: Entity) {
    if query::is_mob(w, e) && !query::is_player(w, e) && query::ticks_this_frame(w, e) {
        mob_ai(w, e);
    }
}

pub fn mob_ai(w: &mut World, e: Entity) {
    assert!(query::is_mob(w, e));
    assert!(!query::is_player(w, e));
    assert!(query::ticks_this_frame(w, e));

    if query::brain_state(w, e) == Some(BrainState::Asleep) {
        let self_loc = query::location(w, e).unwrap();
        // Mob is inactive, but may wake up if it spots the player.
        if let Some(player_loc) = query::player(w).and_then(|e| query::location(w, e)) {
            // TODO: Line-of-sight, stealth concerns, other enemies than
            // player etc.
            if let Some(d) = player_loc.distance_from(self_loc) {
                if d < 6 {
                    wake_up(w, e);
                }
            }
        }

        return;
    }

    if let Some(p) = query::player(w) {
        let loc = query::location(w, e).expect("no location");

        let vec_to_enemy = loc.v2_at(query::location(w, p).expect("no location"));
        if let Some(v) = vec_to_enemy {
            if v.hex_dist() == 1 {
                // Melee range, hit.
                melee(w, e, Dir6::from_v2(v));
            } else {
                // Walk towards.
                let pathing_depth = 16;
                let pathing = Dijkstra::new(vec![query::location(w, p).expect("no location")],
                                            |&loc| !query::blocks_walk(w, loc),
                                            pathing_depth);

                let steps = pathing.sorted_neighbors(&loc);
                if steps.len() > 0 {
                    step(w,
                         e,
                         loc.dir6_towards(steps[0]).expect("No loc pair orientation"));
                } else {
                    let dir = w.flags.rng.gen();
                    step(w, e, dir);
                    // TODO: Fall asleep if things get boring.
                }
            }
        }
    }
}

/// Give player input. Only valid to call if control_state() returned
/// AwaitingInput.
pub fn input(w: &mut World, input: Input) {
    assert!(query::control_state(w) == ControlState::AwaitingInput);
    let p = query::player(w).expect("No player to receive input");
    match input {
        Input::Step(d) => {
            step(w, p, d);
        }
        Input::Melee(d) => {
            melee(w, p, d);
        }
        Input::Shoot(d) => {
            shoot(w, p, d);
        }
        Input::Pass => {}
    }
    w.flags.player_acted = true;

    // Run one world update cycle right away, so that we don't get awkward
    // single frames rendered where the player has acted and the rest of the
    // world hasn't.
    if query::control_state(w) == ControlState::ReadyToUpdate {
        update(w);
    }
}

/// Try to move the entity in direction.
pub fn step(w: &mut World, e: Entity, dir: Dir6) {
    let target_loc = query::location(w, e).unwrap() + dir.to_v2();
    if query::can_enter(w, e, target_loc) {
        place_entity(w, e, target_loc);
    }
}

/// Fight in a direction.
pub fn melee(w: &mut World, e: Entity, dir: Dir6) {
    let loc = query::location(w, e).expect("no location") + dir.to_v2();
    if let Some(e) = query::mob_at(w, loc) {
        let us = query::stats(w, e);
        damage(w, e, us.power + us.attack);
    }
}

pub fn shoot(w: &mut World, e: Entity, dir: Dir6) {
    unimplemented!();
    /*
    let stats = self.stats();

    if stats.ranged_range > 0 {
        action::shoot(self.location().unwrap(), dir, stats.ranged_range, stats.ranged_power);
    }
    */
}

pub fn pick_up(w: &mut World, picker: Entity, item: Entity) -> bool {
    if !query::can_be_picked_up(w, item) {
        return false;
    }

    match query::free_bag_slot(w, picker) {
        Some(slot) => {
            equip(w, item, picker, slot);
            return true;
        }
        // Inventory full.
        None => {
            return false;
        }
    }
}

/// Equip an item to a slot. Slot must be empty.
pub fn equip(w: &mut World, item: Entity, e: Entity, slot: Slot) {
    w.spatial.equip(item, e, slot);
    recompose_stats(w, e)
}

/// Generate composed stats from base stats and the stats of equipped items.
/// This function must be called after any operation that changes the composed
/// stats affecting state of an entity.
pub fn recompose_stats(w: &mut World, e: Entity) {
    let mut stats = query::base_stats(w, e);
    for &slot in [Slot::Body,
                  Slot::Feet,
                  Slot::Head,
                  Slot::Melee,
                  Slot::Ranged,
                  Slot::TrinketF,
                  Slot::TrinketG,
                  Slot::TrinketH,
                  Slot::TrinketI]
                     .iter() {
        if let Some(item) = w.spatial.entity_equipped(e, slot) {
            stats = stats + query::stats(w, item);
        }
    }

    w.ecs.composite_stats.insert(e, CompositeStats(stats));
}

pub fn place_entity(w: &mut World, e: Entity, loc: Location) {
    w.spatial.insert_at(e, loc);
    after_entity_move(w, e);
}

/// Clear map memory of an entity.
pub fn forget_map(w: &mut World, e: Entity) {
    w.ecs.map_memory.get_mut(e).map(|mm| {
        mm.seen.clear();
        mm.remembered.clear();
    });
}

/// Callback when entity moves to a new location.
fn after_entity_move(w: &mut World, e: Entity) {
    let loc = query::location(w, e).expect("Entity must have location for callback");

    do_fov(w, e);

    for item in w.spatial.entities_at(loc).into_iter() {
        if item != e {
            on_step_on(w, e, item);
        }
    }

    if query::is_player(w, e) {
        w.flags.camera = loc;

        if query::terrain(w, loc).is_exit() {
            area::next_level(w);
        }
    }
}

pub fn do_fov(w: &mut World, e: Entity) {
    if !w.ecs.map_memory.contains(e) {
        return;
    }

    if let Some(loc) = query::location(w, e) {
        let sight_range = 12;
        let seen_locs: Vec<Location> = HexFov::new(|pt| query::blocks_sight(w, (loc + pt)),
                                                   sight_range)
                                           .fake_isometric()
                                           .map(|pt| loc + pt)
                                           .collect();
        let mut mm = &mut w.ecs.map_memory[e];
        mm.seen.clear();
        mm.seen.extend(seen_locs.clone().into_iter());
        mm.remembered.extend(seen_locs.into_iter());
    }
}

pub fn on_step_on(w: &mut World, stepper: Entity, item: Entity) {
    if query::is_instant_item(w, item) && query::is_player(w, stepper) {
        unimplemented!();
    }
}

pub fn set_brain_state(w: &mut World, e: Entity, state: BrainState) {
    w.ecs.brain.get_mut(e).map(|b| b.state = state);
}

pub fn wake_up(w: &mut World, e: Entity) {
    if query::brain_state(w, e) == Some(BrainState::Asleep) {
        set_brain_state(w, e, BrainState::Hunting);
    }
}

/// Apply damage to entity, subject to damage reduction.
pub fn damage(w: &mut World, e: Entity, mut power: i32) {
    let stats = query::stats(w, e);
    power -= stats.protection;

    if power < 1 {
        // Give damage a bit under the reduction an off-chance to hit.
        // Power that's too low can't do anything though.
        if power >= -5 {
            power = 1;
        } else {
            return;
        }
    }

    // Every five points of power is one certain hit.
    let full = power / 5;
    // The fractional points are one probabilistic hit.
    let partial = (power % 5) as f32 / 5.0;

    let damage = full +
                 if w.flags.rng.with_chance(partial) {
        1
    } else {
        0
    };
    apply_damage(w, e, damage);
}

/// Actually subtract points from the entity's hit points. Called from
/// damage method.
fn apply_damage(w: &mut World, e: Entity, amount: i32) {
    if amount <= 0 {
        return;
    }
    let max_hp = query::max_hp(w, e);

    let is_killed = w.ecs.health.get_mut(e).map_or(false, |h| {
        h.wounds += amount;
        h.wounds >= max_hp
    });

    msg::push(::Msg::Damage(e));

    if is_killed {
        query::location(w, e).map(|loc| msg::push(::Msg::Gib(loc)));
        kill(w, e);
    }
}

pub fn kill(w: &mut World, e: Entity) {
    // We use not being present in the spatial index as a shorthand for being
    // dead.
    w.spatial.remove(e);
}

////////////////////////////////////////////////////////////////////////

static SAVE_FILENAME: &'static str = "magog_save.dat";

pub fn save_game(w: &World) {
    // Only save if there's still a living player around.
    if query::game_over(w) {
        return;
    }

    let mut file = File::create(SAVE_FILENAME).expect("Opening save file failed");
    w.save(&mut file).expect("Saving game failed");
}

pub fn load_game() -> Result<World, ()> {
    if !save_exists() {
        return Err(());
    }
    let path = Path::new(SAVE_FILENAME);
    let mut file = File::open(&path).expect("Opening save file failed");
    // TODO: Use separate error code instead of panic.
    Ok(World::load(&mut file).expect("Invalid save file"))
}

pub fn _delete_save() {
    let _ = fs::remove_file(SAVE_FILENAME);
}

pub fn save_exists() -> bool {
    fs::metadata(SAVE_FILENAME).is_ok()
}
<|endoftext|>


<commit_before>
use prelude::*;
use super::common::*;

use std::iter;
use std::collections::BTreeMap;

use idents;
use utils;
use tyhandlers::{Direction, ModelTypeSystem};
use model;
use methodinfo::ComMethodInfo;

extern crate proc_macro;

/// Interface level output.
#[derive(Default)]
struct InterfaceOutput {
    iid_arms : Vec<TokenStream>,
    method_impls : BTreeMap< String, MethodImpl >,
}

struct MethodImpl {

    /// _Some_ method info.
    ///
    /// This should not be depended upon for anything type system specific.
    info : ComMethodInfo,

    /// Type system specific implementation for the method.
    impls : BTreeMap< ModelTypeSystem, TokenStream >,
}

impl MethodImpl {
    pub fn new( mi : ComMethodInfo ) -> Self {
        MethodImpl {
            info: mi,
            impls : Default::default(),
        }
    }
}

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: TokenStreamNightly,
    item_tokens: TokenStreamNightly,
) -> Result<TokenStreamNightly, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(),
            attr_tokens.into(),
            &item_tokens.to_string() )?;
    let itf_ident = itf.name();

    let mut itf_output = InterfaceOutput::default();
    for ( &ts, itf_variant ) in itf.variants() {
        process_itf_variant(
                &itf, ts, itf_variant,
                &mut output, &mut itf_output );
    }

    if itf.item_type() == utils::InterfaceType::Trait {
        let mut impls = vec![];
        for ( _, method ) in itf_output.method_impls.iter() {

            let mut impl_branches = vec![];
            for ( ts, method_ts_impl ) in method.impls.iter() {

                let ts_tokens = ts.as_typesystem_tokens();
                impl_branches.push( quote!(
                    if let Some( comptr ) = ComItf::maybe_ptr( self, #ts_tokens ) {
                        #method_ts_impl
                    }
                ) );
            }

            // Format the method arguments into tokens.
            let impl_args = method.info.args.iter().map( |ca| {
                let name = &ca.name;
                let ty = &ca.ty;
                quote!( #name : #ty )
            } );

            let unsafety = if method.info.is_unsafe { quote!( unsafe ) } else { quote!() };
            let self_arg = &method.info.rust_self_arg;
            let method_rust_ident = &method.info.display_name;
            let return_ty = &method.info.rust_return_ty;

            // Rust to COM implementation.
            impls.push( quote!(
                #unsafety fn #method_rust_ident(
                    #self_arg, #( #impl_args ),*
                ) -> #return_ty {

                    #[allow(unused_imports)]
                    use ::intercom::ComInto;
                    #[allow(unused_imports)]
                    use ::intercom::ErrorValue;

                    // Try the available type systems.
                    #( #impl_branches )*

                    // None of the type system pointers were available,
                    // which means this is a null reference.
                    < #return_ty as ::intercom::ErrorValue >::from_com_error(
                            ::intercom::ComError::E_POINTER.into() )
                }
            ) );
        }

        let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
        output.push( quote!(
            #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                #( #impls )*
            }
        ) );
    }

    // Implement the ComInterface for the trait.
    let iid_arms = itf_output.iid_arms;
    let ( deref_impl, deref_ret ) = if itf.item_type() == utils::InterfaceType::Trait {
        (
            quote!( com_itf ),
            quote!( &( #itf_ident + 'static ) )
        )
    } else {

        // Note this is _extremely_ dangerous.
        //
        // Essentially we are assuming here that every #itf_ident pointer represents
        // a ComBox structure that we have created. This will fail the moment
        // the user code implements #itf_ident interface on their own and passes
        // that back to us.
        //
        // There's no real way to get this to work and we might want to just remove
        // the possibility to do 'implicit' interfaces by just impling the struct.
        (
            quote!(
                let some_iunk : &::intercom::ComItf<::intercom::IUnknown> = com_itf.as_ref();
                let iunknown_iid = ::intercom::IUnknown::iid(
                        ::intercom::TypeSystem::Automation )
                            .expect( "IUnknown must have Automation IID" );
                let primary_iunk = some_iunk.query_interface( iunknown_iid )
                        .expect( "All types must implement IUnknown" );

                let combox : *mut ::intercom::ComBox< #itf_ident > =
                        primary_iunk as *mut ::intercom::ComBox< #itf_ident >;
                unsafe {

                    // We are already holding a reference to the 'self', which should
                    // keep this alive. We don't need to maintain a lifetime of the
                    // queried interface.
                    ::intercom::ComBox::release( combox );

                    // Deref.
                    use std::ops::Deref;
                    (*combox).deref()
                }
            ),
            quote!( & #itf_ident )
        )
    };

    output.push( quote!(
        impl ::intercom::ComInterface for #itf_ident {

            #[doc = "Returns the IID of the requested interface."]
            fn iid( ts : ::intercom::TypeSystem ) -> Option< &'static ::intercom::IID > {
                match ts {
                    #( #iid_arms ),*
                }
            }

            fn deref(
                com_itf : &::intercom::ComItf< #itf_ident >
            ) -> #deref_ret {
                #deref_impl
            }
        }
    ) );

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

/// Processes the interface type system variant.
///
/// # Arguments
///
/// * `itf` - Interface details.
/// * `ts` - Type system the variant represents.
/// * `itf_variant` - Interface variant details.
/// * `output` - Direct output emitted for each interface variant.
/// * `itf_output` - Interface variant data for the interface level output.
fn process_itf_variant(
    itf : &model::ComInterface,
    ts : ModelTypeSystem,
    itf_variant : &model::ComInterfaceVariant,
    output : &mut Vec<TokenStream>,
    itf_output : &mut InterfaceOutput,
) {

    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf_variant.unique_name() );
    let vtable_ident = idents::vtable_struct( itf_variant.unique_name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // Construct the iid(ts) match arm for this type system.
    let ts_match = ts.as_typesystem_tokens();
    itf_output.iid_arms.push( quote!( #ts_match => Some( & #iid_ident ) ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut vtbl_fields = vec![];
    if let Some( ref base ) = *itf.base_interface() {
        let vtbl = match base.to_string().as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( &base ); quote!( #vtbl ) }
        };
        vtbl_fields.push( quote!( pub __base : #vtbl ) );
    }

    // Gather all the trait methods for the remaining vtable fields.
    let calling_convention = get_calling_convetion();
    for method_info in itf_variant.methods() {

        let method_ident = &method_info.unique_name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty( com_arg.dir );
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        vtbl_fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );

        let method_name = method_info.display_name.to_string();
        if ! itf_output.method_impls.contains_key( &method_name ) {
            itf_output.method_impls.insert(
                    method_name.clone(),
                    MethodImpl::new( method_info.clone() ) );
        }

        let method_impl = &mut itf_output.method_impls.get_mut( &method_name )
                .expect( "We just ensured this exists three lines up... ;_;" );
        method_impl.impls.insert(
                itf_variant.type_system(),
                rust_to_com_delegate( itf_variant, method_info, &vtable_ident ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #vtbl_fields, )* }
    ) );
}

/// Creates the functions responsible for delegating calls from Rust to COM
/// interfaces.
///
/// # Arguments
///
/// * `itf_variant` - Interface variant details.
/// * `method_info` - Method to delegate.
/// * `vtable_ident` - Vtable to use for the delegation.
fn rust_to_com_delegate(
    itf_variant : &model::ComInterfaceVariant,
    method_info : &ComMethodInfo,
    vtable_ident : &Ident,
) -> TokenStream {

    // The COM out-arguments that mirror the Rust return value will
    // require temporary variables during the COM call. Format their
    // declarations.
    let out_arg_declarations = method_info.returnhandler.com_out_args()
            .iter()
            .map( |ca| {
                let ident = &ca.name;
                let ty = &ca.handler.com_ty( Direction::Retval );
                let default = ca.handler.default_value();
                quote!( let mut #ident : #ty = #default; )
            } ).collect::<Vec<_>>();

    // Format the in and out parameters for the COM call.
    let ( temporaries, params ) : ( Vec<_>, Vec<_> ) = method_info.raw_com_args()
            .into_iter()
            .map( |com_arg| {
                let name = com_arg.name;
                match com_arg.dir {
                    Direction::In => {
                        let param = com_arg.handler.rust_to_com( &name, Direction::In );
                        ( param.temporary, param.value )
                    },
                    Direction::Out | Direction::Retval
                        => ( None, quote!( &mut #name ) ),
                }
            } )
            .unzip();

    // Combine the parameters into the final parameter list.
    // This includes the 'this' pointer and both the IN and OUT
    // parameters.
    let params = iter::once( quote!( comptr.ptr ) ).chain( params );

    // Create the return statement.
    let return_ident = Ident::new( "__result", Span::call_site() );
    let return_statement = method_info
            .returnhandler
            .com_to_rust_return( &return_ident );

    // Resolve some of the fields needed for quote.
    let method_ident = &method_info.unique_name;
    let return_ty = &method_info.rust_return_ty;
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );

    // Construct the final method.
    quote!(
        let vtbl = comptr.ptr as *const *const #vtable_ident;

        #( #temporaries )*

        // Use an IIFE to act as a try/catch block. The various template
        // substitutions might end up using ?'s for error handling. The IIFE allows
        // us to handle the results here immediately.
        #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
        let result : Result< #return_ty, ::intercom::ComError > = ( || unsafe {
            #( #out_arg_declarations )*;
            let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );

            let INTERCOM_iid = #iid_tokens;
            Ok( { #return_statement } )
        } )();

        return match result {
            Ok( v ) => v,
            Err( err ) => < #return_ty as ::intercom::ErrorValue >::from_com_error( err ),
        };
    )
}
<commit_msg>Fix attribute tests<commit_after>
use prelude::*;
use super::common::*;

use std::iter;
use std::collections::BTreeMap;

use idents;
use utils;
use tyhandlers::{Direction, ModelTypeSystem};
use model;
use methodinfo::ComMethodInfo;

extern crate proc_macro;

/// Interface level output.
#[derive(Default)]
struct InterfaceOutput {
    iid_arms : Vec<TokenStream>,
    method_impls : BTreeMap< String, MethodImpl >,
}

struct MethodImpl {

    /// _Some_ method info.
    ///
    /// This should not be depended upon for anything type system specific.
    info : ComMethodInfo,

    /// Type system specific implementation for the method.
    impls : BTreeMap< ModelTypeSystem, TokenStream >,
}

impl MethodImpl {
    pub fn new( mi : ComMethodInfo ) -> Self {
        MethodImpl {
            info: mi,
            impls : Default::default(),
        }
    }
}

/// Expands the `com_interface` attribute.
///
/// The attribute expansion results in the following items:
///
/// - Global IID for the interface.
/// - Virtual table struct definition for the interface.
/// - Implementation for the delegating methods when calling the COM interface
///   from Rust.
pub fn expand_com_interface(
    attr_tokens: TokenStreamNightly,
    item_tokens: TokenStreamNightly,
) -> Result<TokenStreamNightly, model::ParseError>
{
    // Parse the attribute.
    let mut output = vec![];
    let itf = model::ComInterface::parse(
            &lib_name(),
            attr_tokens.into(),
            &item_tokens.to_string() )?;
    let itf_ident = itf.name();

    let mut itf_output = InterfaceOutput::default();
    for ( &ts, itf_variant ) in itf.variants() {
        process_itf_variant(
                &itf, ts, itf_variant,
                &mut output, &mut itf_output );
    }

    if itf.item_type() == utils::InterfaceType::Trait {
        let mut impls = vec![];
        for ( _, method ) in itf_output.method_impls.iter() {

            let mut impl_branches = vec![];
            for ( ts, method_ts_impl ) in method.impls.iter() {

                let ts_tokens = ts.as_typesystem_tokens();
                impl_branches.push( quote!(
                    if let Some( comptr ) = ComItf::maybe_ptr( self, #ts_tokens ) {
                        #method_ts_impl
                    }
                ) );
            }

            // Format the method arguments into tokens.
            let impl_args = method.info.args.iter().map( |ca| {
                let name = &ca.name;
                let ty = &ca.ty;
                quote!( #name : #ty )
            } );

            let unsafety = if method.info.is_unsafe { quote!( unsafe ) } else { quote!() };
            let self_arg = &method.info.rust_self_arg;
            let method_rust_ident = &method.info.display_name;
            let return_ty = &method.info.rust_return_ty;

            // Rust to COM implementation.
            impls.push( quote!(
                #unsafety fn #method_rust_ident(
                    #self_arg, #( #impl_args ),*
                ) -> #return_ty {

                    #[allow(unused_imports)]
                    use ::intercom::ComInto;
                    #[allow(unused_imports)]
                    use ::intercom::ErrorValue;

                    // Try the available type systems.
                    #( #impl_branches )*

                    // None of the type system pointers were available,
                    // which means this is a null reference.
                    < #return_ty as ::intercom::ErrorValue >::from_com_error(
                            ::intercom::ComError::E_POINTER.into() )
                }
            ) );
        }

        let unsafety = if itf.is_unsafe() { quote!( unsafe ) } else { quote!() };
        output.push( quote!(
            #unsafety impl #itf_ident for ::intercom::ComItf< #itf_ident > {
                #( #impls )*
            }
        ) );
    }

    // Implement the ComInterface for the trait.
    let iid_arms = itf_output.iid_arms;
    let ( deref_impl, deref_ret ) = if itf.item_type() == utils::InterfaceType::Trait {
        (
            quote!( com_itf ),
            quote!( &( #itf_ident + 'static ) )
        )
    } else {

        // Note this is _extremely_ dangerous.
        //
        // Essentially we are assuming here that every #itf_ident pointer represents
        // a ComBox structure that we have created. This will fail the moment
        // the user code implements #itf_ident interface on their own and passes
        // that back to us.
        //
        // There's no real way to get this to work and we might want to just remove
        // the possibility to do 'implicit' interfaces by just impling the struct.
        (
            quote!(
                let some_iunk : &::intercom::ComItf<::intercom::IUnknown> = com_itf.as_ref();
                let iunknown_iid = ::intercom::IUnknown::iid(
                        ::intercom::TypeSystem::Automation )
                            .expect( "IUnknown must have Automation IID" );
                let primary_iunk = some_iunk.query_interface( iunknown_iid )
                        .expect( "All types must implement IUnknown" );

                let combox : *mut ::intercom::ComBox< #itf_ident > =
                        primary_iunk as *mut ::intercom::ComBox< #itf_ident >;
                unsafe {

                    // We are already holding a reference to the 'self', which should
                    // keep this alive. We don't need to maintain a lifetime of the
                    // queried interface.
                    ::intercom::ComBox::release( combox );

                    // Deref.
                    use std::ops::Deref;
                    (*combox).deref()
                }
            ),
            quote!( & #itf_ident )
        )
    };

    output.push( quote!(
        impl ::intercom::ComInterface for #itf_ident {

            #[doc = "Returns the IID of the requested interface."]
            fn iid( ts : ::intercom::TypeSystem ) -> Option< &'static ::intercom::IID > {
                match ts {
                    #( #iid_arms ),*
                }
            }

            fn deref(
                com_itf : &::intercom::ComItf< #itf_ident >
            ) -> #deref_ret {
                #deref_impl
            }
        }
    ) );

    Ok( tokens_to_tokenstream( item_tokens, output ) )
}

/// Processes the interface type system variant.
///
/// # Arguments
///
/// * `itf` - Interface details.
/// * `ts` - Type system the variant represents.
/// * `itf_variant` - Interface variant details.
/// * `output` - Direct output emitted for each interface variant.
/// * `itf_output` - Interface variant data for the interface level output.
fn process_itf_variant(
    itf : &model::ComInterface,
    ts : ModelTypeSystem,
    itf_variant : &model::ComInterfaceVariant,
    output : &mut Vec<TokenStream>,
    itf_output : &mut InterfaceOutput,
) {

    let itf_ident = itf.name();
    let visibility = itf.visibility();
    let iid_ident = idents::iid( itf_variant.unique_name() );
    let vtable_ident = idents::vtable_struct( itf_variant.unique_name() );

    // IID_IInterface GUID.
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );
    let iid_doc = format!( "`{}` interface ID.", itf_ident );
    output.push( quote!(
        #[doc = #iid_doc]
        #[allow(non_upper_case_globals)]
        #visibility const #iid_ident : ::intercom::IID = #iid_tokens;
    ) );

    // Construct the iid(ts) match arm for this type system.
    let ts_match = ts.as_typesystem_tokens();
    itf_output.iid_arms.push( quote!( #ts_match => Some( & #iid_ident ) ) );

    // Create a vector for the virtual table fields and insert the base
    // interface virtual table in it if required.
    let mut vtbl_fields = vec![];
    if let Some( ref base ) = *itf.base_interface() {
        let vtbl = match base.to_string().as_ref() {
            "IUnknown" => quote!( ::intercom::IUnknownVtbl ),
            _ => { let vtbl = idents::vtable_struct( &base ); quote!( #vtbl ) }
        };
        vtbl_fields.push( quote!( pub __base : #vtbl ) );
    }

    // Gather all the trait methods for the remaining vtable fields.
    let calling_convention = get_calling_convetion();
    for method_info in itf_variant.methods() {

        let method_ident = &method_info.unique_name;
        let in_out_args = method_info.raw_com_args()
                .into_iter()
                .map( |com_arg| {
                    let name = &com_arg.name;
                    let com_ty = &com_arg.handler.com_ty( com_arg.dir );
                    let dir = match com_arg.dir {
                        Direction::In => quote!(),
                        Direction::Out | Direction::Retval => quote!( *mut )
                    };
                    quote!( #name : #dir #com_ty )
                } );
        let self_arg = quote!( self_vtable : ::intercom::RawComPtr );
        let args = iter::once( self_arg ).chain( in_out_args );

        // Create the vtable field and add it to the vector of fields.
        let ret_ty = method_info.returnhandler.com_ty();
        vtbl_fields.push( quote!(
            pub #method_ident :
                unsafe extern #calling_convention fn( #( #args ),* ) -> #ret_ty
        ) );

        let method_name = method_info.display_name.to_string();
        if ! itf_output.method_impls.contains_key( &method_name ) {
            itf_output.method_impls.insert(
                    method_name.clone(),
                    MethodImpl::new( method_info.clone() ) );
        }

        let method_impl = &mut itf_output.method_impls.get_mut( &method_name )
                .expect( "We just ensured this exists three lines up... ;_;" );
        method_impl.impls.insert(
                itf_variant.type_system(),
                rust_to_com_delegate( itf_variant, method_info, &vtable_ident ) );
    }

    // Create the vtable. We've already gathered all the vtable method
    // pointer fields so defining the struct is simple enough.
    output.push( quote!(
        #[allow(non_camel_case_types)]
        #[repr(C)]
        #[doc(hidden)]
        #visibility struct #vtable_ident { #( #vtbl_fields, )* }
    ) );
}

/// Creates the functions responsible for delegating calls from Rust to COM
/// interfaces.
///
/// # Arguments
///
/// * `itf_variant` - Interface variant details.
/// * `method_info` - Method to delegate.
/// * `vtable_ident` - Vtable to use for the delegation.
fn rust_to_com_delegate(
    itf_variant : &model::ComInterfaceVariant,
    method_info : &ComMethodInfo,
    vtable_ident : &Ident,
) -> TokenStream {

    // The COM out-arguments that mirror the Rust return value will
    // require temporary variables during the COM call. Format their
    // declarations.
    let out_arg_declarations = method_info.returnhandler.com_out_args()
            .iter()
            .map( |ca| {
                let ident = &ca.name;
                let ty = &ca.handler.com_ty( Direction::Retval );
                let default = ca.handler.default_value();
                quote!( let mut #ident : #ty = #default; )
            } ).collect::<Vec<_>>();

    // Format the in and out parameters for the COM call.
    let ( temporaries, params ) : ( Vec<_>, Vec<_> ) = method_info.raw_com_args()
            .into_iter()
            .map( |com_arg| {
                let name = com_arg.name;
                match com_arg.dir {
                    Direction::In => {
                        let param = com_arg.handler.rust_to_com( &name, Direction::In );
                        ( param.temporary, param.value )
                    },
                    Direction::Out | Direction::Retval
                        => ( None, quote!( &mut #name ) ),
                }
            } )
            .unzip();

    // Combine the parameters into the final parameter list.
    // This includes the 'this' pointer and both the IN and OUT
    // parameters.
    let params = iter::once( quote!( comptr.ptr ) ).chain( params );

    // Create the return statement.
    let return_ident = Ident::new( "__result", Span::call_site() );
    let return_statement = method_info
            .returnhandler
            .com_to_rust_return( &return_ident );

    // Resolve some of the fields needed for quote.
    let method_ident = &method_info.unique_name;
    let return_ty = &method_info.rust_return_ty;
    let iid_tokens = utils::get_guid_tokens( itf_variant.iid() );

    // Construct the final method.
    quote!(
        let vtbl = comptr.ptr as *const *const #vtable_ident;

        #( #temporaries )*

        // Use an IIFE to act as a try/catch block. The various template
        // substitutions might end up using ?'s for error handling. The IIFE allows
        // us to handle the results here immediately.
        #[allow(unused_unsafe)]  // The fn itself _might_ be unsafe.
        let result : Result< #return_ty, ::intercom::ComError > = ( || unsafe {
            #( #out_arg_declarations )*
            let #return_ident = ((**vtbl).#method_ident)( #( #params ),* );

            let INTERCOM_iid = #iid_tokens;
            Ok( { #return_statement } )
        } )();

        return match result {
            Ok( v ) => v,
            Err( err ) => < #return_ty as ::intercom::ErrorValue >::from_com_error( err ),
        };
    )
}
<|endoftext|>


<commit_before><commit_msg>Add test that ok-wrapping handles type aliases<commit_after><|endoftext|>