<commit_before>// This is a part of rust-encoding.
// Copyright (c) 2013-2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

//! Legacy traditional Chinese encodings.

use util::StrCharIndex;
use index;
use types::*;

/**
 * Big5-2003 with common extensions. (XXX with asymmetric HKSCS-2008 support)
 *
 * This is a traditional Chinese encoding spanning the region `[81-FE] [40-7E A1-FE]`.
 * Originally a proprietary encoding by the consortium of five companies (hence the name),
 * the Republic of China government standardized Big5-2003 in an appendix of CNS 11643
 * so that CNS 11643 plane 1 and plane 2 have
 * an almost identical set of characters as Big5 (but with a different mapping).
 * The Hong Kong government has an official extension to Big5
 * named Hong Kong Supplementary Character Set (HKSCS).
 *
 * This particular implementation of Big5 includes the widespread ETEN and HKSCS extensions,
 * but excludes less common extensions such as Big5+, Big-5E and Unicode-at-on.
 */
#[deriving(Clone)]
pub struct BigFive2003Encoding;

impl Encoding for BigFive2003Encoding {
    fn name(&self) -> &'static str { "big5-2003" }
    fn whatwg_name(&self) -> Option<&'static str> { Some("big5") } // WHATWG compatibility
    fn encoder(&self) -> Box<Encoder> { BigFive2003Encoder::new() }
    fn decoder(&self) -> Box<Decoder> { BigFive2003HKSCS2008Decoder::new() }
}

/// An encoder for Big5-2003.
#[deriving(Clone)]
pub struct BigFive2003Encoder;

impl BigFive2003Encoder {
    pub fn new() -> Box<Encoder> { box BigFive2003Encoder as Box<Encoder> }
}

impl Encoder for BigFive2003Encoder {
    fn from_self(&self) -> Box<Encoder> { BigFive2003Encoder::new() }
    fn is_ascii_compatible(&self) -> bool { true }

    fn raw_feed(&mut self, input: &str, output: &mut ByteWriter) -> (uint, Option<CodecError>) {
        output.writer_hint(input.len());

        for ((i,j), ch) in input.index_iter() {
            if ch < '\u0080' {
                output.write_byte(ch as u8);
            } else {
                let ptr = index::big5::backward(ch as u32);
                if ptr == 0xffff || ptr < (0xa1 - 0x81) * 157 {
                    // no HKSCS extension (XXX doesn't HKSCS include 0xFA40..0xFEFE?)
                    return (i, Some(CodecError {
                        upto: j, cause: "unrepresentable character".into_maybe_owned()
                    }));
                }
                let lead = ptr / 157 + 0x81;
                let trail = ptr % 157;
                let trailoffset = if trail < 0x3f {0x40} else {0x62};
                output.write_byte(lead as u8);
                output.write_byte((trail + trailoffset) as u8);
            }
        }
        (input.len(), None)
    }

    fn raw_finish(&mut self, _output: &mut ByteWriter) -> Option<CodecError> {
        None
    }
}

ascii_compatible_stateful_decoder! {
    #[doc="A decoder for Big5-2003 with HKSCS-2008 extension."]
    #[deriving(Clone)]
    struct BigFive2003HKSCS2008Decoder;

    module bigfive2003;

    internal pub fn map_two_bytes(lead: u8, trail: u8) -> u32 {
        use index;

        let lead = lead as uint;
        let trail = trail as uint;
        let index = match (lead, trail) {
            (0x81..0xfe, 0x40..0x7e) | (0x81..0xfe, 0xa1..0xfe) => {
                let trailoffset = if trail < 0x7f {0x40} else {0x62};
                (lead - 0x81) * 157 + trail - trailoffset
            }
            _ => 0xffff,
        };
        index::big5::forward(index as u16) // may return two-letter replacements 0..3
    }

    // big5 lead = 0x00
    initial state S0(ctx) {
        case b @ 0x00..0x7f => ctx.emit(b as u32);
        case b @ 0x81..0xfe => S1(ctx, b);
        case _ => ctx.err("invalid sequence");
    }

    // big5 lead != 0x00
    state S1(ctx, lead: u8) {
        case b => match map_two_bytes(lead, b) {
            0xffff => {
                let backup = if b < 0x80 {1} else {0};
                ctx.backup_and_err(backup, "invalid sequence")
            },
            0 /*index=1133*/ => ctx.emit_str("\u00ca\u0304"),
            1 /*index=1135*/ => ctx.emit_str("\u00ca\u030c"),
            2 /*index=1164*/ => ctx.emit_str("\u00ea\u0304"),
            3 /*index=1166*/ => ctx.emit_str("\u00ea\u030c"),
            ch => ctx.emit(ch),
        };
    }
}

#[cfg(test)]
mod bigfive2003_tests {
    extern crate test;
    use super::BigFive2003Encoding;
    use testutils;
    use types::*;

    #[test]
    fn test_encoder_valid() {
        let mut e = BigFive2003Encoding.encoder();
        assert_feed_ok!(e, "A", "", [0x41]);
        assert_feed_ok!(e, "BC", "", [0x42, 0x43]);
        assert_feed_ok!(e, "", "", []);
        assert_feed_ok!(e, "\u4e2d\u83ef\u6c11\u570b", "",
                        [0xa4, 0xa4, 0xb5, 0xd8, 0xa5, 0xc1, 0xb0, 0xea]);
        assert_feed_ok!(e, "1\u20ac/m", "", [0x31, 0xa3, 0xe1, 0x2f, 0x6d]);
        assert_feed_ok!(e, "\uffed", "", [0xf9, 0xfe]);
        assert_finish_ok!(e, []);
    }

    #[test]
    fn test_encoder_invalid() {
        let mut e = BigFive2003Encoding.encoder();
        assert_feed_err!(e, "", "\uffff", "", []);
        assert_feed_err!(e, "?", "\uffff", "!", [0x3f]);
        assert_feed_err!(e, "", "\u3eec", "\u4e00", []); // HKSCS-2008 addition
        assert_finish_ok!(e, []);
    }

    #[test]
    fn test_decoder_valid() {
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_ok!(d, [0x41], [], "A");
        assert_feed_ok!(d, [0x42, 0x43], [], "BC");
        assert_feed_ok!(d, [], [], "");
        assert_feed_ok!(d, [0xa4, 0xa4, 0xb5, 0xd8, 0xa5, 0xc1, 0xb0, 0xea], [],
                        "\u4e2d\u83ef\u6c11\u570b");
        assert_feed_ok!(d, [0x31, 0xa3, 0xe1, 0x2f, 0x6d], [], "1\u20ac/m");
        assert_feed_ok!(d, [0xf9, 0xfe], [], "\uffed");
        assert_feed_ok!(d, [0x87, 0x7e], [], "\u3eec"); // HKSCS-2008 addition
        assert_finish_ok!(d, "");
    }

    #[test]
    fn test_decoder_invalid_trail() {
        // unlike most other cases, valid lead + invalid MSB-set trail are entirely consumed.
        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16771
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_err!(d, [], [0xf0], [0x30, 0x30], "");
        assert_feed_err!(d, [], [0xf0, 0x80], [0x30], "");
        assert_finish_ok!(d, "");
    }

    // TODO more tests

    #[test]
    fn test_decoder_feed_after_finish() {
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_ok!(d, [0xa4, 0x40], [0xa4], "\u4e00");
        assert_finish_err!(d, "");
        assert_feed_ok!(d, [0xa4, 0x40], [], "\u4e00");
        assert_finish_ok!(d, "");
    }

    #[bench]
    fn bench_encode_short_text(bencher: &mut test::Bencher) {
        static Encoding: BigFive2003Encoding = BigFive2003Encoding;
        let s = testutils::TRADITIONAL_CHINESE_TEXT;
        bencher.bytes = s.len() as u64;
        bencher.iter(|| test::black_box({
            Encoding.encode(s.as_slice(), EncodeStrict)
        }))
    }

    #[bench]
    fn bench_decode_short_text(bencher: &mut test::Bencher) {
        static Encoding: BigFive2003Encoding = BigFive2003Encoding;
        let s = Encoding.encode(testutils::TRADITIONAL_CHINESE_TEXT, EncodeStrict).ok().unwrap();
        bencher.bytes = s.len() as u64;
        bencher.iter(|| test::black_box({
            Encoding.decode(s.as_slice(), DecodeStrict)
        }))
    }
}

<commit_msg>added more tests to codec/tradchinese.rs.<commit_after>// This is a part of rust-encoding.
// Copyright (c) 2013-2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

//! Legacy traditional Chinese encodings.

use util::StrCharIndex;
use index;
use types::*;

/**
 * Big5-2003 with common extensions. (XXX with asymmetric HKSCS-2008 support)
 *
 * This is a traditional Chinese encoding spanning the region `[81-FE] [40-7E A1-FE]`.
 * Originally a proprietary encoding by the consortium of five companies (hence the name),
 * the Republic of China government standardized Big5-2003 in an appendix of CNS 11643
 * so that CNS 11643 plane 1 and plane 2 have
 * an almost identical set of characters as Big5 (but with a different mapping).
 * The Hong Kong government has an official extension to Big5
 * named Hong Kong Supplementary Character Set (HKSCS).
 *
 * This particular implementation of Big5 includes the widespread ETEN and HKSCS extensions,
 * but excludes less common extensions such as Big5+, Big-5E and Unicode-at-on.
 */
#[deriving(Clone)]
pub struct BigFive2003Encoding;

impl Encoding for BigFive2003Encoding {
    fn name(&self) -> &'static str { "big5-2003" }
    fn whatwg_name(&self) -> Option<&'static str> { Some("big5") } // WHATWG compatibility
    fn encoder(&self) -> Box<Encoder> { BigFive2003Encoder::new() }
    fn decoder(&self) -> Box<Decoder> { BigFive2003HKSCS2008Decoder::new() }
}

/// An encoder for Big5-2003.
#[deriving(Clone)]
pub struct BigFive2003Encoder;

impl BigFive2003Encoder {
    pub fn new() -> Box<Encoder> { box BigFive2003Encoder as Box<Encoder> }
}

impl Encoder for BigFive2003Encoder {
    fn from_self(&self) -> Box<Encoder> { BigFive2003Encoder::new() }
    fn is_ascii_compatible(&self) -> bool { true }

    fn raw_feed(&mut self, input: &str, output: &mut ByteWriter) -> (uint, Option<CodecError>) {
        output.writer_hint(input.len());

        for ((i,j), ch) in input.index_iter() {
            if ch < '\u0080' {
                output.write_byte(ch as u8);
            } else {
                let ptr = index::big5::backward(ch as u32);
                if ptr == 0xffff || ptr < (0xa1 - 0x81) * 157 {
                    // no HKSCS extension (XXX doesn't HKSCS include 0xFA40..0xFEFE?)
                    return (i, Some(CodecError {
                        upto: j, cause: "unrepresentable character".into_maybe_owned()
                    }));
                }
                let lead = ptr / 157 + 0x81;
                let trail = ptr % 157;
                let trailoffset = if trail < 0x3f {0x40} else {0x62};
                output.write_byte(lead as u8);
                output.write_byte((trail + trailoffset) as u8);
            }
        }
        (input.len(), None)
    }

    fn raw_finish(&mut self, _output: &mut ByteWriter) -> Option<CodecError> {
        None
    }
}

ascii_compatible_stateful_decoder! {
    #[doc="A decoder for Big5-2003 with HKSCS-2008 extension."]
    #[deriving(Clone)]
    struct BigFive2003HKSCS2008Decoder;

    module bigfive2003;

    internal pub fn map_two_bytes(lead: u8, trail: u8) -> u32 {
        use index;

        let lead = lead as uint;
        let trail = trail as uint;
        let index = match (lead, trail) {
            (0x81..0xfe, 0x40..0x7e) | (0x81..0xfe, 0xa1..0xfe) => {
                let trailoffset = if trail < 0x7f {0x40} else {0x62};
                (lead - 0x81) * 157 + trail - trailoffset
            }
            _ => 0xffff,
        };
        index::big5::forward(index as u16) // may return two-letter replacements 0..3
    }

    // big5 lead = 0x00
    initial state S0(ctx) {
        case b @ 0x00..0x7f => ctx.emit(b as u32);
        case b @ 0x81..0xfe => S1(ctx, b);
        case _ => ctx.err("invalid sequence");
    }

    // big5 lead != 0x00
    state S1(ctx, lead: u8) {
        case b => match map_two_bytes(lead, b) {
            0xffff => {
                let backup = if b < 0x80 {1} else {0};
                ctx.backup_and_err(backup, "invalid sequence")
            },
            0 /*index=1133*/ => ctx.emit_str("\u00ca\u0304"),
            1 /*index=1135*/ => ctx.emit_str("\u00ca\u030c"),
            2 /*index=1164*/ => ctx.emit_str("\u00ea\u0304"),
            3 /*index=1166*/ => ctx.emit_str("\u00ea\u030c"),
            ch => ctx.emit(ch),
        };
    }
}

#[cfg(test)]
mod bigfive2003_tests {
    extern crate test;
    use super::BigFive2003Encoding;
    use std::iter::range_inclusive;
    use testutils;
    use types::*;

    #[test]
    fn test_encoder_valid() {
        let mut e = BigFive2003Encoding.encoder();
        assert_feed_ok!(e, "A", "", [0x41]);
        assert_feed_ok!(e, "BC", "", [0x42, 0x43]);
        assert_feed_ok!(e, "", "", []);
        assert_feed_ok!(e, "\u4e2d\u83ef\u6c11\u570b", "",
                        [0xa4, 0xa4, 0xb5, 0xd8, 0xa5, 0xc1, 0xb0, 0xea]);
        assert_feed_ok!(e, "1\u20ac/m", "", [0x31, 0xa3, 0xe1, 0x2f, 0x6d]);
        assert_feed_ok!(e, "\uffed", "", [0xf9, 0xfe]);
        assert_finish_ok!(e, []);
    }

    #[test]
    fn test_encoder_invalid() {
        let mut e = BigFive2003Encoding.encoder();
        assert_feed_err!(e, "", "\uffff", "", []);
        assert_feed_err!(e, "?", "\uffff", "!", [0x3f]);
        assert_feed_err!(e, "", "\u3eec", "\u4e00", []); // HKSCS-2008 addition
        assert_finish_ok!(e, []);
    }

    #[test]
    fn test_decoder_valid() {
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_ok!(d, [0x41], [], "A");
        assert_feed_ok!(d, [0x42, 0x43], [], "BC");
        assert_feed_ok!(d, [], [], "");
        assert_feed_ok!(d, [0xa4, 0xa4, 0xb5, 0xd8, 0xa5, 0xc1, 0xb0, 0xea], [],
                        "\u4e2d\u83ef\u6c11\u570b");
        assert_feed_ok!(d, [], [0xa4], "");
        assert_feed_ok!(d, [0xa4, 0xb5, 0xd8], [0xa5], "\u4e2d\u83ef");
        assert_feed_ok!(d, [0xc1, 0xb0, 0xea], [], "\u6c11\u570b");
        assert_feed_ok!(d, [0x31, 0xa3, 0xe1, 0x2f, 0x6d], [], "1\u20ac/m");
        assert_feed_ok!(d, [0xf9, 0xfe], [], "\uffed");
        assert_feed_ok!(d, [0x87, 0x7e], [], "\u3eec"); // HKSCS-2008 addition
        assert_feed_ok!(d, [0x88, 0x62, 0x88, 0x64, 0x88, 0xa3, 0x88, 0xa5], [],
                        "\xca\u0304\xca\u030c\xea\u0304\xea\u030c"); // two-byte mappings
        assert_finish_ok!(d, "");
    }

    #[test]
    fn test_decoder_invalid_lone_lead_immediate_test_finish() {
        for i in range_inclusive(0x81u8, 0xfe) {
            let mut d = BigFive2003Encoding.decoder();
            assert_feed_ok!(d, [], [i], ""); // wait for a trail
            assert_finish_err!(d, "");
        }

        // 80/FF: immediate failure
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_err!(d, [], [0x80], [], "");
        assert_feed_err!(d, [], [0xff], [], "");
        assert_finish_ok!(d, "");
    }

    #[test]
    fn test_decoder_invalid_lone_lead_followed_by_space() {
        for i in range_inclusive(0x80u8, 0xff) {
            let mut d = BigFive2003Encoding.decoder();
            assert_feed_err!(d, [], [i], [0x20], "");
            assert_finish_ok!(d, "");
        }
    }

    #[test]
    fn test_decoder_invalid_lead_followed_by_invalid_trail() {
        // unlike most other cases, valid lead + invalid MSB-set trail are entirely consumed.
        // https://www.w3.org/Bugs/Public/show_bug.cgi?id=16771
        for i in range_inclusive(0x81u8, 0xfe) {
            let mut d = BigFive2003Encoding.decoder();
            assert_feed_err!(d, [], [i, 0x80], [0x20], "");
            assert_feed_err!(d, [], [i, 0xff], [0x20], "");
            assert_finish_ok!(d, "");
        }

        // 80/FF is not a valid lead and the trail is not consumed
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_err!(d, [], [0x80], [0x80], "");
        assert_feed_err!(d, [], [0x80], [0xff], "");
        assert_feed_err!(d, [], [0xff], [0x80], "");
        assert_feed_err!(d, [], [0xff], [0xff], "");
        assert_finish_ok!(d, "");
    }

    #[test]
    fn test_decoder_feed_after_finish() {
        let mut d = BigFive2003Encoding.decoder();
        assert_feed_ok!(d, [0xa4, 0x40], [0xa4], "\u4e00");
        assert_finish_err!(d, "");
        assert_feed_ok!(d, [0xa4, 0x40], [], "\u4e00");
        assert_finish_ok!(d, "");
    }

    #[bench]
    fn bench_encode_short_text(bencher: &mut test::Bencher) {
        static Encoding: BigFive2003Encoding = BigFive2003Encoding;
        let s = testutils::TRADITIONAL_CHINESE_TEXT;
        bencher.bytes = s.len() as u64;
        bencher.iter(|| test::black_box({
            Encoding.encode(s.as_slice(), EncodeStrict)
        }))
    }

    #[bench]
    fn bench_decode_short_text(bencher: &mut test::Bencher) {
        static Encoding: BigFive2003Encoding = BigFive2003Encoding;
        let s = Encoding.encode(testutils::TRADITIONAL_CHINESE_TEXT, EncodeStrict).ok().unwrap();
        bencher.bytes = s.len() as u64;
        bencher.iter(|| test::black_box({
            Encoding.decode(s.as_slice(), DecodeStrict)
        }))
    }
}

<|endoftext|>


<commit_before>#![feature(test)]

extern crate test;
extern crate data_encoding;

use data_encoding::{HEXLOWER, BASE32, BASE64, BASE64_NOPAD};
use test::Bencher;

#[bench]
fn base02_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 32768];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base02_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 512];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base04_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 16384];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("0123");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base04_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 1024];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("0123");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base08_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 10923];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01234567");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base08_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 1536];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01234567");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base16_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 8192];
    b.iter(|| HEXLOWER.encode_mut(input, output));
}

#[bench]
fn base16_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 2048];
    b.iter(|| HEXLOWER.decode_mut(input, output));
}

#[bench]
fn base32_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 6560];
    b.iter(|| BASE32.encode_mut(input, output));
}

#[bench]
fn base32_decode_base(b: &mut Bencher) {
    let input = &[b'A'; 4096];
    let output = &mut [0u8; 2560];
    b.iter(|| BASE32.decode_mut(input, output));
}

#[bench]
fn base64_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 5462];
    b.iter(|| BASE64_NOPAD.encode_mut(input, output));
}

#[bench]
fn base64_decode_base(b: &mut Bencher) {
    let input = &[b'A'; 4096];
    let output = &mut [0u8; 3072];
    b.iter(|| BASE64_NOPAD.decode_mut(input, output));
}

#[bench]
fn base64_encode_pad(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    let output = &mut [0u8; 5464];
    b.iter(|| BASE64.encode_mut(input, output));
}

#[bench]
fn base64_decode_pad(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    for i in 0 .. 20 {
        let x = 4096 * i / 20 / 4 * 4;
        input[x + 3] = b'=';
        if i % 2 == 0 { input[x + 2] = b'='; }
    }
    let output = &mut [0u8; 3072];
    b.iter(|| BASE64.decode_mut(input, output).unwrap());
}

#[bench]
fn base64_encode_wrap(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 5608];
    let mut spec = data_encoding::BASE64.specification();
    spec.wrap.width = 76;
    spec.wrap.separator.push_str("\r\n");
    let base64 = spec.encoding().unwrap();
    b.iter(|| base64.encode_mut(input, output));
}

#[bench]
fn base64_decode_wrap(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    for i in 0 .. 20 {
        let x = 4096 * i / 20 / 4 * 4;
        input[x + 3] = b'\n';
    }
    let output = &mut [0u8; 3072];
    let mut spec = data_encoding::BASE64.specification();
    spec.wrap.width = 76;
    spec.wrap.separator.push_str("\r\n");
    let base64 = spec.encoding().unwrap();
    b.iter(|| base64.decode_mut(input, output).unwrap());
}
<commit_msg>lib: Add dnscurve benchmark<commit_after>#![feature(test)]

extern crate test;
extern crate data_encoding;

use data_encoding::{HEXLOWER, BASE32, BASE64, BASE64_NOPAD};
use test::Bencher;

#[bench]
fn base02_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 32768];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base02_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 512];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base04_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 16384];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("0123");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base04_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 1024];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("0123");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base08_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 10923];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01234567");
    let base = spec.encoding().unwrap();
    b.iter(|| base.encode_mut(input, output));
}

#[bench]
fn base08_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 1536];
    let mut spec = data_encoding::Specification::new();
    spec.symbols.push_str("01234567");
    let base = spec.encoding().unwrap();
    b.iter(|| base.decode_mut(input, output));
}

#[bench]
fn base16_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 8192];
    b.iter(|| HEXLOWER.encode_mut(input, output));
}

#[bench]
fn base16_decode_base(b: &mut Bencher) {
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 2048];
    b.iter(|| HEXLOWER.decode_mut(input, output));
}

#[bench]
fn base32_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 6560];
    b.iter(|| BASE32.encode_mut(input, output));
}

#[bench]
fn base32_decode_base(b: &mut Bencher) {
    let input = &[b'A'; 4096];
    let output = &mut [0u8; 2560];
    b.iter(|| BASE32.decode_mut(input, output));
}

#[bench]
fn base64_encode_base(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 5462];
    b.iter(|| BASE64_NOPAD.encode_mut(input, output));
}

#[bench]
fn base64_decode_base(b: &mut Bencher) {
    let input = &[b'A'; 4096];
    let output = &mut [0u8; 3072];
    b.iter(|| BASE64_NOPAD.decode_mut(input, output));
}

#[bench]
fn base64_encode_pad(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    let output = &mut [0u8; 5464];
    b.iter(|| BASE64.encode_mut(input, output));
}

#[bench]
fn base64_decode_pad(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    for i in 0 .. 20 {
        let x = 4096 * i / 20 / 4 * 4;
        input[x + 3] = b'=';
        if i % 2 == 0 { input[x + 2] = b'='; }
    }
    let output = &mut [0u8; 3072];
    b.iter(|| BASE64.decode_mut(input, output).unwrap());
}

#[bench]
fn base64_encode_wrap(b: &mut Bencher) {
    let input = &[0u8; 4096];
    let output = &mut [0u8; 5608];
    let mut spec = data_encoding::BASE64.specification();
    spec.wrap.width = 76;
    spec.wrap.separator.push_str("\r\n");
    let base64 = spec.encoding().unwrap();
    b.iter(|| base64.encode_mut(input, output));
}

#[bench]
fn base64_decode_wrap(b: &mut Bencher) {
    let input = &mut [b'A'; 4096];
    for i in 0 .. 20 {
        let x = 4096 * i / 20 / 4 * 4;
        input[x + 3] = b'\n';
    }
    let output = &mut [0u8; 3072];
    let mut spec = data_encoding::BASE64.specification();
    spec.wrap.width = 76;
    spec.wrap.separator.push_str("\r\n");
    let base64 = spec.encoding().unwrap();
    b.iter(|| base64.decode_mut(input, output).unwrap());
}

#[bench]
fn dnscurve_decode_base(b: &mut Bencher) {
    let dns_curve = {
        use data_encoding::{Specification, BitOrder};
        let mut spec = Specification::new();
        spec.symbols.push_str("0123456789bcdfghjklmnpqrstuvwxyz");
        spec.translate.from.push_str("BCDFGHJKLMNPQRSTUVWXYZ");
        spec.translate.to.push_str("bcdfghjklmnpqrstuvwxyz");
        spec.bit_order = BitOrder::LeastSignificantFirst;
        spec.encoding().unwrap()
    };
    let input = &[b'0'; 4096];
    let output = &mut [0u8; 2560];
    b.iter(|| dns_curve.decode_mut(input, output));
}

#[bench]
fn dnscurve_encode_base(b: &mut Bencher) {
    let dns_curve = {
        use data_encoding::{Specification, BitOrder};
        let mut spec = Specification::new();
        spec.symbols.push_str("0123456789bcdfghjklmnpqrstuvwxyz");
        spec.translate.from.push_str("BCDFGHJKLMNPQRSTUVWXYZ");
        spec.translate.to.push_str("bcdfghjklmnpqrstuvwxyz");
        spec.bit_order = BitOrder::LeastSignificantFirst;
        spec.encoding().unwrap()
    };
    let input = &[0u8; 4096];
    let output = &mut [0u8; 6554];
    b.iter(|| dns_curve.encode_mut(input, output));
}
<|endoftext|>


<commit_before>//! A simple example meant to demonstrate some basic usage of oxcable.
//!
//! This example is prone to minor changes, because I also use it as an easy way
//! to try out new audio devices.

extern crate oxcable;

#[cfg(not(test))]
fn main() {
    use oxcable::oscillator;
    use oxcable::delay::Delay;
    use oxcable::graph::{DeviceGraph, Tick};
    use oxcable::io::audio::AudioEngine;
    use oxcable::mixers::Gain;
    use oxcable::oscillator::*;

    println!("Setting up signal chain...");
    let engine = AudioEngine::with_buffer_size(256).unwrap();
    let mut graph = DeviceGraph::new();
    let spk = graph.add_node(engine.default_output(2).unwrap());

    let mic = graph.add_node(engine.default_input(1).unwrap());
    let del = graph.add_node(Delay::new(0.5, 0.5, 0.5, 1));
    graph.add_edge(mic, 0, del, 0).unwrap();
    graph.add_edge(del, 0, spk, 0).unwrap();

    let lfo = graph.add_node(Oscillator::new(oscillator::Sine).freq(10.0));
    let osc = graph.add_node(
        Oscillator::new(Tri(PolyBlep)).freq(440.0).lfo_intensity(0.1)
    );
    let gain = graph.add_node(Gain::new(-6.0, 1));
    graph.add_edge(lfo, 0, osc, 0).unwrap();
    graph.add_edge(osc, 0, gain, 0).unwrap();
    graph.add_edge(gain, 0, spk, 1).unwrap();

    println!("Playing. Press Enter to quit...");
    graph.tick_until_enter();
    println!("Done!");
}
<commit_msg>Update example<commit_after>//! A simple example meant to demonstrate some basic usage of oxcable.
//!
//! This example is prone to minor changes, because I also use it as an easy way
//! to try out new audio devices.

extern crate oxcable;

#[cfg(not(test))]
fn main() {
    use oxcable::oscillator;
    use oxcable::filters::first_order::{Filter, LowPass};
    use oxcable::graph::{DeviceGraph, Tick};
    use oxcable::io::audio::AudioEngine;
    use oxcable::mixers::Gain;
    use oxcable::oscillator::*;

    println!("Setting up signal chain...");
    let engine = AudioEngine::with_buffer_size(256).unwrap();
    let mut graph = DeviceGraph::new();
    let speaker = graph.add_node(engine.default_output(2).unwrap());

    let microphone = graph.add_node(engine.default_input(1).unwrap());
    let filter = graph.add_node(Filter::new(LowPass(8000f32), 1));
    graph.add_edge(microphone, 0, filter, 0).unwrap();
    graph.add_edge(filter, 0, speaker, 0).unwrap();

    let lfo = graph.add_node(Oscillator::new(oscillator::Sine).freq(10.0));
    let oscillator = graph.add_node(
        Oscillator::new(Tri(PolyBlep)).freq(440.0).lfo_intensity(0.1)
    );
    let gain = graph.add_node(Gain::new(-6.0, 1));
    graph.add_edge(lfo, 0, oscillator, 0).unwrap();
    graph.add_edge(oscillator, 0, gain, 0).unwrap();
    graph.add_edge(gain, 0, speaker, 1).unwrap();

    println!("Playing. Press Enter to quit...");
    graph.tick_until_enter();
    println!("Done!");
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::{color, Backend, Color};
use text_ui::pane::Pane;
//use text_ui::widget::DbgDump;
use text_ui::widget::Widget;
use text_ui::widget::{shared, Line, Linear, Readline, Shared, Text};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::sync::{Arc, RwLock};
use std::thread;

use tokio::prelude::Future;
use weaver::{WeaverClient, WeaverCommand, WeaverNotification, WeaverState};

struct WeaverStateWidget {
    state: Shared<WeaverState>,
}

fn render_command_summary(cmd: &WeaverCommand, width: usize) -> Pane {
    let mut pane = Pane::new_width(width);
    let mut offset: usize = 0;
    let status = match cmd.status {
        None => '…',
        Some(0) => '✔',
        _ => '❌',
    };
    let command_line = format!("{} {}", status, cmd.cmd.clone());
    let command_line = text_to_lines(command_line, width as usize);
    let pos = Position::new(0, offset as u16);
    let textlen = command_line.len();
    offset += textlen;
    let size = Size::new(width as u16, textlen as u16);
    pane.push_child(Pane::new_styled(pos, size, command_line, "command"));
    if cmd.stdout.len() > 0 {
        let stdout = text_to_lines(cmd.stdout.clone(), size.width as usize);
        let pos = Position::new(0, offset as u16);
        let textlen = stdout.len();
        offset += textlen;
        let size = Size::new(width as u16, textlen as u16);
        pane.push_child(Pane::new_styled(pos, size, stdout, "stdout"));
    }
    if cmd.stderr.len() > 0 {
        let stderr = text_to_lines(cmd.stderr.clone(), size.width as usize);
        let pos = Position::new(0, offset as u16);
        let textlen = stderr.len();
        let size = Size::new(width as u16, textlen as u16);
        pane.push_child(Pane::new_styled(pos, size, stderr, "stderr"));
    }
    pane
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let height = size.height as usize;
        let mut ctr: usize = 0;
        let state = self.state.read().unwrap();
        let mut children: Vec<Pane> = vec![];
        for (_i, cmd) in state.command_history.iter().rev() {
            let child = render_command_summary(cmd, size.width as usize);
            let offset = child.size.height as usize;

            ctr += offset;
            if ctr >= height {
                // XXX Need to be able to clip the top of the overflow
                break;
            }
            let child = child.offset(Position::new(0, (height - ctr) as u16));
            children.push(child);
        }
        Some(children)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<Readline>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(Readline::new());
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget {
            state: state.clone(),
        });
        //let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        let mut contentbox = Linear::hbox();
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().finalize();
        self.state
            .write()
            .unwrap()
            .run_command(text.clone())
            .unwrap();
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            k => self.input.write().unwrap().process_key(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
    fn style(&self, name: &str) -> (Option<Box<Color>>, Option<Box<Color>>) {
        match name {
            "command" => (None, Some(Box::new(color::Rgb(16, 16, 32)))),
            "stderr" => (None, Some(Box::new(color::Rgb(32, 16, 16)))),
            "input" => (None, Some(Box::new(color::Rgb(32, 32, 32)))),
            _ => (None, None),
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<commit_msg>Option+Enter = newline<commit_after>extern crate futures;
extern crate text_ui;
extern crate tokio;
extern crate tokio_serde_msgpack;
extern crate tokio_uds;
extern crate weaver;

use text_ui::app::App;
use text_ui::backend::{color, Backend, Color};
use text_ui::pane::Pane;
//use text_ui::widget::DbgDump;
use text_ui::widget::Widget;
use text_ui::widget::{shared, Line, Linear, Readline, Shared, Text};
use text_ui::{text_to_lines, Event, Input, Key, Position, Size};

use std::sync::{Arc, RwLock};
use std::thread;

use tokio::prelude::Future;
use weaver::{WeaverClient, WeaverCommand, WeaverNotification, WeaverState};

struct WeaverStateWidget {
    state: Shared<WeaverState>,
}

fn render_command_summary(cmd: &WeaverCommand, width: usize) -> Pane {
    let mut pane = Pane::new_width(width);
    let mut offset: usize = 0;
    let status = match cmd.status {
        None => '…',
        Some(0) => '✔',
        _ => '❌',
    };
    let command_line = format!("{} {}", status, cmd.cmd.clone());
    let command_line = text_to_lines(command_line, width as usize);
    let pos = Position::new(0, offset as u16);
    let textlen = command_line.len();
    offset += textlen;
    let size = Size::new(width as u16, textlen as u16);
    pane.push_child(Pane::new_styled(pos, size, command_line, "command"));
    if cmd.stdout.len() > 0 {
        let stdout = text_to_lines(cmd.stdout.clone(), size.width as usize);
        let pos = Position::new(0, offset as u16);
        let textlen = stdout.len();
        offset += textlen;
        let size = Size::new(width as u16, textlen as u16);
        pane.push_child(Pane::new_styled(pos, size, stdout, "stdout"));
    }
    if cmd.stderr.len() > 0 {
        let stderr = text_to_lines(cmd.stderr.clone(), size.width as usize);
        let pos = Position::new(0, offset as u16);
        let textlen = stderr.len();
        let size = Size::new(width as u16, textlen as u16);
        pane.push_child(Pane::new_styled(pos, size, stderr, "stderr"));
    }
    pane
}

impl Widget for WeaverStateWidget {
    fn render_children(&self, size: Size) -> Option<Vec<Pane>> {
        let height = size.height as usize;
        let mut ctr: usize = 0;
        let state = self.state.read().unwrap();
        let mut children: Vec<Pane> = vec![];
        for (_i, cmd) in state.command_history.iter().rev() {
            let child = render_command_summary(cmd, size.width as usize);
            let offset = child.size.height as usize;

            ctr += offset;
            if ctr >= height {
                // XXX Need to be able to clip the top of the overflow
                break;
            }
            let child = child.offset(Position::new(0, (height - ctr) as u16));
            children.push(child);
        }
        Some(children)
    }
}

struct WeaverTui {
    log: Shared<Text>,
    input: Shared<Readline>,
    vbox: Shared<Linear>,
    content: Shared<Linear>,
    state: Shared<WeaverState>,
    statew: Shared<WeaverStateWidget>,
    show_debug: bool,
}

impl WeaverTui {
    fn new(state: Arc<RwLock<WeaverState>>) -> WeaverTui {
        let log = shared(Text::new(vec![]));
        let input = shared(Readline::new());
        let state: Shared<WeaverState> = state.into();
        let statew = shared(WeaverStateWidget {
            state: state.clone(),
        });
        //let dbgdump = shared(DbgDump::new(&state));
        let show_debug = false;
        let mut contentbox = Linear::hbox();
        //contentbox.push(&dbgdump);
        contentbox.push(&statew);
        let content = shared(contentbox);
        let mut mainbox = Linear::vbox();
        mainbox.push(&content);
        mainbox.push(&input);
        let vbox = shared(mainbox);
        WeaverTui {
            log,
            input,
            vbox,
            content,
            state,
            statew,
            show_debug,
        }
    }

    fn toggle_debug(&mut self) {
        let mut content = self.content.write().unwrap();
        match self.show_debug {
            true => {
                self.show_debug = false;
                content.contents.truncate(0);
                content.push(&self.statew);
            }
            false => {
                self.show_debug = true;
                content.contents.truncate(0);
                content.push(&self.statew);
                content.push(&shared(Line::vertical()));
                content.push(&self.log);
            }
        }
    }

    fn submit_input(&mut self) {
        let text = self.input.write().unwrap().finalize();
        self.state
            .write()
            .unwrap()
            .run_command(text.clone())
            .unwrap();
    }

    fn log_msg(&mut self, msg: &str) {
        let lines: Vec<String> = msg.lines().map(|l| l.to_owned()).collect();
        self.log.write().unwrap().lines.extend(lines);
    }

    fn input(&mut self, key: Key) {
        match key {
            Key::Char('\n') => self.submit_input(),
            Key::Alt('\r') => self.input.write().unwrap().process_key(Key::Char('\n')),
            k => self.input.write().unwrap().process_key(k),
        }
    }
}

impl App for WeaverTui {
    type UI = Shared<Linear>;
    type MyEvent = WeaverNotification;
    fn widget(&self) -> Self::UI {
        self.vbox.clone()
    }
    fn handle_event(&mut self, event: Event<Self::MyEvent>) -> Result<(), Option<String>> {
        match event {
            Event::InputEvent(i) => match i {
                Input::Key(Key::Esc) => Err(None),
                Input::Key(Key::Alt('d')) => {
                    self.toggle_debug();
                    Ok(())
                }
                Input::Key(k) => {
                    self.input(k);
                    Ok(())
                }
                _ => Ok(()),
            },
            Event::AppEvent(_) => {
                self.log_msg(&format!("{:?}", event));
                Ok(())
            }
        }
    }
    fn style(&self, name: &str) -> (Option<Box<Color>>, Option<Box<Color>>) {
        match name {
            "command" => (None, Some(Box::new(color::Rgb(16, 16, 32)))),
            "stderr" => (None, Some(Box::new(color::Rgb(32, 16, 16)))),
            "input" => (None, Some(Box::new(color::Rgb(32, 32, 32)))),
            _ => (None, None),
        }
    }
}

fn main() {
    let be = Backend::new();
    let sender = be.sender.clone();
    let weaver = WeaverClient::new(sender);
    let mut app = WeaverTui::new(weaver.state.clone());
    thread::spawn(move || {
        tokio::run(weaver.map_err(|e| panic!("Client Error: {:#?}", e)));
    });
    app.log_msg("Esc to exit");
    be.run_app(&mut app);
}
<|endoftext|>


<commit_before>extern crate kafka;
extern crate getopts;
extern crate env_logger;

use std::{env, io, fmt, process};
use std::ascii::AsciiExt;

use kafka::consumer::{Consumer, GroupOffsetStorage};

/// This is a very simple command line application reading from a
/// specific kafka topic and dumping the messages to standard output.
fn main() {
    env_logger::init().unwrap();

    let cfg = match Config::from_cmdline() {
        Ok(cfg) => cfg,
        Err(e) => {
            println!("{}", e);
            process::exit(1);
        }
    };
    if let Err(e) = process(cfg) {
        println!("{}", e);
        process::exit(1);
    }
}

fn process(cfg: Config) -> Result<(), Error> {
    let mut c =
        try!(Consumer::from_hosts(cfg.brokers, cfg.topic)
             .with_group(cfg.group)
             .with_fetch_max_wait_time(1000)
             .with_fetch_min_bytes(1_000)
             .with_fetch_max_bytes_per_partition(100_000)
             .with_retry_max_bytes_limit(1_000_000)
             .with_offset_storage(cfg.offset_storage)
             .create());

    let do_commit = !cfg.no_commit;
    loop {
        for ms in try!(c.poll()).iter() {
            for m in ms.messages() {
                let s = String::from_utf8_lossy(m.value);
                println!("{}:{}@{}: {}", ms.topic(), ms.partition(), m.offset, s.trim());
            }
            c.consume_messageset(ms);
        }
        if do_commit {
            try!(c.commit_consumed());
        }
    }
}

// --------------------------------------------------------------------

enum Error {
    Kafka(kafka::error::Error),
    Io(io::Error),
    Literal(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::Kafka(ref e) => write!(f, "kafka-error: {}", e),
            &Error::Io(ref e) => write!(f, "io-error: {}", e),
            &Error::Literal(ref s) => write!(f, "{}", s),
        }
    }
}

impl From<kafka::error::Error> for Error {
    fn from(e: kafka::error::Error) -> Self { Error::Kafka(e) }
}

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

// --------------------------------------------------------------------

struct Config {
    brokers: Vec<String>,
    group: String,
    topic: String,
    no_commit: bool,
    offset_storage: GroupOffsetStorage,
}

impl Config {
    fn from_cmdline() -> Result<Config, Error> {
        let args: Vec<_> = env::args().collect();
        let mut opts = getopts::Options::new();
        opts.optflag("h", "help", "Print this help screen");
        opts.optopt("", "brokers", "Specify kafka brokers (comma separated)", "HOSTS");
        opts.optopt("", "topic", "Specify target topic", "NAME");
        opts.optopt("", "group", "Specify the consumer group", "NAME");
        opts.optflag("", "no-commit", "Do not commit consumed messages");
        opts.optopt("", "offsets", "Specify the offset store [zookeeper, kafka]", "STORE");

        let m = match opts.parse(&args[1..]) {
            Ok(m) => m,
            Err(e) => return Err(Error::Literal(e.to_string())),
        };
        if m.opt_present("help") {
            let brief = format!("{} [options]", args[0]);
            return Err(Error::Literal(opts.usage(&brief)));
        }

        let mut offset_storage = GroupOffsetStorage::Zookeeper;
        if let Some(s) = m.opt_str("offsets") {
            if s.eq_ignore_ascii_case("zookeeper") {
                offset_storage = GroupOffsetStorage::Zookeeper;
            } else if s.eq_ignore_ascii_case("kafka") {
                offset_storage = GroupOffsetStorage::Kafka;
            } else {
                return Err(Error::Literal(format!("unknown offset store: {}", s)));
            }
        }
        Ok(Config {
            brokers: m.opt_str("brokers")
                .unwrap_or_else(|| "localhost:9092".to_owned())
                .split(',')
                .map(|s| s.trim().to_owned())
                .collect(),
            group: m.opt_str("group")
                .unwrap_or_else(|| String::new()),
            topic: m.opt_str("topic")
                .unwrap_or_else(|| "my-topic".to_owned()),
            no_commit: m.opt_present("no-commit"),
            offset_storage: offset_storage,
        })
   }
}
<commit_msg>Make console consumer print message in raw format<commit_after>extern crate kafka;
extern crate getopts;
extern crate env_logger;

use std::{env, fmt, process};
use std::io::{self, Write};
use std::ascii::AsciiExt;

use kafka::consumer::{Consumer, GroupOffsetStorage};

/// This is a very simple command line application reading from a
/// specific kafka topic and dumping the messages to standard output.
fn main() {
    env_logger::init().unwrap();

    let cfg = match Config::from_cmdline() {
        Ok(cfg) => cfg,
        Err(e) => {
            println!("{}", e);
            process::exit(1);
        }
    };
    if let Err(e) = process(cfg) {
        println!("{}", e);
        process::exit(1);
    }
}

fn process(cfg: Config) -> Result<(), Error> {
    let mut c =
        try!(Consumer::from_hosts(cfg.brokers, cfg.topic)
             .with_group(cfg.group)
             .with_fetch_max_wait_time(1000)
             .with_fetch_min_bytes(1_000)
             .with_fetch_max_bytes_per_partition(100_000)
             .with_retry_max_bytes_limit(1_000_000)
             .with_offset_storage(cfg.offset_storage)
             .create());

    let stdout = io::stdout();
    let mut stdout = stdout.lock();
    let mut buf = Vec::with_capacity(1024);

    let do_commit = !cfg.no_commit;
    loop {
        for ms in try!(c.poll()).iter() {
            for m in ms.messages() {
                // ~ clear the output buffer
                unsafe { buf.set_len(0) };
                // ~ format the message for output
                let _ = write!(buf, "{}:{}@{}:\n", ms.topic(), ms.partition(), m.offset);
                buf.extend_from_slice(m.value);
                buf.push(b'\n');
                // ~ write to output channel
                try!(stdout.write_all(&buf));
            }
            c.consume_messageset(ms);
        }
        if do_commit {
            try!(c.commit_consumed());
        }
    }
}

// --------------------------------------------------------------------

enum Error {
    Kafka(kafka::error::Error),
    Io(io::Error),
    Literal(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Error::Kafka(ref e) => write!(f, "kafka-error: {}", e),
            &Error::Io(ref e) => write!(f, "io-error: {}", e),
            &Error::Literal(ref s) => write!(f, "{}", s),
        }
    }
}

impl From<kafka::error::Error> for Error {
    fn from(e: kafka::error::Error) -> Self { Error::Kafka(e) }
}

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

// --------------------------------------------------------------------

struct Config {
    brokers: Vec<String>,
    group: String,
    topic: String,
    no_commit: bool,
    offset_storage: GroupOffsetStorage,
}

impl Config {
    fn from_cmdline() -> Result<Config, Error> {
        let args: Vec<_> = env::args().collect();
        let mut opts = getopts::Options::new();
        opts.optflag("h", "help", "Print this help screen");
        opts.optopt("", "brokers", "Specify kafka brokers (comma separated)", "HOSTS");
        opts.optopt("", "topic", "Specify target topic", "NAME");
        opts.optopt("", "group", "Specify the consumer group", "NAME");
        opts.optflag("", "no-commit", "Do not commit consumed messages");
        opts.optopt("", "offsets", "Specify the offset store [zookeeper, kafka]", "STORE");

        let m = match opts.parse(&args[1..]) {
            Ok(m) => m,
            Err(e) => return Err(Error::Literal(e.to_string())),
        };
        if m.opt_present("help") {
            let brief = format!("{} [options]", args[0]);
            return Err(Error::Literal(opts.usage(&brief)));
        }

        let mut offset_storage = GroupOffsetStorage::Zookeeper;
        if let Some(s) = m.opt_str("offsets") {
            if s.eq_ignore_ascii_case("zookeeper") {
                offset_storage = GroupOffsetStorage::Zookeeper;
            } else if s.eq_ignore_ascii_case("kafka") {
                offset_storage = GroupOffsetStorage::Kafka;
            } else {
                return Err(Error::Literal(format!("unknown offset store: {}", s)));
            }
        }
        Ok(Config {
            brokers: m.opt_str("brokers")
                .unwrap_or_else(|| "localhost:9092".to_owned())
                .split(',')
                .map(|s| s.trim().to_owned())
                .collect(),
            group: m.opt_str("group")
                .unwrap_or_else(|| String::new()),
            topic: m.opt_str("topic")
                .unwrap_or_else(|| "my-topic".to_owned()),
            no_commit: m.opt_present("no-commit"),
            offset_storage: offset_storage,
        })
   }
}
<|endoftext|>


<commit_before>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs, io};
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
    #[error("OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.\nFile Count: {}\nFilepath: {}", .0, .1.to_string_lossy().into_owned())]
    OddResultsCountErr(usize, PathBuf),
    #[error("BadGroupSizeErr: Expected two results per group, one for each branch-project pair.\nCount: {}\nGroup: {:?}", .0, .1.into_iter().map(|group| (&group.version[..], &group.run[..])).collect::<Vec<(&str, &str)>>())]
    BadGroupSizeErr(usize, Vec<MeasurementGroup>),
}

fn calculate(metric: &str, baseline: &Measurement, dev: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.05; // 5% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median.clone(),
                dev: dev.median.clone(),
            }
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev.clone(),
                dev: dev.stddev.clone(),
            }
        },
    ]
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Calculation>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Calculation> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| x.run.clone())
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            match g.len() {
                2 => calculate(&g[0].run, &g[0].measurement, &g[1].measurement).into_iter().map(Ok).collect(),
                i => vec![Err(TestError::BadGroupSizeErr(i, g))],
            }
        })
        .flatten()
        .collect::<Result<Vec<Calculation>, TestError>>()?;

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v.len() {
            0 => Err(TestError::NoResultsErr(results_directory.clone())),
            i if i % 2 == 1 => Err(TestError::OddResultsCountErr(i, results_directory.clone())),
            _ => Ok(()),
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        calculate_regressions(&v_next)
    })
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::OddResultsCountErr(3, Path::new("dummy/path/no_file/").to_path_buf()),
                r#"OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.
File Count: 3
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadGroupSizeErr(1, vec![MeasurementGroup {
                    version: "dev".to_owned(),
                    run: "some command".to_owned(),
                    measurement: Measurement {
                        command: "some command".to_owned(),
                        mean: 1.0,
                        stddev: 1.0,
                        median: 1.0,
                        user: 1.0,
                        system: 1.0,
                        min: 1.0,
                        max: 1.0,
                        times: vec![1.0, 1.1, 0.9, 1.0, 1.1, 0.9, 1.1],
                    }
                }]),
                r#"BadGroupSizeErr: Expected two results per group, one for each branch-project pair.
Count: 1
Group: [("dev", "some command")]"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<commit_msg>enforce branch names at calculation time<commit_after>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::{fs, io};
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
    #[error("OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.\nFile Count: {}\nFilepath: {}", .0, .1.to_string_lossy().into_owned())]
    OddResultsCountErr(usize, PathBuf),
    #[error("BadGroupSizeErr: Expected two results per group, one for each branch-project pair.\nCount: {}\nGroup: {:?}", .0, .1.into_iter().map(|group| (&group.version[..], &group.run[..])).collect::<Vec<(&str, &str)>>())]
    BadGroupSizeErr(usize, Vec<MeasurementGroup>),
    #[error("BadBranchNameErr: Branch names must be 'baseline' and 'dev'.\nFound: {}, {}", .0, .1)]
    BadBranchNameErr(String, String),
}

fn calculate(metric: &str, dev: &Measurement, baseline: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.05; // 5% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median.clone(),
                dev: dev.median.clone(),
            }
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev.clone(),
                dev: dev.stddev.clone(),
            }
        },
    ]
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Calculation>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Calculation> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| x.run.clone())
        .into_iter()
        .map(|(_, group)| {
            let mut g: Vec<MeasurementGroup> = group.collect();
            g.sort_by(|x, y| x.version.cmp(&y.version));

            match g.len() {
                2 => {
                    let dev = &g[0];
                    let baseline = &g[1];

                    if dev.version == "dev" && baseline.version == "baseline" {
                        calculate(&dev.run, &dev.measurement, &baseline.measurement).into_iter().map(Ok).collect()
                    } else {
                        vec![Err(TestError::BadBranchNameErr(baseline.version.clone(), dev.version.clone()))]
                    }
                },
                i => vec![Err(TestError::BadGroupSizeErr(i, g))],
            }
        })
        .flatten()
        .collect::<Result<Vec<Calculation>, TestError>>()?;

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v.len() {
            0 => Err(TestError::NoResultsErr(results_directory.clone())),
            i if i % 2 == 1 => Err(TestError::OddResultsCountErr(i, results_directory.clone())),
            _ => Ok(()),
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        calculate_regressions(&v_next)
    })
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::OddResultsCountErr(3, Path::new("dummy/path/no_file/").to_path_buf()),
                r#"OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.
File Count: 3
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadGroupSizeErr(1, vec![MeasurementGroup {
                    version: "dev".to_owned(),
                    run: "some command".to_owned(),
                    measurement: Measurement {
                        command: "some command".to_owned(),
                        mean: 1.0,
                        stddev: 1.0,
                        median: 1.0,
                        user: 1.0,
                        system: 1.0,
                        min: 1.0,
                        max: 1.0,
                        times: vec![1.0, 1.1, 0.9, 1.0, 1.1, 0.9, 1.1],
                    }
                }]),
                r#"BadGroupSizeErr: Expected two results per group, one for each branch-project pair.
Count: 1
Group: [("dev", "some command")]"#
            ),
            (
                TestError::BadBranchNameErr("boop".to_owned(), "noop".to_owned()).to_path_buf(),
                r#"BadBranchNameErr: Branch names must be 'baseline' and 'dev'.
Found: boop, noop"#
            ),

        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<|endoftext|>


<commit_before>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::hash::Hash;

use scored::MinScored;
use super::visit::{
    Visitable,
    VisitMap,
    IntoEdges,
    EdgeRef,
};

use algo::Measure;

/// [Generic] A* shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to the goal node.
///
/// The graph should be `Visitable` and implement `IntoEdges`. The function `edge_cost` should
/// return the cost for a particular edge, which is used to compute path costs.  The function
/// `estimate_cost` should return the estimated cost to the goal for a particular node, also used
/// to compute path costs. Edge costs must be non-negative.
///
/// Returns a `HashMap` that maps `NodeId` to path cost.
pub fn astar<G, F, H, K>(graph: G, start: G::NodeId, goal: Option<G::NodeId>,
                         mut edge_cost: F, mut estimate_cost: H)
    -> HashMap<G::NodeId, K>
    where G: IntoEdges + Visitable,
          G::NodeId: Eq + Hash,
          F: FnMut(G::EdgeRef) -> K,
          H: FnMut(G::NodeId) -> K,
          K: Measure + Copy,
{
    let mut visited = graph.visit_map();
    let mut visit_next = BinaryHeap::new();
    let mut scores = HashMap::new();

    let zero_score = K::default();
    scores.insert(start, zero_score);
    visit_next.push(MinScored(estimate_cost(start), start));

    while let Some(MinScored(_, node)) = visit_next.pop() {
        if visited.is_visited(&node) {
            continue
        }

        if goal.as_ref() == Some(&node) {
            break
        }

        for edge in graph.edges(node) {
            let next = edge.target();
            if visited.is_visited(&next) {
                continue
            }

            let node_score = *scores.get(&node).unwrap();
            let mut next_score = node_score + edge_cost(edge);

            // TODO: understand this section
            match scores.entry(next) {
                Occupied(ent) => if next_score < *ent.get() {
                    *ent.into_mut() = next_score;
                } else {
                    next_score = *ent.get();
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                }
            }

            let next_estimate_score = next_score + estimate_cost(next);
            visit_next.push(MinScored(next_estimate_score, next));
        }
        visited.visit(node);
    }

    scores
}
<commit_msg>Rework astar to return the path found<commit_after>use std::collections::{
    HashMap,
    BinaryHeap,
};
use std::collections::hash_map::Entry::{
    Occupied,
    Vacant,
};

use std::hash::Hash;

use scored::MinScored;
use super::visit::{
    EdgeRef,
    GraphBase,
    IntoEdges,
    VisitMap,
    Visitable,
};

use algo::Measure;

/// [Generic] A* shortest path algorithm.
///
/// Compute the length of the shortest path from `start` to `finish`.
///
/// The graph should be `Visitable` and implement `IntoEdges`. The function `edge_cost` should
/// return the cost for a particular edge, which is used to compute path costs. The function
/// `estimate_cost` should return the estimated cost to the finish for a particular node, also used
/// to compute path costs. Edge costs must be non-negative.
///
/// Returns a Path of subsequent `NodeId` from start to finish, if one was found.
pub fn astar<G, F, H, K>(graph: G, start: G::NodeId, finish: G::NodeId,
                         mut edge_cost: F, mut estimate_cost: H)
    -> Option<Path<G>>
    where G: IntoEdges + Visitable,
          G::NodeId: Eq + Hash,
          F: FnMut(G::EdgeRef) -> K,
          H: FnMut(G::NodeId) -> K,
          K: Measure + Copy,
{
    let mut visited = graph.visit_map();
    let mut visit_next = BinaryHeap::new();
    let mut scores = HashMap::new();
    let mut path_tracker = PathTracker::<G>::new();

    let zero_score = K::default();
    scores.insert(start, zero_score);
    visit_next.push(MinScored(estimate_cost(start), start));

    while let Some(MinScored(_, node)) = visit_next.pop() {
        if node == finish {
            return Some(path_tracker.reconstruct_path_to(finish));
        }

        // Don't visit the same node several times, as the first time it was visited it was using
        // the shortest available path.
        if visited.is_visited(&node) {
            continue
        }

        visited.visit(node);

        // This lookup can be unwrapped without fear of panic since the node was necessarily scored
        // before adding him to `visit_next`.
        let node_score = *scores.get(&node).unwrap();

        for edge in graph.edges(node) {
            let next = edge.target();
            if visited.is_visited(&next) {
                continue
            }

            let mut next_score = node_score + edge_cost(edge);

            match scores.entry(next) {
                Occupied(ent) => {
                    let old_score = *ent.get();
                    if next_score < old_score {
                        *ent.into_mut() = next_score;
                        path_tracker.set_predecessor(next, node);
                    } else {
                        next_score = old_score;
                    }
                },
                Vacant(ent) => {
                    ent.insert(next_score);
                    path_tracker.set_predecessor(next, node);
                }
            }

            let next_estimate_score = next_score + estimate_cost(next);
            visit_next.push(MinScored(next_estimate_score, next));
        }
    }

    None
}

pub type Path<G: GraphBase> = Vec<G::NodeId>;

struct PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    came_from: HashMap<G::NodeId, G::NodeId>,
}

impl<G> PathTracker<G>
    where G: GraphBase,
          G::NodeId: Eq + Hash,
{
    fn new() -> PathTracker<G> {
        PathTracker {
            came_from: HashMap::new(),
        }
    }

    fn set_predecessor(&mut self, node: G::NodeId, previous: G::NodeId) {
        self.came_from.insert(node, previous);
    }

    fn reconstruct_path_to(&self, last: G::NodeId) -> Path<G> {
        let mut path = vec![last];

        let mut current = last;
        loop {
            if let Some(&previous) = self.came_from.get(&current) {
                path.push(previous);
                current = previous;
            } else {
                break
            }
        }

        path.reverse();

        path
    }
}
<|endoftext|>


<commit_before>use std::process;
use std::process::{Command, Stdio};

fn main() {
    #[cfg(target_os = "macos")]
    fn inner() -> &'static str {
        "open"
    }
    #[cfg(target_os = "windows")]
    fn inner() -> &'static str {
        "powershell"
    }
    #[cfg(target_os = "linux")]
    fn inner() -> &'static str {
        "xdg-open"
    }
    #[cfg(target_os = "unix")]
    fn inner() -> &'static str {
        "xdg-open"
    }

    #[cfg(not(target_os = "windows"))]
    fn arg() -> [&'static str; 1] {
        ["http://groonga.org/docs/"]
    }
    #[cfg(target_os = "windows")]
    fn arg() -> [&'static str; 3] {
        ["-Command", "Start-Process", "http://groonga.org/docs"]
    }

    let command = inner();
    let arg = arg();
    let err = match Command::new(command)
        .args(&arg)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn() {
        Ok(_) => return (),
        Err(e) => e,
    };
    println!("Failed to execute docopen subcommand. reason: {:?}", err);
    process::exit(1);

}
<commit_msg>grnenv-docopen: Cosme<commit_after>use std::process;
use std::process::{Command, Stdio};

fn main() {
    #[cfg(target_os = "macos")]
    fn inner() -> &'static str {
        "open"
    }
    #[cfg(target_os = "windows")]
    fn inner() -> &'static str {
        "powershell"
    }
    #[cfg(target_os = "linux")]
    fn inner() -> &'static str {
        "xdg-open"
    }
    #[cfg(target_os = "unix")]
    fn inner() -> &'static str {
        "xdg-open"
    }

    #[cfg(not(target_os = "windows"))]
    fn arg() -> [&'static str; 1] {
        ["http://groonga.org/docs/"]
    }
    #[cfg(target_os = "windows")]
    fn arg() -> [&'static str; 3] {
        ["-Command", "Start-Process", "http://groonga.org/docs"]
    }

    let command = inner();
    let arg = arg();
    match Command::new(command)
        .args(&arg)
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .spawn() {
        Ok(_) => return (),
        Err(e) => {
            println!("Failed to execute docopen subcommand. reason: {:?}", e);
            process::exit(1);
        }
    };
}
<|endoftext|>


<commit_before>use krate::Crate;
use schema::badges;
use util::CargoResult;

use diesel::pg::{Pg, PgConnection};
use diesel::prelude::*;
use serde_json;
use std::collections::HashMap;

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", tag = "badge_type", content = "attributes")]
pub enum Badge {
    TravisCi {
        repository: String,
        branch: Option<String>,
    },
    Appveyor {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    #[serde(rename = "gitlab")]
    GitLab {
        repository: String,
        branch: Option<String>,
    },
    IsItMaintainedIssueResolution { repository: String },
    IsItMaintainedOpenIssues { repository: String },
    Codecov {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    Coveralls {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct EncodableBadge {
    pub badge_type: String,
    pub attributes: HashMap<String, Option<String>>,
}

impl Queryable<badges::SqlType, Pg> for Badge {
    type Row = (i32, String, serde_json::Value);

    fn build((_, badge_type, attributes): Self::Row) -> Self {
        let json = json!({"badge_type": badge_type, "attributes": attributes});
        serde_json::from_value(json).expect("Invalid CI badge in the database")
    }
}

impl Badge {
    pub fn encodable(self) -> EncodableBadge {
        serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
    }

    pub fn badge_type(&self) -> &'static str {
        match *self {
            Badge::TravisCi { .. } => "travis-ci",
            Badge::Appveyor { .. } => "appveyor",
            Badge::GitLab { .. } => "gitlab",
            Badge::IsItMaintainedIssueResolution { .. } => "is-it-maintained-issue-resolution",
            Badge::IsItMaintainedOpenIssues { .. } => "is-it-maintained-open-issues",
            Badge::Codecov { .. } => "codecov",
            Badge::Coveralls { .. } => "coveralls",
        }
    }

    pub fn update_crate<'a>(
        conn: &PgConnection,
        krate: &Crate,
        badges: Option<&'a HashMap<String, HashMap<String, String>>>,
    ) -> CargoResult<Vec<&'a str>> {
        use diesel::{insert, delete};

        #[derive(Insertable)]
        #[table_name = "badges"]
        struct NewBadge<'a> {
            crate_id: i32,
            badge_type: &'a str,
            attributes: serde_json::Value,
        }

        let mut invalid_badges = vec![];
        let mut new_badges = vec![];

        if let Some(badges) = badges {
            for (k, v) in badges {
                let attributes_json = serde_json::to_value(v).unwrap();

                let json = json!({"badge_type": k, "attributes": attributes_json});
                if serde_json::from_value::<Badge>(json).is_ok() {
                    new_badges.push(NewBadge {
                        crate_id: krate.id,
                        badge_type: &**k,
                        attributes: attributes_json,
                    });
                } else {
                    invalid_badges.push(&**k);
                }
            }
        }

        conn.transaction(|| {
            delete(badges::table.filter(badges::crate_id.eq(krate.id)))
                .execute(conn)?;
            insert(&new_badges).into(badges::table).execute(conn)?;
            Ok(invalid_badges)
        })
    }
}
<commit_msg>Remove unused method (badge_type)<commit_after>use krate::Crate;
use schema::badges;
use util::CargoResult;

use diesel::pg::{Pg, PgConnection};
use diesel::prelude::*;
use serde_json;
use std::collections::HashMap;

#[derive(Debug, PartialEq, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case", tag = "badge_type", content = "attributes")]
pub enum Badge {
    TravisCi {
        repository: String,
        branch: Option<String>,
    },
    Appveyor {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    #[serde(rename = "gitlab")]
    GitLab {
        repository: String,
        branch: Option<String>,
    },
    IsItMaintainedIssueResolution { repository: String },
    IsItMaintainedOpenIssues { repository: String },
    Codecov {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
    Coveralls {
        repository: String,
        branch: Option<String>,
        service: Option<String>,
    },
}

#[derive(PartialEq, Debug, Serialize, Deserialize)]
pub struct EncodableBadge {
    pub badge_type: String,
    pub attributes: HashMap<String, Option<String>>,
}

impl Queryable<badges::SqlType, Pg> for Badge {
    type Row = (i32, String, serde_json::Value);

    fn build((_, badge_type, attributes): Self::Row) -> Self {
        let json = json!({"badge_type": badge_type, "attributes": attributes});
        serde_json::from_value(json).expect("Invalid CI badge in the database")
    }
}

impl Badge {
    pub fn encodable(self) -> EncodableBadge {
        serde_json::from_value(serde_json::to_value(self).unwrap()).unwrap()
    }

    pub fn update_crate<'a>(
        conn: &PgConnection,
        krate: &Crate,
        badges: Option<&'a HashMap<String, HashMap<String, String>>>,
    ) -> CargoResult<Vec<&'a str>> {
        use diesel::{insert, delete};

        #[derive(Insertable)]
        #[table_name = "badges"]
        struct NewBadge<'a> {
            crate_id: i32,
            badge_type: &'a str,
            attributes: serde_json::Value,
        }

        let mut invalid_badges = vec![];
        let mut new_badges = vec![];

        if let Some(badges) = badges {
            for (k, v) in badges {
                let attributes_json = serde_json::to_value(v).unwrap();

                let json = json!({"badge_type": k, "attributes": attributes_json});
                if serde_json::from_value::<Badge>(json).is_ok() {
                    new_badges.push(NewBadge {
                        crate_id: krate.id,
                        badge_type: &**k,
                        attributes: attributes_json,
                    });
                } else {
                    invalid_badges.push(&**k);
                }
            }
        }

        conn.transaction(|| {
            delete(badges::table.filter(badges::crate_id.eq(krate.id)))
                .execute(conn)?;
            insert(&new_badges).into(badges::table).execute(conn)?;
            Ok(invalid_badges)
        })
    }
}
<|endoftext|>


<commit_before>//! Send a test event to pagerduty
//!
//! Usage:
//!     cargo run --bin test-pagerduty event_type [description]
//!
//! Event type can be trigger, acknowledge, or resolve

#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use std::env::args;

use anyhow::Result;

fn main() -> Result<()> {
    let args = args().collect::<Vec<_>>();

    let event_type = &*args[1];
    let description = args.get(2).cloned();

    let event = match event_type {
        "trigger" => on_call::Event::Trigger {
            incident_key: Some("test".into()),
            description: description.unwrap_or_else(|| "Test event".into()),
        },
        "acknowledge" => on_call::Event::Acknowledge {
            incident_key: "test".into(),
            description,
        },
        "resolve" => on_call::Event::Resolve {
            incident_key: "test".into(),
            description,
        },
        _ => panic!("Event type must be trigger, acknowledge, or resolve"),
    };
    event.send()
}
<commit_msg>bin/test-pagerduty: Use `clap` for CLI argument parsing<commit_after>#![warn(clippy::all, rust_2018_idioms)]

mod on_call;

use anyhow::Result;
use clap::Clap;
use failure::_core::str::FromStr;

#[derive(Debug)]
enum EventType {
    Trigger,
    Acknowledge,
    Resolve,
}

impl FromStr for EventType {
    type Err = &'static str;

    fn from_str(value: &str) -> Result<Self, Self::Err> {
        match value {
            "trigger" => Ok(EventType::Trigger),
            "acknowledge" => Ok(EventType::Acknowledge),
            "resolve" => Ok(EventType::Resolve),
            _ => Err("Event type must be trigger, acknowledge, or resolve"),
        }
    }
}

#[derive(Clap, Debug)]
#[clap(name = "test-pagerduty", about = "Send a test event to pagerduty")]
struct Opts {
    #[clap(possible_values = &["trigger", "acknowledge", "resolve"])]
    event_type: EventType,
    description: Option<String>,
}

fn main() -> Result<()> {
    let opts: Opts = Opts::parse();

    let event = match opts.event_type {
        EventType::Trigger => on_call::Event::Trigger {
            incident_key: Some("test".into()),
            description: opts.description.unwrap_or_else(|| "Test event".into()),
        },
        EventType::Acknowledge => on_call::Event::Acknowledge {
            incident_key: "test".into(),
            description: opts.description,
        },
        EventType::Resolve => on_call::Event::Resolve {
            incident_key: "test".into(),
            description: opts.description,
        },
    };
    event.send()
}
<|endoftext|>


<commit_before>//! Simple loader error.

use std::error;
use std::fmt;
use std::io;
use parser::binary::Error as ParseError;

/// Load result.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Load error.
#[derive(Debug, Clone)]
pub enum Error {
    /// Parse error (including I/O error).
    Parse(ParseError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            _ => write!(f, "{}", (self as &error::Error).description()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Parse(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Parse(ref err) => Some(err),
        }
    }
}

impl From<ParseError> for Error {
    fn from(e: ParseError) -> Self {
        Error::Parse(e)
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Parse(e.into())
    }
}
<commit_msg>Add variants to loader error<commit_after>//! Simple loader error.

use std::error;
use std::fmt;
use std::io;
use parser::binary::Error as ParseError;

/// Load result.
pub type Result<T> = ::std::result::Result<T, Error>;

/// Load error.
#[derive(Debug, Clone)]
pub enum Error {
    /// Attribute is invalid.
    ///
    /// This includes the cases below:
    /// - The node has too few or too many node attributes
    /// - The node has wrong types of node attribute values.
    /// - The node has wrong (or unsupported) values of node attributes values.
    InvalidAttribute(String),
    /// Required node is missing.
    MissingNode(String),
    /// Parse error (including I/O error).
    Parse(ParseError),
    /// Got an unexpected node.
    UnexpectedNode(String),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::InvalidAttribute(ref name) => write!(f, "Invalid attribute for node: {}", name),
            Error::MissingNode(ref name) => write!(f, "Missing node: {}", name),
            Error::UnexpectedNode(ref name) => write!(f, "Unexpected node: {}", name),
            _ => write!(f, "{}", (self as &error::Error).description()),
        }
    }
}

impl error::Error for Error {
    fn description(&self) -> &str {
        match *self {
            Error::InvalidAttribute(_) => "Invalid node attribute",
            Error::MissingNode(_) => "Missing node",
            Error::UnexpectedNode(_) => "Unexpected node",
            Error::Parse(ref err) => err.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            Error::Parse(ref err) => Some(err),
            _ => None,
        }
    }
}

impl From<ParseError> for Error {
    fn from(e: ParseError) -> Self {
        Error::Parse(e)
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        Error::Parse(e.into())
    }
}
<|endoftext|>