<commit_before>use core::{BytePos, ByteRange};

/// Type of the string
#[derive(Clone, Copy, Debug)]
enum StrStyle {
    /// normal string starts with "
    Cooked,
    /// Raw(n) => raw string started with n #s
    Raw(usize),
}

#[derive(Clone, Copy)]
enum State {
    Code,
    Comment,
    CommentBlock,
    String(StrStyle),
    Char,
    Finished,
}

#[derive(Clone, Copy)]
pub struct CodeIndicesIter<'a> {
    src: &'a str,
    pos: BytePos,
    state: State,
}

impl<'a> Iterator for CodeIndicesIter<'a> {
    type Item = ByteRange;

    fn next(&mut self) -> Option<ByteRange> {
        match self.state {
            State::Code => Some(self.code()),
            State::Comment => Some(self.comment()),
            State::CommentBlock => Some(self.comment_block()),
            State::String(style) => Some(self.string(style)),
            State::Char => Some(self.char()),
            State::Finished => None,
        }
    }
}

impl<'a> CodeIndicesIter<'a> {
    fn code(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let start = match self.state {
            State::String(_) | State::Char => pos.decrement(), // include quote
            _ => pos,
        };
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if src_bytes.len() > pos.0 => match src_bytes[pos.0] {
                    b'/' => {
                        self.state = State::Comment;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    b'*' => {
                        self.state = State::CommentBlock;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    _ => {}
                },
                b'"' => {
                    // "
                    let str_type = self.detect_str_type(pos);
                    self.state = State::String(str_type);
                    self.pos = pos;
                    return ByteRange::new(start, pos); // include dblquotes
                }
                b'\'' => {
                    // single quotes are also used for lifetimes, so we need to
                    // be confident that this is not a lifetime.
                    // Look for backslash starting the escape, or a closing quote:
                    if src_bytes.len() > pos.increment().0
                        && (src_bytes[pos.0] == b'\\' || src_bytes[pos.increment().0] == b'\'')
                    {
                        self.state = State::Char;
                        self.pos = pos;
                        return ByteRange::new(start, pos); // include single quote
                    }
                }
                _ => {}
            }
        }

        self.state = State::Finished;
        ByteRange::new(start, self.src.len().into())
    }

    fn comment(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            if b == b'\n' {
                if pos.0 + 2 <= src_bytes.len() && src_bytes[pos.0..pos.0 + 2] == [b'/', b'/'] {
                    continue;
                }
                break;
            }
        }
        self.pos = pos;
        self.code()
    }

    fn comment_block(&mut self) -> ByteRange {
        let mut nesting_level = 0usize;
        let mut prev = b' ';
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if prev == b'*' => {
                    if nesting_level == 0 {
                        break;
                    } else {
                        nesting_level -= 1;
                    }
                }
                b'*' if prev == b'/' => {
                    nesting_level += 1;
                }
                _ => {
                    prev = b;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn string(&mut self, str_type: StrStyle) -> ByteRange {
        let src_bytes = self.src.as_bytes();
        let mut pos = self.pos;
        match str_type {
            StrStyle::Raw(level) => {
                // raw string (e.g. br#"\"#)
                #[derive(Debug)]
                enum SharpState {
                    Sharp {
                        // number of preceding #s
                        num_sharps: usize,
                        // Position of last "
                        quote_pos: BytePos,
                    },
                    None, // No preceding "##...
                }
                let mut cur_state = SharpState::None;
                let mut end_was_found = false;
                // detect corresponding end(if start is r##", "##) greedily
                for (i, &b) in src_bytes[self.pos.0..].iter().enumerate() {
                    match cur_state {
                        SharpState::Sharp {
                            num_sharps,
                            quote_pos,
                        } => {
                            cur_state = match b {
                                b'#' => SharpState::Sharp {
                                    num_sharps: num_sharps + 1,
                                    quote_pos,
                                },
                                b'"' => SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                },
                                _ => SharpState::None,
                            }
                        }
                        SharpState::None => {
                            if b == b'"' {
                                cur_state = SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                };
                            }
                        }
                    }
                    if let SharpState::Sharp {
                        num_sharps,
                        quote_pos,
                    } = cur_state
                    {
                        if num_sharps == level {
                            end_was_found = true;
                            pos += quote_pos.increment();
                            break;
                        }
                    }
                }
                if !end_was_found {
                    pos = src_bytes.len().into();
                }
            }
            StrStyle::Cooked => {
                let mut is_not_escaped = true;
                for &b in &src_bytes[pos.0..] {
                    pos = pos.increment();
                    match b {
                        b'"' if is_not_escaped => {
                            break;
                        } // "
                        b'\\' => {
                            is_not_escaped = !is_not_escaped;
                        }
                        _ => {
                            is_not_escaped = true;
                        }
                    }
                }
            }
        };
        self.pos = pos;
        self.code()
    }

    fn char(&mut self) -> ByteRange {
        let mut is_not_escaped = true;
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'\'' if is_not_escaped => {
                    break;
                }
                b'\\' => {
                    is_not_escaped = !is_not_escaped;
                }
                _ => {
                    is_not_escaped = true;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn detect_str_type(&self, pos: BytePos) -> StrStyle {
        let src_bytes = self.src.as_bytes();
        let mut sharp = 0;
        if pos == BytePos::ZERO {
            return StrStyle::Cooked;
        }
        // now pos is at one byte after ", so we have to start at pos - 2
        for &b in src_bytes[..pos.decrement().0].iter().rev() {
            match b {
                b'#' => sharp += 1,
                b'r' => return StrStyle::Raw(sharp),
                _ => return StrStyle::Cooked,
            }
        }
        StrStyle::Cooked
    }
}

/// Returns indices of chunks of code (minus comments and string contents)
pub fn code_chunks(src: &str) -> CodeIndicesIter {
    CodeIndicesIter {
        src,
        state: State::Code,
        pos: BytePos::ZERO,
    }
}

#[cfg(test)]
mod code_indices_iter_test {
    use super::*;
    use testutils::{rejustify, slice};

    #[test]
    fn removes_a_comment() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_consecutive_comments() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    // this is more comment
    // another comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents() {
        let src = &rejustify(
            "
    this is some code \"this is a string\" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_char_contents() {
        let src = &rejustify(
            "
    this is some code \'\"\' more code \'\\x00\' and \'\\\'\' that\'s it
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' more code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' and \'", slice(src, it.next().unwrap()));
        assert_eq!("\' that\'s it", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents_with_a_comment_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a // fake comment \" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_a_comment_with_a_dbl_quote_in_it() {
        let src = &rejustify(
            "
    this is some code // comment with \" double quote
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_multiline_comment() {
        let src = &rejustify(
            "
    this is some code /* this is a
    \"multiline\" comment */some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_nesting_of_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /* block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_documentation_block_comments_nested_into_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /** documentation block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_dblquote_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a \\\" escaped dblquote fake comment \" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_raw_string_with_dangling_escape_in_it() {
        let src = &rejustify(
            "
    this is some code br\" escaped dblquote raw string \\\" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br\"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_slash_before_dblquote_in_it() {
        let src = &rejustify("
    this is some code \"string with an escaped slash, so dbl quote does end the string after all \\\\\" more code
    ");

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_tricky_bit_from_str_rs() {
        let src = &rejustify(
            "
        before(\"\\\\\'\\\\\\\"\\\\\\\\\");
        more_code(\" skip me \")
    ",
        );

        for range in code_chunks(src) {
            let range = || range.to_range();
            println!("BLOB |{}|", &src[range()]);
            if src[range()].contains("skip me") {
                panic!("{}", &src[range()]);
            }
        }
    }

    #[test]
    fn removes_nested_rawstr() {
        let src = &rejustify(
            r####"
    this is some code br###""" r##""##"### more code
    "####,
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br###\"", slice(src, it.next().unwrap()));
        assert_eq!("\"### more code", slice(src, it.next().unwrap()));
    }

}
<commit_msg>When parsing a block comment, set `prev` to `b' '`  after a '/*' or a '*/' has been found.<commit_after>use core::{BytePos, ByteRange};

/// Type of the string
#[derive(Clone, Copy, Debug)]
enum StrStyle {
    /// normal string starts with "
    Cooked,
    /// Raw(n) => raw string started with n #s
    Raw(usize),
}

#[derive(Clone, Copy)]
enum State {
    Code,
    Comment,
    CommentBlock,
    String(StrStyle),
    Char,
    Finished,
}

#[derive(Clone, Copy)]
pub struct CodeIndicesIter<'a> {
    src: &'a str,
    pos: BytePos,
    state: State,
}

impl<'a> Iterator for CodeIndicesIter<'a> {
    type Item = ByteRange;

    fn next(&mut self) -> Option<ByteRange> {
        match self.state {
            State::Code => Some(self.code()),
            State::Comment => Some(self.comment()),
            State::CommentBlock => Some(self.comment_block()),
            State::String(style) => Some(self.string(style)),
            State::Char => Some(self.char()),
            State::Finished => None,
        }
    }
}

impl<'a> CodeIndicesIter<'a> {
    fn code(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let start = match self.state {
            State::String(_) | State::Char => pos.decrement(), // include quote
            _ => pos,
        };
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if src_bytes.len() > pos.0 => match src_bytes[pos.0] {
                    b'/' => {
                        self.state = State::Comment;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    b'*' => {
                        self.state = State::CommentBlock;
                        self.pos = pos.increment();
                        return ByteRange::new(start, pos.decrement());
                    }
                    _ => {}
                },
                b'"' => {
                    // "
                    let str_type = self.detect_str_type(pos);
                    self.state = State::String(str_type);
                    self.pos = pos;
                    return ByteRange::new(start, pos); // include dblquotes
                }
                b'\'' => {
                    // single quotes are also used for lifetimes, so we need to
                    // be confident that this is not a lifetime.
                    // Look for backslash starting the escape, or a closing quote:
                    if src_bytes.len() > pos.increment().0
                        && (src_bytes[pos.0] == b'\\' || src_bytes[pos.increment().0] == b'\'')
                    {
                        self.state = State::Char;
                        self.pos = pos;
                        return ByteRange::new(start, pos); // include single quote
                    }
                }
                _ => {}
            }
        }

        self.state = State::Finished;
        ByteRange::new(start, self.src.len().into())
    }

    fn comment(&mut self) -> ByteRange {
        let mut pos = self.pos;
        let src_bytes = self.src.as_bytes();
        for &b in &src_bytes[pos.0..] {
            pos = pos.increment();
            if b == b'\n' {
                if pos.0 + 2 <= src_bytes.len() && src_bytes[pos.0..pos.0 + 2] == [b'/', b'/'] {
                    continue;
                }
                break;
            }
        }
        self.pos = pos;
        self.code()
    }

    fn comment_block(&mut self) -> ByteRange {
        let mut nesting_level = 0usize;
        let mut prev = b' ';
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'/' if prev == b'*' => {
                    prev = b' ';
                    if nesting_level == 0 {
                        break;
                    } else {
                        nesting_level -= 1;
                    }
                }
                b'*' if prev == b'/' => {
                    prev = b' ';
                    nesting_level += 1;
                }
                _ => {
                    prev = b;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn string(&mut self, str_type: StrStyle) -> ByteRange {
        let src_bytes = self.src.as_bytes();
        let mut pos = self.pos;
        match str_type {
            StrStyle::Raw(level) => {
                // raw string (e.g. br#"\"#)
                #[derive(Debug)]
                enum SharpState {
                    Sharp {
                        // number of preceding #s
                        num_sharps: usize,
                        // Position of last "
                        quote_pos: BytePos,
                    },
                    None, // No preceding "##...
                }
                let mut cur_state = SharpState::None;
                let mut end_was_found = false;
                // detect corresponding end(if start is r##", "##) greedily
                for (i, &b) in src_bytes[self.pos.0..].iter().enumerate() {
                    match cur_state {
                        SharpState::Sharp {
                            num_sharps,
                            quote_pos,
                        } => {
                            cur_state = match b {
                                b'#' => SharpState::Sharp {
                                    num_sharps: num_sharps + 1,
                                    quote_pos,
                                },
                                b'"' => SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                },
                                _ => SharpState::None,
                            }
                        }
                        SharpState::None => {
                            if b == b'"' {
                                cur_state = SharpState::Sharp {
                                    num_sharps: 0,
                                    quote_pos: BytePos(i),
                                };
                            }
                        }
                    }
                    if let SharpState::Sharp {
                        num_sharps,
                        quote_pos,
                    } = cur_state
                    {
                        if num_sharps == level {
                            end_was_found = true;
                            pos += quote_pos.increment();
                            break;
                        }
                    }
                }
                if !end_was_found {
                    pos = src_bytes.len().into();
                }
            }
            StrStyle::Cooked => {
                let mut is_not_escaped = true;
                for &b in &src_bytes[pos.0..] {
                    pos = pos.increment();
                    match b {
                        b'"' if is_not_escaped => {
                            break;
                        } // "
                        b'\\' => {
                            is_not_escaped = !is_not_escaped;
                        }
                        _ => {
                            is_not_escaped = true;
                        }
                    }
                }
            }
        };
        self.pos = pos;
        self.code()
    }

    fn char(&mut self) -> ByteRange {
        let mut is_not_escaped = true;
        let mut pos = self.pos;
        for &b in &self.src.as_bytes()[pos.0..] {
            pos = pos.increment();
            match b {
                b'\'' if is_not_escaped => {
                    break;
                }
                b'\\' => {
                    is_not_escaped = !is_not_escaped;
                }
                _ => {
                    is_not_escaped = true;
                }
            }
        }
        self.pos = pos;
        self.code()
    }

    fn detect_str_type(&self, pos: BytePos) -> StrStyle {
        let src_bytes = self.src.as_bytes();
        let mut sharp = 0;
        if pos == BytePos::ZERO {
            return StrStyle::Cooked;
        }
        // now pos is at one byte after ", so we have to start at pos - 2
        for &b in src_bytes[..pos.decrement().0].iter().rev() {
            match b {
                b'#' => sharp += 1,
                b'r' => return StrStyle::Raw(sharp),
                _ => return StrStyle::Cooked,
            }
        }
        StrStyle::Cooked
    }
}

/// Returns indices of chunks of code (minus comments and string contents)
pub fn code_chunks(src: &str) -> CodeIndicesIter {
    CodeIndicesIter {
        src,
        state: State::Code,
        pos: BytePos::ZERO,
    }
}

#[cfg(test)]
mod code_indices_iter_test {
    use super::*;
    use testutils::{rejustify, slice};

    #[test]
    fn removes_a_comment() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_consecutive_comments() {
        let src = &rejustify(
            "
    this is some code // this is a comment
    // this is more comment
    // another comment
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents() {
        let src = &rejustify(
            "
    this is some code \"this is a string\" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_char_contents() {
        let src = &rejustify(
            "
    this is some code \'\"\' more code \'\\x00\' and \'\\\'\' that\'s it
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' more code \'", slice(src, it.next().unwrap()));
        assert_eq!("\' and \'", slice(src, it.next().unwrap()));
        assert_eq!("\' that\'s it", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_contents_with_a_comment_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a // fake comment \" more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_a_comment_with_a_dbl_quote_in_it() {
        let src = &rejustify(
            "
    this is some code // comment with \" double quote
    some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_multiline_comment() {
        let src = &rejustify(
            "
    this is some code /* this is a
    \"multiline\" comment */some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!("some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_nesting_of_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /* block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_documentation_block_comments_nested_into_block_comments() {
        let src = &rejustify(
            "
    this is some code /* nested /** documentation block */ comment */ some more code
    ",
        );
        let mut it = code_chunks(src);
        assert_eq!("this is some code ", slice(src, it.next().unwrap()));
        assert_eq!(" some more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_dblquote_in_it() {
        let src = &rejustify(
            "
    this is some code \"string with a \\\" escaped dblquote fake comment \" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_raw_string_with_dangling_escape_in_it() {
        let src = &rejustify(
            "
    this is some code br\" escaped dblquote raw string \\\" more code
    ",
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br\"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn removes_string_with_escaped_slash_before_dblquote_in_it() {
        let src = &rejustify("
    this is some code \"string with an escaped slash, so dbl quote does end the string after all \\\\\" more code
    ");

        let mut it = code_chunks(src);
        assert_eq!("this is some code \"", slice(src, it.next().unwrap()));
        assert_eq!("\" more code", slice(src, it.next().unwrap()));
    }

    #[test]
    fn handles_tricky_bit_from_str_rs() {
        let src = &rejustify(
            "
        before(\"\\\\\'\\\\\\\"\\\\\\\\\");
        more_code(\" skip me \")
    ",
        );

        for range in code_chunks(src) {
            let range = || range.to_range();
            println!("BLOB |{}|", &src[range()]);
            if src[range()].contains("skip me") {
                panic!("{}", &src[range()]);
            }
        }
    }

    #[test]
    fn removes_nested_rawstr() {
        let src = &rejustify(
            r####"
    this is some code br###""" r##""##"### more code
    "####,
        );

        let mut it = code_chunks(src);
        assert_eq!("this is some code br###\"", slice(src, it.next().unwrap()));
        assert_eq!("\"### more code", slice(src, it.next().unwrap()));
    }

}
<|endoftext|>


<commit_before>use crate::{
    message::BasicReturnMessage,
    pinky_swear::{Pinky, PinkyBroadcaster},
    BasicProperties, PublisherConfirm,
};
use log::error;
use parking_lot::Mutex;
use std::{collections::VecDeque, sync::Arc};

#[derive(Clone, Debug, Default)]
pub(crate) struct ReturnedMessages {
    inner: Arc<Mutex<Inner>>,
}

impl ReturnedMessages {
    pub(crate) fn start_new_delivery(&self, message: BasicReturnMessage) {
        self.inner.lock().current_message = Some(message);
    }

    pub(crate) fn set_delivery_properties(&self, properties: BasicProperties) {
        if let Some(message) = self.inner.lock().current_message.as_mut() {
            message.delivery.properties = properties;
        }
    }

    pub(crate) fn new_delivery_complete(&self) {
        self.inner.lock().new_delivery_complete();
    }

    pub(crate) fn receive_delivery_content(&self, data: Vec<u8>) {
        if let Some(message) = self.inner.lock().current_message.as_mut() {
            message.delivery.data.extend(data);
        }
    }

    pub(crate) fn drain(&self) -> Vec<BasicReturnMessage> {
        self.inner.lock().messages.drain(..).collect()
    }

    pub(crate) fn register_pinky(
        &self,
        pinky: (Pinky<PublisherConfirm>, PinkyBroadcaster<PublisherConfirm>),
    ) {
        self.inner.lock().pinkies.push_back(pinky);
    }
}

#[derive(Debug, Default)]
pub struct Inner {
    current_message: Option<BasicReturnMessage>,
    messages: Vec<BasicReturnMessage>,
    pinkies: VecDeque<(Pinky<PublisherConfirm>, PinkyBroadcaster<PublisherConfirm>)>,
}

impl Inner {
    fn new_delivery_complete(&mut self) {
        if let Some(message) = self.current_message.take() {
            error!("Server returned us a message: {:?}", message);
            self.messages.push(message.clone()); // FIXME: drop message if the Nack is consumed?
            if let Some((pinky, _broadcaster)) = self.pinkies.pop_front() { // FIXME: swear when we already got the message
                pinky.swear(PublisherConfirm::Nack(message));
            }
        }
    }
}
<commit_msg>make sure we don't care whether we receive nack or basic-return first<commit_after>use crate::{
    message::BasicReturnMessage,
    pinky_swear::{Pinky, PinkyBroadcaster},
    BasicProperties, PublisherConfirm,
};
use log::error;
use parking_lot::Mutex;
use std::{collections::VecDeque, sync::Arc};

#[derive(Clone, Debug, Default)]
pub(crate) struct ReturnedMessages {
    inner: Arc<Mutex<Inner>>,
}

impl ReturnedMessages {
    pub(crate) fn start_new_delivery(&self, message: BasicReturnMessage) {
        self.inner.lock().current_message = Some(message);
    }

    pub(crate) fn set_delivery_properties(&self, properties: BasicProperties) {
        if let Some(message) = self.inner.lock().current_message.as_mut() {
            message.delivery.properties = properties;
        }
    }

    pub(crate) fn new_delivery_complete(&self) {
        self.inner.lock().new_delivery_complete();
    }

    pub(crate) fn receive_delivery_content(&self, data: Vec<u8>) {
        if let Some(message) = self.inner.lock().current_message.as_mut() {
            message.delivery.data.extend(data);
        }
    }

    pub(crate) fn drain(&self) -> Vec<BasicReturnMessage> {
        self.inner.lock().messages.drain(..).collect()
    }

    pub(crate) fn register_pinky(
        &self,
        pinky: (Pinky<PublisherConfirm>, PinkyBroadcaster<PublisherConfirm>),
    ) {
        self.inner.lock().register_pinky(pinky);
    }
}

#[derive(Debug, Default)]
pub struct Inner {
    current_message: Option<BasicReturnMessage>,
    new_messages: VecDeque<BasicReturnMessage>,
    messages: Vec<BasicReturnMessage>,
    pinkies: VecDeque<(Pinky<PublisherConfirm>, PinkyBroadcaster<PublisherConfirm>)>,
}

impl Inner {
    fn register_pinky(&mut self, pinky: (Pinky<PublisherConfirm>, PinkyBroadcaster<PublisherConfirm>)) {
        if let Some(message) = self.new_messages.pop_front() {
            self.forward_message(pinky.0, message);
        } else {
            self.pinkies.push_back(pinky);
        }
    }

    fn new_delivery_complete(&mut self) {
        if let Some(message) = self.current_message.take() {
            error!("Server returned us a message: {:?}", message);
            if let Some((pinky, _broadcaster)) = self.pinkies.pop_front() {
                self.forward_message(pinky, message);
            } else {
                self.new_messages.push_back(message);
            }
        }
    }

    fn forward_message(&mut self, pinky: Pinky<PublisherConfirm>, message: BasicReturnMessage) {
        self.messages.push(message.clone()); // FIXME: drop message if the Nack is consumed?
        pinky.swear(PublisherConfirm::Nack(message));
    }
}
<|endoftext|>


<commit_before>use super::*;

const SPAWNPOINTS: [(f64, f64, Direction); ARENA_MAX_PLAYERS] = [
    // (
    //     ARENA_WIDTH / 2.0,
    //     ARENA_HEIGHT / 2.0,
    //     Direction::Up,
    // ),
    //
    (
        ARENA_WIDTH / 2.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Up,
    ),
    (
        ARENA_WIDTH / 2.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Down,
    ),
    //
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0,
        Direction::Left,
    ),
    //
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Left,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Left,
    ),
];

pub fn calculate_spawnpoints(player_ids: Vec<PlayerId>) -> Vec<(PlayerId, ArenaPoint, Direction)> {
    let mut spawnpoints: Vec<(PlayerId, ArenaPoint, Direction)> = Vec::new();
    let mut spawnpoints_used: Vec<usize> = Vec::new();

    for player_id in player_ids {
        // check if no spawnpoints remain
        if spawnpoints_used.len() >= SPAWNPOINTS.len() {
            error!("No spawnpoints remain!");
            break;
        }

        // randomly select an available spawnpoint
        let mut selected_spawnpoint = OsRng.next_u64() as usize % SPAWNPOINTS.len();
        while spawnpoints_used
            .iter()
            .any(|spawnpoint| *spawnpoint == selected_spawnpoint)
        {
            selected_spawnpoint = OsRng.next_u64() as usize % SPAWNPOINTS.len();
        }
        spawnpoints_used.push(selected_spawnpoint);

        // add the selected spawnpoint to our spawnpoints vector
        let selected_spawnpoint = SPAWNPOINTS[selected_spawnpoint];
        spawnpoints.push((
            player_id,
            ArenaPoint::new(selected_spawnpoint.0, selected_spawnpoint.1),
            selected_spawnpoint.2,
        ));
    }

    spawnpoints
}
<commit_msg>switched spawnpoint rng to u32<commit_after>use super::*;

const SPAWNPOINTS: [(f64, f64, Direction); ARENA_MAX_PLAYERS] = [
    // (
    //     ARENA_WIDTH / 2.0,
    //     ARENA_HEIGHT / 2.0,
    //     Direction::Up,
    // ),
    //
    (
        ARENA_WIDTH / 2.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Up,
    ),
    (
        ARENA_WIDTH / 2.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Down,
    ),
    //
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0,
        Direction::Left,
    ),
    //
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 - ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Right,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 - ARENA_HEIGHT / 4.0,
        Direction::Left,
    ),
    (
        ARENA_WIDTH / 2.0 + ARENA_WIDTH / 4.0,
        ARENA_HEIGHT / 2.0 + ARENA_HEIGHT / 4.0,
        Direction::Left,
    ),
];

pub fn calculate_spawnpoints(player_ids: Vec<PlayerId>) -> Vec<(PlayerId, ArenaPoint, Direction)> {
    let mut spawnpoints: Vec<(PlayerId, ArenaPoint, Direction)> = Vec::new();
    let mut spawnpoints_used: Vec<usize> = Vec::new();

    for player_id in player_ids {
        // check if no spawnpoints remain
        if spawnpoints_used.len() >= SPAWNPOINTS.len() {
            error!("No spawnpoints remain!");
            break;
        }

        // randomly select an available spawnpoint
        let mut selected_spawnpoint = OsRng.next_u32() as usize % SPAWNPOINTS.len();
        while spawnpoints_used
            .iter()
            .any(|spawnpoint| *spawnpoint == selected_spawnpoint)
        {
            selected_spawnpoint = OsRng.next_u32() as usize % SPAWNPOINTS.len();
        }
        spawnpoints_used.push(selected_spawnpoint);

        // add the selected spawnpoint to our spawnpoints vector
        let selected_spawnpoint = SPAWNPOINTS[selected_spawnpoint];
        spawnpoints.push((
            player_id,
            ArenaPoint::new(selected_spawnpoint.0, selected_spawnpoint.1),
            selected_spawnpoint.2,
        ));
    }

    spawnpoints
}
<|endoftext|>


<commit_before>use super::*;
use super::super::async;
use super::super::packets::{self, Encodable, Decodable};
use super::super::status_translator;
use crc::crc32;
use byteorder::{self, BigEndian, ByteOrder};
use std::collections;
use std::net;
use std::thread;
use std::io;
use std::sync::mpsc;
use mio;
use mio::udp;

const SOCKET_TOKEN: mio::Token = mio::Token(0);

#[derive(Debug, Copy, Clone)]
pub enum TimeoutTypes {
    Timeout1000,
    Timeout200,
}

pub enum MioHandlerCommand<H: async::Handler> {
    DoCall(Box<Fn(&mut MioHandler<H>)+Send>),
}

/*This doesn't have a good name.

Basically it exists so that we can pass some stuff around without making the borrow checker mad.  Primarily it "provides" services, so we call it for that.*/
pub struct MioServiceProvider<'a, H: async::Handler> {
    pub socket: &'a udp::UdpSocket,
    pub incoming_packet_buffer: [u8; 1000],
    pub outgoing_packet_buffer: [u8; 1000],
    pub handler: H,
    pub debug_print_enabled: bool,
}

pub struct MioHandler<'a, H: async::Handler> {
    service: MioServiceProvider<'a, H>,
    connections: collections::HashMap<net::SocketAddr, Connection>,
    next_connection_id: u64,
}

impl<'a, H: async::Handler> MioHandler<'a, H> {
    pub fn new(socket: &'a udp::UdpSocket, handler: H)->MioHandler<'a, H> {
        MioHandler {
            service: MioServiceProvider {
                socket: socket,
                incoming_packet_buffer: [0u8; 1000],
                outgoing_packet_buffer: [0u8; 1000],
                handler: handler,
                debug_print_enabled: false,
            },
            connections: collections::HashMap::new(),
            next_connection_id: 1,
        }
    }

    fn got_packet(&mut self, size: usize, address: net::SocketAddr) {
        if size == 0 {return;}
        let maybe_packet = {
            let slice = &self.service.incoming_packet_buffer[0..size];
            let computed_checksum = crc32::checksum_castagnoli(&slice[4..]);
            let expected_checksum = BigEndian::read_u32(&slice[..4]);
            if computed_checksum != expected_checksum {Err(packets::PacketDecodingError::Invalid)}
            else {packets::decode_packet(&slice[4..])}
        };
        if let Err(_) = maybe_packet {return;}
        let packet = maybe_packet.unwrap();
        if self.service.debug_print_enabled {
            println!("Packet from {:?}:", address);
            println!("{:?}", packet);
        }
        if let Some(ref mut conn) = self.connections.get_mut(&address) {
            if conn.handle_incoming_packet(&packet, &mut self.service) {return;}
        }
        match packet {
            packets ::Packet::Connect => {
                if let Some(_) = self.connections.get(&address) {return;}
                let id = self.next_connection_id;
                self.next_connection_id += 1;
                let conn = Connection::new(address, id);
                self.connections.insert(address, conn);
                self.service.send(&packets::Packet::Connected(id), address);
            },
            packets::Packet::StatusRequest(ref req) => {
                self.service.send(&packets::Packet::StatusResponse(status_translator::translate(req)), address);
            },
            p@_ => {
                if self.service.debug_print_enabled {println!("Unhandled.");}
            }
        }
        if self.service.debug_print_enabled {println!("");}
    }

    pub fn enable_debug_print(&mut self) {
        self.service.debug_print_enabled = true;
        println!("Debug printing enabled.");
    }

    pub fn connect(&mut self, address: net:: SocketAddr, request_id: u64) {
        println!("Connect called.");
        let id = self.next_connection_id;
        self.next_connection_id += 1;
        println!("New connection, id = {}", id);
        let mut conn = Connection::new(address, id);
        conn.establish(Some(request_id), &mut self.service);
        self.connections.insert(address, conn);
    }

    pub fn disconnect(&mut self, id: u64, request_id: u64) {
        //todo: fill this out.
    }
}

impl<'A, H: async::Handler> MioServiceProvider<'A, H> {
    pub fn send(&mut self, packet: &packets::Packet, address: net::SocketAddr)->bool {
        if self.debug_print_enabled {
            println!("sending to {:?}:", address);
            println!("{:?}\n", packet);
        }
        if let Ok(size) = packets::encode_packet(packet, &mut self.outgoing_packet_buffer[4..]) {
            let checksum = crc32::checksum_castagnoli(&self.outgoing_packet_buffer[4..size]);
            BigEndian::write_u32(&mut self.outgoing_packet_buffer[..4], checksum);
            if let Ok(Some(sent_bytes)) = self.socket.send_to(&self.outgoing_packet_buffer[..4+size], &address) {
                if sent_bytes == 4+size {return true;}
                else {return false;}
            }
            else {return false;}
        }
        else {return false;};
    }
}

impl<'a, H: async::Handler+Send> mio::Handler for MioHandler<'a, H> {
    type Timeout = TimeoutTypes;
    type Message = MioHandlerCommand<H>;

    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        //We only have one socket, so can avoid the match on the token.
        if events.is_error() {
            //We need to do something sensible here, probably a callback with whatever state we can get.
        }
        if events.is_readable() {
            let result = self.service.socket.recv_from(&mut self.service.incoming_packet_buffer);
            if let Ok(Some((size, address))) = result {
                self.got_packet(size, address);
            }
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Self>, timeout: Self::Timeout) {
        //Rust isn't smart enough to realize that closures only borrow a field, so we pull it out here to satisfy the borrow checker.
        let sender = &mut self.service;
        let rereg = match timeout {
            TimeoutTypes::Timeout200 => {
                self.connections.iter_mut().map(|x| x.1.tick200(sender));
                200
            },
            TimeoutTypes::Timeout1000 => {
                self.connections.iter_mut().map(|x| x.1.tick1000(sender));
                1000
            },
        };
        event_loop.timeout_ms(timeout, rereg).unwrap();
    }

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Self>, message: Self::Message) {
        match message {
            MioHandlerCommand::DoCall(ref f) => f(self),
        }
    }
}

fn mio_server_thread< H: async::Handler+Send>(address: net::SocketAddr, handler: H, notify_created: mpsc::Sender<Result<mio::Sender<MioHandlerCommand<H>>, io::Error>>) {
    let maybe_socket = match address {
        net::SocketAddr::V4(_) => udp::UdpSocket::v4(),
        net::SocketAddr::V6(_) => udp::UdpSocket::v6()
    };
    if let Err(what) = maybe_socket {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let socket = maybe_socket.unwrap();
    if let  Err(what) = socket.bind(&address) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let maybe_loop  = mio::EventLoop::new();
    if let Err(what) = maybe_loop {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let mut event_loop = maybe_loop.unwrap();
    let mut handler = MioHandler::new(&socket, handler);
    if let Err(what)  = event_loop.register(&socket, SOCKET_TOKEN, mio::EventSet::all(), mio::PollOpt::all()) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let timer_error = Err(io::Error::new(io::ErrorKind::Other, "Couldn't create the timer."));
    if let Err(_) = event_loop.timeout_ms(TimeoutTypes::Timeout1000, 1000) {
        notify_created.send(timer_error).unwrap();
        return;
    }
    if let Err(_) = event_loop.timeout_ms(TimeoutTypes::Timeout200, 200) {
        notify_created.send(timer_error);
        return;
    }
    let sender = event_loop.channel();
    notify_created.send(Ok(sender));
    event_loop.run(&mut handler);
}

pub struct MioServer<H: async::Handler> {
    thread: thread::JoinHandle<()>,
    sender: mio::Sender<MioHandlerCommand<H>>,
}

impl<H: async::Handler+Send+'static> MioServer<H> {
    pub fn new(address: net::SocketAddr, handler: H)->Result<MioServer<H>, io::Error> {
        let (sender, receiver) = mpsc::channel();
        let join_handle = thread::spawn(move || mio_server_thread(address, handler, sender));
        let message_sender = try!(receiver.recv().unwrap());
        Ok(MioServer {
            thread: join_handle,
            sender: message_sender,
        })
    }

    pub fn with<F: Fn(&mut MioHandler<H>)+Send+'static>(&mut self, func: F) {
        let command = MioHandlerCommand::DoCall(Box::new(func));
        self.sender.send(command);
    }
}
<commit_msg>Even more debug printing.<commit_after>use super::*;
use super::super::async;
use super::super::packets::{self, Encodable, Decodable};
use super::super::status_translator;
use crc::crc32;
use byteorder::{self, BigEndian, ByteOrder};
use std::collections;
use std::net;
use std::thread;
use std::io;
use std::sync::mpsc;
use mio;
use mio::udp;

const SOCKET_TOKEN: mio::Token = mio::Token(0);

#[derive(Debug, Copy, Clone)]
pub enum TimeoutTypes {
    Timeout1000,
    Timeout200,
}

pub enum MioHandlerCommand<H: async::Handler> {
    DoCall(Box<Fn(&mut MioHandler<H>)+Send>),
}

/*This doesn't have a good name.

Basically it exists so that we can pass some stuff around without making the borrow checker mad.  Primarily it "provides" services, so we call it for that.*/
pub struct MioServiceProvider<'a, H: async::Handler> {
    pub socket: &'a udp::UdpSocket,
    pub incoming_packet_buffer: [u8; 1000],
    pub outgoing_packet_buffer: [u8; 1000],
    pub handler: H,
    pub debug_print_enabled: bool,
}

pub struct MioHandler<'a, H: async::Handler> {
    service: MioServiceProvider<'a, H>,
    connections: collections::HashMap<net::SocketAddr, Connection>,
    next_connection_id: u64,
}

impl<'a, H: async::Handler> MioHandler<'a, H> {
    pub fn new(socket: &'a udp::UdpSocket, handler: H)->MioHandler<'a, H> {
        MioHandler {
            service: MioServiceProvider {
                socket: socket,
                incoming_packet_buffer: [0u8; 1000],
                outgoing_packet_buffer: [0u8; 1000],
                handler: handler,
                debug_print_enabled: false,
            },
            connections: collections::HashMap::new(),
            next_connection_id: 1,
        }
    }

    fn got_packet(&mut self, size: usize, address: net::SocketAddr) {
        if self.service.debug_print_enabled {println!("Incoming packet from {}:", address);}
        if size == 0 {return;}
        let maybe_packet = {
            let slice = &self.service.incoming_packet_buffer[0..size];
            let computed_checksum = crc32::checksum_castagnoli(&slice[4..]);
            let expected_checksum = BigEndian::read_u32(&slice[..4]);
            if computed_checksum != expected_checksum {
                if self.service.debug_print_enabled {println!("Checksum invalid: {} versus {}", computed_checksum, expected_checksum);}
                Err(packets::PacketDecodingError::Invalid)
            }
            else {packets::decode_packet(&slice[4..])}
        };
        if let Err(_) = maybe_packet {return;}
        let packet = maybe_packet.unwrap();
        if self.service.debug_print_enabled {
            println!("{:?}", packet);
        }
        if let Some(ref mut conn) = self.connections.get_mut(&address) {
            if conn.handle_incoming_packet(&packet, &mut self.service) {return;}
        }
        match packet {
            packets ::Packet::Connect => {
                if let Some(_) = self.connections.get(&address) {return;}
                let id = self.next_connection_id;
                self.next_connection_id += 1;
                let conn = Connection::new(address, id);
                self.connections.insert(address, conn);
                self.service.send(&packets::Packet::Connected(id), address);
            },
            packets::Packet::StatusRequest(ref req) => {
                self.service.send(&packets::Packet::StatusResponse(status_translator::translate(req)), address);
            },
            p@_ => {
                if self.service.debug_print_enabled {println!("Unhandled.");}
            }
        }
        if self.service.debug_print_enabled {println!("");}
    }

    pub fn enable_debug_print(&mut self) {
        self.service.debug_print_enabled = true;
        println!("Debug printing enabled.");
    }

    pub fn connect(&mut self, address: net:: SocketAddr, request_id: u64) {
        let id = self.next_connection_id;
        self.next_connection_id += 1;
        if self.service.debug_print_enabled {println!("New connection, id = {}", id);}
        let mut conn = Connection::new(address, id);
        conn.establish(Some(request_id), &mut self.service);
        self.connections.insert(address, conn);
    }

    pub fn disconnect(&mut self, id: u64, request_id: u64) {
        //todo: fill this out.
    }
}

impl<'A, H: async::Handler> MioServiceProvider<'A, H> {
    pub fn send(&mut self, packet: &packets::Packet, address: net::SocketAddr)->bool {
        if self.debug_print_enabled {
            println!("sending to {:?}:", address);
            println!("{:?}\n", packet);
        }
        if let Ok(size) = packets::encode_packet(packet, &mut self.outgoing_packet_buffer[4..]) {
            let checksum = crc32::checksum_castagnoli(&self.outgoing_packet_buffer[4..size]);
            BigEndian::write_u32(&mut self.outgoing_packet_buffer[..4], checksum);
            if let Ok(Some(sent_bytes)) = self.socket.send_to(&self.outgoing_packet_buffer[..4+size], &address) {
                if self.debug_print_enabled {println!("Sent {} bytes", sent_bytes);}
                if sent_bytes == 4+size {return true;}
                else {return false;}
            }
            else {return false;}
        }
        else {return false;};
    }
}

impl<'a, H: async::Handler+Send> mio::Handler for MioHandler<'a, H> {
    type Timeout = TimeoutTypes;
    type Message = MioHandlerCommand<H>;

    fn ready(&mut self, event_loop: &mut mio::EventLoop<Self>, token: mio::Token, events: mio::EventSet) {
        //We only have one socket, so can avoid the match on the token.
        if events.is_error() {
            //We need to do something sensible here, probably a callback with whatever state we can get.
        }
        if events.is_readable() {
            let result = self.service.socket.recv_from(&mut self.service.incoming_packet_buffer);
            if let Ok(Some((size, address))) = result {
                self.got_packet(size, address);
            }
        }
    }

    fn timeout(&mut self, event_loop: &mut mio::EventLoop<Self>, timeout: Self::Timeout) {
        //Rust isn't smart enough to realize that closures only borrow a field, so we pull it out here to satisfy the borrow checker.
        let sender = &mut self.service;
        let rereg = match timeout {
            TimeoutTypes::Timeout200 => {
                self.connections.iter_mut().map(|x| x.1.tick200(sender));
                200
            },
            TimeoutTypes::Timeout1000 => {
                self.connections.iter_mut().map(|x| x.1.tick1000(sender));
                1000
            },
        };
        event_loop.timeout_ms(timeout, rereg).unwrap();
    }

    fn notify(&mut self, event_loop: &mut mio::EventLoop<Self>, message: Self::Message) {
        match message {
            MioHandlerCommand::DoCall(ref f) => f(self),
        }
    }
}

fn mio_server_thread< H: async::Handler+Send>(address: net::SocketAddr, handler: H, notify_created: mpsc::Sender<Result<mio::Sender<MioHandlerCommand<H>>, io::Error>>) {
    let maybe_socket = match address {
        net::SocketAddr::V4(_) => udp::UdpSocket::v4(),
        net::SocketAddr::V6(_) => udp::UdpSocket::v6()
    };
    if let Err(what) = maybe_socket {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let socket = maybe_socket.unwrap();
    if let  Err(what) = socket.bind(&address) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let maybe_loop  = mio::EventLoop::new();
    if let Err(what) = maybe_loop {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let mut event_loop = maybe_loop.unwrap();
    let mut handler = MioHandler::new(&socket, handler);
    if let Err(what)  = event_loop.register(&socket, SOCKET_TOKEN, mio::EventSet::all(), mio::PollOpt::all()) {
        notify_created.send(Err(what)).unwrap();
        return;
    }
    let timer_error = Err(io::Error::new(io::ErrorKind::Other, "Couldn't create the timer."));
    if let Err(_) = event_loop.timeout_ms(TimeoutTypes::Timeout1000, 1000) {
        notify_created.send(timer_error).unwrap();
        return;
    }
    if let Err(_) = event_loop.timeout_ms(TimeoutTypes::Timeout200, 200) {
        notify_created.send(timer_error);
        return;
    }
    let sender = event_loop.channel();
    notify_created.send(Ok(sender));
    event_loop.run(&mut handler);
}

pub struct MioServer<H: async::Handler> {
    thread: thread::JoinHandle<()>,
    sender: mio::Sender<MioHandlerCommand<H>>,
}

impl<H: async::Handler+Send+'static> MioServer<H> {
    pub fn new(address: net::SocketAddr, handler: H)->Result<MioServer<H>, io::Error> {
        let (sender, receiver) = mpsc::channel();
        let join_handle = thread::spawn(move || mio_server_thread(address, handler, sender));
        let message_sender = try!(receiver.recv().unwrap());
        Ok(MioServer {
            thread: join_handle,
            sender: message_sender,
        })
    }

    pub fn with<F: Fn(&mut MioHandler<H>)+Send+'static>(&mut self, func: F) {
        let command = MioHandlerCommand::DoCall(Box::new(func));
        self.sender.send(command);
    }
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#![feature(old_io)]
#![feature(std_misc)]
// import local modules
extern crate doppler;
use doppler::predict as predict;
use doppler::usage as usage;

// import external modules
use std::old_io::Timer;
use std::time::Duration;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let args = usage::args();

    //println!("{:?}", args);

    println!("doppler {} andres.vahter@gmail.com\n\n", VERSION);

    if args.get_bool("const") {
        println!("constant shift mode");

        println!("\tIQ samplerate   : {}", args.get_str("--samplerate"));
        println!("\tIQ data type    : {}\n", args.get_str("--intype"));

        println!("\tfrequency shift : {} Hz", args.get_str("--shift"));
    }
    else if args.get_bool("track") {
        println!("tracking mode");

        println!("\tIQ samplerate   : {}", args.get_str("--samplerate"));
        println!("\tIQ data type    : {}\n", args.get_str("--intype"));

        println!("\tTLE file        : {}", args.get_str("--tlefile"));
        println!("\tTLE name        : {}", args.get_str("--tlename"));
        println!("\tlocation        : {}", args.get_str("--location"));
        println!("\ttime            : {}", args.get_str("--time"));
        println!("\tfrequency       : {} Hz", args.get_str("--freq"));
        println!("\tfrequency shift : {} Hz\n\n\n", args.get_str("--shift"));
    }

    let tle: predict::Tle = predict::Tle{
        name: "ESTCUBE 1".to_string(),
        line1: "1 39161U 13021C   15091.47675532  .00001890  00000-0  31643-3 0  9990".to_string(),
        line2: "2 39161  98.0727 175.0786 0009451 192.0216 168.0788 14.70951130101965".to_string()
    };

    let location: predict::Location = predict::Location{lat_deg:58.64560, lon_deg: 23.15163, alt_m: 8};
    let mut predict: predict::Predict = predict::Predict::new(tle, location);

    let mut timer = Timer::new().unwrap();
    let periodic = timer.periodic(Duration::milliseconds(1000));

    loop {
        periodic.recv().unwrap();
        predict.update(None);
        println!("az         : {:.2}°", predict.sat.az_deg);
        println!("el         : {:.2}°", predict.sat.el_deg);
        println!("range      : {:.0} km", predict.sat.range_km);
        println!("range rate : {:.3} km/sec\n", predict.sat.range_rate_km_sec);

    }
}
<commit_msg>wip: replace old_io timers with std::thread<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Andres Vahter (andres.vahter@gmail.com)
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

// import local modules
extern crate doppler;
use doppler::predict as predict;
use doppler::usage as usage;

// import external modules
use std::thread;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

fn main() {
    let args = usage::args();

    //println!("{:?}", args);

    println!("doppler {} andres.vahter@gmail.com\n\n", VERSION);

    if args.get_bool("const") {
        println!("constant shift mode");

        println!("\tIQ samplerate   : {}", args.get_str("--samplerate"));
        println!("\tIQ data type    : {}\n", args.get_str("--intype"));

        println!("\tfrequency shift : {} Hz", args.get_str("--shift"));
    }
    else if args.get_bool("track") {
        println!("tracking mode");

        println!("\tIQ samplerate   : {}", args.get_str("--samplerate"));
        println!("\tIQ data type    : {}\n", args.get_str("--intype"));

        println!("\tTLE file        : {}", args.get_str("--tlefile"));
        println!("\tTLE name        : {}", args.get_str("--tlename"));
        println!("\tlocation        : {}", args.get_str("--location"));
        println!("\ttime            : {}", args.get_str("--time"));
        println!("\tfrequency       : {} Hz", args.get_str("--freq"));
        println!("\tfrequency shift : {} Hz\n\n\n", args.get_str("--shift"));
    }

    let tle: predict::Tle = predict::Tle{
        name: "ESTCUBE 1".to_string(),
        line1: "1 39161U 13021C   15091.47675532  .00001890  00000-0  31643-3 0  9990".to_string(),
        line2: "2 39161  98.0727 175.0786 0009451 192.0216 168.0788 14.70951130101965".to_string()
    };

    let location: predict::Location = predict::Location{lat_deg:58.64560, lon_deg: 23.15163, alt_m: 8};
    let mut predict: predict::Predict = predict::Predict::new(tle, location);

    let thread = thread::scoped(move || {
        loop {
            predict.update(None);
            println!("az         : {:.2}°", predict.sat.az_deg);
            println!("el         : {:.2}°", predict.sat.el_deg);
            println!("range      : {:.0} km", predict.sat.range_km);
            println!("range rate : {:.3} km/sec\n", predict.sat.range_rate_km_sec);
            thread::sleep_ms(1000);
        }
    });

    let output = thread.join();
}
<|endoftext|>


<commit_before>extern crate objc;

use objc::Id;
use objc::foundation::{NSArray, NSDictionary, NSObject, NSString,
	INSArray, INSCopying, INSDictionary, INSObject, INSString};

fn main() {
	// Create and compare NSObjects
	let obj: Id<NSObject> = INSObject::new();
	println!("{} == {}? {}", obj, obj, obj == obj);

	let obj2: Id<NSObject> = INSObject::new();
	println!("{} == {}? {}", obj, obj2, obj == obj2);

	// Create an NSArray from a Vec
	let objs = vec![obj, obj2];
	let array: Id<NSArray<NSObject>> = INSArray::from_vec(objs);
	for obj in array.object_enumerator() {
		println!("{}", obj);
	}
	println!("{}", array.len());

	// Turn the NSArray back into a Vec
	let mut objs = INSArray::into_vec(array);
	let obj = objs.pop().unwrap();

	// Create an NSString from a str slice
	let string: Id<NSString> = INSString::from_str("Hello, world!");
	println!("{}", string.as_str());
	let string2 = string.copy();
	println!("{}", string2.as_str());

	// Create a dictionary mapping strings to objects
	let keys = &[&*string];
	let vals = vec![obj];
	let dict: Id<NSDictionary<NSString, NSObject>> =
		INSDictionary::from_keys_and_objects(keys.as_slice(), vals);
	println!("{}", dict.object_for(&*string));
	println!("{}", dict.len());
}
<commit_msg>Removed redundant as_slice in example.<commit_after>extern crate objc;

use objc::Id;
use objc::foundation::{NSArray, NSDictionary, NSObject, NSString,
	INSArray, INSCopying, INSDictionary, INSObject, INSString};

fn main() {
	// Create and compare NSObjects
	let obj: Id<NSObject> = INSObject::new();
	println!("{} == {}? {}", obj, obj, obj == obj);

	let obj2: Id<NSObject> = INSObject::new();
	println!("{} == {}? {}", obj, obj2, obj == obj2);

	// Create an NSArray from a Vec
	let objs = vec![obj, obj2];
	let array: Id<NSArray<NSObject>> = INSArray::from_vec(objs);
	for obj in array.object_enumerator() {
		println!("{}", obj);
	}
	println!("{}", array.len());

	// Turn the NSArray back into a Vec
	let mut objs = INSArray::into_vec(array);
	let obj = objs.pop().unwrap();

	// Create an NSString from a str slice
	let string: Id<NSString> = INSString::from_str("Hello, world!");
	println!("{}", string.as_str());
	let string2 = string.copy();
	println!("{}", string2.as_str());

	// Create a dictionary mapping strings to objects
	let keys = &[&*string];
	let vals = vec![obj];
	let dict: Id<NSDictionary<NSString, NSObject>> =
		INSDictionary::from_keys_and_objects(keys, vals);
	println!("{}", dict.object_for(&*string));
	println!("{}", dict.len());
}
<|endoftext|>


<commit_before>#![feature(macro_rules)]
#![crate_name = "basename"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jimmy Lu <jimmy.lu.2011@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::io::{print, println};
use std::str::StrSlice;

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "basename";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let program = strip_dir(args[0].as_slice());

    //
    // Argument parsing
    //
    let opts = [
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m)  => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        println!("Usage: {0:s} NAME [SUFFIX]", program);
        println!("  or: {0:s} OPTION", program);
        println!("Print NAME with any leading directory components removed.");
        println!("If specified, also remove a trailing SUFFIX.");

        print(getopts::usage("", opts).as_slice());

        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", program, VERSION);
        return 0;
    }

    // too few arguments
    if args.len() < 2 {
        println!("{}: {}", program, "missing operand");
        println!("Try '{} --help' for more information.", program);
        return 1;
    }
    // too many arguments
    else if args.len() > 3 {
        println!("{}: extra operand '{}'", program, args[3]);
        println!("Try '{} --help' for more information.", program);
        return 1;
    }

    //
    // Main Program Processing
    //

    let fullname = &args[1];

    let mut name = strip_dir(fullname.as_slice());

    if args.len() > 2 {
        let suffix = args[2].clone();
        name = strip_suffix(name.as_slice(), suffix.as_slice());
    }

    println(name.as_slice());

    0
}

fn strip_dir(fullname: &str) -> String {
    let mut name = String::new();

    for c in fullname.chars().rev() {
        if c == '/' || c == '\\' {
            break;
        }
        name.push(c);
    }

    name.as_slice().chars().rev().collect()
}

fn strip_suffix(name: &str, suffix: &str) -> String {
    if name == suffix {
        return name.into_string();
    }

    if name.ends_with(suffix) {
        return name.slice_to(name.len() - suffix.len()).into_string();
    }

    name.into_string()
}
<commit_msg>basename: fix build<commit_after>#![feature(macro_rules)]
#![crate_name = "basename"]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Jimmy Lu <jimmy.lu.2011@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::io::{print, println};

#[path = "../common/util.rs"]
mod util;

static NAME: &'static str = "basename";
static VERSION: &'static str = "1.0.0";

pub fn uumain(args: Vec<String>) -> int {
    let program = strip_dir(args[0].as_slice());

    //
    // Argument parsing
    //
    let opts = [
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit"),
    ];

    let matches = match getopts::getopts(args.tail(), opts) {
        Ok(m)  => m,
        Err(f) => crash!(1, "Invalid options\n{}", f)
    };

    if matches.opt_present("help") {
        println!("Usage: {0:s} NAME [SUFFIX]", program);
        println!("  or: {0:s} OPTION", program);
        println!("Print NAME with any leading directory components removed.");
        println!("If specified, also remove a trailing SUFFIX.");

        print(getopts::usage("", opts).as_slice());

        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", program, VERSION);
        return 0;
    }

    // too few arguments
    if args.len() < 2 {
        println!("{}: {}", program, "missing operand");
        println!("Try '{} --help' for more information.", program);
        return 1;
    }
    // too many arguments
    else if args.len() > 3 {
        println!("{}: extra operand '{}'", program, args[3]);
        println!("Try '{} --help' for more information.", program);
        return 1;
    }

    //
    // Main Program Processing
    //

    let fullname = &args[1];

    let mut name = strip_dir(fullname.as_slice());

    if args.len() > 2 {
        let suffix = args[2].clone();
        name = strip_suffix(name.as_slice(), suffix.as_slice());
    }

    println(name.as_slice());

    0
}

fn strip_dir(fullname: &str) -> String {
    let mut name = String::new();

    for c in fullname.chars().rev() {
        if c == '/' || c == '\\' {
            break;
        }
        name.push(c);
    }

    name.as_slice().chars().rev().collect()
}

fn strip_suffix(name: &str, suffix: &str) -> String {
    if name == suffix {
        return name.into_string();
    }

    if name.ends_with(suffix) {
        return name.slice_to(name.len() - suffix.len()).into_string();
    }

    name.into_string()
}
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use dist::{self, pkgname, sanitize_sh, tmpdir};

use builder::{Builder, RunConfig, ShouldRun, Step};
use cache::Interned;

pub fn install_docs(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "docs", "rust-docs", stage, Some(host));
}

pub fn install_std(builder: &Builder, stage: u32, target: Interned<String>) {
    install_sh(builder, "std", "rust-std", stage, Some(target));
}

pub fn install_cargo(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "cargo", "cargo", stage, Some(host));
}

pub fn install_rls(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rls", "rls", stage, Some(host));
}

pub fn install_rustfmt(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustfmt", "rustfmt", stage, Some(host));
}

pub fn install_analysis(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "analysis", "rust-analysis", stage, Some(host));
}

pub fn install_src(builder: &Builder, stage: u32) {
    install_sh(builder, "src", "rust-src", stage, None);
}
pub fn install_rustc(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustc", "rustc", stage, Some(host));
}

fn install_sh(
    builder: &Builder,
    package: &str,
    name: &str,
    stage: u32,
    host: Option<Interned<String>>
) {
    let build = builder.build;
    println!("Install {} stage{} ({:?})", package, stage, host);

    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let docdir_default = PathBuf::from("share/doc/rust");
    let bindir_default = PathBuf::from("bin");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = PathBuf::from("share/man");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");

    t!(fs::create_dir_all(&empty_dir));
    let package_name = if let Some(host) = host {
        format!("{}-{}", pkgname(build, name), host)
    } else {
        pkgname(build, name)
    };

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
        .arg(format!("--prefix={}", sanitize_sh(&prefix)))
        .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
        .arg(format!("--docdir={}", sanitize_sh(&docdir)))
        .arg(format!("--bindir={}", sanitize_sh(&bindir)))
        .arg(format!("--libdir={}", sanitize_sh(&libdir)))
        .arg(format!("--mandir={}", sanitize_sh(&mandir)))
        .arg("--disable-ldconfig");
    build.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    ret
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub stage: u32,
            pub target: Interned<String>,
            pub host: Interned<String>,
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_BUILD_TARGETS: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun) -> ShouldRun {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig) {
                run.builder.ensure($name {
                    stage: run.builder.top_stage,
                    target: run.target,
                    host: run.host,
                });
            }

            fn run($sel, $builder: &Builder) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        builder.ensure(dist::Docs { stage: self.stage, host: self.target });
        install_docs(builder, self.stage, self.target);
    };
    Std, "src/libstd", true, only_hosts: true, {
        for target in &builder.build.targets {
            builder.ensure(dist::Std {
                compiler: builder.compiler(self.stage, self.host),
                target: *target
            });
            install_std(builder, self.stage, *target);
        }
    };
    Cargo, "cargo", _config.extended &&
            _config.tools.as_ref().map_or(true, |t| t.contains("cargo")), only_hosts: true, {
        builder.ensure(dist::Cargo { stage: self.stage, target: self.target });
        install_cargo(builder, self.stage, self.target);
    };
    Rls, "rls", _config.extended &&
            _config.tools.as_ref().map_or(true, |t| t.contains("rls")), only_hosts: true, {
        if builder.ensure(dist::Rls { stage: self.stage, target: self.target }).is_some() ||
            builder.config.tools.as_ref().map_or(false, |t| t.contains("rls")) {
            install_rls(builder, self.stage, self.target);
        } else {
            println!("skipping Install RLS stage{} ({})", self.stage, self.target);
        }
    };
    Rustfmt, "rustfmt", _config.extended &&
            _config.tools.as_ref().map_or(true, |t| t.contains("rustfmt")), only_hosts: true, {
        if builder.ensure(dist::Rustfmt { stage: self.stage, target: self.target }).is_some() ||
            builder.config.tools.as_ref().map_or(false, |t| t.contains("rustfmt"))  {
            install_rustfmt(builder, self.stage, self.target);
        } else {
            println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target);
        }
    };
    Analysis, "analysis", _config.extended &&
            _config.tools.as_ref().map_or(true, |t| t.contains("analysis")), only_hosts: false, {
        builder.ensure(dist::Analysis {
            compiler: builder.compiler(self.stage, self.host),
            target: self.target
        });
        install_analysis(builder, self.stage, self.target);
    };
    Src, "src", _config.extended &&
            _config.tools.as_ref().map_or(true, |t| t.contains("src")), only_hosts: true, {
        builder.ensure(dist::Src);
        install_src(builder, self.stage);
    }, ONLY_BUILD;
    Rustc, "src/librustc", true, only_hosts: true, {
        builder.ensure(dist::Rustc {
            compiler: builder.compiler(self.stage, self.target),
        });
        install_rustc(builder, self.stage, self.target);
    };
);
<commit_msg>Refactor checks on list of extended tools.<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use dist::{self, pkgname, sanitize_sh, tmpdir};

use builder::{Builder, RunConfig, ShouldRun, Step};
use cache::Interned;
use config::Config;

pub fn install_docs(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "docs", "rust-docs", stage, Some(host));
}

pub fn install_std(builder: &Builder, stage: u32, target: Interned<String>) {
    install_sh(builder, "std", "rust-std", stage, Some(target));
}

pub fn install_cargo(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "cargo", "cargo", stage, Some(host));
}

pub fn install_rls(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rls", "rls", stage, Some(host));
}

pub fn install_rustfmt(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustfmt", "rustfmt", stage, Some(host));
}

pub fn install_analysis(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "analysis", "rust-analysis", stage, Some(host));
}

pub fn install_src(builder: &Builder, stage: u32) {
    install_sh(builder, "src", "rust-src", stage, None);
}
pub fn install_rustc(builder: &Builder, stage: u32, host: Interned<String>) {
    install_sh(builder, "rustc", "rustc", stage, Some(host));
}

fn install_sh(
    builder: &Builder,
    package: &str,
    name: &str,
    stage: u32,
    host: Option<Interned<String>>
) {
    let build = builder.build;
    println!("Install {} stage{} ({:?})", package, stage, host);

    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let docdir_default = PathBuf::from("share/doc/rust");
    let bindir_default = PathBuf::from("bin");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = PathBuf::from("share/man");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");

    t!(fs::create_dir_all(&empty_dir));
    let package_name = if let Some(host) = host {
        format!("{}-{}", pkgname(build, name), host)
    } else {
        pkgname(build, name)
    };

    let mut cmd = Command::new("sh");
    cmd.current_dir(&empty_dir)
        .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
        .arg(format!("--prefix={}", sanitize_sh(&prefix)))
        .arg(format!("--sysconfdir={}", sanitize_sh(&sysconfdir)))
        .arg(format!("--docdir={}", sanitize_sh(&docdir)))
        .arg(format!("--bindir={}", sanitize_sh(&bindir)))
        .arg(format!("--libdir={}", sanitize_sh(&libdir)))
        .arg(format!("--mandir={}", sanitize_sh(&mandir)))
        .arg("--disable-ldconfig");
    build.run(&mut cmd);
    t!(fs::remove_dir_all(&empty_dir));
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    ret
}

macro_rules! install {
    (($sel:ident, $builder:ident, $_config:ident),
       $($name:ident,
       $path:expr,
       $default_cond:expr,
       only_hosts: $only_hosts:expr,
       $run_item:block $(, $c:ident)*;)+) => {
        $(
            #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
        pub struct $name {
            pub stage: u32,
            pub target: Interned<String>,
            pub host: Interned<String>,
        }

        impl $name {
            #[allow(dead_code)]
            fn should_build(config: &Config) -> bool {
                config.extended && config.tools.as_ref()
                    .map_or(true, |t| t.contains($path))
            }

            #[allow(dead_code)]
            fn should_install(builder: &Builder) -> bool {
                builder.config.tools.as_ref().map_or(false, |t| t.contains($path))
            }
        }

        impl Step for $name {
            type Output = ();
            const DEFAULT: bool = true;
            const ONLY_BUILD_TARGETS: bool = true;
            const ONLY_HOSTS: bool = $only_hosts;
            $(const $c: bool = true;)*

            fn should_run(run: ShouldRun) -> ShouldRun {
                let $_config = &run.builder.config;
                run.path($path).default_condition($default_cond)
            }

            fn make_run(run: RunConfig) {
                run.builder.ensure($name {
                    stage: run.builder.top_stage,
                    target: run.target,
                    host: run.host,
                });
            }

            fn run($sel, $builder: &Builder) {
                $run_item
            }
        })+
    }
}

install!((self, builder, _config),
    Docs, "src/doc", _config.docs, only_hosts: false, {
        builder.ensure(dist::Docs { stage: self.stage, host: self.target });
        install_docs(builder, self.stage, self.target);
    };
    Std, "src/libstd", true, only_hosts: true, {
        for target in &builder.build.targets {
            builder.ensure(dist::Std {
                compiler: builder.compiler(self.stage, self.host),
                target: *target
            });
            install_std(builder, self.stage, *target);
        }
    };
    Cargo, "cargo", Self::should_build(_config), only_hosts: true, {
        builder.ensure(dist::Cargo { stage: self.stage, target: self.target });
        install_cargo(builder, self.stage, self.target);
    };
    Rls, "rls", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rls { stage: self.stage, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_rls(builder, self.stage, self.target);
        } else {
            println!("skipping Install RLS stage{} ({})", self.stage, self.target);
        }
    };
    Rustfmt, "rustfmt", Self::should_build(_config), only_hosts: true, {
        if builder.ensure(dist::Rustfmt { stage: self.stage, target: self.target }).is_some() ||
            Self::should_install(builder) {
            install_rustfmt(builder, self.stage, self.target);
        } else {
            println!("skipping Install Rustfmt stage{} ({})", self.stage, self.target);
        }
    };
    Analysis, "analysis", Self::should_build(_config), only_hosts: false, {
        builder.ensure(dist::Analysis {
            compiler: builder.compiler(self.stage, self.host),
            target: self.target
        });
        install_analysis(builder, self.stage, self.target);
    };
    Src, "src", Self::should_build(_config) , only_hosts: true, {
        builder.ensure(dist::Src);
        install_src(builder, self.stage);
    }, ONLY_BUILD;
    Rustc, "src/librustc", true, only_hosts: true, {
        builder.ensure(dist::Rustc {
            compiler: builder.compiler(self.stage, self.target),
        });
        install_rustc(builder, self.stage, self.target);
    };
);
<|endoftext|>


<commit_before>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use Build;
use dist::{sanitize_sh, tmpdir};

/// Installs everything.
pub fn install(build: &Build, stage: u32, host: &str) {
    let prefix_default = PathBuf::from("/usr/local");
    let docdir_default = PathBuf::from("share/doc/rust");
    let bindir_default = PathBuf::from("bin");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = PathBuf::from("share/man");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));
    if build.config.docs {
        install_sh(&build, "docs", "rust-docs", &build.rust_package_vers(),
                   stage, host, &prefix, &docdir, &bindir, &libdir, &mandir, &empty_dir);
    }

    for target in build.config.target.iter() {
        install_sh(&build, "std", "rust-std", &build.rust_package_vers(),
                   stage, target, &prefix, &docdir, &bindir, &libdir, &mandir, &empty_dir);
    }

    if build.config.extended {
        install_sh(&build, "cargo", "cargo", &build.cargo_package_vers(),
                   stage, host, &prefix, &docdir, &bindir, &libdir, &mandir, &empty_dir);
        install_sh(&build, "rls", "rls", &build.rls_package_vers(),
                   stage, host, &prefix, &docdir, &bindir, &libdir, &mandir, &empty_dir);
    }

    install_sh(&build, "rustc", "rustc", &build.rust_package_vers(),
               stage, host, &prefix, &docdir, &bindir, &libdir, &mandir, &empty_dir);

    t!(fs::remove_dir_all(&empty_dir));
}

fn install_sh(build: &Build, package: &str, name: &str, version: &str, stage: u32, host: &str,
              prefix: &Path, docdir: &Path, bindir: &Path, libdir: &Path, mandir: &Path, empty_dir: &Path) {
    println!("Install {} stage{} ({})", package, stage, host);
    let package_name = format!("{}-{}-{}", name, version, host);

    let mut cmd = Command::new("sh");
    cmd.current_dir(empty_dir)
       .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
       .arg(format!("--prefix={}", sanitize_sh(prefix)))
       .arg(format!("--docdir={}", sanitize_sh(docdir)))
       .arg(format!("--bindir={}", sanitize_sh(bindir)))
       .arg(format!("--libdir={}", sanitize_sh(libdir)))
       .arg(format!("--mandir={}", sanitize_sh(mandir)))
       .arg("--disable-ldconfig");
    build.run(&mut cmd);
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    return ret
}
<commit_msg>rustbuild: pass sysconfdir to install_sh<commit_after>// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Implementation of the install aspects of the compiler.
//!
//! This module is responsible for installing the standard library,
//! compiler, and documentation.

use std::env;
use std::fs;
use std::path::{Path, PathBuf, Component};
use std::process::Command;

use Build;
use dist::{sanitize_sh, tmpdir};

/// Installs everything.
pub fn install(build: &Build, stage: u32, host: &str) {
    let prefix_default = PathBuf::from("/usr/local");
    let sysconfdir_default = PathBuf::from("/etc");
    let docdir_default = PathBuf::from("share/doc/rust");
    let bindir_default = PathBuf::from("bin");
    let libdir_default = PathBuf::from("lib");
    let mandir_default = PathBuf::from("share/man");
    let prefix = build.config.prefix.as_ref().unwrap_or(&prefix_default);
    let sysconfdir = build.config.sysconfdir.as_ref().unwrap_or(&sysconfdir_default);
    let docdir = build.config.docdir.as_ref().unwrap_or(&docdir_default);
    let bindir = build.config.bindir.as_ref().unwrap_or(&bindir_default);
    let libdir = build.config.libdir.as_ref().unwrap_or(&libdir_default);
    let mandir = build.config.mandir.as_ref().unwrap_or(&mandir_default);

    let sysconfdir = prefix.join(sysconfdir);
    let docdir = prefix.join(docdir);
    let bindir = prefix.join(bindir);
    let libdir = prefix.join(libdir);
    let mandir = prefix.join(mandir);

    let destdir = env::var_os("DESTDIR").map(PathBuf::from);

    let prefix = add_destdir(&prefix, &destdir);
    let sysconfdir = add_destdir(&sysconfdir, &destdir);
    let docdir = add_destdir(&docdir, &destdir);
    let bindir = add_destdir(&bindir, &destdir);
    let libdir = add_destdir(&libdir, &destdir);
    let mandir = add_destdir(&mandir, &destdir);

    let empty_dir = build.out.join("tmp/empty_dir");
    t!(fs::create_dir_all(&empty_dir));
    if build.config.docs {
        install_sh(&build, "docs", "rust-docs", &build.rust_package_vers(),
                   stage, host, &prefix, &sysconfdir, &docdir, &bindir, &libdir,
                   &mandir, &empty_dir);
    }

    for target in build.config.target.iter() {
        install_sh(&build, "std", "rust-std", &build.rust_package_vers(),
                   stage, target, &prefix, &sysconfdir, &docdir, &bindir, &libdir,
                   &mandir, &empty_dir);
    }

    if build.config.extended {
        install_sh(&build, "cargo", "cargo", &build.cargo_package_vers(),
                   stage, host, &prefix, &sysconfdir, &docdir, &bindir, &libdir,
                   &mandir, &empty_dir);
        install_sh(&build, "rls", "rls", &build.rls_package_vers(),
                   stage, host, &prefix, &sysconfdir, &docdir, &bindir, &libdir,
                   &mandir, &empty_dir);
    }

    install_sh(&build, "rustc", "rustc", &build.rust_package_vers(),
               stage, host, &prefix, &sysconfdir, &docdir, &bindir, &libdir,
               &mandir, &empty_dir);

    t!(fs::remove_dir_all(&empty_dir));
}

fn install_sh(build: &Build, package: &str, name: &str, version: &str, stage: u32, host: &str,
              prefix: &Path, sysconfdir: &Path, docdir: &Path, bindir: &Path, libdir: &Path,
              mandir: &Path, empty_dir: &Path) {
    println!("Install {} stage{} ({})", package, stage, host);
    let package_name = format!("{}-{}-{}", name, version, host);

    let mut cmd = Command::new("sh");
    cmd.current_dir(empty_dir)
       .arg(sanitize_sh(&tmpdir(build).join(&package_name).join("install.sh")))
       .arg(format!("--prefix={}", sanitize_sh(prefix)))
       .arg(format!("--sysconfdir={}", sanitize_sh(sysconfdir)))
       .arg(format!("--docdir={}", sanitize_sh(docdir)))
       .arg(format!("--bindir={}", sanitize_sh(bindir)))
       .arg(format!("--libdir={}", sanitize_sh(libdir)))
       .arg(format!("--mandir={}", sanitize_sh(mandir)))
       .arg("--disable-ldconfig");
    build.run(&mut cmd);
}

fn add_destdir(path: &Path, destdir: &Option<PathBuf>) -> PathBuf {
    let mut ret = match *destdir {
        Some(ref dest) => dest.clone(),
        None => return path.to_path_buf(),
    };
    for part in path.components() {
        match part {
            Component::Normal(s) => ret.push(s),
            _ => {}
        }
    }
    return ret
}
<|endoftext|>