<commit_before>use crate::constants::*;

#[derive(Clone, Debug, PartialEq)]
pub enum Style {
    Old,
    OldInverse,
    New,
    NewInverse,
    Error,
}

#[derive(Clone, Debug, PartialEq)]
pub struct StyledToken {
    token: String,
    style: Style,
}

impl StyledToken {
    pub fn new(token: String, style: Style) -> StyledToken {
        return StyledToken { token, style };
    }

    pub fn is_whitespace(&self) -> bool {
        let mut chars_iterator = self.token.chars();
        let first_char = chars_iterator.next().unwrap();
        if chars_iterator.next().is_some() {
            // Multiple chars found in this token, but whitespace will only be
            // one per token.
            return false;
        }

        return first_char.is_whitespace();
    }

    pub fn is_word(&self) -> bool {
        let mut chars_iterator = self.token.chars();
        let first_char = chars_iterator.next().unwrap();
        let second_char = chars_iterator.next();
        if second_char.is_some() {
            // We consist of multiple characters, that means we are a word
            return true;
        }

        // If we get here, it means our token consists of one character only. If
        // that single character is alphanumeric, we are a word, otherwise not.
        return first_char.is_alphanumeric();
    }
}

pub struct TokenCollector {
    line_prefix: StyledToken,
    tokens: Vec<StyledToken>,
    rendered: bool,
}

impl Style {
    #[must_use]
    pub fn is_inverse(&self) -> bool {
        match self {
            Style::OldInverse | Style::NewInverse | Style::Error => {
                return true;
            }
            _ => {
                return false;
            }
        }
    }

    pub fn inverted(&self) -> Style {
        return match self {
            Style::Old => Style::OldInverse,
            Style::New => Style::NewInverse,
            Style::OldInverse => Style::OldInverse,
            Style::NewInverse => Style::NewInverse,
            Style::Error => Style::Error,
        };
    }

    #[must_use]
    pub fn color<'a>(&self) -> &'a str {
        match self {
            Style::Old => {
                return OLD;
            }
            Style::OldInverse => {
                return OLD;
            }
            Style::New => {
                return NEW;
            }
            Style::NewInverse => {
                return NEW;
            }
            Style::Error => {
                return ERROR;
            }
        }
    }
}

impl TokenCollector {
    #[must_use]
    pub fn create(line_prefix: StyledToken) -> Self {
        return TokenCollector {
            line_prefix,
            tokens: Vec::new(),
            rendered: false,
        };
    }

    pub fn push(&mut self, token: StyledToken) {
        self.tokens.push(token);
    }

    #[must_use]
    fn render_row(&self, row: &mut [StyledToken]) -> String {
        let mut rendered = String::new();

        if self.line_prefix.style == Style::New {
            highlight_trailing_whitespace(row);
            highlight_nonleading_tab(row);
        }
        highlight_space_between_words(row);

        // Set inverse from prefix
        let mut is_inverse = self.line_prefix.style.is_inverse();
        if is_inverse {
            rendered.push_str(INVERSE_VIDEO);
        }

        // Set line color from prefix
        let mut color = self.line_prefix.style.color();
        rendered.push_str(self.line_prefix.style.color());

        // Render prefix
        rendered.push_str(&self.line_prefix.token);

        for token in row {
            if token.style.is_inverse() && !is_inverse {
                rendered.push_str(INVERSE_VIDEO);
            }
            if is_inverse && !token.style.is_inverse() {
                rendered.push_str(NOT_INVERSE_VIDEO);
            }
            is_inverse = token.style.is_inverse();

            if token.style.color() != color {
                rendered.push_str(token.style.color());
                color = token.style.color();
            }

            rendered.push_str(&token.token);
        }

        rendered.push_str(NORMAL);

        return rendered;
    }

    /// Render all the tokens into a (most of the time multiline) string
    #[must_use]
    pub fn render(&mut self) -> String {
        assert!(!self.rendered);
        let mut current_row: Vec<StyledToken> = Vec::new();
        let mut rendered = String::new();

        let mut tokens = std::mem::take(&mut self.tokens);

        // FIXME: Do highlight_space_between_words() first?
        censor_multi_line_highlights(&mut tokens);

        for token in tokens {
            if token.token == "\n" {
                let rendered_row = &self.render_row(&mut current_row);
                rendered.push_str(rendered_row);
                rendered.push('\n');
                current_row.clear();
                continue;
            }

            current_row.push(token);
        }

        if !current_row.is_empty() {
            let rendered_row = &self.render_row(&mut current_row);
            rendered.push_str(rendered_row);
        }

        self.rendered = true;
        return rendered;
    }
}

fn censor_multi_line_highlights(_: &mut [StyledToken]) {
    // FIXME: Code missing here
}

fn highlight_trailing_whitespace(row: &mut [StyledToken]) {
    for token in row.iter_mut().rev() {
        if !token.is_whitespace() {
            return;
        }

        token.style = Style::Error;
    }
}

fn highlight_nonleading_tab(row: &mut [StyledToken]) {
    let mut token_iter = row.iter_mut();

    // Skip leading TABs
    loop {
        let next = token_iter.next();
        if next.is_none() {
            // Done!
            return;
        }

        let token = next.unwrap();
        if token.token != "\t" {
            // Not a TAB, this means we're out of skipping the leading TABs
            break;
        }
    }

    // Scan the rest of the line for non-leading TABs
    for token in token_iter {
        if token.token != "\t" {
            // Not a TAB, never mind
            continue;
        }

        // Non-leading TAB, mark it!
        token.style = Style::Error;
    }
}

/// Highlight single space between two highlighted words
fn highlight_space_between_words(row: &mut [StyledToken]) {
    enum FoundState {
        Nothing,
        HighlightedWord,
        WordSpace,
    }

    let mut found_state = FoundState::Nothing;
    let mut previous_token: Option<&mut StyledToken> = None;
    for token in row.iter_mut() {
        match found_state {
            FoundState::Nothing => {
                if token.style.is_inverse() && token.is_word() {
                    // Found "Monkey"
                    found_state = FoundState::HighlightedWord;
                }
            }

            FoundState::HighlightedWord => {
                if token.is_whitespace() {
                    // Found "Monkey " (note trailing space)
                    found_state = FoundState::WordSpace;
                } else if token.style.is_inverse() && token.is_word() {
                    found_state = FoundState::HighlightedWord;
                } else {
                    found_state = FoundState::Nothing;
                }
            }

            FoundState::WordSpace => {
                if token.style.is_inverse() && token.is_word() {
                    // Found "Monkey Dance"
                    if let Some(_previous_token) = previous_token {
                        _previous_token.style = _previous_token.style.inverted();
                    }

                    found_state = FoundState::HighlightedWord;
                } else {
                    found_state = FoundState::Nothing;
                }
            }
        }

        previous_token = Some(token);
    }
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_basic() {
        let mut test_me = TokenCollector::create(StyledToken {
            token: "+".to_string(),
            style: Style::New,
        });

        test_me.push(StyledToken {
            token: "hej".to_string(),
            style: Style::New,
        });

        test_me.push(StyledToken {
            token: "\n".to_string(),
            style: Style::New,
        });

        let rendered = test_me.render();
        assert_eq!(rendered, format!("{}+hej{}\n", NEW, NORMAL));
    }

    #[test]
    fn test_add_trailing_whitespace() {
        // Just a whitespace
        let mut row = [StyledToken::new(" ".to_string(), Style::New)];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(row, [StyledToken::new(" ".to_string(), Style::Error)]);

        // Trailing whitespace
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new(" ".to_string(), Style::New),
        ];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new(" ".to_string(), Style::Error),
            ]
        );

        // Leading whitespace
        let mut row = [
            StyledToken::new(" ".to_string(), Style::New),
            StyledToken::new("x".to_string(), Style::New),
        ];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new(" ".to_string(), Style::New),
                StyledToken::new("x".to_string(), Style::New),
            ]
        );
    }

    #[test]
    fn test_removed_trailing_whitespace() {
        // It shouldn't be highlighted, just added ones should
        let mut test_me = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
        test_me.push(StyledToken::new(" ".to_string(), Style::Old));
        let actual = test_me.render();

        assert_eq!(actual, format!("{}- {}", OLD, NORMAL));
    }

    #[test]
    fn test_add_nonleading_tab() {
        // Trailing TAB
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new("\t".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new("\t".to_string(), Style::Error),
            ]
        );

        // Middle TAB
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new("\t".to_string(), Style::New),
            StyledToken::new("y".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new("\t".to_string(), Style::Error),
                StyledToken::new("y".to_string(), Style::New),
            ]
        );

        // Leading TAB (don't highlight)
        let mut row = [
            StyledToken::new("\t".to_string(), Style::New),
            StyledToken::new("x".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("\t".to_string(), Style::New),
                StyledToken::new("x".to_string(), Style::New),
            ]
        );

        // Single TAB (don't highlight because it is leading)
        let mut row = [StyledToken::new("\t".to_string(), Style::New)];
        highlight_nonleading_tab(&mut row);
        assert_eq!(row, [StyledToken::new("\t".to_string(), Style::New),]);
    }

    #[test]
    fn test_removed_nonleading_tab() {
        // It shouldn't be highlighted, just added ones should
        let mut test_me = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
        test_me.push(StyledToken::new("x".to_string(), Style::Old));
        test_me.push(StyledToken::new("\t".to_string(), Style::Old));
        let actual = test_me.render();

        assert_eq!(actual, format!("{}-x\t{}", OLD, NORMAL));
    }

    #[test]
    fn test_highlight_space_between_words() {
        let mut row = [
            StyledToken::new("Monkey".to_string(), Style::NewInverse),
            StyledToken::new(" ".to_string(), Style::New),
            StyledToken::new("Dance".to_string(), Style::NewInverse),
        ];

        highlight_space_between_words(&mut row);

        assert_eq!(
            row,
            [
                StyledToken::new("Monkey".to_string(), Style::NewInverse),
                StyledToken::new(" ".to_string(), Style::NewInverse),
                StyledToken::new("Dance".to_string(), Style::NewInverse),
            ]
        );
    }

    /// "_" is an unhighlighted token, "." is a highlighted token, "n" is an
    /// unhighlighted newline and "N" is a highlighted newline.
    fn test_censor_multiline_highlighting(input: &str, expected_output: &str) {
        let mut row = Vec::new();
        for c in input.chars() {
            let mut token = "x".to_string();
            if c == 'n' || c == 'N' {
                token = '\n'.to_string();
            }

            let mut style = Style::New;
            if c == '.' || c == 'N' {
                style = Style::NewInverse;
            }

            row.push(StyledToken { token, style });
        }

        censor_multi_line_highlights(&mut row);

        let mut actual_output = String::new();
        for token in row {
            if token.token != "\n" && !token.style.is_inverse() {
                actual_output.push('_');
            } else if token.token != "\n" && token.style.is_inverse() {
                actual_output.push('.');
            } else if token.token == "\n" && !token.style.is_inverse() {
                actual_output.push('n');
            } else if token.token == "\n" && token.style.is_inverse() {
                actual_output.push('N');
            } else {
                panic!("How did we get here?");
            }
        }

        assert_eq!(actual_output, expected_output);
    }

    #[test]
    fn test_censor_multiline_highlights_start_at_beginning() {
        test_censor_multiline_highlighting("...n___", "___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_at_end() {
        test_censor_multiline_highlighting("___n...", "___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_midline() {
        test_censor_multiline_highlighting("___n...n___", "___n___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_spans_multiline() {
        test_censor_multiline_highlighting("__.n...n.__", "___n___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_edgecases() {
        test_censor_multiline_highlighting("n.n", "n_n");
        test_censor_multiline_highlighting("...", "___");
    }

    #[test]
    fn test_censor_multiline_highlights_dont_highlight() {
        // Highlighted line parts should not be censored
        test_censor_multiline_highlighting("_.._n.__.", "_.._n.__.");
    }
}
<commit_msg>Fix two test failures<commit_after>use crate::constants::*;

#[derive(Clone, Debug, PartialEq)]
pub enum Style {
    Old,
    OldInverse,
    New,
    NewInverse,
    Error,
}

#[derive(Clone, Debug, PartialEq)]
pub struct StyledToken {
    token: String,
    style: Style,
}

impl StyledToken {
    pub fn new(token: String, style: Style) -> StyledToken {
        return StyledToken { token, style };
    }

    pub fn is_whitespace(&self) -> bool {
        let mut chars_iterator = self.token.chars();
        let first_char = chars_iterator.next().unwrap();
        if chars_iterator.next().is_some() {
            // Multiple chars found in this token, but whitespace will only be
            // one per token.
            return false;
        }

        return first_char.is_whitespace();
    }

    pub fn is_word(&self) -> bool {
        let mut chars_iterator = self.token.chars();
        let first_char = chars_iterator.next().unwrap();
        let second_char = chars_iterator.next();
        if second_char.is_some() {
            // We consist of multiple characters, that means we are a word
            return true;
        }

        // If we get here, it means our token consists of one character only. If
        // that single character is alphanumeric, we are a word, otherwise not.
        return first_char.is_alphanumeric();
    }
}

pub struct TokenCollector {
    line_prefix: StyledToken,
    tokens: Vec<StyledToken>,
    rendered: bool,
}

impl Style {
    #[must_use]
    pub fn is_inverse(&self) -> bool {
        match self {
            Style::OldInverse | Style::NewInverse | Style::Error => {
                return true;
            }
            _ => {
                return false;
            }
        }
    }

    pub fn inverted(&self) -> Style {
        return match self {
            Style::Old => Style::OldInverse,
            Style::New => Style::NewInverse,
            Style::OldInverse => Style::OldInverse,
            Style::NewInverse => Style::NewInverse,
            Style::Error => Style::Error,
        };
    }

    pub fn not_inverted(&self) -> Style {
        return match self {
            Style::Old => Style::Old,
            Style::New => Style::New,
            Style::OldInverse => Style::Old,
            Style::NewInverse => Style::New,
            Style::Error => Style::Error,
        };
    }

    #[must_use]
    pub fn color<'a>(&self) -> &'a str {
        match self {
            Style::Old => {
                return OLD;
            }
            Style::OldInverse => {
                return OLD;
            }
            Style::New => {
                return NEW;
            }
            Style::NewInverse => {
                return NEW;
            }
            Style::Error => {
                return ERROR;
            }
        }
    }
}

impl TokenCollector {
    #[must_use]
    pub fn create(line_prefix: StyledToken) -> Self {
        return TokenCollector {
            line_prefix,
            tokens: Vec::new(),
            rendered: false,
        };
    }

    pub fn push(&mut self, token: StyledToken) {
        self.tokens.push(token);
    }

    #[must_use]
    fn render_row(&self, row: &mut [StyledToken]) -> String {
        let mut rendered = String::new();

        if self.line_prefix.style == Style::New {
            highlight_trailing_whitespace(row);
            highlight_nonleading_tab(row);
        }
        highlight_space_between_words(row);

        // Set inverse from prefix
        let mut is_inverse = self.line_prefix.style.is_inverse();
        if is_inverse {
            rendered.push_str(INVERSE_VIDEO);
        }

        // Set line color from prefix
        let mut color = self.line_prefix.style.color();
        rendered.push_str(self.line_prefix.style.color());

        // Render prefix
        rendered.push_str(&self.line_prefix.token);

        for token in row {
            if token.style.is_inverse() && !is_inverse {
                rendered.push_str(INVERSE_VIDEO);
            }
            if is_inverse && !token.style.is_inverse() {
                rendered.push_str(NOT_INVERSE_VIDEO);
            }
            is_inverse = token.style.is_inverse();

            if token.style.color() != color {
                rendered.push_str(token.style.color());
                color = token.style.color();
            }

            rendered.push_str(&token.token);
        }

        rendered.push_str(NORMAL);

        return rendered;
    }

    /// Render all the tokens into a (most of the time multiline) string
    #[must_use]
    pub fn render(&mut self) -> String {
        assert!(!self.rendered);
        let mut current_row: Vec<StyledToken> = Vec::new();
        let mut rendered = String::new();

        let mut tokens = std::mem::take(&mut self.tokens);

        // FIXME: Do highlight_space_between_words() first?
        censor_multi_line_highlights(&mut tokens);

        for token in tokens {
            if token.token == "\n" {
                let rendered_row = &self.render_row(&mut current_row);
                rendered.push_str(rendered_row);
                rendered.push('\n');
                current_row.clear();
                continue;
            }

            current_row.push(token);
        }

        if !current_row.is_empty() {
            let rendered_row = &self.render_row(&mut current_row);
            rendered.push_str(rendered_row);
        }

        self.rendered = true;
        return rendered;
    }
}

fn censor_multi_line_highlights(rows: &mut [StyledToken]) {
    let mut last_was_highlighted = false;

    let mut first_highlighted_index: usize = 0;
    let mut newline_seen = false;

    for index in 0..rows.len() {
        if index > 0 {
            let last_was_newline = rows[index - 1].token == "\n";

            // Newlines always count as highlighted
            last_was_highlighted = rows[index - 1].style.is_inverse() || last_was_newline;
        }
        let token = &rows[index];
        let is_newline = token.token == "\n";

        // Newlines always count as highlighted
        let is_highlighted = is_newline || token.style.is_inverse();

        if is_highlighted {
            if !last_was_highlighted {
                // Start of new section
                first_highlighted_index = index;
            }
            if is_newline {
                newline_seen = true;
            }
            continue;
        }

        assert!(!is_highlighted);
        if !last_was_highlighted {
            // We're in a run of non-highlighted tokens, do nothing
            continue;
        }

        // Found the end
        assert!(last_was_highlighted && !is_highlighted);
        if !newline_seen {
            // No newline in this highlighted section, no further action needed
            continue;
        }

        // Found end of highlighted section containing newlines, unhighlight!
        #[allow(clippy::needless_range_loop)]
        for unhighlight_index in first_highlighted_index..index {
            rows[unhighlight_index].style = rows[unhighlight_index].style.not_inverted();
        }
    }
}

fn highlight_trailing_whitespace(row: &mut [StyledToken]) {
    for token in row.iter_mut().rev() {
        if !token.is_whitespace() {
            return;
        }

        token.style = Style::Error;
    }
}

fn highlight_nonleading_tab(row: &mut [StyledToken]) {
    let mut token_iter = row.iter_mut();

    // Skip leading TABs
    loop {
        let next = token_iter.next();
        if next.is_none() {
            // Done!
            return;
        }

        let token = next.unwrap();
        if token.token != "\t" {
            // Not a TAB, this means we're out of skipping the leading TABs
            break;
        }
    }

    // Scan the rest of the line for non-leading TABs
    for token in token_iter {
        if token.token != "\t" {
            // Not a TAB, never mind
            continue;
        }

        // Non-leading TAB, mark it!
        token.style = Style::Error;
    }
}

/// Highlight single space between two highlighted words
fn highlight_space_between_words(row: &mut [StyledToken]) {
    enum FoundState {
        Nothing,
        HighlightedWord,
        WordSpace,
    }

    let mut found_state = FoundState::Nothing;
    let mut previous_token: Option<&mut StyledToken> = None;
    for token in row.iter_mut() {
        match found_state {
            FoundState::Nothing => {
                if token.style.is_inverse() && token.is_word() {
                    // Found "Monkey"
                    found_state = FoundState::HighlightedWord;
                }
            }

            FoundState::HighlightedWord => {
                if token.is_whitespace() {
                    // Found "Monkey " (note trailing space)
                    found_state = FoundState::WordSpace;
                } else if token.style.is_inverse() && token.is_word() {
                    found_state = FoundState::HighlightedWord;
                } else {
                    found_state = FoundState::Nothing;
                }
            }

            FoundState::WordSpace => {
                if token.style.is_inverse() && token.is_word() {
                    // Found "Monkey Dance"
                    if let Some(_previous_token) = previous_token {
                        _previous_token.style = _previous_token.style.inverted();
                    }

                    found_state = FoundState::HighlightedWord;
                } else {
                    found_state = FoundState::Nothing;
                }
            }
        }

        previous_token = Some(token);
    }
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_basic() {
        let mut test_me = TokenCollector::create(StyledToken {
            token: "+".to_string(),
            style: Style::New,
        });

        test_me.push(StyledToken {
            token: "hej".to_string(),
            style: Style::New,
        });

        test_me.push(StyledToken {
            token: "\n".to_string(),
            style: Style::New,
        });

        let rendered = test_me.render();
        assert_eq!(rendered, format!("{}+hej{}\n", NEW, NORMAL));
    }

    #[test]
    fn test_add_trailing_whitespace() {
        // Just a whitespace
        let mut row = [StyledToken::new(" ".to_string(), Style::New)];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(row, [StyledToken::new(" ".to_string(), Style::Error)]);

        // Trailing whitespace
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new(" ".to_string(), Style::New),
        ];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new(" ".to_string(), Style::Error),
            ]
        );

        // Leading whitespace
        let mut row = [
            StyledToken::new(" ".to_string(), Style::New),
            StyledToken::new("x".to_string(), Style::New),
        ];
        highlight_trailing_whitespace(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new(" ".to_string(), Style::New),
                StyledToken::new("x".to_string(), Style::New),
            ]
        );
    }

    #[test]
    fn test_removed_trailing_whitespace() {
        // It shouldn't be highlighted, just added ones should
        let mut test_me = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
        test_me.push(StyledToken::new(" ".to_string(), Style::Old));
        let actual = test_me.render();

        assert_eq!(actual, format!("{}- {}", OLD, NORMAL));
    }

    #[test]
    fn test_add_nonleading_tab() {
        // Trailing TAB
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new("\t".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new("\t".to_string(), Style::Error),
            ]
        );

        // Middle TAB
        let mut row = [
            StyledToken::new("x".to_string(), Style::New),
            StyledToken::new("\t".to_string(), Style::New),
            StyledToken::new("y".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("x".to_string(), Style::New),
                StyledToken::new("\t".to_string(), Style::Error),
                StyledToken::new("y".to_string(), Style::New),
            ]
        );

        // Leading TAB (don't highlight)
        let mut row = [
            StyledToken::new("\t".to_string(), Style::New),
            StyledToken::new("x".to_string(), Style::New),
        ];
        highlight_nonleading_tab(&mut row);
        assert_eq!(
            row,
            [
                StyledToken::new("\t".to_string(), Style::New),
                StyledToken::new("x".to_string(), Style::New),
            ]
        );

        // Single TAB (don't highlight because it is leading)
        let mut row = [StyledToken::new("\t".to_string(), Style::New)];
        highlight_nonleading_tab(&mut row);
        assert_eq!(row, [StyledToken::new("\t".to_string(), Style::New),]);
    }

    #[test]
    fn test_removed_nonleading_tab() {
        // It shouldn't be highlighted, just added ones should
        let mut test_me = TokenCollector::create(StyledToken::new("-".to_string(), Style::Old));
        test_me.push(StyledToken::new("x".to_string(), Style::Old));
        test_me.push(StyledToken::new("\t".to_string(), Style::Old));
        let actual = test_me.render();

        assert_eq!(actual, format!("{}-x\t{}", OLD, NORMAL));
    }

    #[test]
    fn test_highlight_space_between_words() {
        let mut row = [
            StyledToken::new("Monkey".to_string(), Style::NewInverse),
            StyledToken::new(" ".to_string(), Style::New),
            StyledToken::new("Dance".to_string(), Style::NewInverse),
        ];

        highlight_space_between_words(&mut row);

        assert_eq!(
            row,
            [
                StyledToken::new("Monkey".to_string(), Style::NewInverse),
                StyledToken::new(" ".to_string(), Style::NewInverse),
                StyledToken::new("Dance".to_string(), Style::NewInverse),
            ]
        );
    }

    /// "_" is an unhighlighted token, "." is a highlighted token, "n" is an
    /// unhighlighted newline and "N" is a highlighted newline.
    fn test_censor_multiline_highlighting(input: &str, expected_output: &str) {
        let mut row = Vec::new();
        for c in input.chars() {
            let mut token = "x".to_string();
            if c == 'n' || c == 'N' {
                token = '\n'.to_string();
            }

            let mut style = Style::New;
            if c == '.' || c == 'N' {
                style = Style::NewInverse;
            }

            row.push(StyledToken { token, style });
        }

        censor_multi_line_highlights(&mut row);

        let mut actual_output = String::new();
        for token in row {
            if token.token != "\n" && !token.style.is_inverse() {
                actual_output.push('_');
            } else if token.token != "\n" && token.style.is_inverse() {
                actual_output.push('.');
            } else if token.token == "\n" && !token.style.is_inverse() {
                actual_output.push('n');
            } else if token.token == "\n" && token.style.is_inverse() {
                actual_output.push('N');
            } else {
                panic!("How did we get here?");
            }
        }

        assert_eq!(actual_output, expected_output);
    }

    #[test]
    fn test_censor_multiline_highlights_start_at_beginning() {
        test_censor_multiline_highlighting("...n___", "___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_at_end() {
        test_censor_multiline_highlighting("___n...", "___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_midline() {
        test_censor_multiline_highlighting("___n...n___", "___n___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_spans_multiline() {
        test_censor_multiline_highlighting("__.n...n.__", "___n___n___");
    }

    #[test]
    fn test_censor_multiline_highlights_edgecases() {
        test_censor_multiline_highlighting("n.n", "n_n");
        test_censor_multiline_highlighting("...", "___");
        test_censor_multiline_highlighting(".n.", "_n_");
        test_censor_multiline_highlighting("_.n.n._", "__n_n__");
    }

    #[test]
    fn test_censor_multiline_highlights_dont_highlight() {
        // Highlighted line parts should not be censored
        test_censor_multiline_highlighting("_.._n.__.", "_.._n.__.");
    }
}
<|endoftext|>


<commit_before>#[macro_export]
macro_rules! composite {
    ( $( $arg:expr ),+ ) => {
        {
            use $crate::component::{Component, SubComponent};
            use $crate::Error;
            use ::futures::{Poll, Async};

            struct CompositeComponent {
                components: Vec<SubComponent>,
                stream: CompositeComponentStream,
            }

            impl Component for CompositeComponent {
                type Error = Error;
                type Stream = CompositeComponentStream;

                fn init(&mut self) -> Result<(), Error> {
                    for component in self.components.iter_mut() {
                        component.0.init()?;
                    }
                    Ok(())
                }

                fn stream(self, handle: Handle) -> Self::Stream {
                    CompositeComponentStream {
                        states: vec![String::new(); self.components.len()],
                        streams: self.components
                            .into_iter()
                            .enumerate()
                            .map(|(id, c)| {
                                Box::new(c.0.into_stream(handle.clone()).map(move |s| (id, s))) as
                                    Box<Stream<Item = (usize, String), Error = Error>>
                            })
                            .collect(),
                    }
                }
            }

            struct CompositeComponentStream {
                streams: Vec<Box<Stream<Item=(usize, String), Error=Error>>>,
                states: Vec<String>,
            };

            impl Stream for CompositeComponentStream {
                type Item = String;
                type Error = Error;

                fn poll(&mut self) -> Poll<Option<String>, Error> {
                    let mut do_update = false;
                    for stream in self.streams.iter_mut() {
                        match stream.poll() {
                            Ok(Async::Ready(Some((id, update)))) => {
                                do_update = true;
                                self.states[id] = update;
                            },
                            Err(err) => return Err(err),
                            _ => {},
                        }
                    }

                    match do_update {
                        true => Ok(Async::Ready(Some(self.states.concat()))),
                        false => Ok(Async::NotReady),
                    }
                }
            }

            CompositeComponent {
                components: vec![$( SubComponent::from($arg) ),+],
                stream: CompositeComponentStream {
                    streams: vec![],
                    states: vec![],
                }
            }
        }
    };
}
<commit_msg>Remove `stream` field as unused.<commit_after>#[macro_export]
macro_rules! composite {
    ( $( $arg:expr ),+ ) => {
        {
            use $crate::component::{Component, SubComponent};
            use $crate::Error;
            use ::futures::{Poll, Async};

            struct CompositeComponent {
                components: Vec<SubComponent>,
            }

            impl Component for CompositeComponent {
                type Error = Error;
                type Stream = CompositeComponentStream;

                fn init(&mut self) -> Result<(), Error> {
                    for component in self.components.iter_mut() {
                        component.0.init()?;
                    }
                    Ok(())
                }

                fn stream(self, handle: Handle) -> Self::Stream {
                    CompositeComponentStream {
                        states: vec![String::new(); self.components.len()],
                        streams: self.components
                            .into_iter()
                            .enumerate()
                            .map(|(id, c)| {
                                Box::new(c.0.into_stream(handle.clone()).map(move |s| (id, s))) as
                                    Box<Stream<Item = (usize, String), Error = Error>>
                            })
                            .collect(),
                    }
                }
            }

            struct CompositeComponentStream {
                streams: Vec<Box<Stream<Item=(usize, String), Error=Error>>>,
                states: Vec<String>,
            };

            impl Stream for CompositeComponentStream {
                type Item = String;
                type Error = Error;

                fn poll(&mut self) -> Poll<Option<String>, Error> {
                    let mut do_update = false;
                    for stream in self.streams.iter_mut() {
                        match stream.poll() {
                            Ok(Async::Ready(Some((id, update)))) => {
                                do_update = true;
                                self.states[id] = update;
                            },
                            Err(err) => return Err(err),
                            _ => {},
                        }
                    }

                    match do_update {
                        true => Ok(Async::Ready(Some(self.states.concat()))),
                        false => Ok(Async::NotReady),
                    }
                }
            }

            CompositeComponent {
                components: vec![$( SubComponent::from($arg) ),+],
            }
        }
    };
}
<|endoftext|>


<commit_before>use Cursive;
use Printer;
use With;
use direction;
use event::{Callback, Event, EventResult, Key};

use std::any::Any;
use std::rc::Rc;

use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::ScrollBase;
use view::Selector;
use view::View;

/// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
    /// A single row, with a label and a view.
    Row(String, Box<View>),
    /// A delimiter between groups.
    Delimiter,
}

impl ListChild {
    fn label(&self) -> &str {
        match *self {
            ListChild::Row(ref label, _) => label,
            _ => "",
        }
    }

    fn view(&mut self) -> Option<&mut Box<View>> {
        match *self {
            ListChild::Row(_, ref mut view) => Some(view),
            _ => None,
        }
    }
}

/// Displays a scrollable list of elements.
pub struct ListView {
    children: Vec<ListChild>,
    scrollbase: ScrollBase,
    focus: usize,
    // This callback is called when the selection is changed.
    on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
}

new_default!(ListView);

impl ListView {
    /// Creates a new, empty `ListView`.
    pub fn new() -> Self {
        ListView {
            children: Vec::new(),
            scrollbase: ScrollBase::new(),
            focus: 0,
            on_select: None,
        }
    }

    /// Returns the number of children, including delimiters.
    pub fn len(&self) -> usize {
        self.children.len()
    }

    /// Returns a reference to the children
    pub fn children(&self) -> &[ListChild] {
        &self.children[..]
    }

    /// Returns a reference to the child at the given position.
    pub fn get_row(&self, id: usize) -> &ListChild {
        &self.children[id]
    }

    /// Gives mutable access to the child at the given position.
    ///
    /// # Panics
    ///
    /// Panics if `id >= self.len()`.
    pub fn row_mut(&mut self, id: usize) -> &mut ListChild {
        &mut self.children[id]
    }

    /// Adds a view to the end of the list.
    pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
        view.take_focus(direction::Direction::none());
        self.children.push(ListChild::Row(label.to_string(), Box::new(view)));
    }

    /// Removes all children from this view.
    pub fn clear(&mut self) {
        self.children.clear();
        self.focus = 0;
    }

    /// Adds a view to the end of the list.
    ///
    /// Chainable variant.
    pub fn child<V: View + 'static>(self, label: &str, view: V) -> Self {
        self.with(|s| s.add_child(label, view))
    }

    /// Adds a delimiter to the end of the list.
    pub fn add_delimiter(&mut self) {
        self.children.push(ListChild::Delimiter);
    }

    /// Adds a delimiter to the end of the list.
    ///
    /// Chainable variant.
    pub fn delimiter(self) -> Self {
        self.with(Self::add_delimiter)
    }

    /// Sets a callback to be used when an item is selected.
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &String) + 'static
    {
        self.on_select = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// Chainable variant.
    pub fn on_select<F>(self, cb: F) -> Self
        where F: Fn(&mut Cursive, &String) + 'static
    {
        self.with(|s| s.set_on_select(cb))
    }

    /// Returns the index of the currently focused item.
    ///
    /// Panics if the list is empty.
    pub fn focus(&self) -> usize {
        self.focus
    }

    fn iter_mut<'a>(&'a mut self, from_focus: bool,
                    source: direction::Relative)
                    -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {

        match source {
            direction::Relative::Front => {
                let start = if from_focus { self.focus } else { 0 };

                Box::new(self.children
                             .iter_mut()
                             .enumerate()
                             .skip(start))
            }
            direction::Relative::Back => {
                let end = if from_focus {
                    self.focus + 1
                } else {
                    self.children.len()
                };
                Box::new(self.children[..end].iter_mut().enumerate().rev())
            }
        }
    }

    fn move_focus(&mut self, n: usize, source: direction::Direction)
                  -> EventResult {
        let i = if let Some(i) =
            source.relative(direction::Orientation::Vertical)
                .and_then(|rel| {
                    // The iterator starts at the focused element.
                    // We don't want that one.
                    self.iter_mut(true, rel)
                        .skip(1)
                        .filter_map(|p| try_focus(p, source))
                        .take(n)
                        .last()
                }) {
            i
        } else {
            return EventResult::Ignored;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(self.on_select.clone().map(|cb| {
            let i = self.focus();
            let focused_string = String::from(self.children[i].label());
            Callback::from_fn(move |s| cb(s, &focused_string))
        }))
    }
}

fn try_focus((i, child): (usize, &mut ListChild), source: direction::Direction)
             -> Option<usize> {
    match *child {
        ListChild::Delimiter => None,
        ListChild::Row(_, ref mut view) => {
            if view.take_focus(source) {
                Some(i)
            } else {
                None
            }
        }

    }
}

impl View for ListView {
    fn draw(&self, printer: &Printer) {
        if self.children.is_empty() {
            return;
        }

        let offset = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0) + 1;

        // println_stderr!("Offset: {}", offset);
        self.scrollbase.draw(printer, |printer, i| match self.children[i] {
            ListChild::Row(ref label, ref view) => {
                printer.print((0, 0), label);
                view.draw(&printer.offset((offset, 0), i == self.focus));
            }
            ListChild::Delimiter => (),
        });
    }

    fn required_size(&mut self, req: Vec2) -> Vec2 {
        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let view_size = self.children
            .iter_mut()
            .filter_map(ListChild::view)
            .map(|v| v.required_size(req).x)
            .max()
            .unwrap_or(0);

        if self.children.len() > req.y {
            Vec2::new(label_width + 1 + view_size + 2, req.y)
        } else {
            Vec2::new(label_width + 1 + view_size, self.children.len())
        }
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.children.len());

        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let spacing = 1;
        let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };

        let available = if label_width + spacing + scrollbar_width > size.x {
            // We have no space for the kids! :(
            0
        } else {
            size.x - label_width - spacing - scrollbar_width
        };

        // println_stderr!("Available: {}", available);

        for child in self.children.iter_mut().filter_map(ListChild::view) {
            child.layout(Vec2::new(available, 1));
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.children.is_empty() {
            return EventResult::Ignored;
        }

        if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
            let result = view.on_event(event.clone());
            if result.is_consumed() {
                return result;
            }
        }

        match event {
            Event::Key(Key::Up) if self.focus > 0 => {
                self.move_focus(1, direction::Direction::down())
            }
            Event::Key(Key::Down) if self.focus + 1 < self.children.len() => {
                self.move_focus(1, direction::Direction::up())
            }
            Event::Key(Key::PageUp) => {
                self.move_focus(10, direction::Direction::down())
            }
            Event::Key(Key::PageDown) => {
                self.move_focus(10, direction::Direction::up())
            }
            Event::Key(Key::Home) |
            Event::Ctrl(Key::Home) => {
                self.move_focus(usize::max_value(),
                                direction::Direction::back())
            }
            Event::Key(Key::End) |
            Event::Ctrl(Key::End) => {
                self.move_focus(usize::max_value(),
                                direction::Direction::front())
            }
            Event::Key(Key::Tab) => {
                self.move_focus(1, direction::Direction::front())
            }
            Event::Shift(Key::Tab) => {
                self.move_focus(1, direction::Direction::back())
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, source: direction::Direction) -> bool {
        let rel = source.relative(direction::Orientation::Vertical);
        let i = if let Some(i) =
            self.iter_mut(rel.is_none(),
                          rel.unwrap_or(direction::Relative::Front))
                .filter_map(|p| try_focus(p, source))
                .next() {
            i
        } else {
            // No one wants to be in focus
            return false;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);
        true
    }

    fn call_on_any<'a>(&mut self, selector: &Selector,
                    mut callback: Box<FnMut(&mut Any) + 'a>) {
        for view in self.children
            .iter_mut()
            .filter_map(ListChild::view) {
            view.call_on_any(selector, Box::new(|any| callback(any)));
        }
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        if let Some(i) = self.children
               .iter_mut()
               .enumerate()
               .filter_map(|(i, v)| v.view().map(|v| (i, v)))
               .filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
               .next() {
            self.focus = i;
            Ok(())
        } else {
            Err(())
        }
    }
}
<commit_msg>Add ListView::is_empty<commit_after>use Cursive;
use Printer;
use With;
use direction;
use event::{Callback, Event, EventResult, Key};

use std::any::Any;
use std::rc::Rc;

use unicode_width::UnicodeWidthStr;
use vec::Vec2;
use view::ScrollBase;
use view::Selector;
use view::View;

/// Represents a child from a [`ListView`].
///
/// [`ListView`]: struct.ListView.html
pub enum ListChild {
    /// A single row, with a label and a view.
    Row(String, Box<View>),
    /// A delimiter between groups.
    Delimiter,
}

impl ListChild {
    fn label(&self) -> &str {
        match *self {
            ListChild::Row(ref label, _) => label,
            _ => "",
        }
    }

    fn view(&mut self) -> Option<&mut Box<View>> {
        match *self {
            ListChild::Row(_, ref mut view) => Some(view),
            _ => None,
        }
    }
}

/// Displays a scrollable list of elements.
pub struct ListView {
    children: Vec<ListChild>,
    scrollbase: ScrollBase,
    focus: usize,
    // This callback is called when the selection is changed.
    on_select: Option<Rc<Fn(&mut Cursive, &String)>>,
}

new_default!(ListView);

impl ListView {
    /// Creates a new, empty `ListView`.
    pub fn new() -> Self {
        ListView {
            children: Vec::new(),
            scrollbase: ScrollBase::new(),
            focus: 0,
            on_select: None,
        }
    }

    /// Returns the number of children, including delimiters.
    pub fn len(&self) -> usize {
        self.children.len()
    }

    /// Returns `true` if this view contains no children.
    ///
    /// Returns `false` if at least a delimiter or a view is present.
    pub fn is_empty(&self) -> bool {
        self.children.is_empty()
    }

    /// Returns a reference to the children
    pub fn children(&self) -> &[ListChild] {
        &self.children[..]
    }

    /// Returns a reference to the child at the given position.
    pub fn get_row(&self, id: usize) -> &ListChild {
        &self.children[id]
    }

    /// Gives mutable access to the child at the given position.
    ///
    /// # Panics
    ///
    /// Panics if `id >= self.len()`.
    pub fn row_mut(&mut self, id: usize) -> &mut ListChild {
        &mut self.children[id]
    }

    /// Adds a view to the end of the list.
    pub fn add_child<V: View + 'static>(&mut self, label: &str, mut view: V) {
        view.take_focus(direction::Direction::none());
        self.children.push(ListChild::Row(label.to_string(), Box::new(view)));
    }

    /// Removes all children from this view.
    pub fn clear(&mut self) {
        self.children.clear();
        self.focus = 0;
    }

    /// Adds a view to the end of the list.
    ///
    /// Chainable variant.
    pub fn child<V: View + 'static>(self, label: &str, view: V) -> Self {
        self.with(|s| s.add_child(label, view))
    }

    /// Adds a delimiter to the end of the list.
    pub fn add_delimiter(&mut self) {
        self.children.push(ListChild::Delimiter);
    }

    /// Adds a delimiter to the end of the list.
    ///
    /// Chainable variant.
    pub fn delimiter(self) -> Self {
        self.with(Self::add_delimiter)
    }

    /// Sets a callback to be used when an item is selected.
    pub fn set_on_select<F>(&mut self, cb: F)
        where F: Fn(&mut Cursive, &String) + 'static
    {
        self.on_select = Some(Rc::new(cb));
    }

    /// Sets a callback to be used when an item is selected.
    ///
    /// Chainable variant.
    pub fn on_select<F>(self, cb: F) -> Self
        where F: Fn(&mut Cursive, &String) + 'static
    {
        self.with(|s| s.set_on_select(cb))
    }

    /// Returns the index of the currently focused item.
    ///
    /// Panics if the list is empty.
    pub fn focus(&self) -> usize {
        self.focus
    }

    fn iter_mut<'a>(&'a mut self, from_focus: bool,
                    source: direction::Relative)
                    -> Box<Iterator<Item = (usize, &mut ListChild)> + 'a> {

        match source {
            direction::Relative::Front => {
                let start = if from_focus { self.focus } else { 0 };

                Box::new(self.children
                             .iter_mut()
                             .enumerate()
                             .skip(start))
            }
            direction::Relative::Back => {
                let end = if from_focus {
                    self.focus + 1
                } else {
                    self.children.len()
                };
                Box::new(self.children[..end].iter_mut().enumerate().rev())
            }
        }
    }

    fn move_focus(&mut self, n: usize, source: direction::Direction)
                  -> EventResult {
        let i = if let Some(i) =
            source.relative(direction::Orientation::Vertical)
                .and_then(|rel| {
                    // The iterator starts at the focused element.
                    // We don't want that one.
                    self.iter_mut(true, rel)
                        .skip(1)
                        .filter_map(|p| try_focus(p, source))
                        .take(n)
                        .last()
                }) {
            i
        } else {
            return EventResult::Ignored;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);

        EventResult::Consumed(self.on_select.clone().map(|cb| {
            let i = self.focus();
            let focused_string = String::from(self.children[i].label());
            Callback::from_fn(move |s| cb(s, &focused_string))
        }))
    }
}

fn try_focus((i, child): (usize, &mut ListChild), source: direction::Direction)
             -> Option<usize> {
    match *child {
        ListChild::Delimiter => None,
        ListChild::Row(_, ref mut view) => {
            if view.take_focus(source) {
                Some(i)
            } else {
                None
            }
        }

    }
}

impl View for ListView {
    fn draw(&self, printer: &Printer) {
        if self.children.is_empty() {
            return;
        }

        let offset = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0) + 1;

        // println_stderr!("Offset: {}", offset);
        self.scrollbase.draw(printer, |printer, i| match self.children[i] {
            ListChild::Row(ref label, ref view) => {
                printer.print((0, 0), label);
                view.draw(&printer.offset((offset, 0), i == self.focus));
            }
            ListChild::Delimiter => (),
        });
    }

    fn required_size(&mut self, req: Vec2) -> Vec2 {
        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let view_size = self.children
            .iter_mut()
            .filter_map(ListChild::view)
            .map(|v| v.required_size(req).x)
            .max()
            .unwrap_or(0);

        if self.children.len() > req.y {
            Vec2::new(label_width + 1 + view_size + 2, req.y)
        } else {
            Vec2::new(label_width + 1 + view_size, self.children.len())
        }
    }

    fn layout(&mut self, size: Vec2) {
        self.scrollbase.set_heights(size.y, self.children.len());

        // We'll show 2 columns: the labels, and the views.
        let label_width = self.children
            .iter()
            .map(ListChild::label)
            .map(UnicodeWidthStr::width)
            .max()
            .unwrap_or(0);

        let spacing = 1;
        let scrollbar_width = if self.children.len() > size.y { 2 } else { 0 };

        let available = if label_width + spacing + scrollbar_width > size.x {
            // We have no space for the kids! :(
            0
        } else {
            size.x - label_width - spacing - scrollbar_width
        };

        // println_stderr!("Available: {}", available);

        for child in self.children.iter_mut().filter_map(ListChild::view) {
            child.layout(Vec2::new(available, 1));
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        if self.children.is_empty() {
            return EventResult::Ignored;
        }

        if let ListChild::Row(_, ref mut view) = self.children[self.focus] {
            let result = view.on_event(event.clone());
            if result.is_consumed() {
                return result;
            }
        }

        match event {
            Event::Key(Key::Up) if self.focus > 0 => {
                self.move_focus(1, direction::Direction::down())
            }
            Event::Key(Key::Down) if self.focus + 1 < self.children.len() => {
                self.move_focus(1, direction::Direction::up())
            }
            Event::Key(Key::PageUp) => {
                self.move_focus(10, direction::Direction::down())
            }
            Event::Key(Key::PageDown) => {
                self.move_focus(10, direction::Direction::up())
            }
            Event::Key(Key::Home) |
            Event::Ctrl(Key::Home) => {
                self.move_focus(usize::max_value(),
                                direction::Direction::back())
            }
            Event::Key(Key::End) |
            Event::Ctrl(Key::End) => {
                self.move_focus(usize::max_value(),
                                direction::Direction::front())
            }
            Event::Key(Key::Tab) => {
                self.move_focus(1, direction::Direction::front())
            }
            Event::Shift(Key::Tab) => {
                self.move_focus(1, direction::Direction::back())
            }
            _ => EventResult::Ignored,
        }
    }

    fn take_focus(&mut self, source: direction::Direction) -> bool {
        let rel = source.relative(direction::Orientation::Vertical);
        let i = if let Some(i) =
            self.iter_mut(rel.is_none(),
                          rel.unwrap_or(direction::Relative::Front))
                .filter_map(|p| try_focus(p, source))
                .next() {
            i
        } else {
            // No one wants to be in focus
            return false;
        };
        self.focus = i;
        self.scrollbase.scroll_to(self.focus);
        true
    }

    fn call_on_any<'a>(&mut self, selector: &Selector,
                    mut callback: Box<FnMut(&mut Any) + 'a>) {
        for view in self.children
            .iter_mut()
            .filter_map(ListChild::view) {
            view.call_on_any(selector, Box::new(|any| callback(any)));
        }
    }

    fn focus_view(&mut self, selector: &Selector) -> Result<(), ()> {
        if let Some(i) = self.children
               .iter_mut()
               .enumerate()
               .filter_map(|(i, v)| v.view().map(|v| (i, v)))
               .filter_map(|(i, v)| v.focus_view(selector).ok().map(|_| i))
               .next() {
            self.focus = i;
            Ok(())
        } else {
            Err(())
        }
    }
}
<|endoftext|>


<commit_before>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

// Represents an upper and lower bound on the multiplicity of a regex token
// Eg. `(Some(0), Some(1))` corresponds to `?` and `(Some(0), None)` corresponds
// to `*`
#[derive(PartialEq, Debug)]
pub struct Multiplicity {
    pub minimum: Option<usize>,
    pub maximum: Option<usize>
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();

    for c in text.chars() {
        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(0),
                                            maximum: Some(1)
                                        }));
                            },
                            _ => { return Err("invalid token before `?` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `?` metacharacter"); }
                }
            },
            '+' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(1),
                                            maximum: None
                                        }));
                            },
                            _ => { return Err("invalid token before `+` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `+` metacharacter"); }
                }
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: Some(1),
                        maximum: Some(1)
                    }
                ));
            }

        }
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: Some(1),
                           maximum: Some(1)
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(0),
                       maximum: Some(1)
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }

    #[test]
    fn handles_one_or_more_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(1),
                       maximum: None
                   })
        ], parse_expressions("a+").unwrap());

        assert!(parse_expressions("+").is_err());
        assert!(parse_expressions("++").is_err());
    }
}

<commit_msg>tokenize character class<commit_after>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

// Represents an upper and lower bound on the multiplicity of a regex token
// Eg. `(Some(0), Some(1))` corresponds to `?` and `(Some(0), None)` corresponds
// to `*`
#[derive(PartialEq, Debug)]
pub struct Multiplicity {
    pub minimum: Option<usize>,
    pub maximum: Option<usize>
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();
    let mut in_char_class = false;
    let mut chars_in_class = Vec::new();

    for c in text.chars() {
        if in_char_class && c != ']' {
            chars_in_class.push(c);
            continue
        }

        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(0),
                                            maximum: Some(1)
                                        }));
                            },
                            _ => { return Err("invalid token before `?` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `?` metacharacter"); }
                }
            },
            '+' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(1),
                                            maximum: None
                                        }));
                            },
                            _ => { return Err("invalid token before `+` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `+` metacharacter"); }
                }
            },
            '[' => {
                in_char_class = true;
                chars_in_class.clear();
            },
            ']' => {
                in_char_class = false;
                result.push(Expression::Token(
                        Token::Class(chars_in_class.clone()),
                        Multiplicity {
                            minimum: Some(1),
                            maximum: Some(1)
                        }));
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: Some(1),
                        maximum: Some(1)
                    }
                ));
            }

        }
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: Some(1),
                           maximum: Some(1)
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(0),
                       maximum: Some(1)
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }

    #[test]
    fn handles_one_or_more_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(1),
                       maximum: None
                   })
        ], parse_expressions("a+").unwrap());

        assert!(parse_expressions("+").is_err());
        assert!(parse_expressions("++").is_err());
    }

    #[test]
    fn handles_character_class() {
        assert_eq!(vec![
                   Expression::Token(Token::Class(vec!['x', 'y', 'z']),
                                     Multiplicity {
                                         minimum: Some(1),
                                         maximum: Some(1)
                                     })
        ], parse_expressions("[xyz]").unwrap());
    }
}

<|endoftext|>


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

use std::io;
use std::fmt;
use std::error::Error;
use std::borrow::Cow;
use std::ops::Add;

use nix;
use term;
use dbus;
use serde;

pub type StratisResult<T> = Result<T, StratisError>;

// Use distinct 'newtype' types for sectors and sector offsets for type safety.
// When needed, these can still be derefed to u64.
// Derive a bunch of stuff so we can do ops on them.
//
custom_derive! {
    #[derive(NewtypeFrom, NewtypeAdd, NewtypeSub, NewtypeDeref,
             NewtypeBitAnd, NewtypeNot, NewtypeDiv, NewtypeRem,
             NewtypeMul,
             Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
    pub struct Sectors(pub u64);
}

// `SumSectors` can be discarded once `std::iter::Sum` is stable.
pub trait SumSectors: Iterator
    where Sectors: Add<Self::Item, Output = Sectors>
{
    fn sum_sectors(self) -> Sectors
        where Self: Sized
    {
        self.fold(Sectors(0), Add::add)
    }
}

impl<T: Iterator> SumSectors for T where Sectors: Add<T::Item, Output = Sectors> {}

impl serde::Serialize for Sectors {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        serializer.serialize_u64(**self)
    }
}

impl serde::Deserialize for Sectors {
    fn deserialize<D>(deserializer: &mut D) -> Result<Sectors, D::Error>
        where D: serde::de::Deserializer
    {
        let val = try!(serde::Deserialize::deserialize(deserializer));
        Ok(Sectors(val))
    }
}

custom_derive! {
    #[derive(NewtypeFrom, NewtypeAdd, NewtypeSub, NewtypeDeref,
             NewtypeBitAnd, NewtypeNot, NewtypeDiv, NewtypeRem,
             NewtypeMul,
             Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
    pub struct SectorOffset(pub u64);
}

// A type for Data Blocks as used by the thin pool.
custom_derive! {
    #[derive(NewtypeFrom, NewtypeAdd, NewtypeSub, NewtypeDeref,
             NewtypeBitAnd, NewtypeNot, NewtypeDiv, NewtypeRem,
             NewtypeMul,
             Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
    pub struct DataBlocks(pub u64);
}

// An error type for errors generated within Stratis
//
#[derive(Debug)]
pub struct InternalError(pub Cow<'static, str>);

impl fmt::Display for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.0)
    }
}

impl Error for InternalError {
    fn description(&self) -> &str {
        &self.0
    }
}

// Define a common error enum.
// See http://blog.burntsushi.net/rust-error-handling/
#[derive(Debug)]
pub enum StratisError {
    Stratis(InternalError),
    Io(io::Error),
    Nix(nix::Error),
    Dbus(dbus::Error),
    Term(term::Error),
}

impl fmt::Display for StratisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            StratisError::Stratis(ref err) => write!(f, "Stratis error: {}", err.0),
            StratisError::Io(ref err) => write!(f, "IO error: {}", err),
            StratisError::Nix(ref err) => write!(f, "Nix error: {}", err.errno().desc()),
            StratisError::Dbus(ref err) => {
                write!(f, "Dbus error: {}", err.message().unwrap_or("Unknown"))
            }
            StratisError::Term(ref err) => write!(f, "Term error: {}", err),
        }
    }
}

impl Error for StratisError {
    fn description(&self) -> &str {
        match *self {
            StratisError::Stratis(ref err) => &err.0,
            StratisError::Io(ref err) => err.description(),
            StratisError::Nix(ref err) => err.errno().desc(),
            StratisError::Dbus(ref err) => err.message().unwrap_or("D-Bus Error"),
            StratisError::Term(ref err) => Error::description(err),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            StratisError::Stratis(ref err) => Some(err),
            StratisError::Io(ref err) => Some(err),
            StratisError::Nix(ref err) => Some(err),
            StratisError::Dbus(ref err) => Some(err),
            StratisError::Term(ref err) => Some(err),
        }
    }
}

impl From<InternalError> for StratisError {
    fn from(err: InternalError) -> StratisError {
        StratisError::Stratis(err)
    }
}

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

impl From<nix::Error> for StratisError {
    fn from(err: nix::Error) -> StratisError {
        StratisError::Nix(err)
    }
}

impl From<dbus::Error> for StratisError {
    fn from(err: dbus::Error) -> StratisError {
        StratisError::Dbus(err)
    }
}

impl From<term::Error> for StratisError {
    fn from(err: term::Error) -> StratisError {
        StratisError::Term(err)
    }
}
<commit_msg>Get rid of DataBlocks type as it is not used.<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::io;
use std::fmt;
use std::error::Error;
use std::borrow::Cow;
use std::ops::Add;

use nix;
use term;
use dbus;
use serde;

pub type StratisResult<T> = Result<T, StratisError>;

// Use distinct 'newtype' types for sectors and sector offsets for type safety.
// When needed, these can still be derefed to u64.
// Derive a bunch of stuff so we can do ops on them.
//
custom_derive! {
    #[derive(NewtypeFrom, NewtypeAdd, NewtypeSub, NewtypeDeref,
             NewtypeBitAnd, NewtypeNot, NewtypeDiv, NewtypeRem,
             NewtypeMul,
             Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
    pub struct Sectors(pub u64);
}

// `SumSectors` can be discarded once `std::iter::Sum` is stable.
pub trait SumSectors: Iterator
    where Sectors: Add<Self::Item, Output = Sectors>
{
    fn sum_sectors(self) -> Sectors
        where Self: Sized
    {
        self.fold(Sectors(0), Add::add)
    }
}

impl<T: Iterator> SumSectors for T where Sectors: Add<T::Item, Output = Sectors> {}

impl serde::Serialize for Sectors {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: serde::Serializer
    {
        serializer.serialize_u64(**self)
    }
}

impl serde::Deserialize for Sectors {
    fn deserialize<D>(deserializer: &mut D) -> Result<Sectors, D::Error>
        where D: serde::de::Deserializer
    {
        let val = try!(serde::Deserialize::deserialize(deserializer));
        Ok(Sectors(val))
    }
}

custom_derive! {
    #[derive(NewtypeFrom, NewtypeAdd, NewtypeSub, NewtypeDeref,
             NewtypeBitAnd, NewtypeNot, NewtypeDiv, NewtypeRem,
             NewtypeMul,
             Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord)]
    pub struct SectorOffset(pub u64);
}

// An error type for errors generated within Stratis
//
#[derive(Debug)]
pub struct InternalError(pub Cow<'static, str>);

impl fmt::Display for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(f, "{}", self.0)
    }
}

impl Error for InternalError {
    fn description(&self) -> &str {
        &self.0
    }
}

// Define a common error enum.
// See http://blog.burntsushi.net/rust-error-handling/
#[derive(Debug)]
pub enum StratisError {
    Stratis(InternalError),
    Io(io::Error),
    Nix(nix::Error),
    Dbus(dbus::Error),
    Term(term::Error),
}

impl fmt::Display for StratisError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            StratisError::Stratis(ref err) => write!(f, "Stratis error: {}", err.0),
            StratisError::Io(ref err) => write!(f, "IO error: {}", err),
            StratisError::Nix(ref err) => write!(f, "Nix error: {}", err.errno().desc()),
            StratisError::Dbus(ref err) => {
                write!(f, "Dbus error: {}", err.message().unwrap_or("Unknown"))
            }
            StratisError::Term(ref err) => write!(f, "Term error: {}", err),
        }
    }
}

impl Error for StratisError {
    fn description(&self) -> &str {
        match *self {
            StratisError::Stratis(ref err) => &err.0,
            StratisError::Io(ref err) => err.description(),
            StratisError::Nix(ref err) => err.errno().desc(),
            StratisError::Dbus(ref err) => err.message().unwrap_or("D-Bus Error"),
            StratisError::Term(ref err) => Error::description(err),
        }
    }

    fn cause(&self) -> Option<&Error> {
        match *self {
            StratisError::Stratis(ref err) => Some(err),
            StratisError::Io(ref err) => Some(err),
            StratisError::Nix(ref err) => Some(err),
            StratisError::Dbus(ref err) => Some(err),
            StratisError::Term(ref err) => Some(err),
        }
    }
}

impl From<InternalError> for StratisError {
    fn from(err: InternalError) -> StratisError {
        StratisError::Stratis(err)
    }
}

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

impl From<nix::Error> for StratisError {
    fn from(err: nix::Error) -> StratisError {
        StratisError::Nix(err)
    }
}

impl From<dbus::Error> for StratisError {
    fn from(err: dbus::Error) -> StratisError {
        StratisError::Dbus(err)
    }
}

impl From<term::Error> for StratisError {
    fn from(err: term::Error) -> StratisError {
        StratisError::Term(err)
    }
}
<|endoftext|>


<commit_before>use rustbox;

use std::default::Default;

use rustbox::Key;
use rustbox::RB_NORMAL;
use rustbox::{Color, RustBox};

use std::time::Duration;

use crate::lila;

use super::GameView;
use super::MenuResult;
use super::MenuView;
use super::RBStyle;
use super::Renderer;
use super::View;

pub struct UI {
    running: bool,
    renderer: Renderer,
    views: Vec<Box<View>>,
    current_view: usize,
    session: lila::Session,
}

impl UI {
    pub fn new(session: lila::Session) -> UI {

        let rb = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        let mut views = Vec::<Box<View>>::new();

        views.push(Box::new(MenuView::new_tv()));
        views.push(Box::new(MenuView::new_playing(&session.user.nowPlaying)));

        return UI {
            running: true,
            renderer: Renderer { rb: rb },
            views: views,
            current_view: 0,
            session: session,
        };
    }

    fn add_view(&mut self, view: Box<View>) {
        self.views.push(view);
    }

    fn add_game(&mut self, name: String, url: String) {
        let game = GameView::new(&self.session, name, url);
        self.add_view(Box::new(game) as Box<View>);
    }

    pub fn start(&mut self) {
        while self.running {
            self.tick();
            self.render();
            self.poll_keys();
        }
    }

    fn prev_view(&mut self) {
        if 0 < self.current_view {
            self.current_view -= 1;
            self.renderer.clear();
        }
    }

    fn next_view(&mut self) {
        if self.current_view < self.views.len() - 1 {
            self.current_view += 1;
            self.renderer.clear();
        }
    }

    fn current_view(&mut self) -> &mut Box<View> {
        self.views.get_mut(self.current_view).unwrap()
    }

    pub fn poll_keys(&mut self) {
        match self.renderer.rb.peek_event(Duration::from_millis(100), false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => self.running = false,
                    Key::Left => self.prev_view(),
                    Key::Right => self.next_view(),
                    key => {
                        match self.current_view().key_event(key) {
                            MenuResult::AddGameView { name, url } => {
                                self.add_game(name, url);
                            }
                            MenuResult::None => {},
                        }
                    }
                }
            },
            Err(e) => panic!("{}", e),
            _ => { }
        }
    }

    pub fn tick(&mut self) {
        self.views.get_mut(self.current_view).unwrap().tick();
    }

    pub fn render(&mut self) {
        let dark  = RBStyle { style: RB_NORMAL, fg: Color::Blue, bg: Color::Black };
        let light = RBStyle { style: RB_NORMAL, fg: Color::Yellow, bg: Color::Black };

        self.renderer.print(0, 0, dark, "|");
        let mut x = 2;
        for (i, view) in self.views.iter().enumerate() {
            if i == self.current_view {
                self.renderer.print(x, 0, light, &view.name());
            } else {
                self.renderer.print(x, 0, dark, &view.name());
            }
            x += view.name().len() + 3;
            self.renderer.print(x- 2, 0, dark, "|");
        }

        self.views.get(self.current_view).unwrap().render(&mut self.renderer);
        self.renderer.present();
    }
}
<commit_msg>2018: prefer dyn Trait syntax<commit_after>use rustbox;

use std::default::Default;

use rustbox::Key;
use rustbox::RB_NORMAL;
use rustbox::{Color, RustBox};

use std::time::Duration;

use crate::lila;

use super::GameView;
use super::MenuResult;
use super::MenuView;
use super::RBStyle;
use super::Renderer;
use super::View;

pub struct UI {
    running: bool,
    renderer: Renderer,
    views: Vec<Box<dyn View>>,
    current_view: usize,
    session: lila::Session,
}

impl UI {
    pub fn new(session: lila::Session) -> UI {

        let rb = match RustBox::init(Default::default()) {
            Result::Ok(v) => v,
            Result::Err(e) => panic!("{}", e),
        };

        let mut views = Vec::<Box<dyn View>>::new();

        views.push(Box::new(MenuView::new_tv()));
        views.push(Box::new(MenuView::new_playing(&session.user.nowPlaying)));

        return UI {
            running: true,
            renderer: Renderer { rb: rb },
            views: views,
            current_view: 0,
            session: session,
        };
    }

    fn add_view(&mut self, view: Box<dyn View>) {
        self.views.push(view);
    }

    fn add_game(&mut self, name: String, url: String) {
        let game = GameView::new(&self.session, name, url);
        self.add_view(Box::new(game) as Box<dyn View>);
    }

    pub fn start(&mut self) {
        while self.running {
            self.tick();
            self.render();
            self.poll_keys();
        }
    }

    fn prev_view(&mut self) {
        if 0 < self.current_view {
            self.current_view -= 1;
            self.renderer.clear();
        }
    }

    fn next_view(&mut self) {
        if self.current_view < self.views.len() - 1 {
            self.current_view += 1;
            self.renderer.clear();
        }
    }

    fn current_view(&mut self) -> &mut Box<dyn View> {
        self.views.get_mut(self.current_view).unwrap()
    }

    pub fn poll_keys(&mut self) {
        match self.renderer.rb.peek_event(Duration::from_millis(100), false) {
            Ok(rustbox::Event::KeyEvent(key)) => {
                match key {
                    Key::Char('q') => self.running = false,
                    Key::Left => self.prev_view(),
                    Key::Right => self.next_view(),
                    key => {
                        match self.current_view().key_event(key) {
                            MenuResult::AddGameView { name, url } => {
                                self.add_game(name, url);
                            }
                            MenuResult::None => {},
                        }
                    }
                }
            },
            Err(e) => panic!("{}", e),
            _ => { }
        }
    }

    pub fn tick(&mut self) {
        self.views.get_mut(self.current_view).unwrap().tick();
    }

    pub fn render(&mut self) {
        let dark  = RBStyle { style: RB_NORMAL, fg: Color::Blue, bg: Color::Black };
        let light = RBStyle { style: RB_NORMAL, fg: Color::Yellow, bg: Color::Black };

        self.renderer.print(0, 0, dark, "|");
        let mut x = 2;
        for (i, view) in self.views.iter().enumerate() {
            if i == self.current_view {
                self.renderer.print(x, 0, light, &view.name());
            } else {
                self.renderer.print(x, 0, dark, &view.name());
            }
            x += view.name().len() + 3;
            self.renderer.print(x- 2, 0, dark, "|");
        }

        self.views.get(self.current_view).unwrap().render(&mut self.renderer);
        self.renderer.present();
    }
}
<|endoftext|>


<commit_before>use c_types;
use c_ares_sys;

use types::AddressFamily;
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_int;
use std::net::{
    Ipv4Addr,
    Ipv6Addr,
    SocketAddrV4,
    SocketAddrV6,
};
use std::str;

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: c_int) -> Option<AddressFamily> {
    match family {
        c_types::AF_INET => Some(AddressFamily::INET),
        c_types::AF_INET6 => Some(AddressFamily::INET6),
        _ => None,
    }
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr { s_addr: u32::from(*ipv4).to_be() }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr { S_un: u32::from(*ipv4).to_be() }
}

// Get an Ipv4Addr from an in_addr.
#[cfg(unix)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.s_addr))
}

#[cfg(windows)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.S_un))
}

// Get an in6_addr from an Ipv6Addr.
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> c_types::in6_addr {
    let segments = ipv6.segments();
    let mut in6_addr: c_types::in6_addr = unsafe { mem::uninitialized() };
    in6_addr.s6_addr[0] = (segments[0] >> 8) as u8;
    in6_addr.s6_addr[1] = segments[0] as u8;
    in6_addr.s6_addr[2] = (segments[1] >> 8) as u8;
    in6_addr.s6_addr[3] = segments[1] as u8;
    in6_addr.s6_addr[4] = (segments[2] >> 8) as u8;
    in6_addr.s6_addr[5] = segments[2] as u8;
    in6_addr.s6_addr[6] = (segments[3] >> 8) as u8;
    in6_addr.s6_addr[7] = segments[3] as u8;
    in6_addr.s6_addr[8] = (segments[4] >> 8) as u8;
    in6_addr.s6_addr[9] = segments[4] as u8;
    in6_addr.s6_addr[10] = (segments[5] >> 8) as u8;
    in6_addr.s6_addr[11] = segments[5] as u8;
    in6_addr.s6_addr[12] = (segments[6] >> 8) as u8;
    in6_addr.s6_addr[13] = segments[6] as u8;
    in6_addr.s6_addr[14] = (segments[7] >> 8) as u8;
    in6_addr.s6_addr[15] = segments[7] as u8;
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_len: mem::size_of::<c_types::sockaddr_in>() as u8,
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

#[cfg(not(any(target_os = "macos",
              target_os = "ios",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "openbsd",
              target_os = "netbsd",
              target_os = "bitrig")))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_len: mem::size_of::<c_types::sockaddr_in6>() as u8,
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(all(unix,not(any(target_os = "macos",
                       target_os = "ios",
                       target_os = "freebsd",
                       target_os = "dragonfly",
                       target_os = "openbsd",
                       target_os = "netbsd",
                       target_os = "bitrig"))))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as i16,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

/// Get the version number of the underlying c-ares library.
///
/// The version is returned as both a string and an integer.  The integer is
/// built up as 24bit number, with 8 separate bits used for major number, minor
/// number and patch number.  For example, the version string "1.2.3" is
/// returned as hexadecimal number 0x010203 (decimal 66051).
pub fn version() -> (&'static str, u32) {
    let mut int_version: c_int = 0;
    let str_version = unsafe {
        let ptr = c_ares_sys::ares_version(&mut int_version);
        let buf = CStr::from_ptr(ptr).to_bytes();
        str::from_utf8_unchecked(buf)
    };
    (str_version, int_version as u32)
}
<commit_msg>docs formatting<commit_after>use c_types;
use c_ares_sys;

use types::AddressFamily;
use std::ffi::CStr;
use std::mem;
use std::os::raw::c_int;
use std::net::{
    Ipv4Addr,
    Ipv6Addr,
    SocketAddrV4,
    SocketAddrV6,
};
use std::str;

// Convert an address family into a more strongly typed AddressFamily.
pub fn address_family(family: c_int) -> Option<AddressFamily> {
    match family {
        c_types::AF_INET => Some(AddressFamily::INET),
        c_types::AF_INET6 => Some(AddressFamily::INET6),
        _ => None,
    }
}

// Get an in_addr from an Ipv4Addr.
#[cfg(unix)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr { s_addr: u32::from(*ipv4).to_be() }
}

#[cfg(windows)]
pub fn ipv4_as_in_addr(ipv4: &Ipv4Addr) -> c_types::in_addr {
    c_types::in_addr { S_un: u32::from(*ipv4).to_be() }
}

// Get an Ipv4Addr from an in_addr.
#[cfg(unix)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.s_addr))
}

#[cfg(windows)]
pub fn ipv4_from_in_addr(in_addr: &c_types::in_addr) -> Ipv4Addr {
    Ipv4Addr::from(u32::from_be(in_addr.S_un))
}

// Get an in6_addr from an Ipv6Addr.
pub fn ipv6_as_in6_addr(ipv6: &Ipv6Addr) -> c_types::in6_addr {
    let segments = ipv6.segments();
    let mut in6_addr: c_types::in6_addr = unsafe { mem::uninitialized() };
    in6_addr.s6_addr[0] = (segments[0] >> 8) as u8;
    in6_addr.s6_addr[1] = segments[0] as u8;
    in6_addr.s6_addr[2] = (segments[1] >> 8) as u8;
    in6_addr.s6_addr[3] = segments[1] as u8;
    in6_addr.s6_addr[4] = (segments[2] >> 8) as u8;
    in6_addr.s6_addr[5] = segments[2] as u8;
    in6_addr.s6_addr[6] = (segments[3] >> 8) as u8;
    in6_addr.s6_addr[7] = segments[3] as u8;
    in6_addr.s6_addr[8] = (segments[4] >> 8) as u8;
    in6_addr.s6_addr[9] = segments[4] as u8;
    in6_addr.s6_addr[10] = (segments[5] >> 8) as u8;
    in6_addr.s6_addr[11] = segments[5] as u8;
    in6_addr.s6_addr[12] = (segments[6] >> 8) as u8;
    in6_addr.s6_addr[13] = segments[6] as u8;
    in6_addr.s6_addr[14] = (segments[7] >> 8) as u8;
    in6_addr.s6_addr[15] = segments[7] as u8;
    in6_addr
}

// Get a sockaddr_in from a SocketAddrV4.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_len: mem::size_of::<c_types::sockaddr_in>() as u8,
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

#[cfg(not(any(target_os = "macos",
              target_os = "ios",
              target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "openbsd",
              target_os = "netbsd",
              target_os = "bitrig")))]
pub fn socket_addrv4_as_sockaddr_in(
    sock_v4: &SocketAddrV4) -> c_types::sockaddr_in {
    let in_addr = ipv4_as_in_addr(sock_v4.ip());
    c_types::sockaddr_in {
        sin_family: c_types::AF_INET as c_types::sa_family_t,
        sin_port: sock_v4.port().to_be(),
        sin_addr: in_addr,
        sin_zero: [0; 8],
    }
}

// Get a sockaddr_in6 from a SocketAddrV6.
#[cfg(any(target_os = "macos",
          target_os = "ios",
          target_os = "freebsd",
          target_os = "dragonfly",
          target_os = "openbsd",
          target_os = "netbsd",
          target_os = "bitrig"))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_len: mem::size_of::<c_types::sockaddr_in6>() as u8,
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(all(unix,not(any(target_os = "macos",
                       target_os = "ios",
                       target_os = "freebsd",
                       target_os = "dragonfly",
                       target_os = "openbsd",
                       target_os = "netbsd",
                       target_os = "bitrig"))))]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as c_types::sa_family_t,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

#[cfg(windows)]
pub fn socket_addrv6_as_sockaddr_in6(
    sock_v6: &SocketAddrV6) -> c_types::sockaddr_in6 {
    let in6_addr = ipv6_as_in6_addr(sock_v6.ip());
    c_types::sockaddr_in6 {
        sin6_family: c_types::AF_INET6 as i16,
        sin6_port: sock_v6.port().to_be(),
        sin6_addr: in6_addr,
        sin6_flowinfo: sock_v6.flowinfo(),
        sin6_scope_id: sock_v6.scope_id(),
    }
}

/// Get the version number of the underlying `c-ares` library.
///
/// The version is returned as both a string and an integer.  The integer is
/// built up as 24bit number, with 8 separate bits used for major number, minor
/// number and patch number.  For example, the version string "1.2.3" is
/// returned as hexadecimal number 0x010203 (decimal 66051).
pub fn version() -> (&'static str, u32) {
    let mut int_version: c_int = 0;
    let str_version = unsafe {
        let ptr = c_ares_sys::ares_version(&mut int_version);
        let buf = CStr::from_ptr(ptr).to_bytes();
        str::from_utf8_unchecked(buf)
    };
    (str_version, int_version as u32)
}
<|endoftext|>