<commit_before>use std::io::{BufferedReader, File, IoResult};

// http://www.midi.org/techspecs/midimessages.php
// http://www.ccarh.org/courses/253/handout/smf/
// http://www.ccarh.org/courses/253-2008/files/midifiles-20080227-2up.pdf

#[repr(u8)]
#[deriving(FromPrimitive, PartialEq, Copy, Show)]
pub enum MidiEventType {
    NoteOff = 0x8,
    NoteOn = 0x9,
    PolyponicKeyPressure = 0xa,
    ControlChange = 0xb,
    ProgramChange = 0xc,
    ChannelPressure = 0xd,
    PitchBendChange = 0xe,
    System = 0xf
}

#[repr(u8)]
#[deriving(FromPrimitive, PartialEq, Copy, Show)]
pub enum MidiSystemEventType {
    SequenceNumber = 0x00,
    TextEvent = 0x01,
    CopyrightNotice = 0x02,
    SequenceOrTrackName = 0x03,
    InstrumentName = 0x04,
    LyricText = 0x05,
    MarkerText = 0x06,
    CuePoint = 0x07,
    MidiChannelPrefixAssignment = 0x20,
    EndOfTrack = 0x2f,
    TempoSetting = 0x51,
    SmpteOffset = 0x54,
    TimeSignature = 0x58,
    SequencerSpecificEvent = 0x7f
}

#[deriving(Copy, Show)]
pub struct MidiEvent {
    pub message_type: MidiEventType,
    pub time: uint,
    pub channel: u8,
    pub value1: u8,
    pub value2: Option<u8>
}

#[deriving(Show)]
pub struct MidiTrack {
    pub messages: Vec<MidiEvent>
}

impl MidiTrack {
    fn new() -> MidiTrack {
        let messages: Vec<MidiEvent> = Vec::new();
        MidiTrack {
            messages: messages
        }
    }
}

#[deriving(Show)]
pub struct MidiSong {
    pub max_time: uint,
    pub time_unit: uint,
    pub tracks: Vec<MidiTrack>,
    pub track_count: uint
}

pub fn read_midi(filename: &str) -> IoResult<MidiSong> {
    let path = Path::new(filename);
    let mut file = BufferedReader::new(File::open(&path));

    // Header
    let _chunk_name = try!(file.read_be_u32()); // MThd
    let _chunk_size = try!(file.read_be_u32());
    let _file_format = try!(file.read_be_u16());
    let track_count = try!(file.read_be_u16());
    let time_division = try!(file.read_be_u16());

    if time_division & 0x8000 == 0 {
        // ticks per beat MIDI
    } else {
        // return Err("frames per second not supported");
        panic!("frames per second MIDI files are not supported")
    }

    let tracks: Vec<MidiTrack> = Vec::new();
    let mut song = MidiSong {
        max_time: 0,
        time_unit: time_division as uint,
        tracks: tracks,
        track_count: track_count as uint
    };

    // Track chunk
    let _track_chunk_name = try!(file.read_be_u32()); // MTrk
    let _track_chunk_size = try!(file.read_be_u32());

    // Track data
    let mut track = MidiTrack::new();
    let mut track_time = 0u;

    let mut keep_reading = true;

    while keep_reading {
        let delta_time = read_variable_number(&mut file).unwrap();
        track_time += delta_time;
        if track_time > song.max_time { song.max_time = track_time };

        let next_byte = try!(file.read_byte());
        let message_type: MidiEventType = FromPrimitive::from_u8(next_byte >> 4).unwrap();
        let channel = next_byte & 0b00001111;

        // TODO: use an enum for this
        match message_type {
            MidiEventType::NoteOff
            | MidiEventType::NoteOn
            | MidiEventType::PolyponicKeyPressure
            | MidiEventType::ControlChange
            | MidiEventType::PitchBendChange => {
                track.messages.push(MidiEvent {
                    message_type: message_type,
                    time: track_time,
                    channel: channel,
                    value1: try!(file.read_byte()),
                    value2: Some(try!(file.read_byte()))
                });
            },
            MidiEventType::ProgramChange
            | MidiEventType::ChannelPressure => {
                track.messages.push(MidiEvent {
                    message_type: message_type,
                    time: track_time,
                    channel: channel,
                    value1: try!(file.read_byte()),
                    value2: None
                });
            },
            MidiEventType::System => {
                let system_message_type = FromPrimitive::from_u8(try!(file.read_byte()));
                let system_data_size = try!(read_variable_number(&mut file));

                match system_message_type {
                    Some(MidiSystemEventType::EndOfTrack) => {
                        song.tracks.push(track);
                        track = MidiTrack::new();

                        let next_track_chunk_name = file.read_be_u32();
                        if next_track_chunk_name.is_ok() {
                            let _next_track_chunk_size = try!(file.read_be_u32());
                            track_time = 0u;
                        } else {
                            keep_reading = false;
                        }
                    },
                    _ => {
                        // Discard unhandled system messages
                        try!(file.read_exact(system_data_size as uint));
                    }
                }
            }
        }
    }

    Ok(song)
}

fn read_variable_number<T>(reader: &mut T) -> IoResult<uint> where T: Reader {
    // http://en.wikipedia.org/wiki/Variable-length_quantity
    // cont. bit---V
    //             7[6 5 4 3 2 1 0]+-+
    // more bytes: 1 b b b b b b b   | concat bits to form new number
    //                               V
    //                             7[6 5 4 3 2 1 0]
    //              no more bytes: 0 b b b b b b b

    let mut octet = try!(reader.read_byte());
    let mut value = (octet & 0b01111111) as uint;
    while octet >= 0b10000000 {
        octet = try!(reader.read_byte());
        value = (value << 7) as uint + (octet & 0b01111111) as uint;
    }

    Ok(value)
}

#[test]
fn it_parses_a_midi_file() {
    let song = read_midi("tests/assets/test.mid").ok().expect("Failed");

    assert_eq!(song.tracks.len(), 2); // metadata track included
    let ref messages = song.tracks[1].messages;

    // ProgramChange
    assert_eq!(messages[0].message_type, MidiEventType::ProgramChange);
    assert_eq!(messages[0].time, 0);
    assert_eq!(messages[0].channel, 0);
    assert_eq!(messages[0].value1, 0);
    assert_eq!(messages[0].value2, None);

    // NoteOn
    assert_eq!(messages[1].message_type, MidiEventType::NoteOn);
    assert_eq!(messages[1].time, 0);
    assert_eq!(messages[1].channel, 0);
    assert_eq!(messages[1].value1, 57);
    assert_eq!(messages[1].value2, Some(64));

    // NoteOff
    assert_eq!(messages[2].message_type, MidiEventType::NoteOff);
    assert_eq!(messages[2].time, 960);
    assert_eq!(messages[2].channel, 0);
    assert_eq!(messages[2].value1, 57);
    assert_eq!(messages[2].value2, Some(0));
}

#[test]
fn it_parses_a_multitrack_midi_file() {
    let song = read_midi("tests/assets/multitrack.mid").unwrap();
    assert_eq!(song.tracks.len(), 3); // metadata track included
}
<commit_msg>Handle MIDI system exclusive messages<commit_after>use std::io::{BufferedReader, File, IoResult};

// http://www.midi.org/techspecs/midimessages.php
// http://www.ccarh.org/courses/253/handout/smf/
// http://www.ccarh.org/courses/253-2008/files/midifiles-20080227-2up.pdf

#[repr(u8)]
#[deriving(FromPrimitive, PartialEq, Copy, Show)]
pub enum MidiEventType {
    NoteOff = 0x8,
    NoteOn = 0x9,
    PolyponicKeyPressure = 0xa,
    ControlChange = 0xb,
    ProgramChange = 0xc,
    ChannelPressure = 0xd,
    PitchBendChange = 0xe,
    System = 0xf
}

#[repr(u8)]
#[deriving(FromPrimitive, PartialEq, Copy, Show)]
pub enum MidiSystemEventType {
    SystemExclusive = 0x0,
    TimeCodeQuaterFrame = 0x1,
    SongPositionPointer = 0x2,
    SongSelect = 0x3,
    TuneRequest = 0x6,
    EndOfSystemExclusive = 0x7,
    TimingClock = 0x8,
    Start = 0xa,
    Continue = 0xb,
    Stop = 0xc,
    ActiveSensing = 0xe,
    SystemResetOrMeta = 0xf
}

#[repr(u8)]
#[deriving(FromPrimitive, PartialEq, Copy, Show)]
pub enum MidiMetaEventType {
    SequenceNumber = 0x00,
    TextEvent = 0x01,
    CopyrightNotice = 0x02,
    SequenceOrTrackName = 0x03,
    InstrumentName = 0x04,
    LyricText = 0x05,
    MarkerText = 0x06,
    CuePoint = 0x07,
    MidiChannelPrefixAssignment = 0x20,
    EndOfTrack = 0x2f,
    TempoSetting = 0x51,
    SmpteOffset = 0x54,
    TimeSignature = 0x58,
    SequencerSpecificEvent = 0x7f
}

#[deriving(Copy, Show)]
pub struct MidiEvent {
    pub message_type: MidiEventType,
    pub time: uint,
    pub channel: u8,
    pub value1: u8,
    pub value2: Option<u8>
}

#[deriving(Show)]
pub struct MidiTrack {
    pub messages: Vec<MidiEvent>
}

impl MidiTrack {
    fn new() -> MidiTrack {
        let messages: Vec<MidiEvent> = Vec::new();
        MidiTrack {
            messages: messages
        }
    }
}

#[deriving(Show)]
pub struct MidiSong {
    pub max_time: uint,
    pub time_unit: uint,
    pub tracks: Vec<MidiTrack>,
    pub track_count: uint
}

pub fn read_midi(filename: &str) -> IoResult<MidiSong> {
    let path = Path::new(filename);
    let mut file = BufferedReader::new(File::open(&path));

    // Header
    let _chunk_name = try!(file.read_be_u32()); // MThd
    let _chunk_size = try!(file.read_be_u32());
    let _file_format = try!(file.read_be_u16());
    let track_count = try!(file.read_be_u16());
    let time_division = try!(file.read_be_u16());

    if time_division & 0x8000 == 0 {
        // ticks per beat MIDI
    } else {
        // return Err("frames per second not supported");
        panic!("frames per second MIDI files are not supported")
    }

    let tracks: Vec<MidiTrack> = Vec::new();
    let mut song = MidiSong {
        max_time: 0,
        time_unit: time_division as uint,
        tracks: tracks,
        track_count: track_count as uint
    };

    // Track chunk
    let _track_chunk_name = try!(file.read_be_u32()); // MTrk
    let _track_chunk_size = try!(file.read_be_u32());

    // Track data
    let mut track = MidiTrack::new();
    let mut track_time = 0u;

    let mut keep_reading = true;

    while keep_reading {
        let delta_time = read_variable_number(&mut file).unwrap();
        track_time += delta_time;
        if track_time > song.max_time { song.max_time = track_time };

        let next_byte = try!(file.read_byte());
        let message_type: MidiEventType = FromPrimitive::from_u8(next_byte >> 4).unwrap();
        let channel = next_byte & 0b00001111;

        match message_type {
            MidiEventType::NoteOff
            | MidiEventType::NoteOn
            | MidiEventType::PolyponicKeyPressure
            | MidiEventType::ControlChange
            | MidiEventType::PitchBendChange => {
                track.messages.push(MidiEvent {
                    message_type: message_type,
                    time: track_time,
                    channel: channel,
                    value1: try!(file.read_byte()),
                    value2: Some(try!(file.read_byte()))
                });
            },
            MidiEventType::ProgramChange
            | MidiEventType::ChannelPressure => {
                track.messages.push(MidiEvent {
                    message_type: message_type,
                    time: track_time,
                    channel: channel,
                    value1: try!(file.read_byte()),
                    value2: None
                });
            },
            MidiEventType::System => {
                // Handle Sysex messages
                let system_message_type: MidiSystemEventType = FromPrimitive::from_u8(channel).unwrap();

                match system_message_type {
                    MidiSystemEventType::SystemExclusive => {
                        // Variable data length: read until EndOfSystemExclusive byte
                        let mut next_byte = try!(file.read_byte()) & 0b00001111;
                        let mut system_message_type: Option<MidiSystemEventType> = FromPrimitive::from_u8(next_byte);
                        while system_message_type != Some(MidiSystemEventType::EndOfSystemExclusive) {
                            next_byte = try!(file.read_byte()) & 0b00001111;
                            system_message_type = FromPrimitive::from_u8(next_byte);
                        }
                    },

                    MidiSystemEventType::TuneRequest
                    | MidiSystemEventType::TimingClock
                    | MidiSystemEventType::TimeCodeQuaterFrame
                    | MidiSystemEventType::Start
                    | MidiSystemEventType::Continue
                    | MidiSystemEventType::Stop
                    | MidiSystemEventType::ActiveSensing => {
                        // Unhandled, these have no data bytes
                        try!(file.read_byte());
                    },

                    MidiSystemEventType::SongPositionPointer
                    | MidiSystemEventType::SongSelect => {
                        // Unhandled, these have two data bytes
                        try!(file.read_byte());
                        try!(file.read_byte());
                    },

                    MidiSystemEventType::EndOfSystemExclusive => {
                        // All EndOfSystemExclusive messages should be captured by SystemExclusive message handling
                        panic!("unexpected EndOfSystemExclusive MIDI message: bad or corrupt file?")
                    },

                    MidiSystemEventType::SystemResetOrMeta => {
                        // Typically these are meta messages
                        let meta_message_type: Option<MidiMetaEventType> = FromPrimitive::from_u8(try!(file.read_byte()));
                        let meta_data_size = try!(read_variable_number(&mut file));

                        match meta_message_type {
                            Some(MidiMetaEventType::EndOfTrack) => {
                                song.tracks.push(track);
                                track = MidiTrack::new();
                                let next_track_chunk_name = file.read_be_u32();
                                if next_track_chunk_name.is_ok() {
                                    let _next_track_chunk_size = try!(file.read_be_u32());
                                    track_time = 0u;
                                } else {
                                    keep_reading = false;
                                }
                            },
                            _ => {
                                // Discard unhandled meta messages
                                try!(file.read_exact(meta_data_size as uint));
                            }
                        }
                    }
                }
            }
        }
    }

    Ok(song)
}

fn read_variable_number<T>(reader: &mut T) -> IoResult<uint> where T: Reader {
    // http://en.wikipedia.org/wiki/Variable-length_quantity
    // cont. bit---V
    //             7[6 5 4 3 2 1 0]+-+
    // more bytes: 1 b b b b b b b   | concat bits to form new number
    //                               V
    //                             7[6 5 4 3 2 1 0]
    //              no more bytes: 0 b b b b b b b

    let mut octet = try!(reader.read_byte());
    let mut value = (octet & 0b01111111) as uint;
    while octet >= 0b10000000 {
        octet = try!(reader.read_byte());
        value = (value << 7) as uint + (octet & 0b01111111) as uint;
    }

    Ok(value)
}

#[test]
fn it_parses_a_midi_file() {
    let song = read_midi("tests/assets/test.mid").ok().expect("Failed");

    assert_eq!(song.tracks.len(), 2); // metadata track included
    let ref messages = song.tracks[1].messages;

    // ProgramChange
    assert_eq!(messages[0].message_type, MidiEventType::ProgramChange);
    assert_eq!(messages[0].time, 0);
    assert_eq!(messages[0].channel, 0);
    assert_eq!(messages[0].value1, 0);
    assert_eq!(messages[0].value2, None);

    // NoteOn
    assert_eq!(messages[1].message_type, MidiEventType::NoteOn);
    assert_eq!(messages[1].time, 0);
    assert_eq!(messages[1].channel, 0);
    assert_eq!(messages[1].value1, 57);
    assert_eq!(messages[1].value2, Some(64));

    // NoteOff
    assert_eq!(messages[2].message_type, MidiEventType::NoteOff);
    assert_eq!(messages[2].time, 960);
    assert_eq!(messages[2].channel, 0);
    assert_eq!(messages[2].value1, 57);
    assert_eq!(messages[2].value2, Some(0));
}

#[test]
fn it_parses_a_multitrack_midi_file() {
    let song = read_midi("tests/assets/multitrack.mid").unwrap();
    assert_eq!(song.tracks.len(), 3); // metadata track included
}
<|endoftext|>


<commit_before>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align", "width"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }
}
<commit_msg>Remove duplicate width<commit_after>use ammonia::Ammonia;
use comrak;

use util::CargoResult;

/// Context for markdown to HTML rendering.
#[allow(missing_debug_implementations)]
pub struct MarkdownRenderer<'a> {
    html_sanitizer: Ammonia<'a>,
}

impl<'a> MarkdownRenderer<'a> {
    /// Creates a new renderer instance.
    pub fn new() -> MarkdownRenderer<'a> {
        let tags = [
            "a",
            "b",
            "blockquote",
            "br",
            "code",
            "dd",
            "del",
            "dl",
            "dt",
            "em",
            "h1",
            "h2",
            "h3",
            "hr",
            "i",
            "img",
            "input",
            "kbd",
            "li",
            "ol",
            "p",
            "pre",
            "s",
            "strike",
            "strong",
            "sub",
            "sup",
            "table",
            "tbody",
            "td",
            "th",
            "thead",
            "tr",
            "ul",
            "hr",
            "span",
        ].iter()
            .cloned()
            .collect();
        let tag_attributes = [
            ("a", ["href", "target"].iter().cloned().collect()),
            (
                "img",
                ["width", "height", "src", "alt", "align"]
                    .iter()
                    .cloned()
                    .collect(),
            ),
            (
                "input",
                ["checked", "disabled", "type"].iter().cloned().collect(),
            ),
        ].iter()
            .cloned()
            .collect();
        let html_sanitizer = Ammonia {
            link_rel: Some("nofollow noopener noreferrer"),
            keep_cleaned_elements: true,
            tags: tags,
            tag_attributes: tag_attributes,
            ..Ammonia::default()
        };
        MarkdownRenderer { html_sanitizer: html_sanitizer }
    }

    /// Renders the given markdown to HTML using the current settings.
    pub fn to_html(&self, text: &str) -> CargoResult<String> {
        let options = comrak::ComrakOptions {
            ext_autolink: true,
            ext_strikethrough: true,
            ext_table: true,
            ext_tagfilter: true,
            ext_tasklist: true,
            ..comrak::ComrakOptions::default()
        };
        let rendered = comrak::markdown_to_html(text, &options);
        Ok(self.html_sanitizer.clean(&rendered))
    }
}

impl<'a> Default for MarkdownRenderer<'a> {
    fn default() -> Self {
        Self::new()
    }
}

/// Renders a markdown text to sanitized HTML.
///
/// The returned text should not contain any harmful HTML tag or attribute (such as iframe,
/// onclick, onmouseover, etc.).
///
/// # Examples
///
/// ```
/// use render::markdown_to_html;
///
/// let text = "[Rust](https://rust-lang.org/) is an awesome *systems programming* language!";
/// let rendered = markdown_to_html(text)?;
/// ```
pub fn markdown_to_html(text: &str) -> CargoResult<String> {
    let renderer = MarkdownRenderer::new();
    renderer.to_html(text)
}

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

    #[test]
    fn empty_text() {
        let text = "";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "");
    }

    #[test]
    fn text_with_script_tag() {
        let text = "foo_readme\n\n";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<script>alert(\'Hello World\')</script>\n"
        );
    }

    #[test]
    fn text_with_iframe_tag() {
        let text = "foo_readme\n\nalert('Hello World')";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme
\n<iframe>alert(\'Hello World\')</iframe>\n"
        );
    }

    #[test]
    fn text_with_unknown_tag() {
        let text = "foo_readme\n\n<unknown>alert('Hello World')</unknown>";
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
foo_readme
\n
alert(\'Hello World\')
\n");
    }

    #[test]
    fn text_with_inline_javascript() {
        let text = r#"foo_readme\n\nCrate page"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(
            result,
            "
foo_readme\\n\\nCrate page
\n"
        );
    }

    // See https://github.com/kivikakk/comrak/issues/37. This panic happened
    // in comrak 0.1.8 but was fixed in 0.1.9.
    #[test]
    fn text_with_fancy_single_quotes() {
        let text = r#"wb’"#;
        let result = markdown_to_html(text).unwrap();
        assert_eq!(result, "
wb’
\n");
    }
}
<|endoftext|>


<commit_before>use std::io:: { Error as IOError };
use std::convert::From;
use std::result:: { Result };
use std::collections:: { HashMap };
use std::default::Default;
use lcov_parser::parser:: { LCOVParser, RecordParseError };
use lcov_parser::record:: { LCOVRecord };
use branch:: { Branch, BranchUnit };
use test:: { Test };
use test_sum:: { TestSum };

pub enum ParseError {
    IOError(IOError),
    RecordParseError(RecordParseError)
}

impl From<IOError> for ParseError {
    fn from(error: IOError) -> Self {
        ParseError::IOError(error)
    }
}

impl From<RecordParseError> for ParseError {
    fn from(error: RecordParseError) -> Self {
        ParseError::RecordParseError(error)
    }
}

pub fn records_from_file(file: &str) -> Result<Vec<LCOVRecord>, ParseError> {
    let parser = try!(LCOVParser::from_file(file));
    Ok(try!(parser.parse()))
}

// key: line_number, value: checksum value
type CheckSum = HashMap<u32, String>;

// key: function name, value: line_number
type FunctionData = HashMap<String, u32>;

struct ReportParser {
    test_name: Option<String>,
    source_name: Option<String>,
    tests: HashMap<String, Test>,
    sum: TestSum,
    checksum: CheckSum,
    func: FunctionData
}

impl ReportParser {
    fn parse(&mut self, file: &str) -> Result<(), ParseError> {
        let records = try!(records_from_file(file));

        for record in records.iter() {
            match record {
                &LCOVRecord::TestName(ref name) => self.on_test_name(name),
                &LCOVRecord::SourceFile(ref name) => self.on_source_file(name),
                &LCOVRecord::Data(ref line_number, ref execution_count, ref checksum) => self.on_data(
                    line_number,
                    execution_count,
                    checksum,
                ),
                &LCOVRecord::FunctionName(ref line_number, ref func_name, ) => self.on_func_name(
                    func_name,
                    line_number
                ),
                &LCOVRecord::FunctionData(ref execution_count, ref func_name) => self.on_func_data(
                    func_name,
                    execution_count
                ),
                _ => { continue; }
            };
        }
        Ok(())
    }
    fn on_test_name(&mut self, test_name: &Option<String>) {
        self.test_name = test_name.clone().or( Some(String::new()) );
    }
    fn on_source_file(&mut self, source_name: &String) {
        self.source_name = Some(source_name.clone());

        let current_test_name = self.test_name.as_ref().unwrap();

        if !self.tests.contains_key(current_test_name) {
            self.tests.insert(current_test_name.to_string(), Test::default());
        }
    }
    fn on_data(&mut self, line_number: &u32, execution_count: &u32, checksum: &Option<String>) {
        self.sum.add_line_count(line_number, execution_count);

        if self.test_name.is_some() {
            let test_name = self.test_name.clone().unwrap();
            let mut test = self.tests.get_mut(&test_name).unwrap();
            test.add_line_count(line_number, execution_count);
        }

        if checksum.is_none() {
            return;
        }

        if !self.checksum.contains_key(line_number) {
            let checksum_value = checksum.clone().unwrap();
            self.checksum.insert(line_number.clone(), checksum_value);
            return;
        }

        let checksum_value = checksum.clone().unwrap();
        let current_checksum = self.checksum.get(line_number).unwrap();
        if current_checksum != &checksum_value {
            println!("{} {}", current_checksum, checksum_value);
        }
    }
    fn on_func_name(&mut self, func_name: &String, line_number: &u32) {
        let _ = self.func.entry(func_name.clone())
            .or_insert(line_number.clone());
    }
    fn on_func_data(&mut self, func_name: &String, execution_count: &u32) {
        self.sum.add_func_count(func_name, execution_count);

        if self.test_name.is_none() {
            return;
        }

        let test_name = self.test_name.clone().unwrap();
        let mut test = self.tests.get_mut(&test_name).unwrap();

        test.add_func_count(func_name, execution_count);
    }
    fn on_branch_data(line_number: &u32, block_number: &u32, branch_number: &u32, taken: &u32) {
/*
        if self.sum.sum_br_count.contains_key(line_number) {
            let mut branch = self.sum.sum_br_count.get_mut(line_number).unwrap();

            if (branch.block == block_number && branch.branch == branch_number) {
            }
        } else {
            self.sum.sum_br_count.insert(line_number.clone(), Branch {
                block: block_number.clone(),
                branch: branch_number.cline(),
                taken: taken.cline()
            }
        }
*/
/*
        let mut branch = self.sum.sum_br_count.entry(line_number.clone())
            .or_insert(Branch {
                block: block_number.clone(),
                branch: branch_number.cline(),
                taken: taken.cline()
            });
*/

//BranchData(u32, u32, u32, u32),   // BRDA:<line number>,<block number>,<branch number>,<taken>

/*
        my ($line, $block, $branch, $taken) =
           ($1, $2, $3, $4);

        last if (!$br_coverage);
        $sumbrcount->{$line} =
            br_ivec_push($sumbrcount->{$line},
                     $block, $branch, $taken);

        # Add test-specific counts
        if (defined($testname)) {
            $testbrcount->{$line} =
                br_ivec_push(
                    $testbrcount->{$line},
                    $block, $branch,
                    $taken);
        }
        last;
*/

    }
}

/*
sub br_ivec_push($$$$)
{
    my ($vec, $block, $branch, $taken) = @_;
	my $offset;
	my $num = br_ivec_len($vec);
	my $i;

	$vec = "" if (!defined($vec));
	$block = $BR_VEC_MAX if $block < 0;

	# Check if branch already exists in vector
	for ($i = 0; $i < $num; $i++) {
		my ($v_block, $v_branch, $v_taken) = br_ivec_get($vec, $i);
		$v_block = $BR_VEC_MAX if $v_block < 0;

		next if ($v_block != $block || $v_branch != $branch);

		# Add taken counts
		$taken = br_taken_add($taken, $v_taken);
		last;
	}

	$offset = $i * $BR_VEC_ENTRIES;
	$taken = br_taken_to_num($taken);

	# Add to vector
	vec($vec, $offset + $BR_BLOCK, $BR_VEC_WIDTH) = $block;
	vec($vec, $offset + $BR_BRANCH, $BR_VEC_WIDTH) = $branch;
	vec($vec, $offset + $BR_TAKEN, $BR_VEC_WIDTH) = $taken;

	return $vec;
}
*/
<commit_msg>add branch marge code<commit_after>use std::io:: { Error as IOError };
use std::convert::From;
use std::result:: { Result };
use std::collections:: { HashMap };
use std::default::Default;
use lcov_parser::parser:: { LCOVParser, RecordParseError };
use lcov_parser::record:: { LCOVRecord };
use branch:: { Branch, BranchUnit };
use test:: { Test };
use test_sum:: { TestSum };

pub enum ParseError {
    IOError(IOError),
    RecordParseError(RecordParseError)
}

impl From<IOError> for ParseError {
    fn from(error: IOError) -> Self {
        ParseError::IOError(error)
    }
}

impl From<RecordParseError> for ParseError {
    fn from(error: RecordParseError) -> Self {
        ParseError::RecordParseError(error)
    }
}

pub fn records_from_file(file: &str) -> Result<Vec<LCOVRecord>, ParseError> {
    let parser = try!(LCOVParser::from_file(file));
    Ok(try!(parser.parse()))
}

// key: line_number, value: checksum value
type CheckSum = HashMap<u32, String>;

// key: function name, value: line_number
type FunctionData = HashMap<String, u32>;

struct ReportParser {
    test_name: Option<String>,
    source_name: Option<String>,
    tests: HashMap<String, Test>,
    sum: TestSum,
    checksum: CheckSum,
    func: FunctionData
}

impl ReportParser {
    fn parse(&mut self, file: &str) -> Result<(), ParseError> {
        let records = try!(records_from_file(file));

        for record in records.iter() {
            match record {
                &LCOVRecord::TestName(ref name) => self.on_test_name(name),
                &LCOVRecord::SourceFile(ref name) => self.on_source_file(name),
                &LCOVRecord::Data(ref line_number, ref execution_count, ref checksum) => self.on_data(
                    line_number,
                    execution_count,
                    checksum,
                ),
                &LCOVRecord::FunctionName(ref line_number, ref func_name) => self.on_func_name(
                    func_name,
                    line_number
                ),
                &LCOVRecord::FunctionData(ref execution_count, ref func_name) => self.on_func_data(
                    func_name,
                    execution_count
                ),
                &LCOVRecord::BranchData(ref line_number, ref block_number, ref branch_number, ref taken) => self.on_branch_data(
                    line_number,
                    block_number,
                    branch_number,
                    taken
                ),
                _ => { continue; }
            };
        }
        Ok(())
    }
    fn on_test_name(&mut self, test_name: &Option<String>) {
        self.test_name = test_name.clone().or( Some(String::new()) );
    }
    fn on_source_file(&mut self, source_name: &String) {
        self.source_name = Some(source_name.clone());

        let current_test_name = self.test_name.as_ref().unwrap();

        if !self.tests.contains_key(current_test_name) {
            self.tests.insert(current_test_name.to_string(), Test::default());
        }
    }
    fn on_data(&mut self, line_number: &u32, execution_count: &u32, checksum: &Option<String>) {
        self.sum.add_line_count(line_number, execution_count);

        if self.test_name.is_some() {
            let test_name = self.test_name.clone().unwrap();
            let mut test = self.tests.get_mut(&test_name).unwrap();
            test.add_line_count(line_number, execution_count);
        }

        if checksum.is_none() {
            return;
        }

        if !self.checksum.contains_key(line_number) {
            let checksum_value = checksum.clone().unwrap();
            self.checksum.insert(line_number.clone(), checksum_value);
            return;
        }

        let checksum_value = checksum.clone().unwrap();
        let current_checksum = self.checksum.get(line_number).unwrap();
        if current_checksum != &checksum_value {
            println!("{} {}", current_checksum, checksum_value);
        }
    }
    fn on_func_name(&mut self, func_name: &String, line_number: &u32) {
        let _ = self.func.entry(func_name.clone())
            .or_insert(line_number.clone());
    }
    fn on_func_data(&mut self, func_name: &String, execution_count: &u32) {
        self.sum.add_func_count(func_name, execution_count);

        if self.test_name.is_none() {
            return;
        }

        let test_name = self.test_name.clone().unwrap();
        let mut test = self.tests.get_mut(&test_name).unwrap();

        test.add_func_count(func_name, execution_count);
    }
    fn on_branch_data(&mut self, line_number: &u32, block_number: &u32, branch_number: &u32, taken: &u32) {
        let ref branch_unit = BranchUnit::new(block_number.clone(), branch_number.clone());

        self.sum.add_branch_count(
            line_number,
            branch_unit,
            taken
        );

        if self.test_name.is_some() {
            let ref test_name = self.test_name.clone().unwrap();
            let mut test = self.tests.get_mut(test_name).unwrap();
            test.add_branch_count(
                line_number,
                branch_unit,
                taken
            );
        }
    }
}
<|endoftext|>


<commit_before>use clock::{GameTime, GameClock};
use framerate::counter::FrameCount;

#[derive(Debug)]
pub struct FrameRunner {
    clock: GameClock,
    counter: Box<FrameCount>,
}

impl FrameRunner {
    pub fn new(clock: GameClock, counter: Box<FrameCount>) -> FrameRunner {
        FrameRunner {
            clock: clock,
            counter: counter,
        }
    }

    pub fn clock(&self) -> &GameClock {
        &self.clock
    }
    pub fn clock_mut(&mut self) -> &mut GameClock {
        &mut self.clock
    }
    pub fn counter(&self) -> &FrameCount {
        self.counter.as_ref()
    }
    pub fn counter_mut(&mut self) -> &mut FrameCount {
        self.counter.as_mut()
    }
    pub fn tick(&mut self) -> GameTime {
        let time = self.clock.tick(self.counter.as_mut());
        time
    }
}
<commit_msg>FrameRunner now takes a type argument instead of using a trait obj.<commit_after>use clock::{GameTime, GameClock};
use framerate::counter::FrameCount;

#[derive(Debug)]
pub struct FrameRunner<C: FrameCount> {
    clock: GameClock,
    counter: C,
}

impl<C> FrameRunner<C>
    where C: FrameCount
{
    pub fn new(clock: GameClock, counter: C) -> FrameRunner<C> {
        FrameRunner {
            clock: clock,
            counter: counter,
        }
    }

    pub fn clock(&self) -> &GameClock {
        &self.clock
    }
    pub fn clock_mut(&mut self) -> &mut GameClock {
        &mut self.clock
    }
    pub fn counter(&self) -> &C {
        &self.counter
    }
    pub fn counter_mut(&mut self) -> &mut C {
        &mut self.counter
    }
    pub fn tick(&mut self) -> GameTime {
        let time = self.clock.tick(&mut self.counter);
        time
    }
}
<|endoftext|>


<commit_before>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::Ordering;
use score::Score;

struct Search {
    config: Configuration,
    current: uint,
    max_index: uint,
    query: String,
    selection: String,
    result: Vec<String>,
}

impl Search {
    fn blank(config: Configuration) -> Search {
        let max_index = config.visible_limit;
        let query = config.initial_search.clone();

        Search::new(config, query, max_index, 0)
    }

    fn new(config: Configuration, query: String, max_index: uint, index: uint) -> Search {
        let mut result = config.choices.clone();
        result = Search::filter(query.as_slice(), result);

        Search { selection: config.choices[index].to_string(),
                 config: config,
                 query: query,
                 max_index: max_index,
                 result: result,
                 current: index }
    }

    fn filter(query: &str, mut choices: Vec<String>) -> Vec<String> {
        choices.sort_by( |a,b| Score::score(a.as_slice(), query).partial_cmp(&Score::score(b.as_slice(), query)).unwrap_or(Ordering::Equal) );
        choices
    }

    fn for_index(self, index: uint) -> Search {
        Search::new(self.config, self.query, self.max_index, index)
    }

    fn down(self) -> Search {
        let next_index = self.next_index();

        self.for_index(next_index)
    }

    fn up(self) -> Search {
        let next_index = self.prev_index();

        self.for_index(next_index)
    }

    fn append_to_search(self, input: String) -> Search {
        let mut new_query = self.query;
        new_query.push_str(input.as_slice());
        Search::new(self.config, new_query, self.max_index, self.current)
    }

    fn next_index(&self) -> uint {
        let mut next_index = self.current + 1;

        if next_index >= self.max_index {
            next_index = 0;
        }

        next_index
    }

    fn prev_index(&self) -> uint {
        if self.current == 0 {
            self.max_index - 1
        } else  {
            self.current - 1
        }
    }

}

#[cfg(test)]

#[test]
fn it_selects_the_first_choice_by_default() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.selection, "one");
}

fn one_two_three() -> Vec<String> {
    vec!["one".to_string(),
         "two".to_string(),
         "three".to_string()]
}

#[test]
fn it_selets_the_second_when_down_is_called() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_end_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_top_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.up().up().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_visible_limit() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, Some(2));
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().selection, "two");
}

//#[test]
fn it_moves_down_the_filtered_search_results() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);
    println!("{}", search.result);
    assert_eq!(search.append_to_search("t".to_string()).down().selection, "three");
}
<commit_msg>Search through choices and update the results field.<commit_after>use configuration::Configuration;
use std::slice::SliceExt;
use std::cmp::Ordering;
use score::Score;

struct Search {
    config: Configuration,
    current: uint,
    max_index: uint,
    query: String,
    selection: String,
    result: Vec<String>,
}

impl Search {
    fn blank(config: Configuration) -> Search {
        let max_index = config.visible_limit;
        let query = config.initial_search.clone();

        Search::new(config, query, max_index, 0)
    }

    fn new(config: Configuration, query: String, max_index: uint, index: uint) -> Search {
        let mut result = config.choices.clone();
        result = Search::filter(query.as_slice(), result);

        Search { selection: result[index].to_string(),
                 config: config,
                 query: query,
                 max_index: max_index,
                 result: result,
                 current: index }
    }

    fn new_for_index(self, index: uint) -> Search {
        Search::new(self.config, self.query, self.max_index, index)
    }

    fn filter(query: &str, choices: Vec<String>) -> Vec<String> {
        let mut filtered = choices.iter().filter_map( |choice| {
            let quality = Score::score(choice.as_slice(), query);
            if quality > 0.0 {
                Some((quality, choice.to_string()))
            } else {
                None
            }
        }).collect::<Vec<(f32, String)>>();

        filtered.sort_by( |&(quality_a, _), &(quality_b, _)| {
            quality_a.partial_cmp(&quality_b).unwrap_or(Ordering::Equal).reverse()
        });

        filtered.iter().map( |&(_, ref choice)| choice.to_string() ).collect::<Vec<String>>()
    }

    fn down(self) -> Search {
        let next_index = self.next_index();
        self.new_for_index(next_index)
    }

    fn up(self) -> Search {
        let next_index = self.prev_index();
        self.new_for_index(next_index)
    }

    fn append_to_search(self, input: String) -> Search {
        let mut new_query = self.query;
        new_query.push_str(input.as_slice());
        Search::new(self.config, new_query, self.max_index, self.current)
    }

    fn next_index(&self) -> uint {
        let next_index = self.current + 1;

        if next_index >= self.max_index { 0 } else { next_index }
    }

    fn prev_index(&self) -> uint {
        if self.current == 0 { self.max_index - 1 } else  { self.current - 1 }
    }
}

#[cfg(test)]

#[test]
fn it_selects_the_first_choice_by_default() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.selection, "one");
}

fn one_two_three() -> Vec<String> {
    vec!["one".to_string(),
         "two".to_string(),
         "three".to_string()]
}

#[test]
fn it_selets_the_second_when_down_is_called() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_end_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().down().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_top_of_list() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);

    assert_eq!(search.up().up().selection, "two");
}

#[test]
fn it_loop_around_when_reaching_visible_limit() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, Some(2));
    let search = Search::blank(config);

    assert_eq!(search.down().down().down().selection, "two");
}

#[test]
fn it_moves_down_the_filtered_search_results() {
    let input =  one_two_three();

    let config = Configuration::from_inputs(input, None, None);
    let search = Search::blank(config);
    assert_eq!(search.append_to_search("t".to_string()).down().selection, "three");
}
<|endoftext|>


<commit_before>use sentry::{ClientInitGuard, ClientOptions, IntoDsn};
use std::borrow::Cow;

#[must_use]
pub fn init() -> Option<ClientInitGuard> {
    dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value")
        .map(|dsn| {
            let mut opts = ClientOptions::from(dsn);
            opts.environment = Some(
                dotenv::var("SENTRY_ENV_API")
                    .map(Cow::Owned)
                    .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API"),
            );

            opts.release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

            sentry::init(opts)
        })
}
<commit_msg>sentry: Add documentation<commit_after>use sentry::{ClientInitGuard, ClientOptions, IntoDsn};
use std::borrow::Cow;

/// Initializes the Sentry SDK from the environment variables.
///
/// If `SENTRY_DSN_API` is not set then Sentry will not be initialized,
/// otherwise it is required to be a valid DSN string. `SENTRY_ENV_API` must
/// be set if a DSN is provided.
///
/// `HEROKU_SLUG_COMMIT`, if present, will be used as the `release` property
/// on all events.
#[must_use]
pub fn init() -> Option<ClientInitGuard> {
    dotenv::var("SENTRY_DSN_API")
        .ok()
        .into_dsn()
        .expect("SENTRY_DSN_API is not a valid Sentry DSN value")
        .map(|dsn| {
            let mut opts = ClientOptions::from(dsn);
            opts.environment = Some(
                dotenv::var("SENTRY_ENV_API")
                    .map(Cow::Owned)
                    .expect("SENTRY_ENV_API must be set when using SENTRY_DSN_API"),
            );

            opts.release = dotenv::var("HEROKU_SLUG_COMMIT").ok().map(Into::into);

            sentry::init(opts)
        })
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::io::{Read, Write};
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::thread;

use message;

pub fn execute(host: String, port: i32) {
    let listener = TcpListener::bind(&format!("{}:{}", host, port)[..]).unwrap();
    let mut channels = Channels { channels: HashMap::new() };

    for stream in listener.incoming() {
        let stream = stream.unwrap();
        let request = message::JoinRequest::receive(&stream);

        match request {
            Ok(request) => {
                match request {
                    message::JoinRequest::Broadcast(ch) =>
                        BroadcastHandler::spawn(&stream, channels.fetch(&ch)),
                    message::JoinRequest::Watch(ch) =>
                        WatchHandler::spawn(&stream, channels.fetch(&ch)),
                }
            }
            Err(e) => println!("{}", e),
        }
    }
}
struct Channels {
    channels: HashMap<String, Arc<Mutex<Channel>>>,
}

struct Channel {
    broadcaster: Option<TcpStream>,
    watchers: Vec<TcpStream>,
}

struct BroadcastHandler {
    stream: TcpStream,
    channel: Arc<Mutex<Channel>>,
}

struct WatchHandler {
    stream: TcpStream,
    channel: Arc<Mutex<Channel>>,
}

impl Channels {
    fn fetch<K: AsRef<str>>(&mut self, key: &K) -> &Arc<Mutex<Channel>> {
        let ch = key.as_ref();

        if self.channels.contains_key(&ch[..]) {
            self.channels.get(&ch[..]).unwrap()
        } else {
            let channel = Channel {
                broadcaster: None,
                watchers: Vec::new(),
            };

            self.channels.insert(ch.to_owned(), Arc::new(Mutex::new(channel)));

            self.fetch(&ch)
        }
    }
}

impl Channel {
    fn takeover(&mut self, stream: TcpStream) {
        match self.broadcaster.as_mut() {
            Some(mut current) => {
                let _ = message::Notification::Closed("Broadcaster has changed".to_owned())
                            .send(&mut current);
                let _ = current.shutdown(Shutdown::Both);
            }
            None => (),
        }

        self.broadcaster = Some(stream);
    }
}

impl BroadcastHandler {
    fn spawn(stream: &TcpStream, channel: &Arc<Mutex<Channel>>) {
        let mut handler = BroadcastHandler {
            stream: stream.try_clone().unwrap(),
            channel: channel.clone(),
        };

        let _ = channel.lock().and_then(|mut ch| {
            ch.takeover(stream.try_clone().unwrap());
            Ok(ch)
        });

        thread::spawn(move || {
            handler.process();
        });
    }

    fn process(&mut self) {
        message::JoinResponse::Success.send(&mut self.stream).unwrap();

        loop {
            let mut buf = vec![0; 128];
            let nread = self.stream.read(&mut buf).unwrap();

            if nread == 0 {
                break;
            }

            buf.truncate(nread);

            for mut watcher in &self.channel.lock().unwrap().watchers {
                let _ = watcher.write(&buf);
            }
        }

        self.stream.shutdown(Shutdown::Both).unwrap();
    }
}

impl WatchHandler {
    fn spawn(stream: &TcpStream, channel: &Arc<Mutex<Channel>>) {
        let mut handler = WatchHandler {
            stream: stream.try_clone().unwrap(),
            channel: channel.clone(),
        };

        thread::spawn(move || {
            handler.process();
        });
    }

    fn process(&mut self) {
        message::JoinResponse::Success.send(&mut self.stream).unwrap();

        match self.channel.lock() {
            Ok(mut channel) => {
                channel.watchers.push(self.stream.try_clone().unwrap());

                match channel.broadcaster.as_mut() {
                    Some(mut broadcaster) => message::Notification::WatcherJoined("".to_owned())
                                                 .send(&mut broadcaster)
                                                 .unwrap(),
                    None => (),
                }
            }
            Err(e) => panic!("{}", e),
        }
    }
}
<commit_msg>Broadcast each message<commit_after>use std::collections::HashMap;
use std::net::{Shutdown, TcpListener, TcpStream};
use std::sync::{Arc, Mutex};
use std::thread;
use std::sync::mpsc::{channel, Receiver, Sender};

use message;

pub fn execute(host: String, port: i32) {
    let listener = TcpListener::bind(&format!("{}:{}", host, port)[..]).unwrap();
    let mut channels = Channels { channels: HashMap::new() };

    for stream in listener.incoming() {
        let stream = stream.unwrap();
        let request = message::JoinRequest::receive(&stream);

        match request {
            Ok(request) => {
                match request {
                    message::JoinRequest::Broadcast(ch) =>
                        BroadcastHandler::spawn(&stream, channels.fetch(&ch)),
                    message::JoinRequest::Watch(ch) =>
                        WatchHandler::spawn(&stream, channels.fetch(&ch)),
                }
            }
            Err(e) => println!("{}", e),
        }
    }
}
struct Channels {
    channels: HashMap<String, Arc<Mutex<Channel>>>,
}

struct Channel {
    broadcaster: Option<TcpStream>,
    watchers: Vec<TcpStream>,
}

struct BroadcastHandler {
    stream: TcpStream,
    channel: Arc<Mutex<Channel>>,
}

struct WatchHandler {
    stream: TcpStream,
    channel: Arc<Mutex<Channel>>,
}

impl Channels {
    fn fetch<K: AsRef<str>>(&mut self, key: &K) -> &Arc<Mutex<Channel>> {
        let ch = key.as_ref();

        if self.channels.contains_key(&ch[..]) {
            self.channels.get(&ch[..]).unwrap()
        } else {
            let channel = Channel {
                broadcaster: None,
                watchers: Vec::new(),
            };

            self.channels.insert(ch.to_owned(), Arc::new(Mutex::new(channel)));

            self.fetch(&ch)
        }
    }
}

impl Channel {
    fn takeover(&mut self, stream: TcpStream) {
        match self.broadcaster.as_mut() {
            Some(mut current) => {
                let _ = message::Notification::Closed("Broadcaster has changed".to_owned())
                            .send(&mut current);
                let _ = current.shutdown(Shutdown::Both);
            }
            None => (),
        }

        self.broadcaster = Some(stream);
    }
}

impl BroadcastHandler {
    fn spawn(stream: &TcpStream, channel: &Arc<Mutex<Channel>>) {
        let mut handler = BroadcastHandler {
            stream: stream.try_clone().unwrap(),
            channel: channel.clone(),
        };

        let _ = channel.lock().and_then(|mut ch| {
            ch.takeover(stream.try_clone().unwrap());
            Ok(ch)
        });

        thread::spawn(move || {
            handler.process();
        });
    }

    fn process(&mut self) {
        message::JoinResponse::Success.send(&mut self.stream).unwrap();

        let (sender, receiver) = channel();

        self.spawn_relay(sender.clone());
        self.broadcast(receiver);

        self.stream.shutdown(Shutdown::Both).unwrap();
    }

    fn spawn_relay(&self, sender: Sender<Option<message::Notification>>) {
        let stream = self.stream.try_clone().unwrap();

        thread::spawn(move || {
            loop {
                match message::Notification::receive(&stream) {
                    Ok(notification) => {
                        match notification {
                            message::Notification::Output(data)   => {
                                let _ = sender.send(Some(message::Notification::Output(data)));
                            },
                            message::Notification::Closed(reason) => {
                                let _ = sender.send(Some(message::Notification::Closed(reason)));
                            },
                            _ => break,
                        };
                    },
                    Err(_) => {
                        sender.send(None).unwrap();
                        break;
                    }
                }
            };
        });
    }

    fn broadcast(&self, receiver: Receiver<Option<message::Notification>>) {
        for message in receiver {
            match message {
                Some(notification) => {
                    for mut watcher in self.channel.lock().unwrap().watchers.iter_mut() {
                        let _ = notification.send(watcher);
                    }
                },
                None => break
            };
        }
    }
}

impl WatchHandler {
    fn spawn(stream: &TcpStream, channel: &Arc<Mutex<Channel>>) {
        let mut handler = WatchHandler {
            stream: stream.try_clone().unwrap(),
            channel: channel.clone(),
        };

        thread::spawn(move || {
            handler.process();
        });
    }

    fn process(&mut self) {
        message::JoinResponse::Success.send(&mut self.stream).unwrap();

        match self.channel.lock() {
            Ok(mut channel) => {
                channel.watchers.push(self.stream.try_clone().unwrap());

                match channel.broadcaster.as_mut() {
                    Some(mut broadcaster) => message::Notification::WatcherJoined("".to_owned())
                                                 .send(&mut broadcaster)
                                                 .unwrap(),
                    None => (),
                }
            }
            Err(e) => panic!("{}", e),
        }
    }
}
<|endoftext|>


<commit_before>use std;
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use rand;

use websocket::{WebSocketStream, Server};
use websocket::Message as WebMessage;
use websocket::Sender as WebSender;
use websocket::client::Sender as WebClientSender;
use websocket::Receiver as WebReceiver;
use websocket::message::Type as WebMessageType;
use websocket::server::Connection;
use websocket::header::WebSocketProtocol;

use payload;
use state::State;

extern crate fsevent;
use self::fsevent::ITEM_MODIFIED;
use self::fsevent::Event as FsEvent;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;

use serde_xml as xml;

#[derive(Debug)]
enum NotifierMessage<'a> {
    WebMessage(WebMessage<'a>),
    FsEvent(FsEvent)
}

pub fn start(port: Option<i32>) -> Result<Server<'static>, std::io::Error> {
    let port = port.unwrap_or(2794);
    let host = format!("127.0.0.1:{}", port);
    Server::bind(host)
}

pub fn connect(connection: Connection<WebSocketStream, WebSocketStream>) {
    let request = connection.read_request().unwrap();
    let headers = request.headers.clone();
    request.validate().unwrap();

    let mut response = request.accept();
    println!("Connected");

    if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
        if protocols.contains(&("superconductor".to_string())) {
            response.headers.set(WebSocketProtocol(vec!["superconductor".to_string()]));
        }
    }

    let mut client = response.send().unwrap();
    let message = WebMessage::text(payload::generate(None));
    client.send_message(&message).unwrap();

    let (tx, rx) = channel::<NotifierMessage>();
    let (mut sender, mut receiver) = client.split();
    let notifier = thread::spawn(move || start_notifier(rx, sender));
    let txc = tx.clone();
    let monitor = thread::spawn(move || start_monitor(txc));
    for message in receiver.incoming_messages() {
        let message: WebMessage = message.unwrap_or(WebMessage::close());
        match message.opcode {
            WebMessageType::Close => {
                tx.send(NotifierMessage::WebMessage(message));
                break;
            },
            _ => tx.send(NotifierMessage::WebMessage(message))
        };
    }
    monitor.join();
    notifier.join();
}

fn start_monitor(tx: Sender<NotifierMessage>) {
    let (ftx, rx) = channel();
    let observer = thread::spawn(move || {
        println!("Monitoring");
        let fsevent = fsevent::FsEvent::new(ftx);
        fsevent.append_path(".");
        fsevent.observe();
    });
    loop {
        let event = rx.recv().unwrap();
        thread::sleep(Duration::from_millis(500));
        let mut changes = vec![];
        loop {
            if let Ok(event) = rx.try_recv() {
                if event.flag.contains(ITEM_MODIFIED) {
                    println!("Registered {:?}", event);
                    changes.push(event);
                } else {
                    println!("Ignored {:?}", event);
                }
            } else {
                break;
            }
        }
        if !changes.is_empty() {
            tx.send(NotifierMessage::FsEvent(changes.pop().unwrap()));
        }
    }
    observer.join();
}

fn start_notifier(rx: Receiver<NotifierMessage>, mut sender: WebClientSender<WebSocketStream>) {
    let mut last_state: Option<State> = None;
    let mut rng = rand::thread_rng();
    loop {
        let event = rx.recv().unwrap();
        match event {
            NotifierMessage::WebMessage(message) => {
                match message.opcode {
                    WebMessageType::Close => {
                        break;
                    },
                    WebMessageType::Ping => {
                        let message = WebMessage::pong(message.payload);
                        sender.send_message(&message).unwrap();
                    },
                    _ => {
                        let payload = String::from_utf8_lossy(message.payload.as_ref());
                        let mut state: State = xml::from_str(&payload).unwrap();

                        last_state = state.apply(last_state, &mut rng).ok();

                        let message = WebMessage::text(payload::generate(Some(state)));
                        sender.send_message(&message).unwrap();
                    }
                }
            },
            NotifierMessage::FsEvent(event) => {
                let message = WebMessage::text(payload::generate(last_state.clone()));
                sender.send_message(&message).unwrap();
            }
        }
    }
}
<commit_msg>Ignores modified lockfiles --- master:    Description: ""   Developer: Jaden Carver   Estimate: ""   Status: Sprint<commit_after>use std;
use std::thread;
use std::thread::JoinHandle;
use std::time::Duration;
use rand;

use websocket::{WebSocketStream, Server};
use websocket::Message as WebMessage;
use websocket::Sender as WebSender;
use websocket::client::Sender as WebClientSender;
use websocket::Receiver as WebReceiver;
use websocket::message::Type as WebMessageType;
use websocket::server::Connection;
use websocket::header::WebSocketProtocol;

use payload;
use state::State;

extern crate fsevent;
use self::fsevent::ITEM_MODIFIED;
use self::fsevent::Event as FsEvent;
use std::sync::mpsc::channel;
use std::sync::mpsc::Sender;
use std::sync::mpsc::Receiver;

use serde_xml as xml;

#[derive(Debug)]
enum NotifierMessage<'a> {
    WebMessage(WebMessage<'a>),
    FsEvent(FsEvent)
}

pub fn start(port: Option<i32>) -> Result<Server<'static>, std::io::Error> {
    let port = port.unwrap_or(2794);
    let host = format!("127.0.0.1:{}", port);
    Server::bind(host)
}

pub fn connect(connection: Connection<WebSocketStream, WebSocketStream>) {
    let request = connection.read_request().unwrap();
    let headers = request.headers.clone();
    request.validate().unwrap();

    let mut response = request.accept();
    println!("Connected");

    if let Some(&WebSocketProtocol(ref protocols)) = headers.get() {
        if protocols.contains(&("superconductor".to_string())) {
            response.headers.set(WebSocketProtocol(vec!["superconductor".to_string()]));
        }
    }

    let mut client = response.send().unwrap();
    let message = WebMessage::text(payload::generate(None));
    client.send_message(&message).unwrap();

    let (tx, rx) = channel::<NotifierMessage>();
    let (mut sender, mut receiver) = client.split();
    let notifier = thread::spawn(move || start_notifier(rx, sender));
    let txc = tx.clone();
    let monitor = thread::spawn(move || start_monitor(txc));
    for message in receiver.incoming_messages() {
        let message: WebMessage = message.unwrap_or(WebMessage::close());
        match message.opcode {
            WebMessageType::Close => {
                tx.send(NotifierMessage::WebMessage(message));
                break;
            },
            _ => tx.send(NotifierMessage::WebMessage(message))
        };
    }
    monitor.join();
    notifier.join();
}

fn start_monitor(tx: Sender<NotifierMessage>) {
    let (ftx, rx) = channel();
    let observer = thread::spawn(move || {
        println!("Monitoring");
        let fsevent = fsevent::FsEvent::new(ftx);
        fsevent.append_path(".");
        fsevent.observe();
    });
    loop {
        let event = rx.recv().unwrap();
        thread::sleep(Duration::from_millis(500));
        let mut changes = vec![];
        loop {
            if let Ok(event) = rx.try_recv() {
                if event.flag.contains(ITEM_MODIFIED) && (!event.path.contains(".git") || !event.path.contains(".lock")) {
                    println!("Registered {:?}", event);
                    changes.push(event);
                } else {
                    println!("Ignored {:?}", event);
                }
            } else {
                break;
            }
        }
        if !changes.is_empty() {
            tx.send(NotifierMessage::FsEvent(changes.pop().unwrap()));
        }
    }
    observer.join();
}

fn start_notifier(rx: Receiver<NotifierMessage>, mut sender: WebClientSender<WebSocketStream>) {
    let mut last_state: Option<State> = None;
    let mut rng = rand::thread_rng();
    loop {
        let event = rx.recv().unwrap();
        match event {
            NotifierMessage::WebMessage(message) => {
                match message.opcode {
                    WebMessageType::Close => {
                        break;
                    },
                    WebMessageType::Ping => {
                        let message = WebMessage::pong(message.payload);
                        sender.send_message(&message).unwrap();
                    },
                    _ => {
                        let payload = String::from_utf8_lossy(message.payload.as_ref());
                        let mut state: State = xml::from_str(&payload).unwrap();

                        last_state = state.apply(last_state, &mut rng).ok();

                        let message = WebMessage::text(payload::generate(Some(state)));
                        sender.send_message(&message).unwrap();
                    }
                }
            },
            NotifierMessage::FsEvent(event) => {
                let message = WebMessage::text(payload::generate(last_state.clone()));
                sender.send_message(&message).unwrap();
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use rustc_serialize::json;
use rustc_serialize::json::{BuilderError, Json};
use curl::{ErrCode, http};
use curl::http::handle::{Method, Request};
use cargo_edit::Manifest;
use regex::Regex;
use std::path::Path;

const REGISTRY_HOST: &'static str = "https://crates.io";

/// Query latest version from crates.io
///
/// The latest version will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - the response from crates.io is an error or in an incorrect format,
/// - or when a crate with the given name does not exist on crates.io.
pub fn get_latest_version(crate_name: &str) -> Result<String, FetchVersionError> {
    if env::var("CARGO_IS_TEST").is_ok() {
        // We are in a simulated reality. Nothing is real here.
        // FIXME: Use actual test handling code.
        return Ok("CURRENT_VERSION_TEST".into());
    }

    let crate_data = try!(fetch_cratesio(&format!("/crates/{}", crate_name)));
    let crate_json = try!(Json::from_str(&crate_data));

    crate_json.as_object()
              .and_then(|c| c.get("crate"))
              .and_then(|c| c.as_object())
              .and_then(|c| c.get("max_version"))
              .and_then(|v| v.as_string())
              .map(|v| v.to_owned())
              .ok_or(FetchVersionError::GetVersion)
}

quick_error! {
    #[derive(Debug)]
    pub enum FetchVersionError {
        CratesIo(err: CratesIoError) {
            from()
            description("crates.io Error")
            display("crates.io Error: {}", err)
            cause(err)
        }
        Json(err: BuilderError) {
            from()
            description("JSON Error")
            display("Error parsing JSON: {}", err)
            cause(err)
        }
        GetVersion { description("get version error") }
    }
}

// ---
// The following was mostly copied from [1] and is therefore
// (c) 2015 Alex Crichton <alex@alexcrichton.com>
//
// [1]: https://github.com/rust-lang/cargo/blob/bd690d8dff83c7b7714f236a08304ee20732382b/src/crates-io/lib.rs
// ---

fn fetch_cratesio(path: &str) -> Result<String, FetchVersionError> {
    let mut http_handle = http::Handle::new();
    let req = Request::new(&mut http_handle, Method::Get)
                  .uri(format!("{}/api/v1{}", REGISTRY_HOST, path))
                  .header("Accept", "application/json")
                  .content_type("application/json");
    handle_cratesio(req.exec()).map_err(From::from)
}

fn handle_cratesio(response: Result<http::Response, ErrCode>) -> Result<String, CratesIoError> {
    let response = try!(response.map_err(CratesIoError::Curl));
    match response.get_code() {
        0 | 200 => {}
        403 => return Err(CratesIoError::Unauthorized),
        404 => return Err(CratesIoError::NotFound),
        _ => return Err(CratesIoError::NotOkResponse(response)),
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(CratesIoError::NonUtf8Body),
    };

    if let Ok(errors) = json::decode::<ApiErrorList>(&body) {
        return Err(CratesIoError::Api(errors.errors.into_iter().map(|s| s.detail).collect()));
    }

    Ok(body)
}

#[derive(RustcDecodable)]
struct ApiErrorList {
    errors: Vec<ApiError>,
}
#[derive(RustcDecodable)]
struct ApiError {
    detail: String,
}

quick_error! {
    #[derive(Debug)]
    pub enum CratesIoError {
        Curl(e: ErrCode) {}
        NotOkResponse(e: http::Response)  {}
        NonUtf8Body  {}
        Api(e: Vec<String>)  {}
        Unauthorized  {}
        NotFound {}
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum FetchGitError {
        FetchGit(err: CratesIoError) {
            from()
            description("fetch error: ")
            display("fetch error: {}", err)
            cause(err)
        }
        ParseRegex { description("parse error: unable to parse git repo url") }
        IncompleteCaptures { description("parse error: the git repo url seems incomplete") }
        LocalCargoToml { description("path error: unable to open Cargo.toml") }
        // RemoteCargoToml { description("path error: unable to open Cargo.toml from the provided repo") }
        ParseCargoToml { description("parse error: unable to parse the external Cargo.toml") }
    }
}

/// Query crate name by accessing a github repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from github is an error or in an incorrect format.
pub fn get_crate_name_from_github(repo: &str) -> Result<String, FetchGitError> {
    let re = Regex::new(r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();

    re.captures(repo)
      .ok_or(FetchGitError::ParseRegex)
      .and_then(|cap| {
          match (cap.at(1), cap.at(2)) {
              (Some(ref user), Some(ref repo)) => {
                  let url = format!("https://raw.githubusercontent.com/{}/{}/master/Cargo.toml",
                                    user,
                                    repo);

                  // FIXME: use Result or modify get_cargo_toml_from_git_url to return Option
                  let data: Result<Manifest, _> = get_cargo_toml_from_git_url(&url).and_then(|m| {
                      m.parse()
                       .map_err(|_| FetchGitError::ParseCargoToml)
                  });
                  data.and_then(|ref manifest| get_name_from_manifest(manifest))
              }
              _ => Err(FetchGitError::IncompleteCaptures),
          }
      })
}

/// Query crate name by accessing a gitlab repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from gitlab is an error or in an incorrect format.
pub fn get_crate_name_from_gitlab(repo: &str) -> Result<String, FetchGitError> {
    let re = Regex::new(r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$")
                 .unwrap();

    re.captures(repo)
      .ok_or(FetchGitError::ParseRegex)
      .and_then(|cap| {
          match (cap.at(1), cap.at(2)) {
              (Some(ref user), Some(ref repo)) => {
                  let url = format!("https://gitlab.com/{}/{}/raw/master/Cargo.toml", user, repo);

                  // FIXME: use Result or modify get_cargo_toml_from_git_url to return Option
                  let data: Result<Manifest, _> = get_cargo_toml_from_git_url(&url).and_then(|m| {
                      m.parse()
                       .map_err(|_| FetchGitError::ParseCargoToml)
                  });
                  data.and_then(|ref manifest| get_name_from_manifest(manifest))
              }
              _ => Err(FetchGitError::IncompleteCaptures),
          }
      })
}

/// Query crate name by accessing Cargo.toml in a local path
///
/// The name will be returned as a string. This will fail, when
/// Cargo.toml is not present in the root of the path.
pub fn get_crate_name_from_path(path: &str) -> Result<String, FetchGitError> {
    let cargo_file = Path::new(path).join("Cargo.toml");
    Manifest::open(&cargo_file.to_str())
        .map_err(|_| FetchGitError::LocalCargoToml)
        .and_then(|ref manifest| get_name_from_manifest(manifest))
}

fn get_name_from_manifest(manifest: &Manifest) -> Result<String, FetchGitError> {
    manifest.data
            .get("package")
            .and_then(|m| m.lookup("name"))
            .and_then(|name| name.as_str().map(|s| s.to_string()))
            .ok_or(FetchGitError::ParseCargoToml)
}

// FIXME: between the code above and below there is a lot of duplication.
fn get_cargo_toml_from_git_url(url: &str) -> Result<String, FetchGitError> {
    if env::var("CARGO_IS_TEST").is_ok() {
        // We are in a simulated reality. Nothing is real here.
        // FIXME: Use actual test handling code.
        return Ok("CURRENT_VERSION_TEST".into());
    }

    let mut http_handle = http::Handle::new();
    let req = Request::new(&mut http_handle, Method::Get)
                  .uri(url)
                  .header("Accept", "text/plain")
                  .content_type("text/plain");
    handle_git_url(req.exec()).map_err(From::from)
}

fn handle_git_url(response: Result<http::Response, ErrCode>) -> Result<String, CratesIoError> {
    let response = try!(response.map_err(CratesIoError::Curl));
    match response.get_code() {
        0 | 200 => {}
        403 => return Err(CratesIoError::Unauthorized),
        404 => return Err(CratesIoError::NotFound),
        _ => return Err(CratesIoError::NotOkResponse(response)),
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(CratesIoError::NonUtf8Body),
    };

    if let Ok(errors) = json::decode::<ApiErrorList>(&body) {
        return Err(CratesIoError::Api(errors.errors.into_iter().map(|s| s.detail).collect()));
    }

    Ok(body)
}
<commit_msg>minor polishing<commit_after>use std::env;
use rustc_serialize::json;
use rustc_serialize::json::{BuilderError, Json};
use curl::{ErrCode, http};
use curl::http::handle::{Method, Request};
use cargo_edit::Manifest;
use regex::Regex;
use std::path::Path;

const REGISTRY_HOST: &'static str = "https://crates.io";

/// Query latest version from crates.io
///
/// The latest version will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - the response from crates.io is an error or in an incorrect format,
/// - or when a crate with the given name does not exist on crates.io.
pub fn get_latest_version(crate_name: &str) -> Result<String, FetchVersionError> {
    if env::var("CARGO_IS_TEST").is_ok() {
        // We are in a simulated reality. Nothing is real here.
        // FIXME: Use actual test handling code.
        return Ok("CURRENT_VERSION_TEST".into());
    }

    let crate_data = try!(fetch_cratesio(&format!("/crates/{}", crate_name)));
    let crate_json = try!(Json::from_str(&crate_data));

    crate_json.as_object()
              .and_then(|c| c.get("crate"))
              .and_then(|c| c.as_object())
              .and_then(|c| c.get("max_version"))
              .and_then(|v| v.as_string())
              .map(|v| v.to_owned())
              .ok_or(FetchVersionError::GetVersion)
}

quick_error! {
    #[derive(Debug)]
    pub enum FetchVersionError {
        CratesIo(err: CratesIoError) {
            from()
            description("crates.io Error")
            display("crates.io Error: {}", err)
            cause(err)
        }
        Json(err: BuilderError) {
            from()
            description("JSON Error")
            display("Error parsing JSON: {}", err)
            cause(err)
        }
        GetVersion { description("get version error") }
    }
}

// ---
// The following was mostly copied from [1] and is therefore
// (c) 2015 Alex Crichton <alex@alexcrichton.com>
//
// [1]: https://github.com/rust-lang/cargo/blob/bd690d8dff83c7b7714f236a08304ee20732382b/src/crates-io/lib.rs
// ---

fn fetch_cratesio(path: &str) -> Result<String, FetchVersionError> {
    let mut http_handle = http::Handle::new();
    let req = Request::new(&mut http_handle, Method::Get)
                  .uri(format!("{}/api/v1{}", REGISTRY_HOST, path))
                  .header("Accept", "application/json")
                  .content_type("application/json");
    handle_cratesio(req.exec()).map_err(From::from)
}

fn handle_cratesio(response: Result<http::Response, ErrCode>) -> Result<String, CratesIoError> {
    let response = try!(response.map_err(CratesIoError::Curl));
    match response.get_code() {
        0 | 200 => {}
        403 => return Err(CratesIoError::Unauthorized),
        404 => return Err(CratesIoError::NotFound),
        _ => return Err(CratesIoError::NotOkResponse(response)),
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(CratesIoError::NonUtf8Body),
    };

    if let Ok(errors) = json::decode::<ApiErrorList>(&body) {
        return Err(CratesIoError::Api(errors.errors.into_iter().map(|s| s.detail).collect()));
    }

    Ok(body)
}

#[derive(RustcDecodable)]
struct ApiErrorList {
    errors: Vec<ApiError>,
}
#[derive(RustcDecodable)]
struct ApiError {
    detail: String,
}

quick_error! {
    #[derive(Debug)]
    pub enum CratesIoError {
        Curl(e: ErrCode) {}
        NotOkResponse(e: http::Response)  {}
        NonUtf8Body  {}
        Api(e: Vec<String>)  {}
        Unauthorized  {}
        NotFound {}
    }
}

quick_error! {
    #[derive(Debug)]
    pub enum FetchGitError {
        FetchGit(err: CratesIoError) {
            from()
            description("fetch error: ")
            display("fetch error: {}", err)
            cause(err)
        }
        ParseRegex { description("parse error: unable to parse git repo url") }
        IncompleteCaptures { description("parse error: the git repo url seems incomplete") }
        LocalCargoToml { description("path error: unable to open Cargo.toml") }
        // RemoteCargoToml { description("path error: unable to open Cargo.toml from the provided repo") }
        ParseCargoToml { description("parse error: unable to parse the external Cargo.toml") }
    }
}

/// Query crate name by accessing a github repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from github is an error or in an incorrect format.
pub fn get_crate_name_from_github(repo: &str) -> Result<String, FetchGitError> {
    let re = Regex::new(r"^https://github.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$").unwrap();

    re.captures(repo)
      .ok_or(FetchGitError::ParseRegex)
      .and_then(|cap| {
          match (cap.at(1), cap.at(2)) {
              (Some(ref user), Some(ref repo)) => {
                  let url = format!("https://raw.githubusercontent.com/{}/{}/master/Cargo.toml",
                                    user,
                                    repo);

                  let data: Result<Manifest, _> = get_cargo_toml_from_git_url(&url).and_then(|m| {
                      m.parse()
                       .map_err(|_| FetchGitError::ParseCargoToml)
                  });
                  data.and_then(|ref manifest| get_name_from_manifest(manifest))
              }
              _ => Err(FetchGitError::IncompleteCaptures),
          }
      })
}

/// Query crate name by accessing a gitlab repo Cargo.toml
///
/// The name will be returned as a string. This will fail, when
///
/// - there is no Internet connection,
/// - Cargo.toml is not present in the root of the master branch,
/// - the response from gitlab is an error or in an incorrect format.
pub fn get_crate_name_from_gitlab(repo: &str) -> Result<String, FetchGitError> {
    let re = Regex::new(r"^https://gitlab.com/([-_0-9a-zA-Z]+)/([-_0-9a-zA-Z]+)(/|.git)?$")
                 .unwrap();

    re.captures(repo)
      .ok_or(FetchGitError::ParseRegex)
      .and_then(|cap| {
          match (cap.at(1), cap.at(2)) {
              (Some(ref user), Some(ref repo)) => {
                  let url = format!("https://gitlab.com/{}/{}/raw/master/Cargo.toml", user, repo);

                  let data: Result<Manifest, _> = get_cargo_toml_from_git_url(&url).and_then(|m| {
                      m.parse()
                       .map_err(|_| FetchGitError::ParseCargoToml)
                  });
                  data.and_then(|ref manifest| get_name_from_manifest(manifest))
              }
              _ => Err(FetchGitError::IncompleteCaptures),
          }
      })
}

/// Query crate name by accessing Cargo.toml in a local path
///
/// The name will be returned as a string. This will fail, when
/// Cargo.toml is not present in the root of the path.
pub fn get_crate_name_from_path(path: &str) -> Result<String, FetchGitError> {
    let cargo_file = Path::new(path).join("Cargo.toml");
    Manifest::open(&cargo_file.to_str())
        .map_err(|_| FetchGitError::LocalCargoToml)
        .and_then(|ref manifest| get_name_from_manifest(manifest))
}

fn get_name_from_manifest(manifest: &Manifest) -> Result<String, FetchGitError> {
    manifest.data
            .get("package")
            .and_then(|m| m.lookup("name"))
            .and_then(|name| name.as_str().map(|s| s.to_string()))
            .ok_or(FetchGitError::ParseCargoToml)
}

// FIXME: between the code above and below there is a lot of duplication.
fn get_cargo_toml_from_git_url(url: &str) -> Result<String, FetchGitError> {

    let mut http_handle = http::Handle::new();
    let req = Request::new(&mut http_handle, Method::Get)
                  .uri(url)
                  .header("Accept", "text/plain")
                  .content_type("text/plain");
    handle_git_url(req.exec()).map_err(From::from)
}

fn handle_git_url(response: Result<http::Response, ErrCode>) -> Result<String, CratesIoError> {
    let response = try!(response.map_err(CratesIoError::Curl));
    match response.get_code() {
        0 | 200 => {}
        403 => return Err(CratesIoError::Unauthorized),
        404 => return Err(CratesIoError::NotFound),
        _ => return Err(CratesIoError::NotOkResponse(response)),
    }

    let body = match String::from_utf8(response.move_body()) {
        Ok(body) => body,
        Err(..) => return Err(CratesIoError::NonUtf8Body),
    };

    if let Ok(errors) = json::decode::<ApiErrorList>(&body) {
        return Err(CratesIoError::Api(errors.errors.into_iter().map(|s| s.detail).collect()));
    }

    Ok(body)
}
<|endoftext|>


<commit_before>extern crate dbus;
use self::dbus::{Connection, Error, NameFlag, ReleaseNameReply};

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),
        })
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name[..]);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<commit_msg>server: add objects to a server<commit_after>extern crate dbus;
use self::dbus::{Connection, NameFlag, ReleaseNameReply};

use super::error::DBusError;
use super::interface::DBusInterface;
use super::object::DBusObject;

use std::collections::btree_map::{BTreeMap, Entry};
use std::error::Error;

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,

    objects: BTreeMap<String, DBusObject<'a>>,
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),

            objects: BTreeMap::new(),
        })
    }

    pub fn add_object(&mut self, path: &str, ifaces: Vec<DBusInterface>) -> Result<&DBusObject<'a>, Box<Error>> {
        match self.objects.entry(path.to_string()) {
            Entry::Vacant(v)    => Ok(v.insert(try!(DBusObject::new(self.conn, ifaces, path)))),
            Entry::Occupied(_)  => Err(Box::new(DBusError::PathAlreadyRegistered(path.to_string()))),
        }
    }

    pub fn remove_object(&mut self, path: &str) -> Result<(), DBusError> {
        match self.objects.remove(path) {
            Some(_) => Ok(()),
            None    => Err(DBusError::NoSuchPath(path.to_string())),
        }
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name[..]);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<|endoftext|>