<commit_before>use hyper::client::Client;
use hyper::client::response::Response;
use hyper::error::Error;
use hyper::header::Headers;
use hyper::method::Method;

/// Trait that represents some methods to send a specific request
pub trait GetResponse {
    /// Given a specific URL, get the header without the content body (useful to not waste time,
    /// ressources and informations)
    fn get_head_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the header without the content body (useful to not
    /// waste time, ressources and informations)
    fn get_head_response_using_headers(&self,
                                       url: &str,
                                       header: Headers)
                                       -> Result<Response, Error>;

    /// Given a specific URL, get the response from the target server
    fn get_http_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the response from the target server
    fn get_http_response_using_headers(&self,
                                       url: &str,
                                       header: Headers)
                                       -> Result<Response, Error>;
}

impl GetResponse for Client {
    fn get_head_response(&self, url: &str) -> Result<Response, Error> {
        self.request(Method::Head, url).send()
    }

    fn get_head_response_using_headers(&self,
                                       url: &str,
                                       custom_header: Headers)
                                       -> Result<Response, Error> {
        self.request(Method::Head, url).headers(custom_header).send()
    }

    fn get_http_response(&self, url: &str) -> Result<Response, Error> {
        self.get_http_response_using_headers(url, Headers::new())
    }

    fn get_http_response_using_headers(&self,
                                       url: &str,
                                       custom_header: Headers)
                                       -> Result<Response, Error> {
        self.request(Method::Get, url).headers(custom_header).send()
    }
}
<commit_msg>Code style<commit_after>use hyper::client::Client;
use hyper::client::response::Response;
use hyper::error::Error;
use hyper::header::Headers;
use hyper::method::Method;

/// Trait that represents some methods to send a specific request
pub trait GetResponse {
    /// Given a specific URL, get the header without the content body (useful to not waste time,
    /// ressources and informations)
    fn get_head_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the header without the content body (useful to not
    /// waste time, ressources and informations)
    fn get_head_response_using_headers(&self,
                                       url: &str,
                                       header: Headers)
                                       -> Result<Response, Error>;

    /// Given a specific URL, get the response from the target server
    fn get_http_response(&self, url: &str) -> Result<Response, Error>;

    /// Given a specific URL and an header, get the response from the target server
    fn get_http_response_using_headers(&self,
                                       url: &str,
                                       header: Headers)
                                       -> Result<Response, Error>;
}

impl GetResponse for Client {
    fn get_head_response(&self, url: &str) -> Result<Response, Error> {
        self.request(Method::Head, url).send()
    }

    fn get_head_response_using_headers(&self,
                                       url: &str,
                                       custom_header: Headers)
                                       -> Result<Response, Error> {
        self.request(Method::Head, url)
            .headers(custom_header)
            .send()
    }

    fn get_http_response(&self, url: &str) -> Result<Response, Error> {
        self.get_http_response_using_headers(url, Headers::new())
    }

    fn get_http_response_using_headers(&self,
                                       url: &str,
                                       custom_header: Headers)
                                       -> Result<Response, Error> {
        self.request(Method::Get, url)
            .headers(custom_header)
            .send()
    }
}
<|endoftext|>


<commit_before>//! Completes identifiers in format string literals.

use ide_db::helpers::format_string::is_format_string;
use itertools::Itertools;
use syntax::{ast, AstToken, TextRange, TextSize};

use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};

/// Complete identifiers in format strings.
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
    let string = match ast::String::cast(ctx.token.clone()) {
        Some(it) if is_format_string(&it) => it,
        _ => return,
    };
    let cursor = ctx.position.offset;
    let lit_start = ctx.token.text_range().start();
    let cursor_in_lit = cursor - lit_start;

    let prefix = &string.text()[..cursor_in_lit.into()];
    let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
    let brace_offset = match braces {
        // escaped brace
        Some(((_, '{'), (_, '{'))) => return,
        Some(((idx, '{'), _)) => lit_start + TextSize::from(idx as u32 + 1),
        _ => return,
    };

    let source_range = TextRange::new(brace_offset, cursor);
    ctx.locals.iter().for_each(|(name, _)| {
        CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
            .add_to(acc);
    })
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::tests::{check_edit, completion_list_no_kw};

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list_no_kw(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn no_completion_without_brace() {
        check(
            r#"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
let foobar = 1;
format_args!("f$0");
}
"#,
            expect![[]],
        );
    }

    #[test]
    fn completes_locals() {
        check_edit(
            "foobar",
            r#"
macro_rules! format_args {
    ($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
    let foobar = 1;
    format_args!("{f$0");
}
"#,
            r#"
macro_rules! format_args {
    ($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
    let foobar = 1;
    format_args!("{foobar");
}
"#,
        );
        check_edit(
            "foobar",
            r#"
macro_rules! format_args {
    ($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
    let foobar = 1;
    format_args!("{$0");
}
"#,
            r#"
macro_rules! format_args {
    ($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
    let foobar = 1;
    format_args!("{foobar");
}
"#,
        );
    }
}
<commit_msg>Temporarily disable format string completions<commit_after>//! Completes identifiers in format string literals.

use ide_db::helpers::format_string::is_format_string;
use itertools::Itertools;
use syntax::{ast, AstToken, TextRange, TextSize};

use crate::{context::CompletionContext, CompletionItem, CompletionItemKind, Completions};

/// Complete identifiers in format strings.
pub(crate) fn format_string(acc: &mut Completions, ctx: &CompletionContext) {
    if true {
        return;
    }
    let string = match ast::String::cast(ctx.token.clone()) {
        Some(it) if is_format_string(&it) => it,
        _ => return,
    };
    let cursor = ctx.position.offset;
    let lit_start = ctx.token.text_range().start();
    let cursor_in_lit = cursor - lit_start;

    let prefix = &string.text()[..cursor_in_lit.into()];
    let braces = prefix.char_indices().rev().skip_while(|&(_, c)| c.is_alphanumeric()).next_tuple();
    let brace_offset = match braces {
        // escaped brace
        Some(((_, '{'), (_, '{'))) => return,
        Some(((idx, '{'), _)) => lit_start + TextSize::from(idx as u32 + 1),
        _ => return,
    };

    let source_range = TextRange::new(brace_offset, cursor);
    ctx.locals.iter().for_each(|(name, _)| {
        CompletionItem::new(CompletionItemKind::Binding, source_range, name.to_smol_str())
            .add_to(acc);
    })
}

#[cfg(test)]
mod tests {
    use expect_test::{expect, Expect};

    use crate::tests::completion_list_no_kw;

    fn check(ra_fixture: &str, expect: Expect) {
        let actual = completion_list_no_kw(ra_fixture);
        expect.assert_eq(&actual);
    }

    #[test]
    fn no_completion_without_brace() {
        check(
            r#"
macro_rules! format_args {
($lit:literal $(tt:tt)*) => { 0 },
}
fn main() {
let foobar = 1;
format_args!("f$0");
}
"#,
            expect![[]],
        );
    }

    //     #[test]
    //     fn completes_locals() {
    //         check_edit(
    //             "foobar",
    //             r#"
    // macro_rules! format_args {
    //     ($lit:literal $(tt:tt)*) => { 0 },
    // }
    // fn main() {
    //     let foobar = 1;
    //     format_args!("{f$0");
    // }
    // "#,
    //             r#"
    // macro_rules! format_args {
    //     ($lit:literal $(tt:tt)*) => { 0 },
    // }
    // fn main() {
    //     let foobar = 1;
    //     format_args!("{foobar");
    // }
    // "#,
    //         );
    //         check_edit(
    //             "foobar",
    //             r#"
    // macro_rules! format_args {
    //     ($lit:literal $(tt:tt)*) => { 0 },
    // }
    // fn main() {
    //     let foobar = 1;
    //     format_args!("{$0");
    // }
    // "#,
    //             r#"
    // macro_rules! format_args {
    //     ($lit:literal $(tt:tt)*) => { 0 },
    // }
    // fn main() {
    //     let foobar = 1;
    //     format_args!("{foobar");
    // }
    // "#,
    //         );
    //     }
}
<|endoftext|>


<commit_before>/*
 * A SHA-1 implementation derived from Paul E. Jones's reference
 * implementation, which is written for clarity, not speed. At some
 * point this will want to be rewritten.
 */

export sha1;
export mk_sha1;

state type sha1 = state obj {
                        // Provide message input as bytes
                        fn input(&vec[u8]);

                        // Provide message input as string
                        fn input_str(&str);

                        // Read the digest as a vector of 20 bytes. After
                        // calling this no further input may provided
                        // until reset is called
                        fn result() -> vec[u8];

                        // Same as above, just a hex-string version.
                        fn result_str() -> str;

                        // Reset the sha1 state for reuse. This is called
                        // automatically during construction
                        fn reset();
};

// Some unexported constants
const uint digest_buf_len = 5;
const uint msg_block_len = 64;

// Builds a sha1 object
fn mk_sha1() -> sha1 {

    state type sha1state = rec(vec[mutable u32] h,
                               mutable u32 len_low,
                               mutable u32 len_high,
                               vec[mutable u8] msg_block,
                               mutable uint msg_block_idx,
                               mutable bool computed);

    fn add_input(&sha1state st, &vec[u8] msg) {
        // FIXME: Should be typestate precondition
        assert (!st.computed);

        for (u8 element in msg) {
            st.msg_block.(st.msg_block_idx) = element;
            st.msg_block_idx += 1u;

            st.len_low += 8u32;
            if (st.len_low == 0u32) {
                st.len_high += 1u32;
                if (st.len_high == 0u32) {
                    // FIXME: Need better failure mode
                    fail;
                }
            }

            if (st.msg_block_idx == msg_block_len) {
                process_msg_block(st);
            }
        }
    }

    fn process_msg_block(&sha1state st) {

        // FIXME: Make precondition
        assert (Vec.len(st.h) == digest_buf_len);

        // Constants
        auto k = vec(0x5A827999u32,
                     0x6ED9EBA1u32,
                     0x8F1BBCDCu32,
                     0xCA62C1D6u32);

        let int t; // Loop counter
        let vec[mutable u32] w = Vec.init_elt_mut[u32](0u32, 80u);

        // Initialize the first 16 words of the vector w
        t = 0;
        while (t < 16) {
            w.(t) = (st.msg_block.(t * 4) as u32) << 24u32;
            w.(t) = w.(t) | ((st.msg_block.(t * 4 + 1) as u32) << 16u32);
            w.(t) = w.(t) | ((st.msg_block.(t * 4 + 2) as u32) << 8u32);
            w.(t) = w.(t) | (st.msg_block.(t * 4 + 3) as u32);
            t += 1;
        }

        // Initialize the rest of vector w
        while (t < 80) {
            auto val = w.(t-3) ^ w.(t-8) ^ w.(t-14) ^ w.(t-16);
            w.(t) = circular_shift(1u32, val);
            t += 1;
        }

        auto a = st.h.(0);
        auto b = st.h.(1);
        auto c = st.h.(2);
        auto d = st.h.(3);
        auto e = st.h.(4);

        let u32 temp;

        t = 0;
        while (t < 20) {
            temp = circular_shift(5u32, a)
                + ((b & c) | ((~b) & d)) + e + w.(t) + k.(0);
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 40) {
            temp = circular_shift(5u32, a)
                + (b ^ c ^ d) + e + w.(t) + k.(1);
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 60) {
            temp = circular_shift(5u32, a)
                + ((b & c) | (b & d) | (c & d)) + e + w.(t) + k.(2);
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 80) {
            temp = circular_shift(5u32, a)
                + (b ^ c ^ d) + e + w.(t) + k.(3);
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        st.h.(0) = st.h.(0) + a;
        st.h.(1) = st.h.(1) + b;
        st.h.(2) = st.h.(2) + c;
        st.h.(3) = st.h.(3) + d;
        st.h.(4) = st.h.(4) + e;

        st.msg_block_idx = 0u;
    }

    fn circular_shift(u32 bits, u32 word) -> u32 {
        // FIXME: This is a workaround for a rustboot
        // "unrecognized quads" codegen bug
        auto bits_hack = bits;
        ret (word << bits_hack) | (word >> (32u32 - bits));
    }

    fn mk_result(&sha1state st) -> vec[u8] {
        if (!st.computed) {
            pad_msg(st);
            st.computed = true;
        }

        let vec[u8] res = vec();
        for (u32 hpart in st.h) {
            auto a = (hpart >> 24u32) & 0xFFu32 as u8;
            auto b = (hpart >> 16u32) & 0xFFu32 as u8;
            auto c = (hpart >> 8u32) & 0xFFu32 as u8;
            auto d = (hpart & 0xFFu32 as u8);
            res += vec(a,b,c,d);
        }
        ret res;
    }

    /*
     * According to the standard, the message must be padded to an even
     * 512 bits.  The first padding bit must be a '1'.  The last 64 bits
     * represent the length of the original message.  All bits in between
     * should be 0.  This function will pad the message according to those
     * rules by filling the msg_block vector accordingly.  It will also
     * call process_msg_block() appropriately.  When it returns, it
     * can be assumed that the message digest has been computed.
     */
    fn pad_msg(&sha1state st) {
        // FIXME: Should be a precondition
        assert (Vec.len(st.msg_block) == msg_block_len);

        /*
         * Check to see if the current message block is too small to hold
         * the initial padding bits and length.  If so, we will pad the
         * block, process it, and then continue padding into a second block.
         */
        if (st.msg_block_idx > 55u) {
            st.msg_block.(st.msg_block_idx) = 0x80u8;
            st.msg_block_idx += 1u;

            while (st.msg_block_idx < msg_block_len) {
                st.msg_block.(st.msg_block_idx) = 0u8;
                st.msg_block_idx += 1u;
            }

            process_msg_block(st);
        } else {
            st.msg_block.(st.msg_block_idx) = 0x80u8;
            st.msg_block_idx += 1u;
        }

        while (st.msg_block_idx < 56u) {
            st.msg_block.(st.msg_block_idx) = 0u8;
            st.msg_block_idx += 1u;
        }

        // Store the message length as the last 8 octets
        st.msg_block.(56) = (st.len_high >> 24u32) & 0xFFu32 as u8;
        st.msg_block.(57) = (st.len_high >> 16u32) & 0xFFu32 as u8;
        st.msg_block.(58) = (st.len_high >> 8u32) & 0xFFu32 as u8;
        st.msg_block.(59) = st.len_high & 0xFFu32 as u8;
        st.msg_block.(60) = (st.len_low >> 24u32) & 0xFFu32 as u8;
        st.msg_block.(61) = (st.len_low >> 16u32) & 0xFFu32 as u8;
        st.msg_block.(62) = (st.len_low >> 8u32) & 0xFFu32 as u8;
        st.msg_block.(63) = st.len_low & 0xFFu32 as u8;

        process_msg_block(st);
    }

    state obj sha1(sha1state st) {

        fn reset() {
            // FIXME: Should be typestate precondition
            assert (Vec.len(st.h) == digest_buf_len);

            st.len_low = 0u32;
            st.len_high = 0u32;
            st.msg_block_idx = 0u;

            st.h.(0) = 0x67452301u32;
            st.h.(1) = 0xEFCDAB89u32;
            st.h.(2) = 0x98BADCFEu32;
            st.h.(3) = 0x10325476u32;
            st.h.(4) = 0xC3D2E1F0u32;

            st.computed = false;
        }

        fn input(&vec[u8] msg) {
            add_input(st, msg);
        }

        fn input_str(&str msg) {
            add_input(st, Str.bytes(msg));
        }

        fn result() -> vec[u8] {
            ret mk_result(st);
        }

        fn result_str() -> str {
            auto r = mk_result(st);
            auto s = "";
            for (u8 b in r) {
                s += UInt.to_str(b as uint, 16u);
            }
            ret s;
        }
    }

    auto st = rec(h = Vec.init_elt_mut[u32](0u32, digest_buf_len),
                  mutable len_low = 0u32,
                  mutable len_high = 0u32,
                  msg_block = Vec.init_elt_mut[u8](0u8, msg_block_len),
                  mutable msg_block_idx = 0u,
                  mutable computed = false);
    auto sh = sha1(st);
    sh.reset();
    ret sh;
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<commit_msg>Remove unnecessary array access from SHA1<commit_after>/*
 * A SHA-1 implementation derived from Paul E. Jones's reference
 * implementation, which is written for clarity, not speed. At some
 * point this will want to be rewritten.
 */

export sha1;
export mk_sha1;

state type sha1 = state obj {
                        // Provide message input as bytes
                        fn input(&vec[u8]);

                        // Provide message input as string
                        fn input_str(&str);

                        // Read the digest as a vector of 20 bytes. After
                        // calling this no further input may provided
                        // until reset is called
                        fn result() -> vec[u8];

                        // Same as above, just a hex-string version.
                        fn result_str() -> str;

                        // Reset the sha1 state for reuse. This is called
                        // automatically during construction
                        fn reset();
};

// Some unexported constants
const uint digest_buf_len = 5;
const uint msg_block_len = 64;

const u32 k0 = 0x5A827999u32;
const u32 k1 = 0x6ED9EBA1u32;
const u32 k2 = 0x8F1BBCDCu32;
const u32 k3 = 0xCA62C1D6u32;

// Builds a sha1 object
fn mk_sha1() -> sha1 {

    state type sha1state = rec(vec[mutable u32] h,
                               mutable u32 len_low,
                               mutable u32 len_high,
                               vec[mutable u8] msg_block,
                               mutable uint msg_block_idx,
                               mutable bool computed);

    fn add_input(&sha1state st, &vec[u8] msg) {
        // FIXME: Should be typestate precondition
        assert (!st.computed);

        for (u8 element in msg) {
            st.msg_block.(st.msg_block_idx) = element;
            st.msg_block_idx += 1u;

            st.len_low += 8u32;
            if (st.len_low == 0u32) {
                st.len_high += 1u32;
                if (st.len_high == 0u32) {
                    // FIXME: Need better failure mode
                    fail;
                }
            }

            if (st.msg_block_idx == msg_block_len) {
                process_msg_block(st);
            }
        }
    }

    fn process_msg_block(&sha1state st) {

        // FIXME: Make precondition
        assert (Vec.len(st.h) == digest_buf_len);

        let int t; // Loop counter
        let vec[mutable u32] w = Vec.init_elt_mut[u32](0u32, 80u);

        // Initialize the first 16 words of the vector w
        t = 0;
        while (t < 16) {
            auto tmp;
            tmp = (st.msg_block.(t * 4) as u32) << 24u32;
            tmp = tmp | ((st.msg_block.(t * 4 + 1) as u32) << 16u32);
            tmp = tmp | ((st.msg_block.(t * 4 + 2) as u32) << 8u32);
            tmp = tmp | (st.msg_block.(t * 4 + 3) as u32);
            w.(t) = tmp;
            t += 1;
        }

        // Initialize the rest of vector w
        while (t < 80) {
            auto val = w.(t-3) ^ w.(t-8) ^ w.(t-14) ^ w.(t-16);
            w.(t) = circular_shift(1u32, val);
            t += 1;
        }

        auto a = st.h.(0);
        auto b = st.h.(1);
        auto c = st.h.(2);
        auto d = st.h.(3);
        auto e = st.h.(4);

        let u32 temp;

        t = 0;
        while (t < 20) {
            temp = circular_shift(5u32, a)
                + ((b & c) | ((~b) & d)) + e + w.(t) + k0;
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 40) {
            temp = circular_shift(5u32, a)
                + (b ^ c ^ d) + e + w.(t) + k1;
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 60) {
            temp = circular_shift(5u32, a)
                + ((b & c) | (b & d) | (c & d)) + e + w.(t) + k2;
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        while (t < 80) {
            temp = circular_shift(5u32, a)
                + (b ^ c ^ d) + e + w.(t) + k3;
            e = d;
            d = c;
            c = circular_shift(30u32, b);
            b = a;
            a = temp;
            t += 1;
        }

        st.h.(0) = st.h.(0) + a;
        st.h.(1) = st.h.(1) + b;
        st.h.(2) = st.h.(2) + c;
        st.h.(3) = st.h.(3) + d;
        st.h.(4) = st.h.(4) + e;

        st.msg_block_idx = 0u;
    }

    fn circular_shift(u32 bits, u32 word) -> u32 {
        // FIXME: This is a workaround for a rustboot
        // "unrecognized quads" codegen bug
        auto bits_hack = bits;
        ret (word << bits_hack) | (word >> (32u32 - bits));
    }

    fn mk_result(&sha1state st) -> vec[u8] {
        if (!st.computed) {
            pad_msg(st);
            st.computed = true;
        }

        let vec[u8] res = vec();
        for (u32 hpart in st.h) {
            auto a = (hpart >> 24u32) & 0xFFu32 as u8;
            auto b = (hpart >> 16u32) & 0xFFu32 as u8;
            auto c = (hpart >> 8u32) & 0xFFu32 as u8;
            auto d = (hpart & 0xFFu32 as u8);
            res += vec(a,b,c,d);
        }
        ret res;
    }

    /*
     * According to the standard, the message must be padded to an even
     * 512 bits.  The first padding bit must be a '1'.  The last 64 bits
     * represent the length of the original message.  All bits in between
     * should be 0.  This function will pad the message according to those
     * rules by filling the msg_block vector accordingly.  It will also
     * call process_msg_block() appropriately.  When it returns, it
     * can be assumed that the message digest has been computed.
     */
    fn pad_msg(&sha1state st) {
        // FIXME: Should be a precondition
        assert (Vec.len(st.msg_block) == msg_block_len);

        /*
         * Check to see if the current message block is too small to hold
         * the initial padding bits and length.  If so, we will pad the
         * block, process it, and then continue padding into a second block.
         */
        if (st.msg_block_idx > 55u) {
            st.msg_block.(st.msg_block_idx) = 0x80u8;
            st.msg_block_idx += 1u;

            while (st.msg_block_idx < msg_block_len) {
                st.msg_block.(st.msg_block_idx) = 0u8;
                st.msg_block_idx += 1u;
            }

            process_msg_block(st);
        } else {
            st.msg_block.(st.msg_block_idx) = 0x80u8;
            st.msg_block_idx += 1u;
        }

        while (st.msg_block_idx < 56u) {
            st.msg_block.(st.msg_block_idx) = 0u8;
            st.msg_block_idx += 1u;
        }

        // Store the message length as the last 8 octets
        st.msg_block.(56) = (st.len_high >> 24u32) & 0xFFu32 as u8;
        st.msg_block.(57) = (st.len_high >> 16u32) & 0xFFu32 as u8;
        st.msg_block.(58) = (st.len_high >> 8u32) & 0xFFu32 as u8;
        st.msg_block.(59) = st.len_high & 0xFFu32 as u8;
        st.msg_block.(60) = (st.len_low >> 24u32) & 0xFFu32 as u8;
        st.msg_block.(61) = (st.len_low >> 16u32) & 0xFFu32 as u8;
        st.msg_block.(62) = (st.len_low >> 8u32) & 0xFFu32 as u8;
        st.msg_block.(63) = st.len_low & 0xFFu32 as u8;

        process_msg_block(st);
    }

    state obj sha1(sha1state st) {

        fn reset() {
            // FIXME: Should be typestate precondition
            assert (Vec.len(st.h) == digest_buf_len);

            st.len_low = 0u32;
            st.len_high = 0u32;
            st.msg_block_idx = 0u;

            st.h.(0) = 0x67452301u32;
            st.h.(1) = 0xEFCDAB89u32;
            st.h.(2) = 0x98BADCFEu32;
            st.h.(3) = 0x10325476u32;
            st.h.(4) = 0xC3D2E1F0u32;

            st.computed = false;
        }

        fn input(&vec[u8] msg) {
            add_input(st, msg);
        }

        fn input_str(&str msg) {
            add_input(st, Str.bytes(msg));
        }

        fn result() -> vec[u8] {
            ret mk_result(st);
        }

        fn result_str() -> str {
            auto r = mk_result(st);
            auto s = "";
            for (u8 b in r) {
                s += UInt.to_str(b as uint, 16u);
            }
            ret s;
        }
    }

    auto st = rec(h = Vec.init_elt_mut[u32](0u32, digest_buf_len),
                  mutable len_low = 0u32,
                  mutable len_high = 0u32,
                  msg_block = Vec.init_elt_mut[u8](0u8, msg_block_len),
                  mutable msg_block_idx = 0u,
                  mutable computed = false);
    auto sh = sha1(st);
    sh.reset();
    ret sh;
}

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C .. 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
<|endoftext|>


<commit_before>
// use self::core::str::FromStr;
use url::{Url, ParseError};

use hyper;
use hyper::client::response::Response;
use hyper::header::ContentType;
use hyper::mime::Mime;
use hyper::header::Connection;
use hyper::header::ConnectionOption;
use hyper::error::HttpResult;

///
///
///
pub struct Client {
    url: Url,
    client: hyper::Client,
}

///
///
///
impl Client {

    ///
    ///
    ///
    pub fn new(url: &str) -> Result<Client, ParseError> {
        let url_parsed = match Url::parse(url) {
            Ok(u) => u,
            Err(e) => return Err(e),
        };

        Ok(Client {
            url: url_parsed,
            client: hyper::Client::new(),
        })
    }

    ///
    ///
    ///
    fn build_url(&self, to_add: &str) -> Result<Url, ParseError> {
        let base_url = &self.url.to_string();
        let new_url: String;
        if base_url.ends_with('/') ^ to_add.starts_with('/') {
            // One trailing or starting '/'
            new_url = format!("{}{}", base_url, to_add).to_string();
        } else if base_url.ends_with('/') && to_add.starts_with('/') {
            // Pair of trailing and starting '/'
            new_url = format!("{}{}", base_url.trim_right_matches('/'), to_add).to_string();
        } else {
            // No trailing or starting '/'
            new_url = format!("{}/{}", base_url, to_add).to_string();
        }

        Url::parse(&new_url)
    }

    ///
    ///
    ///
    pub fn get(&mut self, path: &str) -> HttpResult<Response> {
        let full_url = self.build_url(path).unwrap();
        self.client.get(full_url)
            // set a header
            .header(Connection(vec![ConnectionOption::Close]))
            .header(ContentType("application/json".parse::<Mime>().unwrap()))
            // let 'er go!
            .send()
    }

    ///
    ///
    ///
    pub fn get_series(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

    ///
    ///
    ///
    pub fn get_providers(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

    ///
    ///
    ///
    pub fn get_base_providers(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

}

#[cfg(test)]
mod test{
    extern crate url;
    use url::Url;
    use super::*;

    #[test]
    fn test_url_build() {
        for (url, path, built) in vec![
                ("http://e.co", "/p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co/", "/p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co", "p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co", "p/t/f.html?var=1", "http://e.co/p/t/f.html?var=1"),
            ] {
            let c = Client::new(url).unwrap();
            let built_url = c.build_url(path);
            assert!(Url::parse(built).unwrap() == built_url.unwrap(),
                    "assertion failed: url:{} path:{} built:{} expected:{}", url, path, built, Url::parse(built).unwrap());
        }
    }
}
<commit_msg>Added some doc<commit_after>//! A Web client to Fetcher Web UI
//!
//!

use url::{Url, ParseError};

use hyper;
use hyper::client::response::Response;
use hyper::header::ContentType;
use hyper::mime::Mime;
use hyper::header::Connection;
use hyper::header::ConnectionOption;
use hyper::error::HttpResult;

///
///
///
pub struct Client {
    url: Url,
    client: hyper::Client,
}

///
///
///
impl Client {

    ///
    /// #Example
    ///
    /// ```
    /// use fetcher::client::Client;
    ///
    /// let client = Client::new("http://127.0.0.1/").unwrap();
    /// ```
    pub fn new(url: &str) -> Result<Client, ParseError> {
        let url_parsed = match Url::parse(url) {
            Ok(u) => u,
            Err(e) => return Err(e),
        };

        Ok(Client {
            url: url_parsed,
            client: hyper::Client::new(),
        })
    }

    ///
    ///
    ///
    fn build_url(&self, to_add: &str) -> Result<Url, ParseError> {
        let base_url = &self.url.to_string();
        let new_url: String;
        if base_url.ends_with('/') ^ to_add.starts_with('/') {
            // One trailing or starting '/'
            new_url = format!("{}{}", base_url, to_add).to_string();
        } else if base_url.ends_with('/') && to_add.starts_with('/') {
            // Pair of trailing and starting '/'
            new_url = format!("{}{}", base_url.trim_right_matches('/'), to_add).to_string();
        } else {
            // No trailing or starting '/'
            new_url = format!("{}/{}", base_url, to_add).to_string();
        }

        Url::parse(&new_url)
    }

    /// Raw access to using the HTTP `GET` method at a given `path`
    /// from the base url given at construction.
    ///
    /// #Example
    ///
    /// ```
    /// use fetcher::client::Client;
    ///
    /// let mut client = Client::new("http://127.0.0.1/").unwrap();
    ///
    /// client.get("/api/");
    /// ```
    pub fn get(&mut self, path: &str) -> HttpResult<Response> {
        // FIXME: build_url cannot be asserted to be valid
        let full_url = self.build_url(path).unwrap();
        self.client.get(full_url)
            .header(Connection(vec![ConnectionOption::Close]))
            .header(ContentType("application/json".parse::<Mime>().unwrap()))
            .send()
    }

    ///
    ///
    ///
    pub fn get_series(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

    ///
    ///
    ///
    pub fn get_providers(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

    ///
    ///
    ///
    pub fn get_base_providers(&mut self) -> Result<(), ()> {
        unimplemented!();
    }

}

#[cfg(test)]
mod test{
    extern crate url;
    use url::Url;
    use super::*;

    #[test]
    fn test_url_build() {
        for (url, path, built) in vec![
                ("http://e.co", "/p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co/", "/p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co", "p/t/f.html", "http://e.co/p/t/f.html"),
                ("http://e.co", "p/t/f.html?var=1", "http://e.co/p/t/f.html?var=1"),
            ] {
            let c = Client::new(url).unwrap();
            let built_url = c.build_url(path);
            assert!(Url::parse(built).unwrap() == built_url.unwrap(),
                    "assertion failed: url:{} path:{} built:{} != expected:{}", url, path, built, Url::parse(built).unwrap());
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};

pub struct Connection<'r, 'w, R, W> {
    pub input: &'r mut R,
    pub output: &'w mut W,
    buf: Vec<u8>,
}

impl<'r, 'w, R: Read, W: Write> Connection<'r, 'w, R, W> {

    pub fn new(input: &'r mut R, output: &'w mut W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; MAX_BUF_SIZE),
        }
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;
const MAX_BUF_SIZE: usize = 2 * 1024 * 1024;
<commit_msg>Implement the client heartbeat feature.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use fleetspeak_proto::common::{Message, Address};
use prost;
use prost_types;
use std::io::{Read, Write, Result};
use std::marker::{Send, Sync};

pub struct Connection<'r, 'w, R, W> {
    pub input: &'r mut R,
    pub output: &'w mut W,
    buf: Vec<u8>,
}

impl<'r, 'w, R: Read, W: Write> Connection<'r, 'w, R, W> {

    pub fn new(input: &'r mut R, output: &'w mut W) -> Self {
        Connection {
            input: input,
            output: output,
            buf: vec!(0; MAX_BUF_SIZE),
        }
    }

    pub fn heartbeat(&mut self) -> Result<()> {
        let msg = Message {
            message_type: "Hearbeat".to_string(),
            destination: Some(Address {
                service_name: "system".to_string(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn send<M>(&mut self, service: &str, kind: &str, data: M) -> Result<()>
    where
        M: prost::Message,
    {
        self.encode(data)?;

        let msg = Message {
            message_type: kind.to_string(),
            destination: Some(Address {
                service_name: service.to_string(),
                ..Default::default()
            }),
            data: Some(prost_types::Any {
                value: self.buf.to_vec(),
                ..Default::default()
            }),
            ..Default::default()
        };

        self.emit(msg)
    }

    pub fn receive<M>(&mut self) -> Result<M>
    where
        M: prost::Message + Default,
    {
        let msg = self.collect()?;

        // It is not clear what is the best approach here. If there is no data,
        // should we error-out or return a default value? For the time being we
        // stick to the default approach, but if this proves to be not working
        // well in practice, it might be reconsidered.
        let data = match msg.data {
            Some(data) => data,
            None => return Ok(Default::default()),
        };

        prost::Message::decode(&data.value[..]).map_err(invalid_data_error)
    }

    fn emit(&mut self, msg: Message) -> Result<()> {
        let len = prost::Message::encoded_len(&msg);
        self.encode(msg)?;

        self.output.write_u32::<LittleEndian>(len as u32)?;
        self.output.write(&self.buf[..len])?;
        self.output.write_u32::<LittleEndian>(MAGIC)?;

        Ok(())
    }

    fn collect(&mut self) -> Result<Message> {
        let len = self.input.read_i32::<LittleEndian>()? as usize;
        self.input.read_exact(&mut self.buf[..len])?;

        prost::Message::decode(&self.buf[..len]).map_err(invalid_data_error)
    }

    fn encode<M>(&mut self, msg: M) -> Result<()>
    where
        M: prost::Message,
    {
        msg.encode(&mut self.buf).map_err(invalid_data_error)
    }
}

fn invalid_data_error<E>(err: E) -> std::io::Error
where
    E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
    return std::io::Error::new(std::io::ErrorKind::InvalidData, err);
}

const MAGIC: u32 = 0xf1ee1001;
const MAX_BUF_SIZE: usize = 2 * 1024 * 1024;
<|endoftext|>


<commit_before>//! This module contains the main client code, including the `Rocket` type.
use crate::interpolation::*;
use crate::track::*;

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::prelude::*;
use std::io::Cursor;
use std::net::TcpStream;
use thiserror::Error;

#[derive(Debug, Error)]
/// The `Error` Type. This is the main error type.
pub enum Error {
    #[error("Failed to establish a TCP connection with the Rocket server")]
    Connect(#[source] std::io::Error),
    #[error("Handshake with the Rocket server failed")]
    Handshake(#[source] std::io::Error),
    #[error("The Rocket server greeting {0:?} wasn't correct (check your address and port)")]
    HandshakeGreetingMismatch([u8; 12]),
    #[error("Cannot set Rocket's TCP connection to nonblocking mode")]
    SetNonblocking(#[source] std::io::Error),
    #[error("Rocket server disconnected")]
    IOError(#[from] std::io::Error),
    #[error("Failed to save tracks")]
    SerializeTracks(#[source] bincode::Error),
    #[error("Failed to open tracks for writing")]
    SaveTracks(#[source] std::io::Error),
}

#[derive(Debug)]
enum RocketState {
    New,
    Incomplete(usize),
    Complete,
}

#[derive(Debug, Copy, Clone)]
/// The `Event` Type. These are the various events from the tracker.
pub enum Event {
    /// The tracker changes row.
    SetRow(u32),
    /// The tracker pauses or unpauses.
    Pause(bool),
    /// The tracker asks us to save our track data.
    SaveTracks,
}

enum ReceiveResult {
    Some(Event),
    None,
    Incomplete,
}

#[derive(Debug)]
/// The `Rocket` type. This contains the connected socket and other fields.
pub struct Rocket {
    stream: TcpStream,
    state: RocketState,
    cmd: Vec<u8>,
    tracks: Vec<Track>,
}

impl Rocket {
    /// Construct a new Rocket.
    ///
    /// This constructs a new rocket and connect to localhost on port 1338.
    ///
    /// # Errors
    ///
    /// If a connection cannot be established, or if the handshake fails.
    /// This will raise an `Error`.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// let mut rocket = Rocket::new().unwrap();
    /// ```
    pub fn new() -> Result<Rocket, Error> {
        Rocket::connect("localhost", 1338)
    }

    /// Construct a new Rocket.
    ///
    /// This constructs a new rocket and connects to a specified host and port.
    ///
    /// # Errors
    ///
    /// If a connection cannot be established, or if the handshake fails.
    /// This will raise an `Error`.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// let mut rocket = Rocket::connect("localhost", 1338).unwrap();
    /// ```
    pub fn connect(host: &str, port: u16) -> Result<Rocket, Error> {
        let stream = TcpStream::connect((host, port)).map_err(Error::Connect)?;

        let mut rocket = Rocket {
            stream,
            state: RocketState::New,
            cmd: Vec::new(),
            tracks: Vec::new(),
        };

        rocket.handshake()?;

        rocket
            .stream
            .set_nonblocking(true)
            .map_err(Error::SetNonblocking)?;

        Ok(rocket)
    }

    /// Get a track by name.
    ///
    /// If the track does not yet exist it will be created.
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// # let mut rocket = Rocket::new().unwrap();
    /// let track = rocket.get_track_mut("namespace:track").unwrap();
    /// track.get_value(3.5);
    /// ```
    pub fn get_track_mut(&mut self, name: &str) -> Result<&mut Track, Error> {
        if let Some((i, _)) = self
            .tracks
            .iter()
            .enumerate()
            .find(|(_, t)| t.get_name() == name)
        {
            Ok(&mut self.tracks[i])
        } else {
            // Send GET_TRACK message
            let mut buf = vec![2];
            buf.write_u32::<BigEndian>(name.len() as u32).unwrap();
            buf.extend_from_slice(&name.as_bytes());
            self.stream.write_all(&buf)?;

            self.tracks.push(Track::new(name));
            Ok(self.tracks.last_mut().unwrap())
        }
    }

    /// Get Track by name.
    ///
    /// You should use `get_track_mut` to create a track.
    pub fn get_track(&self, name: &str) -> Option<&Track> {
        self.tracks.iter().find(|t| t.get_name() == name)
    }

    /// Send a SetRow message.
    ///
    /// This changes the current row on the tracker side.
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects.
    pub fn set_row(&mut self, row: u32) -> Result<(), Error> {
        // Send SET_ROW message
        let mut buf = vec![3];
        buf.write_u32::<BigEndian>(row).unwrap();
        self.stream.write_all(&buf).map_err(|e| e.into())
    }

    /// Poll for new events from the tracker.
    ///
    /// This polls from events from the tracker.
    /// You should call this fairly often your main loop.
    /// It is recommended to keep calling this as long as your receive
    /// Some(Event).
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// # let mut rocket = Rocket::new().unwrap();
    /// while let Some(event) = rocket.poll_events().unwrap() {
    ///     match event {
    ///         // Do something with the various events.
    ///         _ => (),
    ///     }
    /// }
    /// ```
    pub fn poll_events(&mut self) -> Result<Option<Event>, Error> {
        loop {
            let result = self.poll_event()?;
            match result {
                ReceiveResult::None => return Ok(None),
                ReceiveResult::Incomplete => (),
                ReceiveResult::Some(event) => return Ok(Some(event)),
            }
        }
    }

    fn poll_event(&mut self) -> Result<ReceiveResult, Error> {
        match self.state {
            RocketState::New => {
                let mut buf = [0; 1];
                match self.stream.read_exact(&mut buf) {
                    Ok(()) => {
                        self.cmd.extend_from_slice(&buf);
                        match self.cmd[0] {
                            0 => self.state = RocketState::Incomplete(4 + 4 + 4 + 1), //SET_KEY
                            1 => self.state = RocketState::Incomplete(4 + 4),         //DELETE_KEY
                            3 => self.state = RocketState::Incomplete(4),             //SET_ROW
                            4 => self.state = RocketState::Incomplete(1),             //PAUSE
                            5 => self.state = RocketState::Complete,                  //SAVE_TRACKS
                            _ => self.state = RocketState::Complete, // Error / Unknown
                        }
                        Ok(ReceiveResult::Incomplete)
                    }
                    Err(e) => match e.kind() {
                        std::io::ErrorKind::WouldBlock => Ok(ReceiveResult::None),
                        _ => Err(e.into()),
                    },
                }
            }
            RocketState::Incomplete(bytes) => {
                let mut buf = vec![0; bytes];
                match self.stream.read(&mut buf) {
                    Ok(bytes_read) => {
                        self.cmd.extend_from_slice(&buf);
                        if bytes - bytes_read > 0 {
                            self.state = RocketState::Incomplete(bytes - bytes_read);
                        } else {
                            self.state = RocketState::Complete;
                        }
                        Ok(ReceiveResult::Incomplete)
                    }
                    Err(e) => match e.kind() {
                        std::io::ErrorKind::WouldBlock => Ok(ReceiveResult::None),
                        _ => Err(e.into()),
                    },
                }
            }
            RocketState::Complete => {
                let mut result = ReceiveResult::None;
                {
                    let mut cursor = Cursor::new(&self.cmd);
                    let cmd = cursor.read_u8().unwrap();
                    match cmd {
                        0 => {
                            let track =
                                &mut self.tracks[cursor.read_u32::<BigEndian>().unwrap() as usize];
                            let row = cursor.read_u32::<BigEndian>().unwrap();
                            let value = cursor.read_f32::<BigEndian>().unwrap();
                            let interpolation = Interpolation::from(cursor.read_u8().unwrap());
                            let key = Key::new(row, value, interpolation);

                            track.set_key(key);
                        }
                        1 => {
                            let track =
                                &mut self.tracks[cursor.read_u32::<BigEndian>().unwrap() as usize];
                            let row = cursor.read_u32::<BigEndian>().unwrap();

                            track.delete_key(row);
                        }
                        3 => {
                            let row = cursor.read_u32::<BigEndian>().unwrap();
                            result = ReceiveResult::Some(Event::SetRow(row));
                        }
                        4 => {
                            let flag = cursor.read_u8().unwrap() == 1;
                            result = ReceiveResult::Some(Event::Pause(flag));
                        }
                        5 => {
                            result = ReceiveResult::Some(Event::SaveTracks);
                            self.save_tracks()?;
                        }
                        _ => println!("Unknown {:?}", cmd),
                    }
                }

                self.cmd.clear();
                self.state = RocketState::New;

                Ok(result)
            }
        }
    }

    fn handshake(&mut self) -> Result<(), Error> {
        let client_greeting = b"hello, synctracker!";
        let server_greeting = b"hello, demo!";

        self.stream
            .write_all(client_greeting)
            .map_err(Error::Handshake)?;

        let mut buf = [0; 12];
        self.stream.read_exact(&mut buf).map_err(Error::Handshake)?;

        if &buf == server_greeting {
            Ok(())
        } else {
            Err(Error::HandshakeGreetingMismatch(buf))
        }
    }

    fn save_tracks(&self) -> Result<(), Error> {
        use std::fs::OpenOptions;
        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open("tracks.bin")
            .map_err(Error::SaveTracks)?;
        bincode::serialize_into(file, &self.tracks).map_err(Error::SerializeTracks)
    }
}
<commit_msg>Add SaveTracksError and improve documentation<commit_after>//! This module contains the main client code, including the `Rocket` type.
use crate::interpolation::*;
use crate::track::*;

use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::prelude::*;
use std::io::Cursor;
use std::net::TcpStream;
use thiserror::Error;

#[derive(Debug, Error)]
pub enum SaveTracksError {
    #[error("Failed to open tracks for writing")]
    OpenFile(#[from] std::io::Error),
    #[error("Failed to serialize tracks into file")]
    SerializeTracks(#[from] bincode::Error),
}

#[derive(Debug, Error)]
/// The `Error` Type. This is the main error type.
pub enum Error {
    #[error("Failed to establish a TCP connection with the Rocket server")]
    Connect(#[source] std::io::Error),
    #[error("Handshake with the Rocket server failed")]
    Handshake(#[source] std::io::Error),
    #[error("The Rocket server greeting {0:?} wasn't correct (check your address and port)")]
    HandshakeGreetingMismatch([u8; 12]),
    #[error("Cannot set Rocket's TCP connection to nonblocking mode")]
    SetNonblocking(#[source] std::io::Error),
    #[error("Rocket server disconnected")]
    IOError(#[from] std::io::Error),
    #[error("Failed to save tracks")]
    SaveTracks(#[source] SaveTracksError),
}

#[derive(Debug)]
enum RocketState {
    New,
    Incomplete(usize),
    Complete,
}

#[derive(Debug, Copy, Clone)]
/// The `Event` Type. These are the various events from the tracker.
pub enum Event {
    /// The tracker changes row.
    SetRow(u32),
    /// The tracker pauses or unpauses.
    Pause(bool),
    /// The tracker asks us to save our track data.
    SaveTracks,
}

enum ReceiveResult {
    Some(Event),
    None,
    Incomplete,
}

#[derive(Debug)]
/// The `Rocket` type. This contains the connected socket and other fields.
pub struct Rocket {
    stream: TcpStream,
    state: RocketState,
    cmd: Vec<u8>,
    tracks: Vec<Track>,
}

impl Rocket {
    /// Construct a new Rocket.
    ///
    /// This constructs a new rocket and connect to localhost on port 1338.
    ///
    /// # Errors
    ///
    /// If a connection cannot be established, or if the handshake fails.
    /// This will raise an `Error`.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// let mut rocket = Rocket::new().unwrap();
    /// ```
    pub fn new() -> Result<Rocket, Error> {
        Rocket::connect("localhost", 1338)
    }

    /// Construct a new Rocket.
    ///
    /// This constructs a new rocket and connects to a specified host and port.
    ///
    /// # Errors
    ///
    /// If a connection cannot be established, or if the handshake fails.
    /// This will raise an `Error`.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// let mut rocket = Rocket::connect("localhost", 1338).unwrap();
    /// ```
    pub fn connect(host: &str, port: u16) -> Result<Rocket, Error> {
        let stream = TcpStream::connect((host, port)).map_err(Error::Connect)?;

        let mut rocket = Rocket {
            stream,
            state: RocketState::New,
            cmd: Vec::new(),
            tracks: Vec::new(),
        };

        rocket.handshake()?;

        rocket
            .stream
            .set_nonblocking(true)
            .map_err(Error::SetNonblocking)?;

        Ok(rocket)
    }

    /// Get a track by name.
    ///
    /// If the track does not yet exist it will be created.
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// # let mut rocket = Rocket::new().unwrap();
    /// let track = rocket.get_track_mut("namespace:track").unwrap();
    /// track.get_value(3.5);
    /// ```
    pub fn get_track_mut(&mut self, name: &str) -> Result<&mut Track, Error> {
        if let Some((i, _)) = self
            .tracks
            .iter()
            .enumerate()
            .find(|(_, t)| t.get_name() == name)
        {
            Ok(&mut self.tracks[i])
        } else {
            // Send GET_TRACK message
            let mut buf = vec![2];
            buf.write_u32::<BigEndian>(name.len() as u32).unwrap();
            buf.extend_from_slice(&name.as_bytes());
            self.stream.write_all(&buf)?;

            self.tracks.push(Track::new(name));
            Ok(self.tracks.last_mut().unwrap())
        }
    }

    /// Get Track by name.
    ///
    /// You should use `get_track_mut` to create a track.
    pub fn get_track(&self, name: &str) -> Option<&Track> {
        self.tracks.iter().find(|t| t.get_name() == name)
    }

    /// Send a SetRow message.
    ///
    /// This changes the current row on the tracker side.
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects.
    pub fn set_row(&mut self, row: u32) -> Result<(), Error> {
        // Send SET_ROW message
        let mut buf = vec![3];
        buf.write_u32::<BigEndian>(row).unwrap();
        self.stream.write_all(&buf).map_err(|e| e.into())
    }

    /// Poll for new events from the tracker.
    ///
    /// This polls from events from the tracker.
    /// You should call this fairly often your main loop.
    /// It is recommended to keep calling this as long as your receive
    /// Some(Event).
    ///
    /// Tracks will be automatically saved to a playable binary format when the editor requests
    /// that.
    ///
    /// # Errors
    ///
    /// This method can return an [IOError](Error::IOError) if Rocket server disconnects,
    /// or an [Error::SaveTracks] if the server asks to export tracks but doing so fails.
    ///
    /// # Examples
    ///
    /// ```rust,no_run
    /// # use rust_rocket::Rocket;
    /// # let mut rocket = Rocket::new().unwrap();
    /// while let Some(event) = rocket.poll_events().unwrap() {
    ///     match event {
    ///         // Do something with the various events.
    ///         _ => (),
    ///     }
    /// }
    /// ```
    pub fn poll_events(&mut self) -> Result<Option<Event>, Error> {
        loop {
            let result = self.poll_event()?;
            match result {
                ReceiveResult::None => return Ok(None),
                ReceiveResult::Incomplete => (),
                ReceiveResult::Some(event) => return Ok(Some(event)),
            }
        }
    }

    fn poll_event(&mut self) -> Result<ReceiveResult, Error> {
        match self.state {
            RocketState::New => {
                let mut buf = [0; 1];
                match self.stream.read_exact(&mut buf) {
                    Ok(()) => {
                        self.cmd.extend_from_slice(&buf);
                        match self.cmd[0] {
                            0 => self.state = RocketState::Incomplete(4 + 4 + 4 + 1), //SET_KEY
                            1 => self.state = RocketState::Incomplete(4 + 4),         //DELETE_KEY
                            3 => self.state = RocketState::Incomplete(4),             //SET_ROW
                            4 => self.state = RocketState::Incomplete(1),             //PAUSE
                            5 => self.state = RocketState::Complete,                  //SAVE_TRACKS
                            _ => self.state = RocketState::Complete, // Error / Unknown
                        }
                        Ok(ReceiveResult::Incomplete)
                    }
                    Err(e) => match e.kind() {
                        std::io::ErrorKind::WouldBlock => Ok(ReceiveResult::None),
                        _ => Err(e.into()),
                    },
                }
            }
            RocketState::Incomplete(bytes) => {
                let mut buf = vec![0; bytes];
                match self.stream.read(&mut buf) {
                    Ok(bytes_read) => {
                        self.cmd.extend_from_slice(&buf);
                        if bytes - bytes_read > 0 {
                            self.state = RocketState::Incomplete(bytes - bytes_read);
                        } else {
                            self.state = RocketState::Complete;
                        }
                        Ok(ReceiveResult::Incomplete)
                    }
                    Err(e) => match e.kind() {
                        std::io::ErrorKind::WouldBlock => Ok(ReceiveResult::None),
                        _ => Err(e.into()),
                    },
                }
            }
            RocketState::Complete => {
                let mut result = ReceiveResult::None;
                {
                    let mut cursor = Cursor::new(&self.cmd);
                    let cmd = cursor.read_u8().unwrap();
                    match cmd {
                        0 => {
                            let track =
                                &mut self.tracks[cursor.read_u32::<BigEndian>().unwrap() as usize];
                            let row = cursor.read_u32::<BigEndian>().unwrap();
                            let value = cursor.read_f32::<BigEndian>().unwrap();
                            let interpolation = Interpolation::from(cursor.read_u8().unwrap());
                            let key = Key::new(row, value, interpolation);

                            track.set_key(key);
                        }
                        1 => {
                            let track =
                                &mut self.tracks[cursor.read_u32::<BigEndian>().unwrap() as usize];
                            let row = cursor.read_u32::<BigEndian>().unwrap();

                            track.delete_key(row);
                        }
                        3 => {
                            let row = cursor.read_u32::<BigEndian>().unwrap();
                            result = ReceiveResult::Some(Event::SetRow(row));
                        }
                        4 => {
                            let flag = cursor.read_u8().unwrap() == 1;
                            result = ReceiveResult::Some(Event::Pause(flag));
                        }
                        5 => {
                            result = ReceiveResult::Some(Event::SaveTracks);
                            self.save_tracks().map_err(Error::SaveTracks)?;
                        }
                        _ => println!("Unknown {:?}", cmd),
                    }
                }

                self.cmd.clear();
                self.state = RocketState::New;

                Ok(result)
            }
        }
    }

    fn handshake(&mut self) -> Result<(), Error> {
        let client_greeting = b"hello, synctracker!";
        let server_greeting = b"hello, demo!";

        self.stream
            .write_all(client_greeting)
            .map_err(Error::Handshake)?;

        let mut buf = [0; 12];
        self.stream.read_exact(&mut buf).map_err(Error::Handshake)?;

        if &buf == server_greeting {
            Ok(())
        } else {
            Err(Error::HandshakeGreetingMismatch(buf))
        }
    }

    fn save_tracks(&self) -> Result<(), SaveTracksError> {
        use std::fs::OpenOptions;
        let file = OpenOptions::new()
            .write(true)
            .create(true)
            .truncate(true)
            .open("tracks.bin")?;
        bincode::serialize_into(file, &self.tracks).map_err(|e| e.into())
    }
}
<|endoftext|>


<commit_before>use base64;
use directories::ProjectDirs;
use error::ConfigError;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, ErrorKind, Write};
use std::path::PathBuf;
use toml;

pub const DEFAULT_CONFIG_NAME: &str = "config.toml";

lazy_static! {
    static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", env!("CARGO_PKG_NAME"));
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
    pub series: HashMap<String, PathBuf>,
    pub anilist: Anilist,
}

impl Config {
    pub fn new() -> Config {
        Config {
            series: HashMap::new(),
            anilist: Anilist::new(),
        }
    }

    pub fn load() -> Result<Config, ConfigError> {
        let path = get_config_file_path()?;

        let file_contents = match fs::read_to_string(&path) {
            Ok(contents) => contents,
            Err(err) => match err.kind() {
                ErrorKind::NotFound => return Ok(Config::new()),
                _ => return Err(err.into()),
            },
        };

        let config = toml::from_str(&file_contents)?;
        Ok(config)
    }

    pub fn save(&self) -> Result<(), ConfigError> {
        let path = get_config_file_path()?;
        let mut file = File::create(path)?;
        let toml = toml::to_string_pretty(self)?;

        write!(file, "{}", toml)?;
        Ok(())
    }

    pub fn remove_invalid_series(&mut self) {
        self.series.retain(|_, path: &mut PathBuf| path.exists());
    }
}

fn get_config_file_path() -> io::Result<PathBuf> {
    let path = PROJECT_DIRS.config_dir();

    if !path.exists() {
        fs::create_dir(path)?;
    }

    let mut path = PathBuf::from(path);
    path.push(DEFAULT_CONFIG_NAME);

    Ok(path)
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AccessToken {
    pub token: Option<String>,
}

impl AccessToken {
    pub fn new(token: Option<String>) -> AccessToken {
        AccessToken { token }
    }

    pub fn encode(&mut self, token: &str) {
        self.token = Some(base64::encode(token));
    }

    pub fn decode(&self) -> Result<String, ConfigError> {
        let token = self.token.as_ref().ok_or(ConfigError::TokenNotSet)?;
        let bytes = base64::decode(token).map_err(ConfigError::FailedTokenDecode)?;
        let string = String::from_utf8(bytes)?;

        Ok(string)
    }

    pub fn is_set(&self) -> bool {
        self.token.is_some()
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Anilist {
    #[serde(flatten)]
    pub token: AccessToken,
}

impl Anilist {
    pub fn new() -> Anilist {
        Anilist {
            token: AccessToken::new(None),
        }
    }
}
<commit_msg>[core] Fix startup error on Windows<commit_after>use base64;
use directories::ProjectDirs;
use error::ConfigError;
use std::collections::HashMap;
use std::fs::{self, File};
use std::io::{self, ErrorKind, Write};
use std::path::PathBuf;
use toml;

pub const DEFAULT_CONFIG_NAME: &str = "config.toml";

lazy_static! {
    static ref PROJECT_DIRS: ProjectDirs = ProjectDirs::from("", "", env!("CARGO_PKG_NAME"));
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Config {
    pub series: HashMap<String, PathBuf>,
    pub anilist: Anilist,
}

impl Config {
    pub fn new() -> Config {
        Config {
            series: HashMap::new(),
            anilist: Anilist::new(),
        }
    }

    pub fn load() -> Result<Config, ConfigError> {
        let path = get_config_file_path()?;

        let file_contents = match fs::read_to_string(&path) {
            Ok(contents) => contents,
            Err(err) => match err.kind() {
                ErrorKind::NotFound => return Ok(Config::new()),
                _ => return Err(err.into()),
            },
        };

        let config = toml::from_str(&file_contents)?;
        Ok(config)
    }

    pub fn save(&self) -> Result<(), ConfigError> {
        let path = get_config_file_path()?;
        let mut file = File::create(path)?;
        let toml = toml::to_string_pretty(self)?;

        write!(file, "{}", toml)?;
        Ok(())
    }

    pub fn remove_invalid_series(&mut self) {
        self.series.retain(|_, path: &mut PathBuf| path.exists());
    }
}

fn get_config_file_path() -> io::Result<PathBuf> {
    let path = PROJECT_DIRS.config_dir();

    if !path.exists() {
        fs::create_dir_all(path)?;
    }

    let mut path = PathBuf::from(path);
    path.push(DEFAULT_CONFIG_NAME);

    Ok(path)
}

#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct AccessToken {
    pub token: Option<String>,
}

impl AccessToken {
    pub fn new(token: Option<String>) -> AccessToken {
        AccessToken { token }
    }

    pub fn encode(&mut self, token: &str) {
        self.token = Some(base64::encode(token));
    }

    pub fn decode(&self) -> Result<String, ConfigError> {
        let token = self.token.as_ref().ok_or(ConfigError::TokenNotSet)?;
        let bytes = base64::decode(token).map_err(ConfigError::FailedTokenDecode)?;
        let string = String::from_utf8(bytes)?;

        Ok(string)
    }

    pub fn is_set(&self) -> bool {
        self.token.is_some()
    }
}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Anilist {
    #[serde(flatten)]
    pub token: AccessToken,
}

impl Anilist {
    pub fn new() -> Anilist {
        Anilist {
            token: AccessToken::new(None),
        }
    }
}
<|endoftext|>


<commit_before>//! Provides access to the symbolserver config
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use serde_yaml;
use url::Url;
use rusoto::Region;
use chrono::Duration;
use log::LogLevelFilter;

use super::{Result, ErrorKind};

#[derive(Deserialize, Debug, Default, Clone)]
struct AwsConfig {
    access_key: Option<String>,
    secret_key: Option<String>,
    bucket_url: Option<String>,
    region: Option<String>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct ServerConfig {
    host: Option<String>,
    port: Option<u16>,
    healthcheck_ttl: Option<u32>,
    sync_interval: Option<u32>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct LogConfig {
    level: Option<String>,
    file: Option<PathBuf>,
}

/// Central config object that exposes the information from
/// the symbolserver yaml config.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct Config {
    #[serde(default)]
    aws: AwsConfig,
    #[serde(default)]
    server: ServerConfig,
    #[serde(default)]
    log: LogConfig,
    symbol_dir: Option<PathBuf>,
}

impl Config {
    /// Loads a config from a given file
    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let mut f = fs::File::open(path)?;
        serde_yaml::from_reader(&mut f).map_err(|err| {
            ErrorKind::ConfigError(err).into()
        })
    }

    /// Loads a config from the default location
    pub fn load_default() -> Result<Config> {
        let mut home = match env::home_dir() {
            Some(home) => home,
            None => { return Ok(Default::default()) },
        };
        home.push(".symbolserver.yml");

        Ok(if let Ok(_) = fs::metadata(&home) {
            Config::load_file(&home)?
        } else {
            Default::default()
        })
    }

    /// Return the AWS access key
    pub fn get_aws_access_key<'a>(&'a self) -> Option<&str> {
        self.aws.access_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS secret key
    pub fn get_aws_secret_key<'a>(&'a self) -> Option<&str> {
        self.aws.secret_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS S3 bucket URL
    pub fn get_aws_bucket_url<'a>(&'a self) -> Result<Url> {
        let url = if let Some(value) = self.aws.bucket_url.as_ref() {
            Url::parse(value)?
        } else {
            return Err(ErrorKind::MissingConfigKey(
                "aws.bucket_url").into());
        };
        if url.scheme() != "s3" {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The scheme for the bucket URL needs to be s3").into());
        } else if url.host_str().is_none() {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The bucket URL is missing a name").into());
        }
        Ok(url)
    }

    /// Overrides the AWS bucket URL.
    pub fn set_aws_bucket_url(&mut self, value: &str) {
        self.aws.bucket_url = Some(value.to_string());
    }

    /// Return the AWS region
    pub fn get_aws_region(&self) -> Result<Region> {
        let region_opt = self.aws.region
            .as_ref()
            .map(|x| x.to_string())
            .or_else(|| env::var("AWS_DEFAULT_REGION").ok());

        if let Some(region) = region_opt {
            if let Ok(rv) = region.parse() {
                Ok(rv)
            } else {
                Err(ErrorKind::BadConfigKey(
                    "aws.region", "An unknown AWS region was provided").into())
            }
        } else {
            Ok(Region::UsEast1)
        }
    }

    /// Overrides the AWS region
    pub fn set_aws_region(&mut self, value: Region) {
        self.aws.region = Some(value.to_string());
    }

    /// Return the path where symbols are stored.
    pub fn get_symbol_dir(&self) -> Result<&Path> {
        if let Some(ref path) = self.symbol_dir {
            Ok(path.as_path())
        } else {
            Err(ErrorKind::MissingConfigKey("symbol_dir").into())
        }
    }

    /// Override the symbol dir.
    pub fn set_symbol_dir<P: AsRef<Path>>(&mut self, value: P) {
        self.symbol_dir = Some(value.as_ref().to_path_buf());
    }

    /// Return the bind target for the http server
    pub fn get_server_socket_addr(&self) -> Result<(&str, u16)> {
        let host = self.server.host.as_ref().map(|x| x.as_str()).unwrap_or("127.0.0.1");
        let port = self.server.port.unwrap_or(3000);
        Ok((host, port))
    }

    /// Return the server healthcheck ttl
    pub fn get_server_healthcheck_ttl(&self) -> Result<Duration> {
        let ttl = self.server.healthcheck_ttl.unwrap_or(60);
        Ok(Duration::seconds(ttl as i64))
    }

    /// Return the server sync interval
    pub fn get_server_sync_interval(&self) -> Result<Duration> {
        let ttl = self.server.sync_interval.unwrap_or(60);
        if ttl <= 0 {
            Err(ErrorKind::BadConfigKey(
                "server.sync_interval", "Sync interval has to be positive").into())
        } else {
            Ok(Duration::seconds(ttl as i64))
        }
    }

    /// Return the log level filter
    pub fn get_log_level_filter(&self) -> Result<LogLevelFilter> {
        if let Some(ref lvl) = self.log.level {
            lvl.parse().map_err(|_| ErrorKind::BadConfigKey(
                "log.level", "unknown log level").into())
        } else {
            Ok(LogLevelFilter::Info)
        }
    }

    /// Override the log level filter in the config
    pub fn set_log_level_filter(&mut self, value: LogLevelFilter) {
        self.log.level = Some(value.to_string());
    }

    /// Return the log filename
    pub fn get_log_filename(&self) -> Result<Option<&Path>> {
        if let Some(ref path) = self.log.file {
            Ok(Some(&*path))
        } else {
            Ok(None)
        }
    }
}
<commit_msg>Renamed symbolserver config<commit_after>//! Provides access to the symbolserver config
use std::env;
use std::fs;
use std::path::{Path, PathBuf};

use serde_yaml;
use url::Url;
use rusoto::Region;
use chrono::Duration;
use log::LogLevelFilter;

use super::{Result, ErrorKind};

#[derive(Deserialize, Debug, Default, Clone)]
struct AwsConfig {
    access_key: Option<String>,
    secret_key: Option<String>,
    bucket_url: Option<String>,
    region: Option<String>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct ServerConfig {
    host: Option<String>,
    port: Option<u16>,
    healthcheck_ttl: Option<u32>,
    sync_interval: Option<u32>,
}

#[derive(Deserialize, Debug, Default, Clone)]
struct LogConfig {
    level: Option<String>,
    file: Option<PathBuf>,
}

/// Central config object that exposes the information from
/// the symbolserver yaml config.
#[derive(Deserialize, Debug, Default, Clone)]
pub struct Config {
    #[serde(default)]
    aws: AwsConfig,
    #[serde(default)]
    server: ServerConfig,
    #[serde(default)]
    log: LogConfig,
    symbol_dir: Option<PathBuf>,
}

impl Config {
    /// Loads a config from a given file
    pub fn load_file<P: AsRef<Path>>(path: P) -> Result<Config> {
        let mut f = fs::File::open(path)?;
        serde_yaml::from_reader(&mut f).map_err(|err| {
            ErrorKind::ConfigError(err).into()
        })
    }

    /// Loads a config from the default location
    pub fn load_default() -> Result<Config> {
        let mut home = match env::home_dir() {
            Some(home) => home,
            None => { return Ok(Default::default()) },
        };
        home.push(".sentry-symbolserver.yml");

        Ok(if let Ok(_) = fs::metadata(&home) {
            Config::load_file(&home)?
        } else {
            Default::default()
        })
    }

    /// Return the AWS access key
    pub fn get_aws_access_key<'a>(&'a self) -> Option<&str> {
        self.aws.access_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS secret key
    pub fn get_aws_secret_key<'a>(&'a self) -> Option<&str> {
        self.aws.secret_key.as_ref().map(|x| &**x)
    }

    /// Return the AWS S3 bucket URL
    pub fn get_aws_bucket_url<'a>(&'a self) -> Result<Url> {
        let url = if let Some(value) = self.aws.bucket_url.as_ref() {
            Url::parse(value)?
        } else {
            return Err(ErrorKind::MissingConfigKey(
                "aws.bucket_url").into());
        };
        if url.scheme() != "s3" {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The scheme for the bucket URL needs to be s3").into());
        } else if url.host_str().is_none() {
            return Err(ErrorKind::BadConfigKey(
                "aws.bucket_url", "The bucket URL is missing a name").into());
        }
        Ok(url)
    }

    /// Overrides the AWS bucket URL.
    pub fn set_aws_bucket_url(&mut self, value: &str) {
        self.aws.bucket_url = Some(value.to_string());
    }

    /// Return the AWS region
    pub fn get_aws_region(&self) -> Result<Region> {
        let region_opt = self.aws.region
            .as_ref()
            .map(|x| x.to_string())
            .or_else(|| env::var("AWS_DEFAULT_REGION").ok());

        if let Some(region) = region_opt {
            if let Ok(rv) = region.parse() {
                Ok(rv)
            } else {
                Err(ErrorKind::BadConfigKey(
                    "aws.region", "An unknown AWS region was provided").into())
            }
        } else {
            Ok(Region::UsEast1)
        }
    }

    /// Overrides the AWS region
    pub fn set_aws_region(&mut self, value: Region) {
        self.aws.region = Some(value.to_string());
    }

    /// Return the path where symbols are stored.
    pub fn get_symbol_dir(&self) -> Result<&Path> {
        if let Some(ref path) = self.symbol_dir {
            Ok(path.as_path())
        } else {
            Err(ErrorKind::MissingConfigKey("symbol_dir").into())
        }
    }

    /// Override the symbol dir.
    pub fn set_symbol_dir<P: AsRef<Path>>(&mut self, value: P) {
        self.symbol_dir = Some(value.as_ref().to_path_buf());
    }

    /// Return the bind target for the http server
    pub fn get_server_socket_addr(&self) -> Result<(&str, u16)> {
        let host = self.server.host.as_ref().map(|x| x.as_str()).unwrap_or("127.0.0.1");
        let port = self.server.port.unwrap_or(3000);
        Ok((host, port))
    }

    /// Return the server healthcheck ttl
    pub fn get_server_healthcheck_ttl(&self) -> Result<Duration> {
        let ttl = self.server.healthcheck_ttl.unwrap_or(60);
        Ok(Duration::seconds(ttl as i64))
    }

    /// Return the server sync interval
    pub fn get_server_sync_interval(&self) -> Result<Duration> {
        let ttl = self.server.sync_interval.unwrap_or(60);
        if ttl <= 0 {
            Err(ErrorKind::BadConfigKey(
                "server.sync_interval", "Sync interval has to be positive").into())
        } else {
            Ok(Duration::seconds(ttl as i64))
        }
    }

    /// Return the log level filter
    pub fn get_log_level_filter(&self) -> Result<LogLevelFilter> {
        if let Some(ref lvl) = self.log.level {
            lvl.parse().map_err(|_| ErrorKind::BadConfigKey(
                "log.level", "unknown log level").into())
        } else {
            Ok(LogLevelFilter::Info)
        }
    }

    /// Override the log level filter in the config
    pub fn set_log_level_filter(&mut self, value: LogLevelFilter) {
        self.log.level = Some(value.to_string());
    }

    /// Return the log filename
    pub fn get_log_filename(&self) -> Result<Option<&Path>> {
        if let Some(ref path) = self.log.file {
            Ok(Some(&*path))
        } else {
            Ok(None)
        }
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use clap::{App, Arg};
use toml;
use rustc_serialize::Decodable;
use rr_result::{RrResult, err_message, rr_error_message};
use version_kind::VersionKind;
use utils::map_file;
use cargo_proj::CargoProj;

/// The configuration used to run `rusty-release`.
#[derive(Debug)]
pub struct Config {
    /// which part of the project version should be incremented
    pub version_kind: VersionKind,

    /// start directory for the search of the 'Cargo.toml'
    pub start_dir: PathBuf,

    /// publish to crates.io
    pub cargo_publish: bool,

    /// push to git remote repository
    pub git_push: bool,

    /// string template for the commit message
    commit_message: String,

    /// string template for the tag name
    tag_name: String,

    /// the editor command, used for opening of the changelog
    editor: String
}

/// Helper macro to apply the settings from ConfigFromFile to Config
macro_rules! config {
    ( $file_config:ident, [ $( $field_name:ident ),* ]) => {{
        let mut config = try!(Config::default());
        $(
            if let Some(f) = $file_config.$field_name {
                config.$field_name = f;
            }
        )*

        config
    }}
}

impl Config {
   pub fn from_file_and_command_args() -> RrResult<Config> {
       let matches = try!(App::new("rusty-release")
           .about("Make a release for a cargo project")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg_from_usage("<VERSION_KIND> 'Which version number gets increased (major, minor or patch)'")
           .arg(Arg::with_name("start-dir")
                .short("s")
                .long("start-dir")
                .value_names(&["DIR"])
                .help("Start directory for the search of the Cargo.toml (default: current working directory)")
                .takes_value(true))
           .arg_from_usage("-n --no-cargo-publish 'Do not publish to crates.io'")
           .arg_from_usage("-N --no-git-push 'Do not push to remote git repository'")
           .get_matches_safe());

       let start_dir = matches.value_of("start-dir")
           .map(PathBuf::from)
           .unwrap_or(try!(env::current_dir()));

       if ! start_dir.is_dir() {
           return err_message(format!("Invalid directory given to '--start-dir': '{}'!", start_dir.display()));
       }

       let mut config = try!(Config::from_file());
       config.version_kind = value_t_or_exit!(matches.value_of("VERSION_KIND"), VersionKind);
       config.start_dir = start_dir;

       if matches.is_present("no-cargo-publish") {
           config.cargo_publish = ! matches.is_present("no-cargo-publish");
       }

       if matches.is_present("no-git-push") {
           config.git_push = ! matches.is_present("no-git-push");
       }

       try!(config.check());
       Ok(config)
   }

   pub fn commit_message(&self, proj: &CargoProj) -> String {
       Template(&self.commit_message).render(proj)
   }

   pub fn tag_name(&self, proj: &CargoProj) -> String {
       Template(&self.tag_name).render(proj)
   }

   pub fn editor(&self) -> Command {
       let editor_and_args = self.editor.split(' ').collect::<Vec<&str>>();
       let mut cmd = Command::new(editor_and_args[0]);
       let args = editor_and_args.iter().skip(1);
       for arg in args {
           cmd.arg(arg);
       }

       cmd
   }

   fn from_file() -> RrResult<Config> {
       let curr_file_config = try!(ConfigFromFile::load_from_current_dir());
       let home_file_config = try!(ConfigFromFile::load_from_home_dir());

       let file_config = match (curr_file_config, home_file_config) {
           (Some(cfc), Some(hfc)) => cfc.combine(&hfc),
           (Some(cfc), None)      => cfc,
           (None     , Some(hfc)) => hfc,
           (None     , None)      => ConfigFromFile::default()
       };

       let config = config!(file_config, [
           cargo_publish,
           git_push,
           commit_message,
           tag_name,
           editor
       ]);

       Ok(config)
   }

   fn default() -> RrResult<Config> {
       Ok(Config {
           version_kind: VersionKind::Patch,
           start_dir: try!(env::current_dir()),
           cargo_publish: true,
           git_push: true,
           commit_message: "<PROJ_NAME> <NEW_VERSION>".to_string(),
           tag_name: "<PROJ_NAME>-<NEW_VERSION>".to_string(),
           editor: "gvim -o".to_string()
       })
   }

   fn check(&self) -> RrResult<()> {
       if self.commit_message.is_empty() {
           return err_message("Invalid, empty commit message!");
       }

       if self.tag_name.is_empty() {
           return err_message("Invalid empty tag name!");
       }

       if self.editor.is_empty() {
           return err_message("Invalid, empty editor command!");
       }

       Ok(())
   }
}

/// Represents a string template that contains placeholders that can be replaced.
/// Currently only the placeholders '<PROJ_NAME>' - representing the name of the
/// cargo project and '<NEW_VERSION>' - representing the version of the release -
/// are supported.
#[derive(Debug)]
struct Template<'a>(&'a str);

impl<'a> Template<'a> {
    pub fn render(&self, proj: &CargoProj) -> String {
        self.0.replace("<PROJ_NAME>", &format!("{}", proj.name()))
            .replace("<NEW_VERSION>", &format!("{}", proj.version()))
    }
}

/// Represents the data from a `.rusty-release.toml` configuration file.
#[derive(RustcDecodable, Debug, Default)]
struct ConfigFromFile {
    pub cargo_publish: Option<bool>,
    pub git_push: Option<bool>,
    pub commit_message: Option<String>,
    pub tag_name: Option<String>,
    pub editor: Option<String>
}

impl ConfigFromFile {
    fn load_from_current_dir() -> RrResult<Option<ConfigFromFile>> {
        let path = try!(env::current_dir()).join(config_file_name());
        if ! path.is_file() {
            return Ok(None);
        }

        Ok(Some(try!(ConfigFromFile::load_from_file(&path))))
    }

    fn load_from_home_dir() -> RrResult<Option<ConfigFromFile>> {
        if let Some(path) = env::home_dir().map(|d| d.join(config_file_name())) {
            if path.is_file() {
                return Ok(Some(try!(ConfigFromFile::load_from_file(&path))));
            }
        }

        Ok(None)
    }

    fn load_from_file(path: &Path) -> RrResult<ConfigFromFile> {
        map_file(path, |contents| {
            let mut parser = toml::Parser::new(&contents);
            let value = try!(parser.parse()
                .ok_or_else(|| rr_error_message(format!("Couldn't parse toml file '{}': {:?}",
                                                        path.display(), parser.errors))));

            let mut decoder = toml::Decoder::new(toml::Value::Table(value));
            Ok(try!(ConfigFromFile::decode(&mut decoder)))
        })
    }

    fn combine(&self, other: &ConfigFromFile) -> ConfigFromFile {
        ConfigFromFile {
            cargo_publish: self.cargo_publish.or(other.cargo_publish),
            git_push: self.git_push.or(other.git_push),
            commit_message: self.commit_message.clone().or(other.commit_message.clone()),
            tag_name: self.tag_name.clone().or(other.tag_name.clone()),
            editor: self.editor.clone().or(other.editor.clone())
        }
    }
}

fn config_file_name() -> &'static str {
    ".rusty-release.toml"
}
<commit_msg>Clone a bit less<commit_after>use std::env;
use std::path::{Path, PathBuf};
use std::process::Command;
use clap::{App, Arg};
use toml;
use rustc_serialize::Decodable;
use rr_result::{RrResult, err_message, rr_error_message};
use version_kind::VersionKind;
use utils::map_file;
use cargo_proj::CargoProj;

/// The configuration used to run `rusty-release`.
#[derive(Debug)]
pub struct Config {
    /// which part of the project version should be incremented
    pub version_kind: VersionKind,

    /// start directory for the search of the 'Cargo.toml'
    pub start_dir: PathBuf,

    /// publish to crates.io
    pub cargo_publish: bool,

    /// push to git remote repository
    pub git_push: bool,

    /// string template for the commit message
    commit_message: String,

    /// string template for the tag name
    tag_name: String,

    /// the editor command, used for opening of the changelog
    editor: String
}

/// Helper macro to apply the settings from ConfigFromFile to Config
macro_rules! config {
    ( $file_config:ident, [ $( $field_name:ident ),* ]) => {{
        let mut config = try!(Config::default());
        $(
            if let Some(f) = $file_config.$field_name {
                config.$field_name = f;
            }
        )*

        config
    }}
}

impl Config {
   pub fn from_file_and_command_args() -> RrResult<Config> {
       let matches = try!(App::new("rusty-release")
           .about("Make a release for a cargo project")
           .version(crate_version!())
           .author("Daniel Trstenjak <daniel.trstenjak@gmail.com>")
           .arg_from_usage("<VERSION_KIND> 'Which version number gets increased (major, minor or patch)'")
           .arg(Arg::with_name("start-dir")
                .short("s")
                .long("start-dir")
                .value_names(&["DIR"])
                .help("Start directory for the search of the Cargo.toml (default: current working directory)")
                .takes_value(true))
           .arg_from_usage("-n --no-cargo-publish 'Do not publish to crates.io'")
           .arg_from_usage("-N --no-git-push 'Do not push to remote git repository'")
           .get_matches_safe());

       let start_dir = matches.value_of("start-dir")
           .map(PathBuf::from)
           .unwrap_or(try!(env::current_dir()));

       if ! start_dir.is_dir() {
           return err_message(format!("Invalid directory given to '--start-dir': '{}'!", start_dir.display()));
       }

       let mut config = try!(Config::from_file());
       config.version_kind = value_t_or_exit!(matches.value_of("VERSION_KIND"), VersionKind);
       config.start_dir = start_dir;

       if matches.is_present("no-cargo-publish") {
           config.cargo_publish = ! matches.is_present("no-cargo-publish");
       }

       if matches.is_present("no-git-push") {
           config.git_push = ! matches.is_present("no-git-push");
       }

       try!(config.check());
       Ok(config)
   }

   pub fn commit_message(&self, proj: &CargoProj) -> String {
       Template(&self.commit_message).render(proj)
   }

   pub fn tag_name(&self, proj: &CargoProj) -> String {
       Template(&self.tag_name).render(proj)
   }

   pub fn editor(&self) -> Command {
       let editor_and_args = self.editor.split(' ').collect::<Vec<&str>>();
       let mut cmd = Command::new(editor_and_args[0]);
       let args = editor_and_args.iter().skip(1);
       for arg in args {
           cmd.arg(arg);
       }

       cmd
   }

   fn from_file() -> RrResult<Config> {
       let curr_file_config = try!(ConfigFromFile::load_from_current_dir());
       let home_file_config = try!(ConfigFromFile::load_from_home_dir());

       let file_config = match (curr_file_config, home_file_config) {
           (Some(cfc), Some(hfc)) => cfc.combine(&hfc),
           (Some(cfc), None)      => cfc,
           (None     , Some(hfc)) => hfc,
           (None     , None)      => ConfigFromFile::default()
       };

       let config = config!(file_config, [
           cargo_publish,
           git_push,
           commit_message,
           tag_name,
           editor
       ]);

       Ok(config)
   }

   fn default() -> RrResult<Config> {
       Ok(Config {
           version_kind: VersionKind::Patch,
           start_dir: try!(env::current_dir()),
           cargo_publish: true,
           git_push: true,
           commit_message: "<PROJ_NAME> <NEW_VERSION>".to_string(),
           tag_name: "<PROJ_NAME>-<NEW_VERSION>".to_string(),
           editor: "gvim -o".to_string()
       })
   }

   fn check(&self) -> RrResult<()> {
       if self.commit_message.is_empty() {
           return err_message("Invalid, empty commit message!");
       }

       if self.tag_name.is_empty() {
           return err_message("Invalid empty tag name!");
       }

       if self.editor.is_empty() {
           return err_message("Invalid, empty editor command!");
       }

       Ok(())
   }
}

/// Represents a string template that contains placeholders that can be replaced.
/// Currently only the placeholders '<PROJ_NAME>' - representing the name of the
/// cargo project and '<NEW_VERSION>' - representing the version of the release -
/// are supported.
#[derive(Debug)]
struct Template<'a>(&'a str);

impl<'a> Template<'a> {
    pub fn render(&self, proj: &CargoProj) -> String {
        self.0.replace("<PROJ_NAME>", &format!("{}", proj.name()))
            .replace("<NEW_VERSION>", &format!("{}", proj.version()))
    }
}

/// Represents the data from a `.rusty-release.toml` configuration file.
#[derive(RustcDecodable, Debug, Default)]
struct ConfigFromFile {
    pub cargo_publish: Option<bool>,
    pub git_push: Option<bool>,
    pub commit_message: Option<String>,
    pub tag_name: Option<String>,
    pub editor: Option<String>
}

impl ConfigFromFile {
    fn load_from_current_dir() -> RrResult<Option<ConfigFromFile>> {
        let path = try!(env::current_dir()).join(config_file_name());
        if ! path.is_file() {
            return Ok(None);
        }

        Ok(Some(try!(ConfigFromFile::load_from_file(&path))))
    }

    fn load_from_home_dir() -> RrResult<Option<ConfigFromFile>> {
        if let Some(path) = env::home_dir().map(|d| d.join(config_file_name())) {
            if path.is_file() {
                return Ok(Some(try!(ConfigFromFile::load_from_file(&path))));
            }
        }

        Ok(None)
    }

    fn load_from_file(path: &Path) -> RrResult<ConfigFromFile> {
        map_file(path, |contents| {
            let mut parser = toml::Parser::new(&contents);
            let value = try!(parser.parse()
                .ok_or_else(|| rr_error_message(format!("Couldn't parse toml file '{}': {:?}",
                                                        path.display(), parser.errors))));

            let mut decoder = toml::Decoder::new(toml::Value::Table(value));
            Ok(try!(ConfigFromFile::decode(&mut decoder)))
        })
    }

    fn combine(&self, other: &ConfigFromFile) -> ConfigFromFile {
        ConfigFromFile {
            cargo_publish: self.cargo_publish.or(other.cargo_publish),
            git_push: self.git_push.or(other.git_push),
            commit_message: self.commit_message.as_ref().or(other.commit_message.as_ref()).cloned(),
            tag_name: self.tag_name.as_ref().or(other.tag_name.as_ref()).cloned(),
            editor: self.editor.as_ref().or(other.editor.as_ref()).cloned()
        }
    }
}

fn config_file_name() -> &'static str {
    ".rusty-release.toml"
}
<|endoftext|>


<commit_before>use cards::Card;

// All these functions need reverse sorted vectors of Cards
pub fn has_pair(card_vec: &Vec<&Card>) -> bool {
    let mut prev: &Card = card_vec[0];
    for card in card_vec.iter().skip(1) {
        if *card == prev {
            return true;
        }
        prev = *card
    }
    return false;
}
<commit_msg>Make a general has_set function to make has_pair general<commit_after>use cards::Card;

// All these functions need reverse sorted vectors of Cards
pub fn has_pair(card_vec: &Vec<&Card>) -> bool {
    has_set(2, card_vec)
}

fn has_set(set_size: u8, card_vec: &Vec<&Card>) -> bool {
    let mut prev: &Card = card_vec[0];
    let mut count: u8 = 1;
    for card in card_vec.iter().skip(1) {
        if *card == prev {
            count += 1;
            if count == set_size {
                return true
            }
            continue;
        }
        prev = *card;
        count = 1;
    }
    false
}
<|endoftext|>