<commit_before>//! API wrappers for common use cases like highlighting strings and
//! files without caring about intermediate semantic representation
//! and caching.

use parsing::{ScopeStack, ParseState, SyntaxDefinition, SyntaxSet};
use highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{self, BufReader};
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);
        // use util::debug_print_ops;
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

/// Convenience struct containing everything you need to highlight a file.
/// Use the `reader` to get the lines of the file and the `highlight_lines` to highlight them.
/// See the `new` method docs for more information.
pub struct HighlightFile<'a> {
    pub reader: BufReader<File>,
    pub highlight_lines: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
    /// Constructs a file reader and a line highlighter to get you reading files as fast as possible.
    /// Auto-detects the syntax from the extension and constructs a `HighlightLines` with the correct syntax and theme.
    ///
    /// # Examples
    /// This example uses `reader.lines()` to get lines without a newline character.
    /// See the `syncat` example for an example of reading lines with a newline character, which gets slightly more robust
    /// and fast syntax highlighting, at the cost of a couple extra lines of code.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// let ss = SyntaxSet::load_defaults_nonewlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// for maybe_line in highlighter.reader.lines() {
    ///     let line = maybe_line.unwrap();
    ///     let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
    ///     println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    /// }
    /// ```
    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 f = try!(File::open(path));
        let syntax = try!(ss.find_syntax_for_file(path))
            .unwrap_or_else(|| ss.find_syntax_plain_text());

        Ok(HighlightFile {
            reader: BufReader::new(f),
            highlight_lines: 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);
    }

    #[test]
    fn can_highlight_file() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        HighlightFile::new("testdata/highlight_test.erb",
                           &ss,
                           &ts.themes["base16-ocean.dark"])
            .unwrap();
    }
}
<commit_msg>Add ScopeRegionIterator<commit_after>//! API wrappers for common use cases like highlighting strings and
//! files without caring about intermediate semantic representation
//! and caching.

use parsing::{ScopeStack, ParseState, SyntaxDefinition, SyntaxSet, ScopeStackOp};
use highlighting::{Highlighter, HighlightState, HighlightIterator, Theme, Style};
use std::io::{self, BufReader};
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);
        // use util::debug_print_ops;
        // debug_print_ops(line, &ops);
        let iter =
            HighlightIterator::new(&mut self.highlight_state, &ops[..], line, &self.highlighter);
        iter.collect()
    }
}

/// Convenience struct containing everything you need to highlight a file.
/// Use the `reader` to get the lines of the file and the `highlight_lines` to highlight them.
/// See the `new` method docs for more information.
pub struct HighlightFile<'a> {
    pub reader: BufReader<File>,
    pub highlight_lines: HighlightLines<'a>,
}

impl<'a> HighlightFile<'a> {
    /// Constructs a file reader and a line highlighter to get you reading files as fast as possible.
    /// Auto-detects the syntax from the extension and constructs a `HighlightLines` with the correct syntax and theme.
    ///
    /// # Examples
    /// This example uses `reader.lines()` to get lines without a newline character.
    /// See the `syncat` example for an example of reading lines with a newline character, which gets slightly more robust
    /// and fast syntax highlighting, at the cost of a couple extra lines of code.
    ///
    /// ```
    /// use syntect::parsing::SyntaxSet;
    /// use syntect::highlighting::{ThemeSet, Style};
    /// use syntect::util::as_24_bit_terminal_escaped;
    /// use syntect::easy::HighlightFile;
    /// use std::io::BufRead;
    ///
    /// let ss = SyntaxSet::load_defaults_nonewlines();
    /// let ts = ThemeSet::load_defaults();
    ///
    /// let mut highlighter = HighlightFile::new("testdata/highlight_test.erb", &ss, &ts.themes["base16-ocean.dark"]).unwrap();
    /// for maybe_line in highlighter.reader.lines() {
    ///     let line = maybe_line.unwrap();
    ///     let regions: Vec<(Style, &str)> = highlighter.highlight_lines.highlight(&line);
    ///     println!("{}", as_24_bit_terminal_escaped(&regions[..], true));
    /// }
    /// ```
    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 f = try!(File::open(path));
        let syntax = try!(ss.find_syntax_for_file(path))
            .unwrap_or_else(|| ss.find_syntax_plain_text());

        Ok(HighlightFile {
            reader: BufReader::new(f),
            highlight_lines: HighlightLines::new(syntax, theme),
        })
    }
}

/// Iterator over the regions of a line which a given the operation from the parser applies.
///
/// To use just keep your own `ScopeStack` and then `ScopeStack.apply(op)` the operation that is yielded
/// at the top of your `for` loop over this iterator. Now you have a substring of the line and the scope stack
/// for that token.
///
/// **Note:** This will often return empty regions, just `continue` after applying the op if you don't want them.
#[derive(Debug)]
pub struct ScopeRegionIterator<'a> {
    ops: &'a [(usize, ScopeStackOp)],
    line: &'a str,
    index: usize,
    last_str_index: usize,
}

impl<'a> ScopeRegionIterator<'a> {
    pub fn new(ops: &'a [(usize, ScopeStackOp)], line: &'a str) -> ScopeRegionIterator<'a> {
        ScopeRegionIterator {
            ops: ops,
            line: line,
            index: 0,
            last_str_index: 0,
        }
    }
}

static NOOP_OP: ScopeStackOp = ScopeStackOp::Noop;
impl<'a> Iterator for ScopeRegionIterator<'a> {
    type Item = (&'a str, &'a ScopeStackOp);
    fn next(&mut self) -> Option<Self::Item> {
        let next_str_i = if self.index >= self.ops.len() {
            if self.last_str_index >= self.line.len() {
                return None;
            }
            self.line.len()
        } else {
            self.ops[self.index].0
        };
        let substr = &self.line[self.last_str_index..next_str_i];
        self.last_str_index = next_str_i;
        let op = if self.index == 0 {
            &NOOP_OP
        } else {
            &self.ops[self.index-1].1
        };
        self.index += 1;
        Some((substr, op))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use parsing::{SyntaxSet, ParseState, ScopeStack};
    use highlighting::ThemeSet;
    use std::str::FromStr;
    #[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);
    }

    #[test]
    fn can_highlight_file() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let ts = ThemeSet::load_defaults();
        HighlightFile::new("testdata/highlight_test.erb",
                           &ss,
                           &ts.themes["base16-ocean.dark"])
            .unwrap();
    }

    #[test]
    fn can_find_regions() {
        let ss = SyntaxSet::load_defaults_nonewlines();
        let mut state = ParseState::new(ss.find_syntax_by_extension("rb").unwrap());
        let line = "lol=5+2";
        let ops = state.parse_line(line);

        let mut stack = ScopeStack::new();
        let mut token_count = 0;
        for (s, op) in ScopeRegionIterator::new(&ops, line) {
            stack.apply(op);
            if s.is_empty() { // in this case we don't care about blank tokens
                continue;
            }
            if token_count == 1 {
                assert_eq!(stack, ScopeStack::from_str("source.ruby keyword.operator.assignment.ruby").unwrap());
                assert_eq!(s, "=");
            }
            token_count += 1;
            println!("{:?} {}", s, stack);
        }
        assert_eq!(token_count, 5);
    }
}
<|endoftext|>


<commit_before>use std::convert::TryFrom;

use log::trace;
use nom::{
    branch::permutation,
    bytes::streaming::take,
    combinator::{complete, flat_map, map, map_parser, opt, verify},
    number::streaming::{be_f32, be_f64},
    sequence::{pair, preceded},
    Err, IResult, Needed, Parser,
};

/*
struct Document {
    header: Header,
    body: Vec<Element>,
}

struct Header {}

#[derive(Debug)]
enum ElementData {
    Signed(i64),
    Unsigned(u64),
    Float(f64),
    PlainString(String),
    UTF8String(String),
    Date(u64),
    Master(Vec<Element>),
    Binary(Vec<u8>),
    Unknown(u64),
}

#[derive(Debug)]
pub struct Element {
    id: u64,
    size: u64,
    data: ElementData,
}
*/

#[derive(Debug, PartialEq)]
pub struct Error<'a> {
    input: &'a [u8],
    kind: ErrorKind,
}

#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    Nom(nom::error::ErrorKind),
    Custom(u8),
}

impl<'a> nom::error::ParseError<&'a [u8]> for Error<'a> {
    fn from_error_kind(input: &'a [u8], kind: nom::error::ErrorKind) -> Self {
        Error {
            input,
            kind: ErrorKind::Nom(kind),
        }
    }

    fn append(_input: &'a [u8], _kind: nom::error::ErrorKind, other: Self) -> Self {
        other
    }
}

pub fn custom_error(input: &[u8], code: u8) -> Error {
    Error {
        input,
        kind: ErrorKind::Custom(code),
    }
}

pub(crate) fn usize_error(input: &[u8], size: u64) -> Result<usize, nom::Err<Error>> {
    usize::try_from(size).map_err(|_| {
        log::error!("Not possible to convert size into usize");
        nom::Err::Error(custom_error(input, 0))
    })
}

pub fn vint(input: &[u8]) -> IResult<&[u8], u64, Error> {
    if input.is_empty() {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let v = input[0];
    let len = v.leading_zeros();

    if len == 8 {
        return Err(Err::Error(custom_error(input, 100)));
    }

    if input.len() <= len as usize {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let mut val = u64::from(v ^ (1 << (7 - len)));

    trace!(
        "vint {:08b} {:08b} {:08b} {}",
        val,
        v,
        (1 << (8 - len)),
        len
    );

    for i in 0..len as usize {
        val = (val << 8) | u64::from(input[i + 1]);
    }

    trace!("     result {:08x}", val);

    Ok((&input[len as usize + 1..], val))
}

// The ID are represented in the specification as their binary representation
// do not drop the marker bit.
pub fn vid(input: &[u8]) -> IResult<&[u8], u64, Error> {
    if input.is_empty() {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let v = input[0];
    let len = v.leading_zeros();

    if len == 8 {
        return Err(Err::Error(custom_error(input, 100)));
    }

    if input.len() <= len as usize {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let mut val = u64::from(v);

    trace!("vid {:08b} {:08b} {:08b} {}", val, v, (1 << (8 - len)), len);

    for i in 0..len as usize {
        val = (val << 8) | u64::from(input[i + 1]);
    }

    trace!("     result {:08x}", val);

    Ok((&input[len as usize + 1..], val))
}

pub fn parse_uint_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], u64, Error> {
    move |input| {
        let mut val = 0;

        if size > 8 {
            return Err(Err::Error(custom_error(input, 102)));
        }

        for i in input.iter().take(size as usize) {
            val = (val << 8) | u64::from(*i);
        }

        Ok((&input[size as usize..], val))
    }
}

pub fn parse_int_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], i64, Error> {
    move |input| {
        let mut val = 0;

        if size > 8 {
            return Err(Err::Error(custom_error(input, 103)));
        }

        for i in input.iter().take(size as usize) {
            val = (val << 8) | u64::from(*i);
        }

        //FIXME: is that right?
        Ok((&input[size as usize..], val as i64))
    }
}

pub fn parse_str_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], String, Error> {
    move |input| {
        map(take(usize_error(input, size)?), |data: &[u8]| {
            String::from_utf8(data.to_owned()).unwrap_or_default()
        })(input)
    }
}

pub fn parse_binary_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], Vec<u8>, Error> {
    move |input| {
        map(take(usize_error(input, size)?), |data: &[u8]| {
            data.to_owned()
        })(input)
    }
}

//FIXME: handle default values
//FIXME: is that really following IEEE_754-1985 ?
pub fn parse_float_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], f64, Error> {
    move |input| {
        if size == 0 {
            Ok((input, 0f64))
        } else if size == 4 {
            map(map_parser(take(4usize), be_f32), f64::from)(input)
        } else if size == 8 {
            map_parser(take(8usize), be_f64)(input)
        } else {
            Err(Err::Error(custom_error(input, 104)))
        }
    }
}

fn ebml_support<'a, G, H, O1>(
    id: u64,
    second: G,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error>
where
    G: Fn(u64) -> H,
    H: Parser<&'a [u8], O1, Error<'a>>,
{
    move |i| {
        flat_map(pair(verify(vid, |val| *val == id), vint), |(_, size)| {
            second(size)
        })(i)
    }
}

pub fn ebml_uint<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], u64, Error> {
    ebml_support(id, parse_uint_data)
}

pub fn ebml_int<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], i64, Error> {
    ebml_support(id, parse_int_data)
}

pub fn ebml_float<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], f64, Error> {
    ebml_support(id, parse_float_data)
}

pub fn ebml_str<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], String, Error> {
    ebml_support(id, parse_str_data)
}

pub fn ebml_binary<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Vec<u8>, Error> {
    ebml_support(id, parse_binary_data)
}

pub fn ebml_binary_ref(id: u64) -> impl Fn(&[u8]) -> IResult<&[u8], &[u8], Error> {
    move |i| {
        pair(verify(vid, |val| *val == id), vint)(i)
            .and_then(|(i, (_, size))| take(usize_error(i, size)?)(i))
    }
}

pub fn ebml_master<'a, G, O1>(
    id: u64,
    second: G,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error>
where
    G: Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error> + Copy,
{
    move |i| {
        pair(verify(vid, |val| *val == id), vint)(i)
            .and_then(|(i, (_, size))| map_parser(take(usize_error(i, size)?), second)(i))
    }
}

pub fn eat_void<'a, G, O1>(second: G) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error<'a>>
where
    G: Parser<&'a [u8], O1, Error<'a>> + Copy,
{
    move |i| preceded(opt(skip_void), second)(i)
}

pub fn skip_void(input: &[u8]) -> IResult<&[u8], &[u8], Error> {
    pair(verify(vid, |val| *val == 0xEC), vint)(input)
        .and_then(|(i, (_, size))| take(usize_error(input, size)?)(i))
}

#[derive(Debug, Clone, PartialEq)]
pub struct EBMLHeader {
    pub version: u64,
    pub read_version: u64,
    pub max_id_length: u64,
    pub max_size_length: u64,
    pub doc_type: String,
    pub doc_type_version: u64,
    pub doc_type_read_version: u64,
}

pub fn ebml_header(input: &[u8]) -> IResult<&[u8], EBMLHeader, Error> {
    ebml_master(0x1A45DFA3, |i| {
        map(
            permutation((
                ebml_uint(0x4286), // version
                ebml_uint(0x42F7), // read_version
                ebml_uint(0x42F2), // max id length
                ebml_uint(0x42F3), // max size length
                ebml_str(0x4282),  // doctype
                ebml_uint(0x4287), // doctype version
                ebml_uint(0x4285), // doctype_read version
                opt(complete(skip_void)),
            )),
            |t| EBMLHeader {
                version: t.0,
                read_version: t.1,
                max_id_length: t.2,
                max_size_length: t.3,
                doc_type: t.4,
                doc_type_version: t.5,
                doc_type_read_version: t.6,
            },
        )(i)
    })(input)
}

#[cfg(test)]
#[allow(non_upper_case_globals)]
mod tests {
    use super::*;
    use log::trace;
    use nom::{HexDisplay, Offset};

    const single_stream: &'static [u8] = include_bytes!("../assets/single_stream.mkv");
    const webm: &'static [u8] = include_bytes!("../assets/big-buck-bunny_trailer.webm");

    #[test]
    fn variable_integer() {
        let val01 = [0b10000000];
        //        let val01 = [0b01000000, 0b1];

        match vint(&val01) {
            Ok((_, v)) => assert!(0 == v),
            _ => panic!(),
        }
    }

    /*
    #[test]
    fn header() {
        trace!("{}", single_stream[..8].to_hex(8));
        trace!("{:b} {:b}", single_stream[0], single_stream[1]);
        trace!("{:#?}", parse_element(single_stream));
        panic!();
    }*/

    #[test]
    fn mkv_header() {
        trace!("{}", single_stream[..8].to_hex(8));
        trace!("{:b} {:b}", single_stream[0], single_stream[1]);
        trace!("{:?}", ebml_header(&single_stream[..100]).unwrap());
    }

    #[test]
    fn webm_header() {
        trace!("{}", webm[..8].to_hex(8));
        let res = ebml_header(&webm[..100]);
        trace!("{:?}", res);

        if let Ok((i, _)) = res {
            trace!("offset: {} bytes", webm.offset(i));
        } else {
            panic!();
        }
    }
}
<commit_msg>Remove useless FIXME<commit_after>use std::convert::TryFrom;

use log::trace;
use nom::{
    branch::permutation,
    bytes::streaming::take,
    combinator::{complete, flat_map, map, map_parser, opt, verify},
    number::streaming::{be_f32, be_f64},
    sequence::{pair, preceded},
    Err, IResult, Needed, Parser,
};

/*
struct Document {
    header: Header,
    body: Vec<Element>,
}

struct Header {}

#[derive(Debug)]
enum ElementData {
    Signed(i64),
    Unsigned(u64),
    Float(f64),
    PlainString(String),
    UTF8String(String),
    Date(u64),
    Master(Vec<Element>),
    Binary(Vec<u8>),
    Unknown(u64),
}

#[derive(Debug)]
pub struct Element {
    id: u64,
    size: u64,
    data: ElementData,
}
*/

#[derive(Debug, PartialEq)]
pub struct Error<'a> {
    input: &'a [u8],
    kind: ErrorKind,
}

#[derive(Debug, PartialEq)]
pub enum ErrorKind {
    Nom(nom::error::ErrorKind),
    Custom(u8),
}

impl<'a> nom::error::ParseError<&'a [u8]> for Error<'a> {
    fn from_error_kind(input: &'a [u8], kind: nom::error::ErrorKind) -> Self {
        Error {
            input,
            kind: ErrorKind::Nom(kind),
        }
    }

    fn append(_input: &'a [u8], _kind: nom::error::ErrorKind, other: Self) -> Self {
        other
    }
}

pub fn custom_error(input: &[u8], code: u8) -> Error {
    Error {
        input,
        kind: ErrorKind::Custom(code),
    }
}

pub(crate) fn usize_error(input: &[u8], size: u64) -> Result<usize, nom::Err<Error>> {
    usize::try_from(size).map_err(|_| {
        log::error!("Not possible to convert size into usize");
        nom::Err::Error(custom_error(input, 0))
    })
}

pub fn vint(input: &[u8]) -> IResult<&[u8], u64, Error> {
    if input.is_empty() {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let v = input[0];
    let len = v.leading_zeros();

    if len == 8 {
        return Err(Err::Error(custom_error(input, 100)));
    }

    if input.len() <= len as usize {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let mut val = u64::from(v ^ (1 << (7 - len)));

    trace!(
        "vint {:08b} {:08b} {:08b} {}",
        val,
        v,
        (1 << (8 - len)),
        len
    );

    for i in 0..len as usize {
        val = (val << 8) | u64::from(input[i + 1]);
    }

    trace!("     result {:08x}", val);

    Ok((&input[len as usize + 1..], val))
}

// The ID are represented in the specification as their binary representation
// do not drop the marker bit.
pub fn vid(input: &[u8]) -> IResult<&[u8], u64, Error> {
    if input.is_empty() {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let v = input[0];
    let len = v.leading_zeros();

    if len == 8 {
        return Err(Err::Error(custom_error(input, 100)));
    }

    if input.len() <= len as usize {
        return Err(Err::Incomplete(Needed::new(1)));
    }

    let mut val = u64::from(v);

    trace!("vid {:08b} {:08b} {:08b} {}", val, v, (1 << (8 - len)), len);

    for i in 0..len as usize {
        val = (val << 8) | u64::from(input[i + 1]);
    }

    trace!("     result {:08x}", val);

    Ok((&input[len as usize + 1..], val))
}

pub fn parse_uint_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], u64, Error> {
    move |input| {
        let mut val = 0;

        if size > 8 {
            return Err(Err::Error(custom_error(input, 102)));
        }

        for i in input.iter().take(size as usize) {
            val = (val << 8) | u64::from(*i);
        }

        Ok((&input[size as usize..], val))
    }
}

pub fn parse_int_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], i64, Error> {
    move |input| {
        let mut val = 0;

        if size > 8 {
            return Err(Err::Error(custom_error(input, 103)));
        }

        for i in input.iter().take(size as usize) {
            val = (val << 8) | u64::from(*i);
        }

        Ok((&input[size as usize..], val as i64))
    }
}

pub fn parse_str_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], String, Error> {
    move |input| {
        map(take(usize_error(input, size)?), |data: &[u8]| {
            String::from_utf8(data.to_owned()).unwrap_or_default()
        })(input)
    }
}

pub fn parse_binary_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], Vec<u8>, Error> {
    move |input| {
        map(take(usize_error(input, size)?), |data: &[u8]| {
            data.to_owned()
        })(input)
    }
}

//FIXME: handle default values
//FIXME: is that really following IEEE_754-1985 ?
pub fn parse_float_data(size: u64) -> impl Fn(&[u8]) -> IResult<&[u8], f64, Error> {
    move |input| {
        if size == 0 {
            Ok((input, 0f64))
        } else if size == 4 {
            map(map_parser(take(4usize), be_f32), f64::from)(input)
        } else if size == 8 {
            map_parser(take(8usize), be_f64)(input)
        } else {
            Err(Err::Error(custom_error(input, 104)))
        }
    }
}

fn ebml_support<'a, G, H, O1>(
    id: u64,
    second: G,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error>
where
    G: Fn(u64) -> H,
    H: Parser<&'a [u8], O1, Error<'a>>,
{
    move |i| {
        flat_map(pair(verify(vid, |val| *val == id), vint), |(_, size)| {
            second(size)
        })(i)
    }
}

pub fn ebml_uint<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], u64, Error> {
    ebml_support(id, parse_uint_data)
}

pub fn ebml_int<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], i64, Error> {
    ebml_support(id, parse_int_data)
}

pub fn ebml_float<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], f64, Error> {
    ebml_support(id, parse_float_data)
}

pub fn ebml_str<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], String, Error> {
    ebml_support(id, parse_str_data)
}

pub fn ebml_binary<'a>(id: u64) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], Vec<u8>, Error> {
    ebml_support(id, parse_binary_data)
}

pub fn ebml_binary_ref(id: u64) -> impl Fn(&[u8]) -> IResult<&[u8], &[u8], Error> {
    move |i| {
        pair(verify(vid, |val| *val == id), vint)(i)
            .and_then(|(i, (_, size))| take(usize_error(i, size)?)(i))
    }
}

pub fn ebml_master<'a, G, O1>(
    id: u64,
    second: G,
) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error>
where
    G: Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error> + Copy,
{
    move |i| {
        pair(verify(vid, |val| *val == id), vint)(i)
            .and_then(|(i, (_, size))| map_parser(take(usize_error(i, size)?), second)(i))
    }
}

pub fn eat_void<'a, G, O1>(second: G) -> impl Fn(&'a [u8]) -> IResult<&'a [u8], O1, Error<'a>>
where
    G: Parser<&'a [u8], O1, Error<'a>> + Copy,
{
    move |i| preceded(opt(skip_void), second)(i)
}

pub fn skip_void(input: &[u8]) -> IResult<&[u8], &[u8], Error> {
    pair(verify(vid, |val| *val == 0xEC), vint)(input)
        .and_then(|(i, (_, size))| take(usize_error(input, size)?)(i))
}

#[derive(Debug, Clone, PartialEq)]
pub struct EBMLHeader {
    pub version: u64,
    pub read_version: u64,
    pub max_id_length: u64,
    pub max_size_length: u64,
    pub doc_type: String,
    pub doc_type_version: u64,
    pub doc_type_read_version: u64,
}

pub fn ebml_header(input: &[u8]) -> IResult<&[u8], EBMLHeader, Error> {
    ebml_master(0x1A45DFA3, |i| {
        map(
            permutation((
                ebml_uint(0x4286), // version
                ebml_uint(0x42F7), // read_version
                ebml_uint(0x42F2), // max id length
                ebml_uint(0x42F3), // max size length
                ebml_str(0x4282),  // doctype
                ebml_uint(0x4287), // doctype version
                ebml_uint(0x4285), // doctype_read version
                opt(complete(skip_void)),
            )),
            |t| EBMLHeader {
                version: t.0,
                read_version: t.1,
                max_id_length: t.2,
                max_size_length: t.3,
                doc_type: t.4,
                doc_type_version: t.5,
                doc_type_read_version: t.6,
            },
        )(i)
    })(input)
}

#[cfg(test)]
#[allow(non_upper_case_globals)]
mod tests {
    use super::*;
    use log::trace;
    use nom::{HexDisplay, Offset};

    const single_stream: &'static [u8] = include_bytes!("../assets/single_stream.mkv");
    const webm: &'static [u8] = include_bytes!("../assets/big-buck-bunny_trailer.webm");

    #[test]
    fn variable_integer() {
        let val01 = [0b10000000];
        //        let val01 = [0b01000000, 0b1];

        match vint(&val01) {
            Ok((_, v)) => assert!(0 == v),
            _ => panic!(),
        }
    }

    /*
    #[test]
    fn header() {
        trace!("{}", single_stream[..8].to_hex(8));
        trace!("{:b} {:b}", single_stream[0], single_stream[1]);
        trace!("{:#?}", parse_element(single_stream));
        panic!();
    }*/

    #[test]
    fn mkv_header() {
        trace!("{}", single_stream[..8].to_hex(8));
        trace!("{:b} {:b}", single_stream[0], single_stream[1]);
        trace!("{:?}", ebml_header(&single_stream[..100]).unwrap());
    }

    #[test]
    fn webm_header() {
        trace!("{}", webm[..8].to_hex(8));
        let res = ebml_header(&webm[..100]);
        trace!("{:?}", res);

        if let Ok((i, _)) = res {
            trace!("offset: {} bytes", webm.offset(i));
        } else {
            panic!();
        }
    }
}
<|endoftext|>


<commit_before>use std::rc::Rc;

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Expr {
    Int(isize),
    Cons(Rc<Expr>, Rc<Expr>),
    Nil,
    Sym(String),
    Str(String),
    Lambda(Rc<Expr>, Rc<Expr>),
    FLambda(Prim),
    EOF
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Prim {
    Add,
    Sub,
    Div,
    Mul,
    Concat,
    Funcall
}

impl Expr {
    pub fn cons(car: Expr, cdr: Expr) -> Expr {
        Expr::Cons(Rc::new(car), Rc::new(cdr))
    }

    pub fn list1(a1: Expr) -> Expr {
        Expr::cons(a1, Expr::Nil)
    }
    pub fn list2(a1: Expr, a2: Expr) -> Expr {
        Expr::cons(a1, Expr::list1(a2))
    }
}
<commit_msg>add 'Double' prim for get_args test'<commit_after>use std::rc::Rc;

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Expr {
    Int(isize),
    Cons(Rc<Expr>, Rc<Expr>),
    Nil,
    Sym(String),
    Str(String),
    Lambda(Rc<Expr>, Rc<Expr>),
    FLambda(Prim),
    EOF
}

#[derive(PartialEq, Eq, Clone, Debug)]
pub enum Prim {
    Add,
    Sub,
    Div,
    Mul,
    Concat,
    Funcall,
    Double
}

impl Expr {
    pub fn cons(car: Expr, cdr: Expr) -> Expr {
        Expr::Cons(Rc::new(car), Rc::new(cdr))
    }

    pub fn list1(a1: Expr) -> Expr {
        Expr::cons(a1, Expr::Nil)
    }
    pub fn list2(a1: Expr, a2: Expr) -> Expr {
        Expr::cons(a1, Expr::list1(a2))
    }
}
<|endoftext|>


<commit_before>use {Error, FakeDebug, atom_syndication, futures, lettre, reqwest, rss, std};
use config::Config;
use escapade::Escapable;
use log::{LogKind, LogLevel, Logger};
use std::str::FromStr;
use std::sync::{Arc, Mutex};

const NUM_FETCHERS: usize = 32;
const CHANNEL_CAPACITY: usize = 2 * NUM_FETCHERS;
const FETCH_TIMEOUT_SECS: u64 = 60;

#[derive(Clone, Debug, PartialEq)]
pub struct Feed {
    pub meta: FeedMeta,
    pub items: Vec<FeedItem>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct FeedMeta {
    pub feed_url: String, // this is the same URL as what's in the database
    pub title: String,
}

#[derive(Clone, Debug, PartialEq)]
pub struct FeedItem {
    pub id: String,
    pub title: Option<String>,
    pub link: Option<String>,
    pub content: Option<String>,
}

pub trait Sender {
    fn send(&self, logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error>;
}

#[cfg(test)]
#[derive(Debug)]
pub struct RecorderSender {
    recorded_items: Mutex<Vec<(FeedMeta, FeedItem)>>,
}

#[cfg(test)]
impl RecorderSender {
    pub fn new() -> Self {
        RecorderSender { recorded_items: Mutex::new(Vec::new()) }
    }

    pub fn recorded_items(self) -> Vec<(FeedMeta, FeedItem)> {
        self.recorded_items.into_inner().unwrap()
    }
}

#[cfg(test)]
impl Sender for RecorderSender {
    fn send(&self, _logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error> {
        self.recorded_items.lock().unwrap().push((
            feed_meta.clone(),
            feed_item.clone(),
        ));
        Ok(())
    }
}

#[derive(Debug)]
pub struct EmailSender {
    config: Config,
    mail_client: FakeDebug<Mutex<lettre::transport::smtp::SmtpTransport>>, // TODO: Need newer lettre crate for Debug impl
    no_send: bool,
}

impl EmailSender {
    pub fn new(config: &Config) -> Result<Self, Error> {

        let mail_client = lettre::transport::smtp::SmtpTransportBuilder::new(&config.smtp_server)
            .map_err(|e| ("Failed to construct mail client", e))?
            .credentials(&config.smtp_username, &config.smtp_password)
            .security_level(lettre::transport::smtp::SecurityLevel::AlwaysEncrypt)
            .build();

        Ok(EmailSender {
            config: config.clone(),
            mail_client: FakeDebug(Mutex::new(mail_client)),
            no_send: false,
        })
    }

    pub fn with_no_send(mut self, no_send: bool) -> Self {
        self.no_send = no_send;
        self
    }
}

impl Sender for EmailSender {
    fn send(&self, logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error> {

        use lettre::transport::EmailTransport;

        let item_title = feed_item.title.as_ref().map(|x| x.as_str()).unwrap_or(
            "(N/a)",
        );

        if feed_item.content.is_none() {
            panic!("Got no content for {}", feed_meta.feed_url);
        }

        let item_content = feed_item.content.as_ref().map(|x| x.as_str()).unwrap_or("");

        let body = match feed_item.link {
            None => format!(
                r#"
{}
{}"#,
                item_title.escape().into_inner(),
                item_content
            ),
            Some(ref link) => format!(
                r#"
{}
{}
{}
"#,
                link.escape().into_inner(),
                item_title.escape().into_inner(),
                item_content,
                link.escape().into_inner(),
                link.escape().into_inner()
            ),
        };

        let email = lettre::email::EmailBuilder::new()
            .to(self.config.recipient.as_ref())
            .from((
                self.config.smtp_username.as_ref(),
                feed_meta.title.as_ref(),
            ))
            .subject(&item_title)
            .header(("Content-Type", "text/html"))
            .body(&body)
            .build()
            .map_err(|e| ("Failed to construct email message", e))?;

        /*
        use std::io::Write;
        let stdout = std::io::stdout();
        writeln!(stdout.lock(), "Sending {:?}", email).unwrap();
        */

        if !self.no_send {

            logger.log(
                LogLevel::Verbose,
                LogKind::Info,
                format!("Sending {} — {:?}", feed_meta.feed_url, item_title),
            );

            self.mail_client.lock().unwrap().send(email).map_err(|e| {
                (
                    format!(
                        "Failed to send email (feed url: {}, feed item id: {})",
                        feed_meta.feed_url,
                        feed_item.id
                    ),
                    e,
                )
            })?;
        } else {
            logger.log(
                LogLevel::Verbose,
                LogKind::Info,
                format!("Not sending {} — {:?}", feed_meta.feed_url, item_title),
            );
        }

        Ok(())
    }
}

pub trait Fetcher {
    type Stream: futures::Stream<Item = Feed, Error = Error>;
    fn fetch(self, logger: Arc<Logger>, feed_urls: Vec<String>) -> Self::Stream;
}

#[derive(Debug)]
pub struct NetFetcher {
    client: Arc<Mutex<reqwest::Client>>,
}

impl NetFetcher {
    pub fn new() -> Result<Self, Error> {

        let mut client = reqwest::Client::new().map_err(|e| {
            ("Failed to construct HTTP client", e)
        })?;

        client.timeout(std::time::Duration::new(FETCH_TIMEOUT_SECS, 0));

        Ok(NetFetcher { client: Arc::new(Mutex::new(client)) })
    }

    // It's kinda poor to wrap a channel in an Arc<Mutex<>>, but we need the
    // Sync and Send traits.
    fn fetch_thread(
        logger: Arc<Logger>,
        client: Arc<Mutex<reqwest::Client>>,
        feed_urls: Arc<Mutex<Vec<String>>>,
        send_chan: Arc<Mutex<futures::sink::Wait<futures::sync::mpsc::Sender<Result<Feed, String>>>>>,
    ) {

        let fetch_it = |feed_url: &str| -> Result<String, Error> {

            use std::io::Read;

            logger.log(
                LogLevel::Normal,
                LogKind::Info,
                format!("Fetching {}", feed_url),
            );

            let request = {
                client.lock().unwrap().get(feed_url)
            };

            let mut response = request.send().map_err(|e| {
                (format!("Failed to fetch feed (feed URL: {}): {}", feed_url, e))
            })?;

            let mut body = String::new();
            response.read_to_string(&mut body).map_err(|e| {
                (format!("Failed to read feed body (feed URL: {}): {}", feed_url, e))
            })?;

            Ok(body)
        };

        loop {
            let feed_url = match feed_urls.lock().unwrap().pop() {
                None => return, // no more feeds to fetch
                Some(x) => x,
            };

            // As soon as the channel closes, exit this thread.

            match fetch_it(&feed_url) {
                Err(e) => {
                    match send_chan.lock().unwrap().send(Err(e.to_string())) {
                        Err(_) => return, // channel closed
                        Ok(_) => {}
                    }
                }
                Ok(body) => {

                    let feed = match parse_syndication(&feed_url, &body) {
                        Err(e) => {
                            logger.log(LogLevel::Important, LogKind::Error, e);
                            continue;
                        }
                        Ok(x) => x,
                    };

                    match send_chan.lock().unwrap().send(Ok(feed)) {
                        Err(_) => return, // channel closed
                        Ok(_) => {}
                    }
                }
            }
        }
    }
}

impl Fetcher for NetFetcher {
    type Stream = NetFetcherStream;
    fn fetch(self, logger: Arc<Logger>, feed_urls: Vec<String>) -> Self::Stream {

        // We use (possibly) multiple fetcher threads, each of which sends
        // the feeds it receives through a channel to the stream poller.

        let feed_urls = Arc::new(Mutex::new(feed_urls));
        let (send_chan, recv_chan) = futures::sync::mpsc::channel(CHANNEL_CAPACITY);

        let threads = (0..NUM_FETCHERS)
            .into_iter()
            .map(|_| {
                let logger = logger.clone();
                let client = self.client.clone();
                let feed_urls = feed_urls.clone();
                let send_chan = send_chan.clone();
                std::thread::spawn(move || {
                    use futures::Sink;
                    Self::fetch_thread(
                        logger,
                        client,
                        feed_urls,
                        Arc::new(Mutex::new(send_chan.wait())),
                    )
                })
            })
            .collect();

        NetFetcherStream {
            threads: threads,
            recv_chan: recv_chan,
        }
    }
}

#[derive(Debug)]
pub struct NetFetcherStream {
    threads: Vec<std::thread::JoinHandle<()>>,
    recv_chan: futures::sync::mpsc::Receiver<Result<Feed, String>>,
}

impl futures::Stream for NetFetcherStream {
    type Item = Feed;
    type Error = Error;
    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        match self.recv_chan.poll().unwrap() {
            futures::Async::NotReady => Ok(futures::Async::NotReady),
            futures::Async::Ready(None) => Ok(futures::Async::Ready(None)),
            futures::Async::Ready(Some(Err(e))) => Err(Error::from(e)),
            futures::Async::Ready(Some(Ok(x))) => Ok(futures::Async::Ready(Some(x))),
        }
    }
}

#[cfg(test)]
#[derive(Clone, Debug)]
pub struct MockFetcher {
    mock_items: Vec<Result<Feed, String>>,
}

#[cfg(test)]
impl Fetcher for MockFetcher {
    type Stream = futures::stream::Iter<std::vec::IntoIter<Result<Feed, Error>>>;
    fn fetch(self, _logger: Arc<Logger>, _feed_urls: Vec<String>) -> Self::Stream {

        let items = self.mock_items
            .into_iter()
            .map(|x| match x {
                Err(s) => Err(Error::from(s)),
                Ok(x) => Ok(x),
            })
            .collect::<Vec<_>>();

        futures::stream::iter(items)
    }
}

#[cfg(test)]
impl From<Vec<Result<Feed, String>>> for MockFetcher {
    fn from(mock_items: Vec<Result<Feed, String>>) -> Self {
        MockFetcher { mock_items: mock_items }
    }
}

fn parse_syndication(feed_url: &str, body: &str) -> Result<Feed, Error> {

    // First try as RSS, then as Atom.

    match rss::Channel::read_from(std::io::Cursor::new(body)) {
        Err(..) => {}
        Ok(channel) => {

            return Ok(Feed {
                meta: FeedMeta {
                    feed_url: String::from(feed_url),
                    title: String::from(channel.title()),
                },
                items: channel
                    .items()
                    .iter()
                    .map(|item| -> Result<FeedItem, Error> {
                        Ok(FeedItem {
                            id: item.guid()
                                .map(|x| String::from(x.value()))
                                .or(item.link().map(|x| String::from(x)))
                                .ok_or(format!(
                                    "Cannot determine unique identifier for RSS item (feed URL: {})",
                                    feed_url
                                ))?,
                            title: item.title().map(|x| String::from(x)),
                            link: item.link().map(|x| String::from(x)),
                            content: item.content().or(item.description()).map(
                                |x| String::from(x),
                            ),
                        })
                    })
                    .collect::<Result<_, _>>()?,
            });
        }
    }

    let raw = atom_syndication::Feed::from_str(body).map_err(|e| {
        ((format!("Failed to parse feed (feed URL: {})", feed_url), e))
    })?;

    Ok(Feed {
        meta: FeedMeta {
            feed_url: String::from(feed_url),
            title: raw.title,
        },
        items: raw.entries
            .into_iter()
            .map(|entry| {
                FeedItem {
                    id: entry.id,
                    title: Some(entry.title),
                    link: entry.links.first().map(|x| x.href.clone()),
                    content: entry.content.map(|x| match x {
                        atom_syndication::Content::Text(x) => x.escape().into_inner(),
                        atom_syndication::Content::Html(x) => x,
                        atom_syndication::Content::Xhtml(x) => x.to_string(),
                    }),
                }
            })
            .collect(),
    })
}

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

    #[test]
    fn rss_content_is_in_description() {

        let source = r#"<rss version="2.0">
<channel>
<title>alpha</title>
<link>http://bravo</link>
<description>charlie</description>
<item>
<title>delta</title>
<link>http://echo</link>
<description>foxtrot</description>
<guid>golf</guid>
</item>
</channel>
</rss>"#;

        let got = super::parse_syndication("http://example.com", source).unwrap();

        let expected = Feed {
            meta: FeedMeta {
                feed_url: String::from("http://example.com"),
                title: String::from("alpha"),
            },
            items: vec![
                FeedItem {
                    id: String::from("golf"),
                    title: Some(String::from("delta")),
                    link: Some(String::from("http://echo")),
                    content: Some(String::from("foxtrot")),
                },
            ],
        };

        assert_eq!(got, expected);

    }

}
<commit_msg>Don't panic on feed item without content<commit_after>use {Error, FakeDebug, atom_syndication, futures, lettre, reqwest, rss, std};
use config::Config;
use escapade::Escapable;
use log::{LogKind, LogLevel, Logger};
use std::str::FromStr;
use std::sync::{Arc, Mutex};

const NUM_FETCHERS: usize = 32;
const CHANNEL_CAPACITY: usize = 2 * NUM_FETCHERS;
const FETCH_TIMEOUT_SECS: u64 = 60;

#[derive(Clone, Debug, PartialEq)]
pub struct Feed {
    pub meta: FeedMeta,
    pub items: Vec<FeedItem>,
}

#[derive(Clone, Debug, PartialEq)]
pub struct FeedMeta {
    pub feed_url: String, // this is the same URL as what's in the database
    pub title: String,
}

#[derive(Clone, Debug, PartialEq)]
pub struct FeedItem {
    pub id: String,
    pub title: Option<String>,
    pub link: Option<String>,
    pub content: Option<String>,
}

pub trait Sender {
    fn send(&self, logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error>;
}

#[cfg(test)]
#[derive(Debug)]
pub struct RecorderSender {
    recorded_items: Mutex<Vec<(FeedMeta, FeedItem)>>,
}

#[cfg(test)]
impl RecorderSender {
    pub fn new() -> Self {
        RecorderSender { recorded_items: Mutex::new(Vec::new()) }
    }

    pub fn recorded_items(self) -> Vec<(FeedMeta, FeedItem)> {
        self.recorded_items.into_inner().unwrap()
    }
}

#[cfg(test)]
impl Sender for RecorderSender {
    fn send(&self, _logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error> {
        self.recorded_items.lock().unwrap().push((
            feed_meta.clone(),
            feed_item.clone(),
        ));
        Ok(())
    }
}

#[derive(Debug)]
pub struct EmailSender {
    config: Config,
    mail_client: FakeDebug<Mutex<lettre::transport::smtp::SmtpTransport>>, // TODO: Need newer lettre crate for Debug impl
    no_send: bool,
}

impl EmailSender {
    pub fn new(config: &Config) -> Result<Self, Error> {

        let mail_client = lettre::transport::smtp::SmtpTransportBuilder::new(&config.smtp_server)
            .map_err(|e| ("Failed to construct mail client", e))?
            .credentials(&config.smtp_username, &config.smtp_password)
            .security_level(lettre::transport::smtp::SecurityLevel::AlwaysEncrypt)
            .build();

        Ok(EmailSender {
            config: config.clone(),
            mail_client: FakeDebug(Mutex::new(mail_client)),
            no_send: false,
        })
    }

    pub fn with_no_send(mut self, no_send: bool) -> Self {
        self.no_send = no_send;
        self
    }
}

impl Sender for EmailSender {
    fn send(&self, logger: &Logger, feed_meta: &FeedMeta, feed_item: &FeedItem) -> Result<(), Error> {

        use lettre::transport::EmailTransport;

        let item_title = feed_item.title.as_ref().map(|x| x.as_str()).unwrap_or(
            "(N/a)",
        );

        let item_content = feed_item.content.as_ref().map(|x| x.as_str()).unwrap_or("");

        let body = match feed_item.link {
            None => format!(
                r#"
{}
{}"#,
                item_title.escape().into_inner(),
                item_content
            ),
            Some(ref link) => format!(
                r#"
{}
{}
{}
"#,
                link.escape().into_inner(),
                item_title.escape().into_inner(),
                item_content,
                link.escape().into_inner(),
                link.escape().into_inner()
            ),
        };

        let email = lettre::email::EmailBuilder::new()
            .to(self.config.recipient.as_ref())
            .from((
                self.config.smtp_username.as_ref(),
                feed_meta.title.as_ref(),
            ))
            .subject(&item_title)
            .header(("Content-Type", "text/html"))
            .body(&body)
            .build()
            .map_err(|e| ("Failed to construct email message", e))?;

        /*
        use std::io::Write;
        let stdout = std::io::stdout();
        writeln!(stdout.lock(), "Sending {:?}", email).unwrap();
        */

        if !self.no_send {

            logger.log(
                LogLevel::Verbose,
                LogKind::Info,
                format!("Sending {} — {:?}", feed_meta.feed_url, item_title),
            );

            self.mail_client.lock().unwrap().send(email).map_err(|e| {
                (
                    format!(
                        "Failed to send email (feed url: {}, feed item id: {})",
                        feed_meta.feed_url,
                        feed_item.id
                    ),
                    e,
                )
            })?;
        } else {
            logger.log(
                LogLevel::Verbose,
                LogKind::Info,
                format!("Not sending {} — {:?}", feed_meta.feed_url, item_title),
            );
        }

        Ok(())
    }
}

pub trait Fetcher {
    type Stream: futures::Stream<Item = Feed, Error = Error>;
    fn fetch(self, logger: Arc<Logger>, feed_urls: Vec<String>) -> Self::Stream;
}

#[derive(Debug)]
pub struct NetFetcher {
    client: Arc<Mutex<reqwest::Client>>,
}

impl NetFetcher {
    pub fn new() -> Result<Self, Error> {

        let mut client = reqwest::Client::new().map_err(|e| {
            ("Failed to construct HTTP client", e)
        })?;

        client.timeout(std::time::Duration::new(FETCH_TIMEOUT_SECS, 0));

        Ok(NetFetcher { client: Arc::new(Mutex::new(client)) })
    }

    // It's kinda poor to wrap a channel in an Arc<Mutex<>>, but we need the
    // Sync and Send traits.
    fn fetch_thread(
        logger: Arc<Logger>,
        client: Arc<Mutex<reqwest::Client>>,
        feed_urls: Arc<Mutex<Vec<String>>>,
        send_chan: Arc<Mutex<futures::sink::Wait<futures::sync::mpsc::Sender<Result<Feed, String>>>>>,
    ) {

        let fetch_it = |feed_url: &str| -> Result<String, Error> {

            use std::io::Read;

            logger.log(
                LogLevel::Normal,
                LogKind::Info,
                format!("Fetching {}", feed_url),
            );

            let request = {
                client.lock().unwrap().get(feed_url)
            };

            let mut response = request.send().map_err(|e| {
                (format!("Failed to fetch feed (feed URL: {}): {}", feed_url, e))
            })?;

            let mut body = String::new();
            response.read_to_string(&mut body).map_err(|e| {
                (format!("Failed to read feed body (feed URL: {}): {}", feed_url, e))
            })?;

            Ok(body)
        };

        loop {
            let feed_url = match feed_urls.lock().unwrap().pop() {
                None => return, // no more feeds to fetch
                Some(x) => x,
            };

            // As soon as the channel closes, exit this thread.

            match fetch_it(&feed_url) {
                Err(e) => {
                    match send_chan.lock().unwrap().send(Err(e.to_string())) {
                        Err(_) => return, // channel closed
                        Ok(_) => {}
                    }
                }
                Ok(body) => {

                    let feed = match parse_syndication(&feed_url, &body) {
                        Err(e) => {
                            logger.log(LogLevel::Important, LogKind::Error, e);
                            continue;
                        }
                        Ok(x) => x,
                    };

                    match send_chan.lock().unwrap().send(Ok(feed)) {
                        Err(_) => return, // channel closed
                        Ok(_) => {}
                    }
                }
            }
        }
    }
}

impl Fetcher for NetFetcher {
    type Stream = NetFetcherStream;
    fn fetch(self, logger: Arc<Logger>, feed_urls: Vec<String>) -> Self::Stream {

        // We use (possibly) multiple fetcher threads, each of which sends
        // the feeds it receives through a channel to the stream poller.

        let feed_urls = Arc::new(Mutex::new(feed_urls));
        let (send_chan, recv_chan) = futures::sync::mpsc::channel(CHANNEL_CAPACITY);

        let threads = (0..NUM_FETCHERS)
            .into_iter()
            .map(|_| {
                let logger = logger.clone();
                let client = self.client.clone();
                let feed_urls = feed_urls.clone();
                let send_chan = send_chan.clone();
                std::thread::spawn(move || {
                    use futures::Sink;
                    Self::fetch_thread(
                        logger,
                        client,
                        feed_urls,
                        Arc::new(Mutex::new(send_chan.wait())),
                    )
                })
            })
            .collect();

        NetFetcherStream {
            threads: threads,
            recv_chan: recv_chan,
        }
    }
}

#[derive(Debug)]
pub struct NetFetcherStream {
    threads: Vec<std::thread::JoinHandle<()>>,
    recv_chan: futures::sync::mpsc::Receiver<Result<Feed, String>>,
}

impl futures::Stream for NetFetcherStream {
    type Item = Feed;
    type Error = Error;
    fn poll(&mut self) -> futures::Poll<Option<Self::Item>, Self::Error> {
        match self.recv_chan.poll().unwrap() {
            futures::Async::NotReady => Ok(futures::Async::NotReady),
            futures::Async::Ready(None) => Ok(futures::Async::Ready(None)),
            futures::Async::Ready(Some(Err(e))) => Err(Error::from(e)),
            futures::Async::Ready(Some(Ok(x))) => Ok(futures::Async::Ready(Some(x))),
        }
    }
}

#[cfg(test)]
#[derive(Clone, Debug)]
pub struct MockFetcher {
    mock_items: Vec<Result<Feed, String>>,
}

#[cfg(test)]
impl Fetcher for MockFetcher {
    type Stream = futures::stream::Iter<std::vec::IntoIter<Result<Feed, Error>>>;
    fn fetch(self, _logger: Arc<Logger>, _feed_urls: Vec<String>) -> Self::Stream {

        let items = self.mock_items
            .into_iter()
            .map(|x| match x {
                Err(s) => Err(Error::from(s)),
                Ok(x) => Ok(x),
            })
            .collect::<Vec<_>>();

        futures::stream::iter(items)
    }
}

#[cfg(test)]
impl From<Vec<Result<Feed, String>>> for MockFetcher {
    fn from(mock_items: Vec<Result<Feed, String>>) -> Self {
        MockFetcher { mock_items: mock_items }
    }
}

fn parse_syndication(feed_url: &str, body: &str) -> Result<Feed, Error> {

    // First try as RSS, then as Atom.

    match rss::Channel::read_from(std::io::Cursor::new(body)) {
        Err(..) => {}
        Ok(channel) => {

            return Ok(Feed {
                meta: FeedMeta {
                    feed_url: String::from(feed_url),
                    title: String::from(channel.title()),
                },
                items: channel
                    .items()
                    .iter()
                    .map(|item| -> Result<FeedItem, Error> {
                        Ok(FeedItem {
                            id: item.guid()
                                .map(|x| String::from(x.value()))
                                .or(item.link().map(|x| String::from(x)))
                                .ok_or(format!(
                                    "Cannot determine unique identifier for RSS item (feed URL: {})",
                                    feed_url
                                ))?,
                            title: item.title().map(|x| String::from(x)),
                            link: item.link().map(|x| String::from(x)),
                            content: item.content().or(item.description()).map(
                                |x| String::from(x),
                            ),
                        })
                    })
                    .collect::<Result<_, _>>()?,
            });
        }
    }

    let raw = atom_syndication::Feed::from_str(body).map_err(|e| {
        ((format!("Failed to parse feed (feed URL: {})", feed_url), e))
    })?;

    Ok(Feed {
        meta: FeedMeta {
            feed_url: String::from(feed_url),
            title: raw.title,
        },
        items: raw.entries
            .into_iter()
            .map(|entry| {
                FeedItem {
                    id: entry.id,
                    title: Some(entry.title),
                    link: entry.links.first().map(|x| x.href.clone()),
                    content: entry.content.map(|x| match x {
                        atom_syndication::Content::Text(x) => x.escape().into_inner(),
                        atom_syndication::Content::Html(x) => x,
                        atom_syndication::Content::Xhtml(x) => x.to_string(),
                    }),
                }
            })
            .collect(),
    })
}

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

    #[test]
    fn rss_content_is_in_description() {

        let source = r#"<rss version="2.0">
<channel>
<title>alpha</title>
<link>http://bravo</link>
<description>charlie</description>
<item>
<title>delta</title>
<link>http://echo</link>
<description>foxtrot</description>
<guid>golf</guid>
</item>
</channel>
</rss>"#;

        let got = super::parse_syndication("http://example.com", source).unwrap();

        let expected = Feed {
            meta: FeedMeta {
                feed_url: String::from("http://example.com"),
                title: String::from("alpha"),
            },
            items: vec![
                FeedItem {
                    id: String::from("golf"),
                    title: Some(String::from("delta")),
                    link: Some(String::from("http://echo")),
                    content: Some(String::from("foxtrot")),
                },
            ],
        };

        assert_eq!(got, expected);

    }

}
<|endoftext|>


<commit_before>use std::io::{Read, Seek};
use std::mem;

use Result;
use truetype::{Tape, Value};
use truetype::compound::*;

/// A font.
#[derive(Default)]
pub struct Font {
    pub offset_table: OffsetTable,
    pub char_mapping: Option<CharMapping>,
    pub font_header: Option<FontHeader>,
    pub horizontal_header: Option<HorizontalHeader>,
    pub horizontal_metrics: Option<HorizontalMetrics>,
    pub maximum_profile: Option<MaximumProfile>,
    pub naming_table: Option<NamingTable>,
    pub postscript: Option<PostScript>,
    pub windows_metrics: Option<WindowsMetrics>,
}

macro_rules! tag(
    ($value:expr) => (unsafe {
        mem::transmute::<_, [u8; 4]>(u32::from_be(mem::transmute($value)))
    });
);

macro_rules! checksum_and_jump(
    ($record:ident, $tape:ident, $process:expr) => ({
        if !try!($record.checksum($tape, $process)) {
            raise!("found a corrupted font table");
        }
        try!($tape.jump($record.offset as u64));
    });
    ($record:ident, $tape:ident) => (
        checksum_and_jump!($record, $tape, |_, word| word);
    );
);

impl Font {
    #[inline]
    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<Font> {
        macro_rules! sort(
            ($records:expr) => ({
                let mut records = $records.iter().collect::<Vec<_>>();
                records.sort_by(|one, two| {
                    priority(&tag!(one.tag)).cmp(&priority(&tag!(two.tag)))
                });
                records
            });
        );

        let mut font = Font { offset_table: try!(Value::read(tape)), .. Font::default() };
        if &tag!(font.offset_table.header.version) != b"OTTO" {
            raise!("the font format is invalid");
        }

        for record in sort!(font.offset_table.records) {
            macro_rules! set(
                ($field:ident, $value:expr) => ({
                    checksum_and_jump!(record, tape);
                    font.$field = Some(try!($value));
                });
                ($field:ident) => (set!($field, Value::read(tape)));
            );
            match &tag!(record.tag) {
                b"cmap" => set!(char_mapping),
                b"head" => {
                    checksum_and_jump!(record, tape, |i, word| if i == 2 { 0 } else { word });
                    font.font_header = Some(try!(Value::read(tape)));
                },
                b"hhea" => set!(horizontal_header),
                b"hmtx" => {
                    let header = match font.horizontal_header {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    let profile = match font.maximum_profile {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    set!(horizontal_metrics,HorizontalMetrics::read(tape, header, profile));
                },
                b"maxp" => set!(maximum_profile),
                b"name" => set!(naming_table),
                b"post" => set!(postscript),
                b"OS/2" => set!(windows_metrics),
                _ => {},
            }
        }

        Ok(font)
    }
}

fn priority(tag: &[u8; 4]) -> usize {
    use std::collections::HashMap;
    use std::sync::{Once, ONCE_INIT};

    unsafe {
        static mut PRIORITY: *const HashMap<[u8; 4], usize> = 0 as *const _;
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            let mut map: HashMap<[u8; 4], usize> = HashMap::new();
            map.insert(*b"hmtx", 42);
            PRIORITY = mem::transmute(Box::new(map));
        });
        *(&*PRIORITY).get(tag).unwrap_or(&0)
    }
}
<commit_msg>Fix styles<commit_after>use std::io::{Read, Seek};
use std::mem;

use Result;
use truetype::{Tape, Value};
use truetype::compound::*;

/// A font.
#[derive(Default)]
pub struct Font {
    pub offset_table: OffsetTable,
    pub char_mapping: Option<CharMapping>,
    pub font_header: Option<FontHeader>,
    pub horizontal_header: Option<HorizontalHeader>,
    pub horizontal_metrics: Option<HorizontalMetrics>,
    pub maximum_profile: Option<MaximumProfile>,
    pub naming_table: Option<NamingTable>,
    pub postscript: Option<PostScript>,
    pub windows_metrics: Option<WindowsMetrics>,
}

macro_rules! tag(
    ($value:expr) => (unsafe {
        mem::transmute::<_, [u8; 4]>(u32::from_be(mem::transmute($value)))
    });
);

macro_rules! checksum_and_jump(
    ($record:ident, $tape:ident, $process:expr) => ({
        if !try!($record.checksum($tape, $process)) {
            raise!("found a corrupted font table");
        }
        try!($tape.jump($record.offset as u64));
    });
    ($record:ident, $tape:ident) => (
        checksum_and_jump!($record, $tape, |_, word| word);
    );
);

impl Font {
    #[inline]
    pub fn read<T: Read + Seek>(tape: &mut T) -> Result<Font> {
        macro_rules! sort(
            ($records:expr) => ({
                let mut records = $records.iter().collect::<Vec<_>>();
                records.sort_by(|one, two| {
                    priority(&tag!(one.tag)).cmp(&priority(&tag!(two.tag)))
                });
                records
            });
        );

        let mut font = Font { offset_table: try!(Value::read(tape)), .. Font::default() };
        if &tag!(font.offset_table.header.version) != b"OTTO" {
            raise!("the font format is invalid");
        }

        for record in sort!(font.offset_table.records) {
            macro_rules! set(
                ($field:ident, $value:expr) => ({
                    checksum_and_jump!(record, tape);
                    font.$field = Some(try!($value));
                });
                ($field:ident) => (set!($field, Value::read(tape)));
            );
            match &tag!(record.tag) {
                b"cmap" => set!(char_mapping),
                b"head" => {
                    checksum_and_jump!(record, tape, |i, word| if i == 2 { 0 } else { word });
                    font.font_header = Some(try!(Value::read(tape)));
                },
                b"hhea" => set!(horizontal_header),
                b"hmtx" => {
                    let header = match font.horizontal_header {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    let profile = match font.maximum_profile {
                        Some(ref table) => table,
                        _ => continue,
                    };
                    set!(horizontal_metrics, HorizontalMetrics::read(tape, header, profile));
                },
                b"maxp" => set!(maximum_profile),
                b"name" => set!(naming_table),
                b"post" => set!(postscript),
                b"OS/2" => set!(windows_metrics),
                _ => {},
            }
        }

        Ok(font)
    }
}

fn priority(tag: &[u8; 4]) -> usize {
    use std::collections::HashMap;
    use std::sync::{Once, ONCE_INIT};

    unsafe {
        static mut PRIORITY: *const HashMap<[u8; 4], usize> = 0 as *const _;
        static ONCE: Once = ONCE_INIT;
        ONCE.call_once(|| {
            let mut map: HashMap<[u8; 4], usize> = HashMap::new();
            map.insert(*b"hmtx", 42);
            PRIORITY = mem::transmute(Box::new(map));
        });
        *(&*PRIORITY).get(tag).unwrap_or(&0)
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;

extern crate abstract_ns;
extern crate futures;
extern crate getopts;
extern crate ns_dns_tokio;
extern crate rand;
extern crate tokio_core;
extern crate tokio_io;

// the device under test
extern crate net_interceptor;

use abstract_ns::Resolver;
use futures::{Future, Map, Stream, Async, Poll};
use futures::future;
use futures::sync::oneshot;
use futures::sync::oneshot::{Sender};
use ns_dns_tokio::DnsResolver;
use tokio_core::net::{TcpStream, TcpListener};
use tokio_core::reactor::{Core, Handle};
use tokio_io::{AsyncRead, io};

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::fmt::{Debug, Display};
use std::time::{Duration};
use std::sync::{Arc, Mutex, Barrier};
use std::thread;

use std::io::Error;
use std::ops::Deref;
use std::ops::DerefMut;
use std::borrow::BorrowMut;

use net_interceptor::logging::unittestlogger;

fn add_ten<F>(future: F) -> Map<F, fn(i32) -> i32>
    where F: Future<Item=i32>,
{
    error!("shutdown");
    fn add(a: i32) -> i32 { a + 10 }
    future.map(add)
}

pub struct AbortCondition {
    condition: bool,
    reactor_handle: futures::task::Task,
}

impl AbortCondition {

    fn set(&mut self) {
        debug!("abort condition met");
        self.condition = true;
        self.reactor_handle.unpark();
    }

}

impl Future for AbortCondition {

    type Item = ();
    type Error = Error;

    fn poll(&mut self) -> Poll<(), Error> {
        if  self.condition {
            Ok(Async::Ready(()))
        } else {
            self.reactor_handle = futures::task::park();
            Ok(Async::NotReady)
        }
    }

}

fn stopper(sender: Sender<i64>) -> Arc<Barrier> {
    let counting = Arc::new(Barrier::new(2));
    let val_for_thread = counting.clone();
    thread::spawn(move || {
        warn!("waiting for end");
        val_for_thread.wait();
        warn!("signalling end");
        sender.send(1).unwrap();
    });
    counting
}

#[test]
fn test_infra_connect() {
    unittestlogger();

    // this is the main event loop, powered by tokio core
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let bind_socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12000);
    let listener = TcpListener::bind(&bind_socket_addr, &handle)
        .expect(&format!("Unable to bind to {}", &bind_socket_addr));
    warn!("Listening on {}", listener.local_addr().unwrap());

    let addr = listener.local_addr().unwrap();
    assert!(addr.port() == 12000, "should accept {}");

//    let mut abort = AbortCondition { condition: false, reactor_handle: futures::task::park() };

    let connections = listener.incoming();
    let (tx, signal) = oneshot::channel::<i64>();

    let mut barrier = stopper(tx);
    warn!("stopper activated");

    let server = connections.for_each(move |(socket, _peer_addr)| {
        warn!("println connection from {}", _peer_addr);
        warn!("connection received");
        barrier.wait();
        Ok(())
    });

    let wait = core.handle().spawn(server.map_err(|_|()));

    fn abortCondition() -> futures::Poll<(), std::io::Error> {
        warn!("poll");
        if true {
            Ok(Async::Ready(()))
        }
        else {
            futures::task::park().unpark();
            Ok(Async::NotReady)
        }
    }

    //let abort = futures::future::poll_fn(abortCondition);

    //let timeout = tokio_core::reactor::Timeout::new_at(std::time::Duration::seconds(1));
    // run server, stop on panic
//    core.run(server.select2(timeout)).unwrap();
//    core.run(abort).unwrap();
    // core.spawn(server);
//    core.run(server.select(abort)).unwrap();
    core.run(signal).unwrap();
}
<commit_msg>stripped code, but test still blocks<commit_after>#[macro_use]
extern crate log;

extern crate abstract_ns;
extern crate futures;
extern crate getopts;
extern crate ns_dns_tokio;
extern crate rand;
extern crate tokio_core;
extern crate tokio_io;

// the device under test
extern crate net_interceptor;

use futures::{Future, Stream};
use futures::sync::oneshot;
use futures::sync::oneshot::{Sender};
use tokio_core::net::{TcpListener};
use tokio_core::reactor::Core;

use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::{Arc, Barrier};
use std::thread;

use net_interceptor::logging::unittestlogger;

fn stopper(oneshot_sender_stream: Sender<i64>) -> Arc<Barrier> {
    // barrier awaits the releaser thread and the signalling one
    let barrier = Arc::new(Barrier::new(2));
    let copy_for_thread = barrier.clone();
    // the releaser thread
    thread::spawn(move || {
        debug!("waiting for end");
        copy_for_thread.wait();
        debug!("signalling end");
        // release the future blockig core.run()
        oneshot_sender_stream.send(1).unwrap();
    });
    barrier
}

#[test]
fn test_infra_connect() {
    unittestlogger();

    // this is the main event loop, powered by tokio core
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let bind_socket_addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 12000);
    let listener = TcpListener::bind(&bind_socket_addr, &handle)
        .expect(&format!("Unable to bind to {}", &bind_socket_addr));
    warn!("Listening on {}", listener.local_addr().unwrap());

    let addr = listener.local_addr().unwrap();
    assert!(addr.port() == 12000, "should accept {}");

    let connections = listener.incoming();
    let (tx, signal) = oneshot::channel::<i64>();
    let barrier = stopper(tx);

    let acceptor = connections.for_each(move |(socket, _peer_addr)| {
        warn!("connection from {}", _peer_addr);
        barrier.wait();
        Ok(())
    });

    // register the acceptor in the reactor core
    // the map_err is a hack to satisfy connections.for_each above
    handle.spawn(acceptor.map_err(|_|()));

    core.run(signal).unwrap();
}
<|endoftext|>


<commit_before>// heap.rs

//! Utility functions and algorithms for minimun heaps.

/// I implemented this as a trait.
///
/// If I was only interested in heap_sort, I'd write something like
/// fn heap_sort<T: Ord>(&mut Vec<T>) {
///      fn min_heapify<T: Ord>(&mut Vec<T>) { ...
///      }
///      ...
/// }

use std::fmt::{Show};
use std::ptr::{swap};
use std::vec::{Vec};

pub trait Heap<T> {
    fn heap_sort(&mut self);
    fn heap_min<'a>(&'a self) -> &'a T;
    fn heap_extract_min(&mut self) -> T;
    fn min_heapify(&mut self, uint, uint);
    fn build_min_heap(&mut self);
    fn left(&self, uint) -> uint;
    fn right(&self, uint) -> uint;
    fn parent(&self, uint) -> uint;
    fn swap(&mut self, uint, uint);
}

impl <T: Ord> Heap<T> for Vec<T> {
    fn heap_sort(&mut self) {
        self.build_min_heap();
        for i in range(1, self.len()).rev()  {
            self.swap(0, i);
            self.min_heapify(0, i);
        }
    }

    fn heap_min<'a>(&'a self) -> &'a T {
        self.get(0)
    }

    fn heap_extract_min(&mut self) -> T {
        let o = self.swap_remove(0);
        let self_len = self.len();
        self.min_heapify(0, self_len);
        return o.unwrap()
    }

    fn min_heapify(&mut self, i: uint, j: uint) {
        let l = self.left(i);
        let r = self.right(i);
        let mut smallest = i;
        if l < j && self.get(l) > self.get(smallest) {
            smallest = l;
        }
        if r < j && self.get(r) > self.get(smallest) {
            smallest = r;
        }
        if smallest != i {
            self.swap(i, smallest);
            self.min_heapify(smallest, j);
        }
    }

    fn build_min_heap(&mut self) {
        let self_len = self.len();
        for i in range(0, self_len / 2 + 1).rev() {
            self.min_heapify(i, self_len);
        }
    }

    fn left(&self, i: uint) -> uint {
        2 * i + 1
    }

    fn right(&self, i: uint) -> uint {
        2 * i + 2
    }

    fn parent(&self, i: uint) -> uint {
        i / 2
    }

    fn swap(&mut self, i: uint, j: uint) {
        unsafe{
            let ptr_to_i: *mut T = self.get_mut(i);
            let ptr_to_j: *mut T = self.get_mut(j);
            swap(ptr_to_i, ptr_to_j);
        }
    }

}
<commit_msg>Removed unused import<commit_after>// heap.rs

//! Utility functions and algorithms for minimun heaps.

/// I implemented this as a trait.
///
/// If I was only interested in heap_sort, I'd write something like
/// fn heap_sort<T: Ord>(&mut Vec<T>) {
///      fn min_heapify<T: Ord>(&mut Vec<T>) { ...
///      }
///      ...
/// }

use std::ptr::{swap};
use std::vec::{Vec};

pub trait Heap<T> {
    fn heap_sort(&mut self);
    fn heap_min<'a>(&'a self) -> &'a T;
    fn heap_extract_min(&mut self) -> T;
    fn min_heapify(&mut self, uint, uint);
    fn build_min_heap(&mut self);
    fn left(&self, uint) -> uint;
    fn right(&self, uint) -> uint;
    fn parent(&self, uint) -> uint;
    fn swap(&mut self, uint, uint);
}

impl <T: Ord> Heap<T> for Vec<T> {
    fn heap_sort(&mut self) {
        self.build_min_heap();
        for i in range(1, self.len()).rev()  {
            self.swap(0, i);
            self.min_heapify(0, i);
        }
    }

    fn heap_min<'a>(&'a self) -> &'a T {
        self.get(0)
    }

    fn heap_extract_min(&mut self) -> T {
        let o = self.swap_remove(0);
        let self_len = self.len();
        self.min_heapify(0, self_len);
        return o.unwrap()
    }

    fn min_heapify(&mut self, i: uint, j: uint) {
        let l = self.left(i);
        let r = self.right(i);
        let mut smallest = i;
        if l < j && self.get(l) > self.get(smallest) {
            smallest = l;
        }
        if r < j && self.get(r) > self.get(smallest) {
            smallest = r;
        }
        if smallest != i {
            self.swap(i, smallest);
            self.min_heapify(smallest, j);
        }
    }

    fn build_min_heap(&mut self) {
        let self_len = self.len();
        for i in range(0, self_len / 2 + 1).rev() {
            self.min_heapify(i, self_len);
        }
    }

    fn left(&self, i: uint) -> uint {
        2 * i + 1
    }

    fn right(&self, i: uint) -> uint {
        2 * i + 2
    }

    fn parent(&self, i: uint) -> uint {
        i / 2
    }

    fn swap(&mut self, i: uint, j: uint) {
        unsafe{
            let ptr_to_i: *mut T = self.get_mut(i);
            let ptr_to_j: *mut T = self.get_mut(j);
            swap(ptr_to_i, ptr_to_j);
        }
    }

}
<|endoftext|>


<commit_before>
pub struct List {
}
<commit_msg>Fix Travis<commit_after>
pub enum List {
    Numbered,
    Ordered,
}
<|endoftext|>


<commit_before>//! We setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself during the
//! initial `cargo check`. That way, we avoid checking the actual project, and
//! only build proc macros and build.rs.
//!
//! Code taken from IntelliJ :0)
//!     https://github.com/intellij-rust/intellij-rust/blob/master/native-helper/src/main.rs
use std::{
    ffi::OsString,
    io,
    process::{Command, Stdio},
};

/// ExitCode/ExitStatus are impossible to create :(.
pub(crate) struct ExitCode(pub(crate) Option<i32>);

pub(crate) fn run_rustc_skipping_cargo_checking(
    rustc_executable: OsString,
    args: Vec<OsString>,
) -> io::Result<ExitCode> {
    let is_cargo_check = args.iter().any(|arg| {
        let arg = arg.to_string_lossy();
        // `cargo check` invokes `rustc` with `--emit=metadata` argument.
        //
        // https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate
        // link —     Generates the crates specified by --crate-type. The default
        //            output filenames depend on the crate type and platform. This
        //            is the default if --emit is not specified.
        // metadata — Generates a file containing metadata about the crate.
        //            The default output filename is CRATE_NAME.rmeta.
        arg.starts_with("--emit=") && arg.contains("metadata") && !arg.contains("link")
    });
    if is_cargo_check {
        return Ok(ExitCode(Some(0)));
    }
    run_rustc(rustc_executable, args)
}

fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> {
    let mut child = Command::new(rustc_executable)
        .args(args)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()?;
    Ok(ExitCode(child.wait()?.code()))
}
<commit_msg>Auto merge of #13010 - Veykril:build-script-probes, r=Veykril<commit_after>//! We setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself during the
//! initial `cargo check`. That way, we avoid checking the actual project, and
//! only build proc macros and build.rs.
//!
//! Code taken from IntelliJ :0)
//!     https://github.com/intellij-rust/intellij-rust/blob/master/native-helper/src/main.rs
use std::{
    ffi::OsString,
    io,
    process::{Command, Stdio},
};

/// ExitCode/ExitStatus are impossible to create :(.
pub(crate) struct ExitCode(pub(crate) Option<i32>);

pub(crate) fn run_rustc_skipping_cargo_checking(
    rustc_executable: OsString,
    args: Vec<OsString>,
) -> io::Result<ExitCode> {
    // `CARGO_CFG_TARGET_ARCH` is only set by cargo when executing build scripts
    // We don't want to exit out checks unconditionally with success if a build
    // script tries to invoke checks themselves
    // See https://github.com/rust-lang/rust-analyzer/issues/12973 for context
    let not_invoked_by_build_script = std::env::var_os("CARGO_CFG_TARGET_ARCH").is_none();
    let is_cargo_check = args.iter().any(|arg| {
        let arg = arg.to_string_lossy();
        // `cargo check` invokes `rustc` with `--emit=metadata` argument.
        //
        // https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate
        // link —     Generates the crates specified by --crate-type. The default
        //            output filenames depend on the crate type and platform. This
        //            is the default if --emit is not specified.
        // metadata — Generates a file containing metadata about the crate.
        //            The default output filename is CRATE_NAME.rmeta.
        arg.starts_with("--emit=") && arg.contains("metadata") && !arg.contains("link")
    });
    if not_invoked_by_build_script && is_cargo_check {
        return Ok(ExitCode(Some(0)));
    }
    run_rustc(rustc_executable, args)
}

fn run_rustc(rustc_executable: OsString, args: Vec<OsString>) -> io::Result<ExitCode> {
    let mut child = Command::new(rustc_executable)
        .args(args)
        .stdin(Stdio::inherit())
        .stdout(Stdio::inherit())
        .stderr(Stdio::inherit())
        .spawn()?;
    Ok(ExitCode(child.wait()?.code()))
}
<|endoftext|>


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

//! Timing functions.

use heartbeats;
use ipc_channel::ipc::{self, IpcReceiver};
use profile_traits::time::{ProfilerCategory, ProfilerChan, ProfilerMsg, TimerMetadata};
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::f64;
use std::thread::sleep_ms;
use std_time::precise_time_ns;
use util::task::spawn_named;

pub trait Formattable {
    fn format(&self) -> String;
}

impl Formattable for Option<TimerMetadata> {
    fn format(&self) -> String {
        match self {
            // TODO(cgaebel): Center-align in the format strings as soon as rustc supports it.
            &Some(ref meta) => {
                let url = &*meta.url;
                let url = if url.len() > 30 {
                    &url[..30]
                } else {
                    url
                };
                let incremental = if meta.incremental { "    yes" } else { "    no " };
                let iframe = if meta.iframe { "  yes" } else { "  no " };
                format!(" {:14} {:9} {:30}", incremental, iframe, url)
            },
            &None =>
                format!(" {:14} {:9} {:30}", "    N/A", "  N/A", "             N/A")
        }
    }
}

impl Formattable for ProfilerCategory {
    // some categories are subcategories of LayoutPerformCategory
    // and should be printed to indicate this
    fn format(&self) -> String {
        let padding = match *self {
            ProfilerCategory::LayoutStyleRecalc |
            ProfilerCategory::LayoutRestyleDamagePropagation |
            ProfilerCategory::LayoutNonIncrementalReset |
            ProfilerCategory::LayoutGeneratedContent |
            ProfilerCategory::LayoutMain |
            ProfilerCategory::LayoutDispListBuild |
            ProfilerCategory::LayoutShaping |
            ProfilerCategory::LayoutDamagePropagate |
            ProfilerCategory::PaintingPerTile |
            ProfilerCategory::PaintingPrepBuff => "+ ",
            ProfilerCategory::LayoutParallelWarmup |
            ProfilerCategory::LayoutSelectorMatch |
            ProfilerCategory::LayoutTreeBuilder => "| + ",
            _ => ""
        };
        let name = match *self {
            ProfilerCategory::Compositing => "Compositing",
            ProfilerCategory::LayoutPerform => "Layout",
            ProfilerCategory::LayoutStyleRecalc => "Style Recalc",
            ProfilerCategory::LayoutRestyleDamagePropagation => "Restyle Damage Propagation",
            ProfilerCategory::LayoutNonIncrementalReset => "Non-incremental reset (temporary)",
            ProfilerCategory::LayoutSelectorMatch => "Selector Matching",
            ProfilerCategory::LayoutTreeBuilder => "Tree Building",
            ProfilerCategory::LayoutDamagePropagate => "Damage Propagation",
            ProfilerCategory::LayoutGeneratedContent => "Generated Content Resolution",
            ProfilerCategory::LayoutMain => "Primary Layout Pass",
            ProfilerCategory::LayoutParallelWarmup => "Parallel Warmup",
            ProfilerCategory::LayoutShaping => "Shaping",
            ProfilerCategory::LayoutDispListBuild => "Display List Construction",
            ProfilerCategory::PaintingPerTile => "Painting Per Tile",
            ProfilerCategory::PaintingPrepBuff => "Buffer Prep",
            ProfilerCategory::Painting => "Painting",
            ProfilerCategory::ImageDecoding => "Image Decoding",
        };
        format!("{}{}", padding, name)
    }
}

type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option<TimerMetadata>), Vec<f64>>;

// back end of the profiler that handles data aggregation and performance metrics
pub struct Profiler {
    pub port: IpcReceiver<ProfilerMsg>,
    buckets: ProfilerBuckets,
    pub last_msg: Option<ProfilerMsg>,
}

impl Profiler {
    pub fn create(period: Option<f64>) -> ProfilerChan {
        let (chan, port) = ipc::channel().unwrap();
        match period {
            Some(period) => {
                let period = (period * 1000.) as u32;
                let chan = chan.clone();
                spawn_named("Time profiler timer".to_owned(), move || {
                    loop {
                        sleep_ms(period);
                        if chan.send(ProfilerMsg::Print).is_err() {
                            break;
                        }
                    }
                });
                // Spawn the time profiler.
                spawn_named("Time profiler".to_owned(), move || {
                    let mut profiler = Profiler::new(port);
                    profiler.start();
                });
            }
            None => {
                // No-op to handle messages when the time profiler is inactive.
                spawn_named("Time profiler".to_owned(), move || {
                    loop {
                        match port.recv() {
                            Err(_) | Ok(ProfilerMsg::Exit) => break,
                            _ => {}
                        }
                    }
                });
            }
        }
        heartbeats::init();

        ProfilerChan(chan)
    }

    pub fn new(port: IpcReceiver<ProfilerMsg>) -> Profiler {
        Profiler {
            port: port,
            buckets: BTreeMap::new(),
            last_msg: None,
        }
    }

    pub fn start(&mut self) {
        loop {
            let msg = self.port.recv();
            match msg {
               Ok(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               _ => break
            }
        }
    }

    fn find_or_insert(&mut self, k: (ProfilerCategory, Option<TimerMetadata>), t: f64) {
        match self.buckets.get_mut(&k) {
            None => {},
            Some(v) => { v.push(t); return; },
        }

        self.buckets.insert(k, vec!(t));
    }

    fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
        match msg.clone() {
            ProfilerMsg::Time(k, t) => {
                heartbeats::maybe_heartbeat(&k.0, t.0, t.1, 0, 0);
                let ms = (t.1 - t.0) as f64 / 1000000f64;
                self.find_or_insert(k, ms);
            },
            ProfilerMsg::Print => match self.last_msg {
                // only print if more data has arrived since the last printout
                Some(ProfilerMsg::Time(..)) => self.print_buckets(),
                _ => ()
            },
            ProfilerMsg::Exit => {
                heartbeats::cleanup();
                return false;
            },
        };
        self.last_msg = Some(msg);
        true
    }

    fn print_buckets(&mut self) {
        println!("{:35} {:14} {:9} {:30} {:15} {:15} {:-15} {:-15} {:-15}",
                 "_category_", "_incremental?_", "_iframe?_",
                 "            _url_", "    _mean (ms)_", "  _median (ms)_",
                 "     _min (ms)_", "     _max (ms)_", "      _events_");
        for (&(ref category, ref meta), ref mut data) in &mut self.buckets {
            data.sort_by(|a, b| {
                if a < b {
                    Ordering::Less
                } else {
                    Ordering::Greater
                }
            });
            let data_len = data.len();
            if data_len > 0 {
                let (mean, median, min, max) =
                    (data.iter().map(|&x|x).sum::<f64>() / (data_len as f64),
                     data[data_len / 2],
                     data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
                     data.iter().fold(-f64::INFINITY, |a, &b| a.max(b)));
                println!("{:-35}{} {:15.4} {:15.4} {:15.4} {:15.4} {:15}",
                         category.format(), meta.format(), mean, median, min, max, data_len);
            }
        }
        println!("");
    }
}

pub fn time<T, F>(msg: &str, callback: F) -> T
    where F: Fn() -> T
{
    let start_time = precise_time_ns();
    let val = callback();
    let end_time = precise_time_ns();
    let ms = (end_time - start_time) as f64 / 1000000f64;
    if ms >= 5f64 {
        debug!("{} took {} ms", msg, ms);
    }
    return val;
}
<commit_msg>Auto merge of #7504 - ddrmanxbxfr:RemoveUnusedTimeFunction, r=jdm<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! Timing functions.

use heartbeats;
use ipc_channel::ipc::{self, IpcReceiver};
use profile_traits::time::{ProfilerCategory, ProfilerChan, ProfilerMsg, TimerMetadata};
use std::borrow::ToOwned;
use std::cmp::Ordering;
use std::collections::BTreeMap;
use std::f64;
use std::thread::sleep_ms;
use std_time::precise_time_ns;
use util::task::spawn_named;

pub trait Formattable {
    fn format(&self) -> String;
}

impl Formattable for Option<TimerMetadata> {
    fn format(&self) -> String {
        match self {
            // TODO(cgaebel): Center-align in the format strings as soon as rustc supports it.
            &Some(ref meta) => {
                let url = &*meta.url;
                let url = if url.len() > 30 {
                    &url[..30]
                } else {
                    url
                };
                let incremental = if meta.incremental { "    yes" } else { "    no " };
                let iframe = if meta.iframe { "  yes" } else { "  no " };
                format!(" {:14} {:9} {:30}", incremental, iframe, url)
            },
            &None =>
                format!(" {:14} {:9} {:30}", "    N/A", "  N/A", "             N/A")
        }
    }
}

impl Formattable for ProfilerCategory {
    // some categories are subcategories of LayoutPerformCategory
    // and should be printed to indicate this
    fn format(&self) -> String {
        let padding = match *self {
            ProfilerCategory::LayoutStyleRecalc |
            ProfilerCategory::LayoutRestyleDamagePropagation |
            ProfilerCategory::LayoutNonIncrementalReset |
            ProfilerCategory::LayoutGeneratedContent |
            ProfilerCategory::LayoutMain |
            ProfilerCategory::LayoutDispListBuild |
            ProfilerCategory::LayoutShaping |
            ProfilerCategory::LayoutDamagePropagate |
            ProfilerCategory::PaintingPerTile |
            ProfilerCategory::PaintingPrepBuff => "+ ",
            ProfilerCategory::LayoutParallelWarmup |
            ProfilerCategory::LayoutSelectorMatch |
            ProfilerCategory::LayoutTreeBuilder => "| + ",
            _ => ""
        };
        let name = match *self {
            ProfilerCategory::Compositing => "Compositing",
            ProfilerCategory::LayoutPerform => "Layout",
            ProfilerCategory::LayoutStyleRecalc => "Style Recalc",
            ProfilerCategory::LayoutRestyleDamagePropagation => "Restyle Damage Propagation",
            ProfilerCategory::LayoutNonIncrementalReset => "Non-incremental reset (temporary)",
            ProfilerCategory::LayoutSelectorMatch => "Selector Matching",
            ProfilerCategory::LayoutTreeBuilder => "Tree Building",
            ProfilerCategory::LayoutDamagePropagate => "Damage Propagation",
            ProfilerCategory::LayoutGeneratedContent => "Generated Content Resolution",
            ProfilerCategory::LayoutMain => "Primary Layout Pass",
            ProfilerCategory::LayoutParallelWarmup => "Parallel Warmup",
            ProfilerCategory::LayoutShaping => "Shaping",
            ProfilerCategory::LayoutDispListBuild => "Display List Construction",
            ProfilerCategory::PaintingPerTile => "Painting Per Tile",
            ProfilerCategory::PaintingPrepBuff => "Buffer Prep",
            ProfilerCategory::Painting => "Painting",
            ProfilerCategory::ImageDecoding => "Image Decoding",
        };
        format!("{}{}", padding, name)
    }
}

type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option<TimerMetadata>), Vec<f64>>;

// back end of the profiler that handles data aggregation and performance metrics
pub struct Profiler {
    pub port: IpcReceiver<ProfilerMsg>,
    buckets: ProfilerBuckets,
    pub last_msg: Option<ProfilerMsg>,
}

impl Profiler {
    pub fn create(period: Option<f64>) -> ProfilerChan {
        let (chan, port) = ipc::channel().unwrap();
        match period {
            Some(period) => {
                let period = (period * 1000.) as u32;
                let chan = chan.clone();
                spawn_named("Time profiler timer".to_owned(), move || {
                    loop {
                        sleep_ms(period);
                        if chan.send(ProfilerMsg::Print).is_err() {
                            break;
                        }
                    }
                });
                // Spawn the time profiler.
                spawn_named("Time profiler".to_owned(), move || {
                    let mut profiler = Profiler::new(port);
                    profiler.start();
                });
            }
            None => {
                // No-op to handle messages when the time profiler is inactive.
                spawn_named("Time profiler".to_owned(), move || {
                    loop {
                        match port.recv() {
                            Err(_) | Ok(ProfilerMsg::Exit) => break,
                            _ => {}
                        }
                    }
                });
            }
        }
        heartbeats::init();

        ProfilerChan(chan)
    }

    pub fn new(port: IpcReceiver<ProfilerMsg>) -> Profiler {
        Profiler {
            port: port,
            buckets: BTreeMap::new(),
            last_msg: None,
        }
    }

    pub fn start(&mut self) {
        loop {
            let msg = self.port.recv();
            match msg {
               Ok(msg) => {
                   if !self.handle_msg(msg) {
                       break
                   }
               }
               _ => break
            }
        }
    }

    fn find_or_insert(&mut self, k: (ProfilerCategory, Option<TimerMetadata>), t: f64) {
        match self.buckets.get_mut(&k) {
            None => {},
            Some(v) => { v.push(t); return; },
        }

        self.buckets.insert(k, vec!(t));
    }

    fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
        match msg.clone() {
            ProfilerMsg::Time(k, t) => {
                heartbeats::maybe_heartbeat(&k.0, t.0, t.1, 0, 0);
                let ms = (t.1 - t.0) as f64 / 1000000f64;
                self.find_or_insert(k, ms);
            },
            ProfilerMsg::Print => match self.last_msg {
                // only print if more data has arrived since the last printout
                Some(ProfilerMsg::Time(..)) => self.print_buckets(),
                _ => ()
            },
            ProfilerMsg::Exit => {
                heartbeats::cleanup();
                return false;
            },
        };
        self.last_msg = Some(msg);
        true
    }

    fn print_buckets(&mut self) {
        println!("{:35} {:14} {:9} {:30} {:15} {:15} {:-15} {:-15} {:-15}",
                 "_category_", "_incremental?_", "_iframe?_",
                 "            _url_", "    _mean (ms)_", "  _median (ms)_",
                 "     _min (ms)_", "     _max (ms)_", "      _events_");
        for (&(ref category, ref meta), ref mut data) in &mut self.buckets {
            data.sort_by(|a, b| {
                if a < b {
                    Ordering::Less
                } else {
                    Ordering::Greater
                }
            });
            let data_len = data.len();
            if data_len > 0 {
                let (mean, median, min, max) =
                    (data.iter().map(|&x|x).sum::<f64>() / (data_len as f64),
                     data[data_len / 2],
                     data.iter().fold(f64::INFINITY, |a, &b| a.min(b)),
                     data.iter().fold(-f64::INFINITY, |a, &b| a.max(b)));
                println!("{:-35}{} {:15.4} {:15.4} {:15.4} {:15.4} {:15}",
                         category.format(), meta.format(), mean, median, min, max, data_len);
            }
        }
        println!("");
    }
}
<|endoftext|>