<commit_before>use parsing::{ScopeStack, ParseState, SyntaxDefinition};
use highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
// use util::debug_print_ops;

/// Simple way to go directly from lines of text to coloured
/// tokens.
///
/// Depending on how you load the syntaxes (see the `SyntaxSet` docs)
/// you can either pass this strings with trailing `\n`s or without.
///
/// # Examples
/// Prints coloured lines of a string to the terminal
///
/// ```
/// use syntect::easy::HighlightLines;
/// use syntect::parsing::SyntaxSet;
/// use syntect::highlighting::{ThemeSet, Style};
/// use syntect::util::as_24_bit_terminal_escaped;
///
/// // Load these once at the start of your program
/// let ps = SyntaxSet::load_defaults_nonewlines();
/// let ts = ThemeSet::load_defaults();
///
/// let syntax = ps.find_syntax_by_extension("rs").unwrap();
/// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
/// let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}";
/// for line in s.lines() {
///     let ranges: Vec<(Style, &str)> = h.highlight(line);
///     let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
///     println!("{}", escaped);
/// }
/// ```
pub struct HighlightLines<'a> {
    highlighter: Highlighter<'a>,
    parse_state: ParseState,
    highlight_state: HighlightState,
}

impl<'a> HighlightLines<'a> {
    pub fn new(syntax: &SyntaxDefinition, theme: &'a Theme) -> HighlightLines<'a> {
        let highlighter = Highlighter::new(theme);
        let hstate = HighlightState::new(&highlighter, ScopeStack::new());
        HighlightLines {
            highlighter: highlighter,
            parse_state: ParseState::new(syntax),
            highlight_state: hstate,
        }
    }

    /// Highlights a line of a file
    pub fn highlight<'b>(&mut self, line: &'b str) -> Vec<(Style, &'b str)> {
        // println!("{}", self.highlight_state.path);
        let ops = self.parse_state.parse_line(&line);
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parsing::SyntaxSet;
    use highlighting::ThemeSet;
    #[test]
    fn can_highlight_lines() {
        let ps = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        let syntax = ps.find_syntax_by_extension("rs").unwrap();
        let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
        let ranges = h.highlight("pub struct Wow { hi: u64 }");
        assert!(ranges.len() > 4);
    }
}
<commit_msg>defunct highlight files<commit_after>use parsing::{ScopeStack, ParseState, SyntaxDefinition, SyntaxSet};
use highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{BufReader, self};
use std::fs::File;
use std::path::Path;
// use util::debug_print_ops;

/// Simple way to go directly from lines of text to coloured
/// tokens.
///
/// Depending on how you load the syntaxes (see the `SyntaxSet` docs)
/// you can either pass this strings with trailing `\n`s or without.
///
/// # Examples
/// Prints coloured lines of a string to the terminal
///
/// ```
/// use syntect::easy::HighlightLines;
/// use syntect::parsing::SyntaxSet;
/// use syntect::highlighting::{ThemeSet, Style};
/// use syntect::util::as_24_bit_terminal_escaped;
///
/// // Load these once at the start of your program
/// let ps = SyntaxSet::load_defaults_nonewlines();
/// let ts = ThemeSet::load_defaults();
///
/// let syntax = ps.find_syntax_by_extension("rs").unwrap();
/// let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
/// let s = "pub struct Wow { hi: u64 }\nfn blah() -> u64 {}";
/// for line in s.lines() {
///     let ranges: Vec<(Style, &str)> = h.highlight(line);
///     let escaped = as_24_bit_terminal_escaped(&ranges[..], true);
///     println!("{}", escaped);
/// }
/// ```
pub struct HighlightLines<'a> {
    highlighter: Highlighter<'a>,
    parse_state: ParseState,
    highlight_state: HighlightState,
}

impl<'a> HighlightLines<'a> {
    pub fn new(syntax: &SyntaxDefinition, theme: &'a Theme) -> HighlightLines<'a> {
        let highlighter = Highlighter::new(theme);
        let hstate = HighlightState::new(&highlighter, ScopeStack::new());
        HighlightLines {
            highlighter: highlighter,
            parse_state: ParseState::new(syntax),
            highlight_state: hstate,
        }
    }

    /// Highlights a line of a file
    pub fn highlight<'b>(&mut self, line: &'b str) -> Vec<(Style, &'b str)> {
        // println!("{}", self.highlight_state.path);
        let ops = self.parse_state.parse_line(&line);
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

pub struct HighlightFile<'a> {
    reader: BufReader<File>,
    line_highlighter: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
    pub fn new<P: AsRef<Path>>(path_obj: P, ss: &SyntaxSet, theme: &'a Theme) -> io::Result<HighlightFile<'a>> {
        let path: &Path = path_obj.as_ref();
        let extension = path.extension().and_then(|x| x.to_str()).unwrap_or("");
        let mut f = try!(File::open(path));
        let reader = BufReader::new(f);
        let syntax = ss.find_syntax_by_extension(extension).unwrap();
        Ok(HighlightFile {
            reader: reader,
            line_highlighter: HighlightLines::new(syntax, theme),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parsing::SyntaxSet;
    use highlighting::ThemeSet;
    #[test]
    fn can_highlight_lines() {
        let ps = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        let syntax = ps.find_syntax_by_extension("rs").unwrap();
        let mut h = HighlightLines::new(syntax, &ts.themes["base16-ocean.dark"]);
        let ranges = h.highlight("pub struct Wow { hi: u64 }");
        assert!(ranges.len() > 4);
    }
}
<|endoftext|>


<commit_before>use encoding::types::EncodingRef;

use std::u32;
use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
    MoveChar(instr::CharMoveType),
    Delete,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

macro_rules! vi_delete {
    ( $ctx:ident with $dc:ident $x:expr ) => {
        vi_repeat!($ctx, $x);
        if $ctx.vi_mode == ViMode::Delete {
            $dc.delete();
            $ctx.vi_mode = ViMode::Normal;
        }
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::DeleteLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::MoveCursorLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_left() });
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_right() });
                    }
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    let mut dc = ctx.buf.start_delete();
                    dc.move_start();
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                        ctx.vi_mode = ViMode::Normal;
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::MoveCharMode(mode) => {
                    ctx.vi_mode = ViMode::MoveChar(mode);
                    Cont(false)
                }
                instr::Instr::DeleteMode => {
                    ctx.vi_mode = ViMode::Delete;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => {
                            let mut dc = ctx.buf.start_delete();
                            vi_delete!(ctx with dc { dc.move_start(); false });
                        }
                        // otherwise add a digit to the count
                        (_, i) => {
                            if ctx.vi_count <= (u32::MAX - i) / 10 {
                                ctx.vi_count = ctx.vi_count * 10 + i
                            }
                        }
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word());
                        if ctx.vi_mode == ViMode::Delete {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = ViMode::Normal;
                        }
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word_ws());
                        if ctx.vi_mode == ViMode::Delete {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = ViMode::Normal;
                        }
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_word() });
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_word_ws() });
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_back() });
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_ws_back() });
                    Cont(false)
                }
                instr::Instr::MoveCharRight(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_right(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveCharLeft(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_left(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharRight(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_right(c, count) {
                        dc.move_left();
                        if ctx.vi_mode == ViMode::Delete {
                            dc.delete();
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharLeft(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_left(c, count) {
                        dc.move_right();
                        if ctx.vi_mode == ViMode::Delete {
                            dc.delete();
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<commit_msg>make 'x' command work like vi when at eol<commit_after>use encoding::types::EncodingRef;

use std::u32;
use error::Error;
use history::{Cursor, History};
use buffer::Buffer;
use parser::{parse, ParseError, ParseSuccess};
use instr;

#[derive(Copy,Clone)]
pub enum EditMode {
    Emacs,
    Vi,
}

#[derive(Copy, Clone, PartialEq)]
pub enum ViMode {
    Insert,
    Normal,
    Replace,
    MoveChar(instr::CharMoveType),
    Delete,
}

pub struct EditCtx<'a> {
    buf: Buffer,
    history_cursor: Cursor<'a>,
    prompt: &'a str,
    seq: Vec<u8>,
    enc: EncodingRef,
    mode: EditMode,
    vi_mode: ViMode,
    vi_count: u32,
}

impl<'a> EditCtx<'a> {

    pub fn new(prompt: &'a str, history: &'a History, enc: EncodingRef, mode: EditMode) -> Self {
        EditCtx {
            buf: Buffer::new(),
            history_cursor: Cursor::new(history),
            prompt: prompt,
            seq: Vec::new(),
            enc: enc,
            mode: mode,
            // always start in insert mode
            vi_mode: ViMode::Insert,
            vi_count: 0,
        }
    }

    pub fn fill(&mut self, byte: u8) {
        self.seq.push(byte)
    }

}

pub enum EditResult<C> {
    Cont(C),
    Halt(Result<String, Error>)
}

macro_rules! vi_repeat {
    ( $ctx:ident, $x:expr ) => {
        match $ctx.vi_count {
            0 => { $x; }
            _ => for _ in 0..$ctx.vi_count {
                if !$x {
                    break;
                }
            },
        }
        $ctx.vi_count = 0;
    };
    ( $ctx:ident, $operation:expr, $step:expr ) => {
        match $ctx.vi_count {
            0 => { $operation; }
            _ => {
                if $operation {
                    for _ in 1..$ctx.vi_count {
                        if !$step {
                            break;
                        }
                        if !$operation {
                            break;
                        }
                    }
                }
            }
        }
        $ctx.vi_count = 0;
    };
}

macro_rules! vi_delete {
    ( $ctx:ident with $dc:ident $x:expr ) => {
        vi_repeat!($ctx, $x);
        if $ctx.vi_mode == ViMode::Delete {
            $dc.delete();
            $ctx.vi_mode = ViMode::Normal;
        }
    };
}

pub fn edit<'a>(ctx: &mut EditCtx<'a>) -> EditResult<Vec<u8>> {
    use self::EditResult::*;

    let res = match parse(&ctx.seq, ctx.enc) {
        Err(ParseError::Error(len)) => {
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            Cont(false)
        },
        Err(ParseError::Incomplete) => Cont(false),
        Ok(ParseSuccess(token, len)) => {
            let res = match instr::interpret_token(token, ctx.mode, ctx.vi_mode) {
                instr::Instr::Done => {
                    Halt(Ok(ctx.buf.drain()))
                },
                instr::Instr::DeleteCharLeftOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_left_of_cursor());
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursor => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::DeleteCharRightOfCursorOrEOF => {
                    if !ctx.buf.delete_char_right_of_cursor() {
                        Halt(Err(Error::EndOfFile))
                    } else {
                        Cont(false)
                    }
                },
                instr::Instr::DeleteLine => {
                    ctx.buf.drain();
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::MoveCursorLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_left() });
                    Cont(false)
                },
                instr::Instr::MoveCursorRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_right() });
                    }
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorStart => {
                    ctx.vi_count = 0;
                    let mut dc = ctx.buf.start_delete();
                    dc.move_start();
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                        ctx.vi_mode = ViMode::Normal;
                    }
                    Cont(false)
                },
                instr::Instr::MoveCursorEnd => {
                    ctx.vi_count = 0;
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_end(); false });
                    }
                    if ctx.vi_mode == ViMode::Normal {
                        ctx.buf.exclude_eol();
                    }
                    Cont(false)
                },
                instr::Instr::HistoryPrev => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.incr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::HistoryNext => {
                    vi_repeat!(ctx, {
                        let end = ctx.history_cursor.decr();
                        if end {
                            ctx.buf.swap()
                        }
                        ctx.history_cursor.get().map(|s| ctx.buf.replace(s));
                        end
                    });
                    Cont(false)
                },
                instr::Instr::NormalMode => {
                    if ctx.vi_mode == ViMode::Insert {
                        // cursor moves left when leaving insert mode
                        ctx.buf.move_left();
                    }
                    ctx.vi_mode = ViMode::Normal;
                    ctx.vi_count = 0;
                    Cont(false)
                }
                instr::Instr::ReplaceMode => {
                    ctx.vi_mode = ViMode::Replace;
                    Cont(false)
                }
                instr::Instr::MoveCharMode(mode) => {
                    ctx.vi_mode = ViMode::MoveChar(mode);
                    Cont(false)
                }
                instr::Instr::DeleteMode => {
                    ctx.vi_mode = ViMode::Delete;
                    Cont(false)
                }
                instr::Instr::Insert => {
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::InsertStart => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_start();
                    Cont(false)
                }
                instr::Instr::Append => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_right();
                    Cont(false)
                }
                instr::Instr::AppendEnd => {
                    ctx.vi_mode = ViMode::Insert;
                    ctx.buf.move_end();
                    Cont(false)
                }
                instr::Instr::Digit(i) => {
                    match (ctx.vi_count, i) {
                        // if count is 0, then 0 moves to the start of a line
                        (0, 0) => {
                            let mut dc = ctx.buf.start_delete();
                            vi_delete!(ctx with dc { dc.move_start(); false });
                        }
                        // otherwise add a digit to the count
                        (_, i) => {
                            if ctx.vi_count <= (u32::MAX - i) / 10 {
                                ctx.vi_count = ctx.vi_count * 10 + i
                            }
                        }
                    }
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word());
                        if ctx.vi_mode == ViMode::Delete {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = ViMode::Normal;
                        }
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveEndOfWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_repeat!(ctx, dc.move_to_end_of_word_ws());
                        if ctx.vi_mode == ViMode::Delete {
                            dc.move_right(); // vi deletes an extra character
                            dc.delete();
                            ctx.vi_mode = ViMode::Normal;
                        }
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_word() });
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordWsRight => {
                    {
                        let mut dc = ctx.buf.start_delete();
                        vi_delete!(ctx with dc { dc.move_word_ws() });
                    }
                    ctx.buf.exclude_eol();
                    Cont(false)
                }
                instr::Instr::MoveWordLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_back() });
                    Cont(false)
                }
                instr::Instr::MoveWordWsLeft => {
                    let mut dc = ctx.buf.start_delete();
                    vi_delete!(ctx with dc { dc.move_word_ws_back() });
                    Cont(false)
                }
                instr::Instr::MoveCharRight(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_right(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveCharLeft(c) => {
                    let mut dc = ctx.buf.start_delete();
                    dc.move_to_char_left(c, match ctx.vi_count {
                        0 => 1,
                        n => n,
                    });
                    if ctx.vi_mode == ViMode::Delete {
                        dc.delete();
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharRight(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_right(c, count) {
                        dc.move_left();
                        if ctx.vi_mode == ViMode::Delete {
                            dc.delete();
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::MoveBeforeCharLeft(c) => {
                    let count = match ctx.vi_count {
                        0 => 1,
                        n => n,
                    };

                    let mut dc = ctx.buf.start_delete();
                    if dc.move_to_char_left(c, count) {
                        dc.move_right();
                        if ctx.vi_mode == ViMode::Delete {
                            dc.delete();
                        }
                    }
                    ctx.vi_count = 0;
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
                instr::Instr::Substitute => {
                    vi_repeat!(ctx, ctx.buf.delete_char_right_of_cursor());
                    ctx.vi_mode = ViMode::Insert;
                    Cont(false)
                }
                instr::Instr::Noop => {
                    Cont(false)
                },
                instr::Instr::Cancel => Halt(Err(Error::Cancel)),
                instr::Instr::Clear => {
                    Cont(true)
                },
                instr::Instr::InsertAtCursor(text) => {
                    ctx.buf.insert_chars_at_cursor(text);
                    Cont(false)
                }
                instr::Instr::ReplaceAtCursor(text) => {
                    vi_repeat!(
                        ctx,
                        {
                            ctx.buf.replace_chars_at_cursor(text.clone());
                            true
                        },
                        {
                            ctx.buf.move_right();
                            ctx.buf.exclude_eol()
                        }
                    );
                    ctx.vi_mode = ViMode::Normal;
                    Cont(false)
                }
            };
            for _ in (0..len) {
                ctx.seq.remove(0);
            };
            res
        }
    };
    match res {
        Cont(clear) => EditResult::Cont(ctx.buf.get_line(ctx.prompt, clear)),
        Halt(res) => EditResult::Halt(res)
    }
}
<|endoftext|>


<commit_before>use nix::sys::stat::fstat;
use nix::sys::mman::{ mmap, munmap, PROT_READ, MAP_SHARED };
use nix::libc::size_t;

use std::os::unix::io::AsRawFd;
use std::slice::from_raw_parts_mut;
use std::ptr::null_mut;
use std::fs::File;
use std::path::{ PathBuf, Path};
use std::collections::{ HashSet, HashMap };
use std::io::{ Result, Write, Error, ErrorKind };

use super::parser::{ RDBSer, RDBDec, Record, DatabaseNumber, RDBVersion };

pub fn memory_map_read<F, A>(file: &File, f: F) -> Result
    where F: FnOnce(&mut [u8]) -> A
{
    let fd = file.as_raw_fd();
    let sz = try!(fstat(fd)).st_size as size_t;
    let mm = try!(mmap(null_mut(), sz, PROT_READ, MAP_SHARED, fd, 0));
    let s = unsafe { from_raw_parts_mut(mm as *mut u8, sz) };
    let result = f(s);
    try!(munmap(mm, sz));
    Ok(result)
}

pub struct PartRDB {
    check_duplication: bool,
    output_dir:        String,
    files:             HashMap<u32, File>,
    keys:              HashMap<u32, HashSet<String>>,
}

const PART_FILE_PREFIX:  &'static str = "PART_";
const PART_FILE_SUFFIX:  &'static str = ".rdb";
const MERGE_FILE:        &'static str = "MERGE.rdb";
const MERGE_RDB_VERSION: &'static str = "0006";

impl PartRDB{
    pub fn new(check_duplication: bool, output_dir: String) -> Result<Self> {
        assert_result!(Path::new(&output_dir).is_dir(), Error::new(ErrorKind::NotFound, "no such directory"));
        Ok(PartRDB {
            check_duplication: check_duplication,
            output_dir:        output_dir,
            files:             HashMap::new(),
            keys:              HashMap::new(),
        })
    }

    fn part_rdb_path(&self, db_num: u32) -> PathBuf {
        let name = format!("{}{:08x}{}", PART_FILE_PREFIX, db_num, PART_FILE_SUFFIX);
        Path::new(&self.output_dir).join(&name)
    }

    fn merge_rdb_path(&self) -> PathBuf {
        Path::new(&self.output_dir).join(MERGE_FILE)
    }

    pub fn write<'a>(&mut self, db_num: DatabaseNumber<'a>, record: &Record) -> Result<()> {
        let DatabaseNumber(_, num) = db_num;

        if !self.files.contains_key(&num) {
            let path = self.part_rdb_path(num);
            info!("create temporary rdb: {:?}", path);
            let mut file = try!(File::create(path));
            try!(db_num.ser(&mut file));
            self.files.insert(num, file);
        }

        if !self.keys.contains_key(&num) {
            self.keys.insert(num, HashSet::new());
        }

        let &Record(key, _, _) = record;
        let key = try!(String::decode(&key));
        match (self.keys.get_mut(&num), self.files.get_mut(&num)) {
            (Some(ref mut kset), Some(ref mut file)) => {
                if !self.check_duplication || !kset.contains(&key) {
                    try!(record.ser(file));
                    kset.insert(key);
                } else {
                    warn!("duplicate key, discard: {}", key);
                }
            },
            _ => unreachable!(),
        }

        Ok(())
    }

    pub fn close_part_files(&mut self) {
        self.files = HashMap::new();
    }

    pub fn merge(&self) -> Result<usize> {
        let version = RDBVersion(MERGE_RDB_VERSION.as_bytes());
        let mut mfile = try!(File::create(self.merge_rdb_path()));
        let mut n = try!(version.ser(&mut mfile));

        for key in self.keys.keys() {
            let sfile = try!(File::open(self.part_rdb_path(*key)));
            let result = memory_map_read(&sfile, |bytes| {
                mfile.write(bytes)
            });
            n += try!(try!(result));
        }

        n += try!(mfile.write(&[0xff][..]));
        // Disable CRC64 checksum
        n += try!(mfile.write(&[0x00; 8][..]));

        Ok(n)
    }
}
<commit_msg>refactoring<commit_after>use nix::sys::stat::fstat;
use nix::sys::mman::{ mmap, munmap, PROT_READ, MAP_SHARED };
use nix::libc::size_t;

use std::os::unix::io::AsRawFd;
use std::slice::from_raw_parts_mut;
use std::ptr::null_mut;
use std::fs::File;
use std::path::{ PathBuf, Path};
use std::collections::{ HashSet, HashMap };
use std::io::{ Result, Write, Error, ErrorKind };

use super::parser::{ RDBSer, RDBDec, Record, DatabaseNumber, RDBVersion };

pub fn memory_map_read<F, A>(file: &File, f: F) -> Result
    where F: FnOnce(&mut [u8]) -> A
{
    let fd = file.as_raw_fd();
    let sz = try!(fstat(fd)).st_size as size_t;
    let mm = try!(mmap(null_mut(), sz, PROT_READ, MAP_SHARED, fd, 0));
    let s = unsafe { from_raw_parts_mut(mm as *mut u8, sz) };
    let result = f(s);
    try!(munmap(mm, sz));
    Ok(result)
}

pub struct PartRDB {
    check_duplication: bool,
    output_dir:        String,
    files:             HashMap<u32, File>,
    keys:              HashMap<u32, HashSet<String>>,
}

const PART_FILE_PREFIX:  &'static str = "PART_";
const PART_FILE_SUFFIX:  &'static str = ".rdb";
const MERGE_FILE:        &'static str = "MERGE.rdb";
const MERGE_RDB_VERSION: &'static str = "0006";

fn part_rdb_path(output_dir: &String, db_num: u32) -> PathBuf {
    let name = format!("{}{:08x}{}", PART_FILE_PREFIX, db_num, PART_FILE_SUFFIX);
    Path::new(output_dir).join(&name)
}

fn merge_rdb_path(output_dir: &String) -> PathBuf {
    Path::new(output_dir).join(MERGE_FILE)
}

impl PartRDB{
    pub fn new(check_duplication: bool, output_dir: String) -> Result<Self> {
        assert_result!(Path::new(&output_dir).is_dir(), Error::new(ErrorKind::NotFound, "no such directory"));
        Ok(PartRDB {
            check_duplication: check_duplication,
            output_dir:        output_dir,
            files:             HashMap::new(),
            keys:              HashMap::new(),
        })
    }

    pub fn write<'a>(&mut self, db_num: DatabaseNumber<'a>, record: &Record) -> Result<()> {
        let DatabaseNumber(_, num) = db_num;

        if !self.files.contains_key(&num) {
            let path = part_rdb_path(&self.output_dir, num);
            info!("create temporary rdb: {:?}", path);
            let mut file = try!(File::create(path));
            try!(db_num.ser(&mut file));
            self.files.insert(num, file);
        }

        if !self.keys.contains_key(&num) {
            self.keys.insert(num, HashSet::new());
        }

        let &Record(key, _, _) = record;
        let key = try!(String::decode(&key));
        match (self.keys.get_mut(&num), self.files.get_mut(&num)) {
            (Some(ref mut kset), Some(ref mut file)) => {
                if !self.check_duplication || !kset.contains(&key) {
                    try!(record.ser(file));
                    kset.insert(key);
                } else {
                    warn!("duplicate key, discard: {}", key);
                }
            },
            _ => unreachable!(),
        }

        Ok(())
    }

    pub fn close_part_files(&mut self) {
        self.files = HashMap::new();
    }

    pub fn merge(&self) -> Result<usize> {
        let version = RDBVersion(MERGE_RDB_VERSION.as_bytes());
        let mut mfile = try!(File::create(merge_rdb_path(&self.output_dir)));
        let mut n = try!(version.ser(&mut mfile));

        for key in self.keys.keys() {
            let sfile = try!(File::open(part_rdb_path(&self.output_dir, *key)));
            let result = memory_map_read(&sfile, |bytes| {
                mfile.write(bytes)
            });
            n += try!(try!(result));
        }

        n += try!(mfile.write(&[0xff][..]));
        // Disable CRC64 checksum
        n += try!(mfile.write(&[0x00; 8][..]));

        Ok(n)
    }
}
<|endoftext|>


<commit_before>extern crate http;
extern crate hyper;
extern crate native_tls;
extern crate tokio_tcp;
extern crate tokio_tls;

use crate::real_std::{
    fmt, fs,
    path::Path,
    sync::{Arc, Mutex},
};

use self::{http::StatusCode, hyper::service::Service, hyper::Chunk, hyper::Server};

use futures::{
    future::{self, Either},
    Async, Future, Stream,
};

use self::http::header::{HeaderMap, HeaderName, HeaderValue};

use crate::base::types::{ArcType, Type};

use crate::vm::{
    self,
    api::{
        generic, Collect, Eff, Function, Getable, OpaqueValue, PushAsRef, Pushable, VmType, WithVM,
        IO,
    },
    thread::{ActiveThread, RootedThread, Thread},
    ExternModule, Variants,
};

macro_rules! try_future {
    ($e:expr) => {
        try_future!($e, Box::new)
    };
    ($e:expr, $f:expr) => {
        match $e {
            Ok(x) => x,
            Err(err) => return $f(::futures::future::err(err.into())),
        }
    };
}

struct HttpEffect;
impl VmType for HttpEffect {
    type Type = Self;
    fn make_type(vm: &Thread) -> ArcType {
        let r = generic::R::make_type(vm);
        Type::app(
            vm.find_type_info("std.http.types.HttpEffect")
                .map(|alias| alias.into_type())
                .unwrap_or_else(|_| Type::hole()),
            collect![r],
        )
    }
}

type Handler<T> = Eff<HttpEffect, T>;

struct Headers(HeaderMap);

impl VmType for Headers {
    type Type = Vec<(String, Vec<u8>)>;

    fn make_type(vm: &Thread) -> ArcType {
        Vec::<(String, Vec<u8>)>::make_type(vm)
    }
}

impl<'vm> Pushable<'vm> for Headers {
    fn push(self, context: &mut ActiveThread<'vm>) -> vm::Result<()> {
        Collect::new(
            self.0
                .iter()
                .map(|(name, value)| (name.as_str(), value.as_bytes())),
        )
        .push(context)
    }
}

impl<'vm, 'value> Getable<'vm, 'value> for Headers {
    fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
        Headers(
            Collect::from_value(vm, value)
                // TODO Error somehow on invalid headers
                .filter_map(|(name, value): (&str, &[u8])| {
                    match (HeaderName::from_bytes(name.as_bytes()), HeaderValue::from_bytes(value)) {
                        (Ok(name), Ok(value)) => Some((name, value)),
                        _ => None,
                    }
                })
                .collect(),
        )
    }
}

// By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
// threads
#[derive(Userdata)]
#[gluon(crate_name = "::vm")]
// Representation of a http body that is in the prograss of being read
pub struct Body(Arc<Mutex<Box<Stream<Item = PushAsRef<Chunk, [u8]>, Error = vm::Error> + Send>>>);

// Types implementing `Userdata` requires a `std::fmt::Debug` implementation so it can be displayed
impl fmt::Debug for Body {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Body")
    }
}

// Since `Body` implements `Userdata` gluon will automatically marshal the gluon representation
// into `&Body` argument
fn read_chunk(
    body: &Body,
) -> impl Future<Item = IO<Option<PushAsRef<Chunk, [u8]>>>, Error = vm::Error> {
    use futures::future::poll_fn;

    let body = body.0.clone();
    poll_fn(move || {
        let mut stream = body.lock().unwrap();
        stream.poll().map(|r#async| r#async.map(IO::Value))
    })
}

// A http body that is being written
#[derive(Userdata)]
#[gluon(crate_name = "::vm")]
pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);

impl fmt::Debug for ResponseBody {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Response")
    }
}

fn write_response(
    response: &ResponseBody,
    bytes: &[u8],
) -> impl Future<Item = IO<()>, Error = vm::Error> {
    use futures::future::poll_fn;

    // Turn `bytes´ into a `Chunk` which can be sent to the http body
    let mut unsent_chunk = Some(bytes.to_owned().into());
    let response = response.0.clone();
    poll_fn(move || {
        info!("Starting response send");
        let mut sender = response.lock().unwrap();
        let sender = sender
            .as_mut()
            .expect("Sender has been dropped while still in use");
        let chunk = unsent_chunk
            .take()
            .expect("Attempt to poll after chunk is sent");
        match sender.poll_ready() {
            Ok(Async::NotReady) => {
                unsent_chunk = Some(chunk);
                return Ok(Async::NotReady);
            }
            Ok(Async::Ready(_)) => (),
            Err(err) => {
                info!("Could not send http response {}", err);
                return Ok(IO::Exception(err.to_string()).into());
            }
        }
        match sender.send_data(chunk) {
            Ok(()) => Ok(Async::Ready(IO::Value(()))),
            Err(chunk) => {
                unsent_chunk = Some(chunk);
                Ok(IO::Exception("Could not send http response".into()).into())
            }
        }
    })
}

#[derive(Debug, Userdata)]
#[gluon(crate_name = "::vm")]
struct Uri(http::Uri);

// Next we define some record types which are marshalled to and from gluon. These have equivalent
// definitions in http_types.glu
field_decl! { http, method, uri, status, body, request, response, headers }

type Request = record_type! {
    method => String,
    uri => Uri,
    body => Body
};

type Response = record_type! {
    status => u16,
    headers => Headers
};

type HttpState = record_type! {
    request => Request,
    response => ResponseBody
};

#[derive(Getable, VmType)]
#[gluon(crate_name = "::vm")]
struct Settings<'a> {
    port: u16,
    tls_cert: Option<&'a Path>,
}

fn listen(
    settings: Settings,
    value: WithVM<OpaqueValue<RootedThread, Handler<Response>>>,
) -> impl Future<Item = IO<()>, Error = vm::Error> + Send + 'static {
    let WithVM {
        value: handler,
        vm: thread,
    } = value;

    let thread = match thread.new_thread() {
        Ok(thread) => thread,
        Err(err) => return Either::A(future::err(err)),
    };

    // Retrieve the `handle` function from the http module which we use to evaluate values of type
    // `Handler Response`
    type ListenFn = fn(OpaqueValue<RootedThread, Handler<Response>>, HttpState) -> IO<Response>;
    let handle: Function<RootedThread, ListenFn> = thread
        .get_global("std.http.handle")
        .unwrap_or_else(|err| panic!("{}", err));

    struct Listen {
        handle: Function<RootedThread, ListenFn>,
        handler: OpaqueValue<RootedThread, Handler<Response>>,
    }

    impl Service for Listen {
        type ReqBody = hyper::Body;
        type ResBody = hyper::Body;
        type Error = vm::Error;
        type Future =
            Box<Future<Item = http::Response<hyper::Body>, Error = Self::Error> + Send + 'static>;

        fn call(&mut self, request: http::Request<hyper::Body>) -> Self::Future {
            let (parts, body) = request.into_parts();
            let gluon_request = record_no_decl! {
                method => parts.method.as_str().to_owned(),
                uri => Uri(parts.uri),
                // Since `Body` implements `Userdata` it can be directly pushed to gluon
                body => Body(Arc::new(Mutex::new(Box::new(body
                    .map_err(|err| vm::Error::Message(format!("{}", err)))
                    // `PushAsRef` makes the `body` parameter act as a `&[u8]` which means it is
                    // marshalled to `Array Byte` in gluon
                    .map(PushAsRef::<_, [u8]>::new)))))
            };
            let (response_sender, response_body) = hyper::Body::channel();
            let response_sender = Arc::new(Mutex::new(Some(response_sender)));
            let http_state = record_no_decl! {
                request => gluon_request,
                response => ResponseBody(response_sender.clone())
            };

            let child_thread = try_future!(self.handle.vm().new_thread());
            let mut handle = try_future!(self.handle.re_root(child_thread));

            Box::new(
                handle.call_async(self.handler.clone(), http_state).then(
                    move |result| match result {
                        Ok(value) => {
                            match value {
                                IO::Value(record_p! { status, headers }) => {
                                    // Drop the sender to so that it the receiver stops waiting for
                                    // more chunks
                                    *response_sender.lock().unwrap() = None;

                                    let status = StatusCode::from_u16(status)
                                        .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
                                    let mut response = http::Response::builder()
                                        .status(status)
                                        .body(response_body)
                                        .unwrap();
                                    *response.headers_mut() = headers.0;
                                    Ok(response)
                                }
                                IO::Exception(err) => {
                                    info!("{}", err);
                                    Ok(http::Response::builder()
                                        .status(StatusCode::INTERNAL_SERVER_ERROR)
                                        .body("".into())
                                        .unwrap())
                                }
                            }
                        }
                        Err(err) => {
                            info!("{}", err);
                            Ok(http::Response::builder()
                                .status(StatusCode::INTERNAL_SERVER_ERROR)
                                .body("".into())
                                .unwrap())
                        }
                    },
                ),
            )
        }
    }

    let addr = format!("0.0.0.0:{}", settings.port).parse().unwrap();

    if let Some(cert_path) = settings.tls_cert {
        let identity = try_future!(
            fs::read(cert_path).map_err(|err| vm::Error::Message(format!(
                "Unable to open certificate `{}`: {}",
                cert_path.display(),
                err
            ))),
            Either::A
        );

        let identity = try_future!(
            native_tls::Identity::from_pkcs12(&identity, "")
                .map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        );
        let acceptor = tokio_tls::TlsAcceptor::from(try_future!(
            native_tls::TlsAcceptor::new(identity)
                .map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        ));

        let http = hyper::server::conn::Http::new();
        let listener = try_future!(
            tokio_tcp::TcpListener::bind(&addr).map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        );
        let incoming = listener
            .incoming()
            .and_then(move |stream| {
                acceptor.accept(stream).map(Some).or_else(|err| {
                    info!("Unable to accept TLS connection: {}", err);
                    Ok(None)
                })
            })
            .filter_map(|opt_tls_stream| opt_tls_stream);

        let future = http
            .serve_incoming(incoming, move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .and_then(|connecting| connecting)
            .for_each(|connection| {
                hyper::rt::spawn(connection.map_err(|err| info!("{}", err)));
                Ok(())
            })
            .map(|_| IO::Value(()))
            .map_err(|err| vm::Error::from(format!("Server error: {}", err)));
        return Either::B(Either::A(future));
    }

    Either::B(Either::B(
        Server::bind(&addr)
            .serve(move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .map_err(|err| vm::Error::from(format!("Server error: {}", err)))
            .map(|_| IO::Value(())),
    ))
}

// To let the `http_types` module refer to `Body` and `ResponseBody` we register these types in a
// separate function which is called before loading `http_types`
pub fn load_types(vm: &Thread) -> vm::Result<ExternModule> {
    vm.register_type::<Body>("Body", &[])?;
    vm.register_type::<ResponseBody>("ResponseBody", &[])?;
    vm.register_type::<Uri>("Uri", &[])?;

    ExternModule::new(
        vm,
        record! {
            // Define the types so that they can be used from gluon
            type Body => Body,
            type ResponseBody => ResponseBody,
            type Uri => Uri,
            type Method => String,
            type StatusCode => u16,
            type Request => Request,
            type Response => Response,
            type Headers => Headers,
            type HttpState => HttpState
        },
    )
}

macro_rules! uri_binds {
    ($($id: ident)*) => {
        record!{
            $(
                $id => primitive!(1, concat!("std.http.prim.uri.", stringify!($id)), |u: &Uri| (u.0).$id())
            ),*
        }
    }
}

mod std {
    pub(crate) mod http {
        pub(crate) use crate::http as prim;
    }
}

pub fn load(vm: &Thread) -> vm::Result<ExternModule> {
    ExternModule::new(
        vm,
        record! {
            listen => primitive!(2, async fn std::http::prim::listen),
            read_chunk => primitive!(1, async fn std::http::prim::read_chunk),
            write_response => primitive!(2, async fn std::http::prim::write_response),
            port => primitive!(1, "std.http.prim.uri.port", |u: &Uri| (u.0).port_part().map(|p| p.as_u16())),
            uri => uri_binds!(path host query to_string)
        },
    )
}
<commit_msg>fix(std.http): Don't let one slow TLS connection block the server<commit_after>extern crate http;
extern crate hyper;
extern crate native_tls;
extern crate tokio_tcp;
extern crate tokio_tls;

use crate::real_std::{
    fmt, fs,
    path::Path,
    sync::{Arc, Mutex},
};

use self::{http::StatusCode, hyper::service::Service, hyper::Chunk, hyper::Server};

use futures::{
    future::{self, Either},
    Async, Future, Stream,
};

use self::http::header::{HeaderMap, HeaderName, HeaderValue};

use crate::base::types::{ArcType, Type};

use crate::vm::{
    self,
    api::{
        generic, Collect, Eff, Function, Getable, OpaqueValue, PushAsRef, Pushable, VmType, WithVM,
        IO,
    },
    thread::{ActiveThread, RootedThread, Thread},
    ExternModule, Variants,
};

macro_rules! try_future {
    ($e:expr) => {
        try_future!($e, Box::new)
    };
    ($e:expr, $f:expr) => {
        match $e {
            Ok(x) => x,
            Err(err) => return $f(::futures::future::err(err.into())),
        }
    };
}

struct HttpEffect;
impl VmType for HttpEffect {
    type Type = Self;
    fn make_type(vm: &Thread) -> ArcType {
        let r = generic::R::make_type(vm);
        Type::app(
            vm.find_type_info("std.http.types.HttpEffect")
                .map(|alias| alias.into_type())
                .unwrap_or_else(|_| Type::hole()),
            collect![r],
        )
    }
}

type Handler<T> = Eff<HttpEffect, T>;

struct Headers(HeaderMap);

impl VmType for Headers {
    type Type = Vec<(String, Vec<u8>)>;

    fn make_type(vm: &Thread) -> ArcType {
        Vec::<(String, Vec<u8>)>::make_type(vm)
    }
}

impl<'vm> Pushable<'vm> for Headers {
    fn push(self, context: &mut ActiveThread<'vm>) -> vm::Result<()> {
        Collect::new(
            self.0
                .iter()
                .map(|(name, value)| (name.as_str(), value.as_bytes())),
        )
        .push(context)
    }
}

impl<'vm, 'value> Getable<'vm, 'value> for Headers {
    fn from_value(vm: &'vm Thread, value: Variants<'value>) -> Self {
        Headers(
            Collect::from_value(vm, value)
                // TODO Error somehow on invalid headers
                .filter_map(|(name, value): (&str, &[u8])| {
                    match (HeaderName::from_bytes(name.as_bytes()), HeaderValue::from_bytes(value)) {
                        (Ok(name), Ok(value)) => Some((name, value)),
                        _ => None,
                    }
                })
                .collect(),
        )
    }
}

// By implementing `Userdata` on `Body` it can be automatically pushed and retrieved from gluon
// threads
#[derive(Userdata)]
#[gluon(crate_name = "::vm")]
// Representation of a http body that is in the prograss of being read
pub struct Body(Arc<Mutex<Box<Stream<Item = PushAsRef<Chunk, [u8]>, Error = vm::Error> + Send>>>);

// Types implementing `Userdata` requires a `std::fmt::Debug` implementation so it can be displayed
impl fmt::Debug for Body {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Body")
    }
}

// Since `Body` implements `Userdata` gluon will automatically marshal the gluon representation
// into `&Body` argument
fn read_chunk(
    body: &Body,
) -> impl Future<Item = IO<Option<PushAsRef<Chunk, [u8]>>>, Error = vm::Error> {
    use futures::future::poll_fn;

    let body = body.0.clone();
    poll_fn(move || {
        let mut stream = body.lock().unwrap();
        stream.poll().map(|r#async| r#async.map(IO::Value))
    })
}

// A http body that is being written
#[derive(Userdata)]
#[gluon(crate_name = "::vm")]
pub struct ResponseBody(Arc<Mutex<Option<hyper::body::Sender>>>);

impl fmt::Debug for ResponseBody {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "hyper::Response")
    }
}

fn write_response(
    response: &ResponseBody,
    bytes: &[u8],
) -> impl Future<Item = IO<()>, Error = vm::Error> {
    use futures::future::poll_fn;

    // Turn `bytes´ into a `Chunk` which can be sent to the http body
    let mut unsent_chunk = Some(bytes.to_owned().into());
    let response = response.0.clone();
    poll_fn(move || {
        info!("Starting response send");
        let mut sender = response.lock().unwrap();
        let sender = sender
            .as_mut()
            .expect("Sender has been dropped while still in use");
        let chunk = unsent_chunk
            .take()
            .expect("Attempt to poll after chunk is sent");
        match sender.poll_ready() {
            Ok(Async::NotReady) => {
                unsent_chunk = Some(chunk);
                return Ok(Async::NotReady);
            }
            Ok(Async::Ready(_)) => (),
            Err(err) => {
                info!("Could not send http response {}", err);
                return Ok(IO::Exception(err.to_string()).into());
            }
        }
        match sender.send_data(chunk) {
            Ok(()) => Ok(Async::Ready(IO::Value(()))),
            Err(chunk) => {
                unsent_chunk = Some(chunk);
                Ok(IO::Exception("Could not send http response".into()).into())
            }
        }
    })
}

#[derive(Debug, Userdata)]
#[gluon(crate_name = "::vm")]
struct Uri(http::Uri);

// Next we define some record types which are marshalled to and from gluon. These have equivalent
// definitions in http_types.glu
field_decl! { http, method, uri, status, body, request, response, headers }

type Request = record_type! {
    method => String,
    uri => Uri,
    body => Body
};

type Response = record_type! {
    status => u16,
    headers => Headers
};

type HttpState = record_type! {
    request => Request,
    response => ResponseBody
};

#[derive(Getable, VmType)]
#[gluon(crate_name = "::vm")]
struct Settings<'a> {
    port: u16,
    tls_cert: Option<&'a Path>,
}

fn listen(
    settings: Settings,
    value: WithVM<OpaqueValue<RootedThread, Handler<Response>>>,
) -> impl Future<Item = IO<()>, Error = vm::Error> + Send + 'static {
    let WithVM {
        value: handler,
        vm: thread,
    } = value;

    let thread = match thread.new_thread() {
        Ok(thread) => thread,
        Err(err) => return Either::A(future::err(err)),
    };

    // Retrieve the `handle` function from the http module which we use to evaluate values of type
    // `Handler Response`
    type ListenFn = fn(OpaqueValue<RootedThread, Handler<Response>>, HttpState) -> IO<Response>;
    let handle: Function<RootedThread, ListenFn> = thread
        .get_global("std.http.handle")
        .unwrap_or_else(|err| panic!("{}", err));

    struct Listen {
        handle: Function<RootedThread, ListenFn>,
        handler: OpaqueValue<RootedThread, Handler<Response>>,
    }

    impl Service for Listen {
        type ReqBody = hyper::Body;
        type ResBody = hyper::Body;
        type Error = vm::Error;
        type Future =
            Box<Future<Item = http::Response<hyper::Body>, Error = Self::Error> + Send + 'static>;

        fn call(&mut self, request: http::Request<hyper::Body>) -> Self::Future {
            let (parts, body) = request.into_parts();
            let gluon_request = record_no_decl! {
                method => parts.method.as_str().to_owned(),
                uri => Uri(parts.uri),
                // Since `Body` implements `Userdata` it can be directly pushed to gluon
                body => Body(Arc::new(Mutex::new(Box::new(body
                    .map_err(|err| vm::Error::Message(format!("{}", err)))
                    // `PushAsRef` makes the `body` parameter act as a `&[u8]` which means it is
                    // marshalled to `Array Byte` in gluon
                    .map(PushAsRef::<_, [u8]>::new)))))
            };
            let (response_sender, response_body) = hyper::Body::channel();
            let response_sender = Arc::new(Mutex::new(Some(response_sender)));
            let http_state = record_no_decl! {
                request => gluon_request,
                response => ResponseBody(response_sender.clone())
            };

            let child_thread = try_future!(self.handle.vm().new_thread());
            let mut handle = try_future!(self.handle.re_root(child_thread));

            Box::new(
                handle.call_async(self.handler.clone(), http_state).then(
                    move |result| match result {
                        Ok(value) => {
                            match value {
                                IO::Value(record_p! { status, headers }) => {
                                    // Drop the sender to so that it the receiver stops waiting for
                                    // more chunks
                                    *response_sender.lock().unwrap() = None;

                                    let status = StatusCode::from_u16(status)
                                        .unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
                                    let mut response = http::Response::builder()
                                        .status(status)
                                        .body(response_body)
                                        .unwrap();
                                    *response.headers_mut() = headers.0;
                                    Ok(response)
                                }
                                IO::Exception(err) => {
                                    info!("{}", err);
                                    Ok(http::Response::builder()
                                        .status(StatusCode::INTERNAL_SERVER_ERROR)
                                        .body("".into())
                                        .unwrap())
                                }
                            }
                        }
                        Err(err) => {
                            info!("{}", err);
                            Ok(http::Response::builder()
                                .status(StatusCode::INTERNAL_SERVER_ERROR)
                                .body("".into())
                                .unwrap())
                        }
                    },
                ),
            )
        }
    }

    let addr = format!("0.0.0.0:{}", settings.port).parse().unwrap();

    if let Some(cert_path) = settings.tls_cert {
        let identity = try_future!(
            fs::read(cert_path).map_err(|err| vm::Error::Message(format!(
                "Unable to open certificate `{}`: {}",
                cert_path.display(),
                err
            ))),
            Either::A
        );

        let identity = try_future!(
            native_tls::Identity::from_pkcs12(&identity, "")
                .map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        );
        let acceptor = tokio_tls::TlsAcceptor::from(try_future!(
            native_tls::TlsAcceptor::new(identity)
                .map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        ));

        let http = hyper::server::conn::Http::new();
        let listener = try_future!(
            tokio_tcp::TcpListener::bind(&addr).map_err(|err| vm::Error::Message(err.to_string())),
            Either::A
        );
        let incoming = listener
            .incoming()
            .map(move |stream| {
                acceptor.accept(stream).map(Some).or_else(|err| {
                    info!("Unable to accept TLS connection: {}", err);
                    Ok(None)
                })
            })
            .buffer_unordered(100)
            .filter_map(|opt_tls_stream| opt_tls_stream);

        let future = http
            .serve_incoming(incoming, move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .and_then(|connecting| connecting)
            .for_each(|connection| {
                hyper::rt::spawn(connection.map_err(|err| info!("{}", err)));
                Ok(())
            })
            .map(|_| IO::Value(()))
            .map_err(|err| vm::Error::from(format!("Server error: {}", err)));
        return Either::B(Either::A(future));
    }

    Either::B(Either::B(
        Server::bind(&addr)
            .serve(move || -> Result<_, hyper::Error> {
                Ok(Listen {
                    handle: handle.clone(),
                    handler: handler.clone(),
                })
            })
            .map_err(|err| vm::Error::from(format!("Server error: {}", err)))
            .map(|_| IO::Value(())),
    ))
}

// To let the `http_types` module refer to `Body` and `ResponseBody` we register these types in a
// separate function which is called before loading `http_types`
pub fn load_types(vm: &Thread) -> vm::Result<ExternModule> {
    vm.register_type::<Body>("Body", &[])?;
    vm.register_type::<ResponseBody>("ResponseBody", &[])?;
    vm.register_type::<Uri>("Uri", &[])?;

    ExternModule::new(
        vm,
        record! {
            // Define the types so that they can be used from gluon
            type Body => Body,
            type ResponseBody => ResponseBody,
            type Uri => Uri,
            type Method => String,
            type StatusCode => u16,
            type Request => Request,
            type Response => Response,
            type Headers => Headers,
            type HttpState => HttpState
        },
    )
}

macro_rules! uri_binds {
    ($($id: ident)*) => {
        record!{
            $(
                $id => primitive!(1, concat!("std.http.prim.uri.", stringify!($id)), |u: &Uri| (u.0).$id())
            ),*
        }
    }
}

mod std {
    pub(crate) mod http {
        pub(crate) use crate::http as prim;
    }
}

pub fn load(vm: &Thread) -> vm::Result<ExternModule> {
    ExternModule::new(
        vm,
        record! {
            listen => primitive!(2, async fn std::http::prim::listen),
            read_chunk => primitive!(1, async fn std::http::prim::read_chunk),
            write_response => primitive!(2, async fn std::http::prim::write_response),
            port => primitive!(1, "std.http.prim.uri.port", |u: &Uri| (u.0).port_part().map(|p| p.as_u16())),
            uri => uri_binds!(path host query to_string)
        },
    )
}
<|endoftext|>


<commit_before>use progress::ProgressBar;

macro_rules! progress_iterator_trait {
    ($trait:ident , $struct:ident) => {
        pub trait $trait
        where
            Self: Sized,
        {
            fn progress_with(self, progress: ProgressBar) -> $struct<Self>;

            fn progress_count(self, len: u64) -> $struct<Self> {
                self.progress_with(ProgressBar::new(len))
            }

            fn progress(self) -> $struct<Self> {
                self.progress_count(0)
            }
        }
    };
}

progress_iterator_trait! { ProgressIterator, ProgressBarIter }

pub struct ProgressBarIter<T> {
    it: T,
    progress: ProgressBar,
}

impl<S, T: Iterator<Item = S>> Iterator for ProgressBarIter<T> {
    type Item = S;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.it.next();

        if next.is_some() {
            self.progress.inc(1);
        } else {
            self.progress.finish();
        }

        next
    }
}

impl<S, T: Iterator<Item = S>> ProgressIterator for T {
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> {
        ProgressBarIter { it: self, progress }
    }
}

#[cfg(feature = "with_rayon")]
pub mod rayon {
    use super::*;
    use rayon::iter::{
        plumbing::Consumer, plumbing::Folder, plumbing::UnindexedConsumer, ParallelIterator,
    };
    use std::sync::{Arc, Mutex};
    pub struct ParProgressBarIter<T> {
        it: T,
        progress: Arc<Mutex<ProgressBar>>,
    }

    progress_iterator_trait! { ParallelProgressIterator, ParProgressBarIter }

    impl<S: Send, T: ParallelIterator<Item = S>> ParallelProgressIterator for T {
        fn progress_with(self, progress: ProgressBar) -> ParProgressBarIter<Self> {
            ParProgressBarIter {
                it: self,
                progress: Arc::new(Mutex::new(progress)),
            }
        }
    }

    struct ProgressConsumer<C> {
        base: C,
        progress: Arc<Mutex<ProgressBar>>,
    }

    impl<C> ProgressConsumer<C> {
        fn new(base: C, progress: Arc<Mutex<ProgressBar>>) -> Self {
            ProgressConsumer { base, progress }
        }
    }

    impl<T, C: Consumer<T>> Consumer<T> for ProgressConsumer<C> {
        type Folder = ProgressFolder<C::Folder>;
        type Reducer = C::Reducer;
        type Result = C::Result;

        fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
            let (left, right, reducer) = self.base.split_at(index);
            (
                ProgressConsumer::new(left, self.progress.clone()),
                ProgressConsumer::new(right, self.progress.clone()),
                reducer,
            )
        }

        fn into_folder(self) -> Self::Folder {
            ProgressFolder {
                base: self.base.into_folder(),
                progress: self.progress.clone(),
            }
        }

        fn full(&self) -> bool {
            self.base.full()
        }
    }

    impl<T, C: UnindexedConsumer<T>> UnindexedConsumer<T> for ProgressConsumer<C> {
        fn split_off_left(&self) -> Self {
            ProgressConsumer::new(self.base.split_off_left(), self.progress.clone())
        }

        fn to_reducer(&self) -> Self::Reducer {
            self.base.to_reducer()
        }
    }

    struct ProgressFolder<C> {
        base: C,
        progress: Arc<Mutex<ProgressBar>>,
    }

    impl<T, C: Folder<T>> Folder<T> for ProgressFolder<C> {
        type Result = C::Result;

        fn consume(self, item: T) -> Self {
            self.progress.lock().unwrap().inc(1);
            ProgressFolder {
                base: self.base.consume(item),
                progress: self.progress,
            }
        }

        fn complete(self) -> C::Result {
            self.base.complete()
        }

        fn full(&self) -> bool {
            self.base.full()
        }
    }

    impl<S: Send, T: ParallelIterator<Item = S>> ParallelIterator for ParProgressBarIter<T> {
        type Item = S;

        fn drive_unindexed<C: UnindexedConsumer<Self::Item>>(self, consumer: C) -> C::Result {
            let consumer1 = ProgressConsumer::new(consumer, self.progress.clone());
            self.it.drive_unindexed(consumer1)
        }
    }

    #[cfg(test)]
    mod test {
        use iter::{rayon::ParallelProgressIterator, ProgressIterator};
        use progress::ProgressBar;
        use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

        #[test]
        fn it_can_wrap_a_parallel_iterator() {
            let v = vec![1, 2, 3];
            let pb = ProgressBar::new(v.len() as u64);
            let wrap = |it: ProgressBarIter<_>| {
                assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
            };

            wrap(v.par_iter().progress());
            wrap(v.par_iter().progress_count(3));
            wrap({
                let pb = ProgressBar::new(v.len() as u64);
                v.par_iter().progress_with(pb)
            });
        }

    }
}

#[cfg(test)]
mod test {
    use iter::{ProgressBarIter, ProgressIterator};
    use progress::ProgressBar;

    #[test]
    fn it_can_wrap_an_iterator() {
        let v = vec![1, 2, 3];
        let wrap = |it: ProgressBarIter<_>| {
            assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
        };

        wrap(v.iter().progress());
        wrap(v.iter().progress_count(3));
        wrap({
            let pb = ProgressBar::new(v.len() as u64);
            v.iter().progress_with(pb)
        });
    }
}
<commit_msg>Remove trait, add size_hint to non-parallel iterator<commit_after>use progress::ProgressBar;

pub trait ProgressIterator
where
    Self: Sized + Iterator,
{
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self>;

    fn progress_count(self, len: u64) -> ProgressBarIter<Self> {
        self.progress_with(ProgressBar::new(len))
    }

    fn progress(self) -> ProgressBarIter<Self> {
        let n = match self.size_hint() {
            (_, Some(n)) => n as u64,
            _ => 0,
        };
        self.progress_count(n)
    }
}

pub struct ProgressBarIter<T> {
    it: T,
    progress: ProgressBar,
}

impl<S, T: Iterator<Item = S>> Iterator for ProgressBarIter<T> {
    type Item = S;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.it.next();

        if next.is_some() {
            self.progress.inc(1);
        } else {
            self.progress.finish();
        }

        next
    }
}

impl<S, T: Iterator<Item = S>> ProgressIterator for T {
    fn progress_with(self, progress: ProgressBar) -> ProgressBarIter<Self> {
        ProgressBarIter { it: self, progress }
    }
}

#[cfg(feature = "with_rayon")]
pub mod rayon {
    use super::*;
    use rayon::iter::{
        plumbing::Consumer, plumbing::Folder, plumbing::UnindexedConsumer, ParallelIterator,
    };
    use std::sync::{Arc, Mutex};
    pub struct ParProgressBarIter<T> {
        it: T,
        progress: Arc<Mutex<ProgressBar>>,
    }

    pub trait ParallelProgressIterator
    where
        Self: Sized,
    {
        fn progress_with(self, progress: ProgressBar) -> ParProgressBarIter<Self>;

        fn progress_count(self, len: u64) -> ParProgressBarIter<Self> {
            self.progress_with(ProgressBar::new(len))
        }

        fn progress(self) -> ParProgressBarIter<Self> {
            self.progress_count(0)
        }
    }

    impl<S: Send, T: ParallelIterator<Item = S>> ParallelProgressIterator for T {
        fn progress_with(self, progress: ProgressBar) -> ParProgressBarIter<Self> {
            ParProgressBarIter {
                it: self,
                progress: Arc::new(Mutex::new(progress)),
            }
        }
    }

    struct ProgressConsumer<C> {
        base: C,
        progress: Arc<Mutex<ProgressBar>>,
    }

    impl<C> ProgressConsumer<C> {
        fn new(base: C, progress: Arc<Mutex<ProgressBar>>) -> Self {
            ProgressConsumer { base, progress }
        }
    }

    impl<T, C: Consumer<T>> Consumer<T> for ProgressConsumer<C> {
        type Folder = ProgressFolder<C::Folder>;
        type Reducer = C::Reducer;
        type Result = C::Result;

        fn split_at(self, index: usize) -> (Self, Self, Self::Reducer) {
            let (left, right, reducer) = self.base.split_at(index);
            (
                ProgressConsumer::new(left, self.progress.clone()),
                ProgressConsumer::new(right, self.progress.clone()),
                reducer,
            )
        }

        fn into_folder(self) -> Self::Folder {
            ProgressFolder {
                base: self.base.into_folder(),
                progress: self.progress.clone(),
            }
        }

        fn full(&self) -> bool {
            self.base.full()
        }
    }

    impl<T, C: UnindexedConsumer<T>> UnindexedConsumer<T> for ProgressConsumer<C> {
        fn split_off_left(&self) -> Self {
            ProgressConsumer::new(self.base.split_off_left(), self.progress.clone())
        }

        fn to_reducer(&self) -> Self::Reducer {
            self.base.to_reducer()
        }
    }

    struct ProgressFolder<C> {
        base: C,
        progress: Arc<Mutex<ProgressBar>>,
    }

    impl<T, C: Folder<T>> Folder<T> for ProgressFolder<C> {
        type Result = C::Result;

        fn consume(self, item: T) -> Self {
            self.progress.lock().unwrap().inc(1);
            ProgressFolder {
                base: self.base.consume(item),
                progress: self.progress,
            }
        }

        fn complete(self) -> C::Result {
            self.base.complete()
        }

        fn full(&self) -> bool {
            self.base.full()
        }
    }

    impl<S: Send, T: ParallelIterator<Item = S>> ParallelIterator for ParProgressBarIter<T> {
        type Item = S;

        fn drive_unindexed<C: UnindexedConsumer<Self::Item>>(self, consumer: C) -> C::Result {
            let consumer1 = ProgressConsumer::new(consumer, self.progress.clone());
            self.it.drive_unindexed(consumer1)
        }
    }

    #[cfg(test)]
    mod test {
        use iter::{rayon::ParallelProgressIterator, ProgressIterator};
        use progress::ProgressBar;
        use rayon::iter::{IntoParallelRefIterator, ParallelIterator};

        #[test]
        fn it_can_wrap_a_parallel_iterator() {
            let v = vec![1, 2, 3];
            let pb = ProgressBar::new(v.len() as u64);
            let wrap = |it: ProgressBarIter<_>| {
                assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
            };

            wrap(v.par_iter().progress());
            wrap(v.par_iter().progress_count(3));
            wrap({
                let pb = ProgressBar::new(v.len() as u64);
                v.par_iter().progress_with(pb)
            });
        }

    }
}

#[cfg(test)]
mod test {
    use iter::{ProgressBarIter, ProgressIterator};
    use progress::ProgressBar;

    #[test]
    fn it_can_wrap_an_iterator() {
        let v = vec![1, 2, 3];
        let wrap = |it: ProgressBarIter<_>| {
            assert_eq!(it.map(|x| x * 2).collect::<Vec<_>>(), vec![2, 4, 6]);
        };

        wrap(v.iter().progress());
        wrap(v.iter().progress_count(3));
        wrap({
            let pb = ProgressBar::new(v.len() as u64);
            v.iter().progress_with(pb)
        });
    }
}
<|endoftext|>


<commit_before>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use std::ffi::CStr;

#[derive(Clone)]
pub struct List(HandleRc);

impl List {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupList(ptr::null_mut());
            List(HandleRc::new(ih))
        }
    }

    pub fn dropdown(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "DROPDOWN\0") == "YES"
        }
    }

    pub fn set_dropdown(&self, dropdown: bool) -> &Self {
        set_str_attribute(self.handle(), "DROPDOWN\0", if dropdown { "YES\0" } else { "NO\0" });
        self
    }

    pub fn edit_box(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "EDITBOX\0") == "YES"
        }
    }

    pub fn set_edit_box(&self, dropdown: bool) -> &Self {
        set_str_attribute(self.handle(), "EDITBOX\0", if dropdown { "YES\0" } else { "NO\0" });
        self
    }

    pub fn multiple(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "MULTIPLE\0") == "YES"
        }
    }

    pub fn set_multiple(&self, multiple: bool) -> &Self {
        set_str_attribute(self.handle(), "MULTIPLE\0", if multiple { "YES\0" } else { "NO\0" });
        self
    }

    // An `index` of 0 is the first item.
    pub fn item(&self, index: usize) -> String {
        get_str_attribute(self.handle(), &format!("{}\0", index + 1))
    }

    // An `index` of 0 is the first item.
    pub fn set_item(&self, index: usize, text: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("{}\0", index + 1), text);
        self
    }

    pub fn set_items<'a, I, T>(&self, items: I) -> &Self
                               where I: IntoIterator<Item=T>, T: AsRef<str> {
        let mut index = 0;
        for item in items {
            self.set_item(index, item.as_ref());
            index += 1;
        }
        reset_attribute(self.handle(), &format!("{}\0", index + 1));
        self
    }

    pub fn append_item(&self, text: &str) -> &Self {
        set_str_attribute(self.handle(), "APPENDITEM\0", text);
        self
    }

    // An `index` of 0 is the first item.
    pub fn insert_item(&self, index: usize, text: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("INSERTITEM{}\0", index + 1), text);
        self
    }

    // An `index` of 0 is the first item.
    pub fn remove_item(&self, index: usize) -> &Self {
        set_str_attribute(self.handle(), "REMOVEITEM\0", &format!("{}\0", index + 1));
        self
    }

    // An `index` of 0 is the first item.
    pub fn clear(&self) -> &Self {
        set_str_attribute(self.handle(), "REMOVEITEM\0", "ALL\0");
        self
    }

    pub fn count(&self) -> usize {
        unsafe {
            let s = get_str_attribute_slice(self.handle(), "COUNT\0");
            s.parse().unwrap()
        }
    }

    /// Panics if `edit_box` is false.
    pub fn value_text(&self) -> String {
        assert!(self.edit_box());
        get_str_attribute(self.handle(), "VALUE\0")
    }

    /// Returns the index of the selected item or `None` if no item is selected.
    ///
    /// Panics if `edit_box` is true or `multiple` is true.
    pub fn value_single(&self) -> Option<usize> {
        assert!(!self.edit_box());
        assert!(!self.multiple());

        unsafe {
            let s = get_str_attribute_slice(self.handle(), "VALUE\0");
            s.parse::<usize>().ok().into_iter().filter(|i| *i != 0).next().map(|i| i - 1)
        }
    }

    /// Returns the indexes of all selected items.
    ///
    /// Panics if `edit_box` is true or `multiple` is false.
    pub fn value_multiple(&self) -> Vec<usize> {
        assert!(!self.edit_box());
        assert!(self.multiple());

        unsafe {
            let s = get_str_attribute_slice(self.handle(), "VALUE\0");
            let sel = s.as_bytes().iter().enumerate().filter(|&(_, c)| *c == b'+').map(|(i, _)| i);
            sel.collect()
        }
    }

    // visible_items
    // visible_columns
    // visible_lines
}

impl_control_traits!(List);

impl ActiveAttribute for List {}
impl MinMaxSizeAttribute for List {}
impl VisibleAttribute for List {}

impl MenuCommonCallbacks for List {}

#[derive(Clone)]
pub struct ListActionArgs<'a> {
    pub text: &'a str,
    pub item_index: usize,
    pub selected: bool,
    _dummy: (),
}

impl_callbacks! {
    List {
        "ACTION\0" => action_event {
            ACTION_CALLBACKS<FnMut(&ListActionArgs), ListActionCallbackToken>
        }
        unsafe extern fn list_action_cb(ih: *mut Ihandle, text: *mut c_char, item: c_int, state: c_int) -> c_int {
            with_callbacks(ih, &ACTION_CALLBACKS, |cbs| {
                let text_str = CStr::from_ptr(text).to_string_lossy();
                let args = ListActionArgs {
                    text: &*text_str,
                    item_index: (item - 1) as usize,
                    selected: state == 1,
                    _dummy: (),
                };
                for cb in cbs {
                    (&mut *cb.1.borrow_mut())(&args);
                }
                IUP_DEFAULT
            })
        }
    }
}
<commit_msg>Fix a copy and paste error in List::set_edit_box<commit_after>/* Copyright 2016 Jordan Miner
 *
 * Licensed under the MIT license <LICENSE or
 * http://opensource.org/licenses/MIT>. This file may not be copied,
 * modified, or distributed except according to those terms.
 */

use super::control_prelude::*;
use std::ffi::CStr;

#[derive(Clone)]
pub struct List(HandleRc);

impl List {
    pub fn new() -> Self {
        unsafe {
            ::iup_open();
            let ih = IupList(ptr::null_mut());
            List(HandleRc::new(ih))
        }
    }

    pub fn dropdown(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "DROPDOWN\0") == "YES"
        }
    }

    pub fn set_dropdown(&self, dropdown: bool) -> &Self {
        set_str_attribute(self.handle(), "DROPDOWN\0", if dropdown { "YES\0" } else { "NO\0" });
        self
    }

    pub fn edit_box(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "EDITBOX\0") == "YES"
        }
    }

    pub fn set_edit_box(&self, edit_box: bool) -> &Self {
        set_str_attribute(self.handle(), "EDITBOX\0", if edit_box { "YES\0" } else { "NO\0" });
        self
    }

    pub fn multiple(&self) -> bool {
        unsafe {
            get_str_attribute_slice(self.handle(), "MULTIPLE\0") == "YES"
        }
    }

    pub fn set_multiple(&self, multiple: bool) -> &Self {
        set_str_attribute(self.handle(), "MULTIPLE\0", if multiple { "YES\0" } else { "NO\0" });
        self
    }

    // An `index` of 0 is the first item.
    pub fn item(&self, index: usize) -> String {
        get_str_attribute(self.handle(), &format!("{}\0", index + 1))
    }

    // An `index` of 0 is the first item.
    pub fn set_item(&self, index: usize, text: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("{}\0", index + 1), text);
        self
    }

    pub fn set_items<'a, I, T>(&self, items: I) -> &Self
                               where I: IntoIterator<Item=T>, T: AsRef<str> {
        let mut index = 0;
        for item in items {
            self.set_item(index, item.as_ref());
            index += 1;
        }
        reset_attribute(self.handle(), &format!("{}\0", index + 1));
        self
    }

    pub fn append_item(&self, text: &str) -> &Self {
        set_str_attribute(self.handle(), "APPENDITEM\0", text);
        self
    }

    // An `index` of 0 is the first item.
    pub fn insert_item(&self, index: usize, text: &str) -> &Self {
        set_str_attribute(self.handle(), &format!("INSERTITEM{}\0", index + 1), text);
        self
    }

    // An `index` of 0 is the first item.
    pub fn remove_item(&self, index: usize) -> &Self {
        set_str_attribute(self.handle(), "REMOVEITEM\0", &format!("{}\0", index + 1));
        self
    }

    // An `index` of 0 is the first item.
    pub fn clear(&self) -> &Self {
        set_str_attribute(self.handle(), "REMOVEITEM\0", "ALL\0");
        self
    }

    pub fn count(&self) -> usize {
        unsafe {
            let s = get_str_attribute_slice(self.handle(), "COUNT\0");
            s.parse().unwrap()
        }
    }

    /// Panics if `edit_box` is false.
    pub fn value_text(&self) -> String {
        assert!(self.edit_box());
        get_str_attribute(self.handle(), "VALUE\0")
    }

    /// Returns the index of the selected item or `None` if no item is selected.
    ///
    /// Panics if `edit_box` is true or `multiple` is true.
    pub fn value_single(&self) -> Option<usize> {
        assert!(!self.edit_box());
        assert!(!self.multiple());

        unsafe {
            let s = get_str_attribute_slice(self.handle(), "VALUE\0");
            s.parse::<usize>().ok().into_iter().filter(|i| *i != 0).next().map(|i| i - 1)
        }
    }

    /// Returns the indexes of all selected items.
    ///
    /// Panics if `edit_box` is true or `multiple` is false.
    pub fn value_multiple(&self) -> Vec<usize> {
        assert!(!self.edit_box());
        assert!(self.multiple());

        unsafe {
            let s = get_str_attribute_slice(self.handle(), "VALUE\0");
            let sel = s.as_bytes().iter().enumerate().filter(|&(_, c)| *c == b'+').map(|(i, _)| i);
            sel.collect()
        }
    }

    // visible_items
    // visible_columns
    // visible_lines
}

impl_control_traits!(List);

impl ActiveAttribute for List {}
impl MinMaxSizeAttribute for List {}
impl VisibleAttribute for List {}

impl MenuCommonCallbacks for List {}

#[derive(Clone)]
pub struct ListActionArgs<'a> {
    pub text: &'a str,
    pub item_index: usize,
    pub selected: bool,
    _dummy: (),
}

impl_callbacks! {
    List {
        "ACTION\0" => action_event {
            ACTION_CALLBACKS<FnMut(&ListActionArgs), ListActionCallbackToken>
        }
        unsafe extern fn list_action_cb(ih: *mut Ihandle, text: *mut c_char, item: c_int, state: c_int) -> c_int {
            with_callbacks(ih, &ACTION_CALLBACKS, |cbs| {
                let text_str = CStr::from_ptr(text).to_string_lossy();
                let args = ListActionArgs {
                    text: &*text_str,
                    item_index: (item - 1) as usize,
                    selected: state == 1,
                    _dummy: (),
                };
                for cb in cbs {
                    (&mut *cb.1.borrow_mut())(&args);
                }
                IUP_DEFAULT
            })
        }
    }
}
<|endoftext|>