<commit_before>//! Checks for if expressions that contain only an if expression.
//!
//! For example, the lint would catch:
//!
//! ```
//! if x {
//!     if y {
//!         println!("Hello world");
//!     }
//! }
//! ```
//!
//! This lint is **warn** by default

use rustc::lint::*;
use syntax::ast;

use utils::{in_macro, snippet_block, span_lint_and_then};
use utils::sugg::Sugg;

/// **What it does:** This lint checks for nested `if`-statements which can be collapsed by
/// `&&`-combining their conditions and for `else { if .. }` expressions that can be collapsed to
/// `else if ..`.
///
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which makes code look more complex than it really is.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// if x {
///     if y {
///         …
///     }
/// }
///
/// // or
///
/// if x {
///     …
/// } else {
///     if y {
///         …
///     }
/// }
/// ```
///
/// Should be written:
///
/// ```rust
/// if x && y {
///     …
/// }
///
/// // or
///
/// if x {
///     …
/// } else if y {
///     …
/// }
/// ```
declare_lint! {
    pub COLLAPSIBLE_IF,
    Warn,
    "`if`s that can be collapsed (e.g. `if x { if y { … } }` and `else { if x { … } }`)"
}

#[derive(Copy,Clone)]
pub struct CollapsibleIf;

impl LintPass for CollapsibleIf {
    fn get_lints(&self) -> LintArray {
        lint_array!(COLLAPSIBLE_IF)
    }
}

impl EarlyLintPass for CollapsibleIf {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
        if !in_macro(cx, expr.span) {
            check_if(cx, expr)
        }
    }
}

fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
    match expr.node {
        ast::ExprKind::If(ref check, ref then, ref else_) => {
            if let Some(ref else_) = *else_ {
                check_collapsible_maybe_if_let(cx, else_);
            } else {
                check_collapsible_no_if_let(cx, expr, check, then);
            }
        }
        ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
            check_collapsible_maybe_if_let(cx, else_);
        }
        _ => (),
    }
}

fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
    if_let_chain! {[
        let ast::ExprKind::Block(ref block) = else_.node,
        let Some(ref else_) = expr_block(block),
        !in_macro(cx, else_.span),
    ], {
        match else_.node {
            ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
                span_lint_and_then(cx,
                                   COLLAPSIBLE_IF,
                                   block.span,
                                   "this `else { if .. }` block can be collapsed", |db| {
                    db.span_suggestion(block.span, "try", snippet_block(cx, else_.span, "..").into_owned());
                });
            }
            _ => (),
        }
    }}
}

fn check_collapsible_no_if_let(
    cx: &EarlyContext,
    expr: &ast::Expr,
    check: &ast::Expr,
    then: &ast::Block,
) {
    if_let_chain! {[
        let Some(inner) = expr_block(then),
        let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
    ], {
        if expr.span.expn_id != inner.span.expn_id {
            return;
        }
        span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
            let lhs = Sugg::ast(cx, check, "..");
            let rhs = Sugg::ast(cx, check_inner, "..");
            db.span_suggestion(expr.span,
                               "try",
                               format!("if {} {}",
                                       lhs.and(rhs),
                                       snippet_block(cx, content.span, "..")));
        });
    }}
}

/// If the block contains only one expression, returns it.
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
    let mut it = block.stmts.iter();

    if let (Some(stmt), None) = (it.next(), it.next()) {
        match stmt.node {
            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
            _ => None,
        }
    } else {
        None
    }
}
<commit_msg>Dogfood<commit_after>//! Checks for if expressions that contain only an if expression.
//!
//! For example, the lint would catch:
//!
//! ```
//! if x {
//!     if y {
//!         println!("Hello world");
//!     }
//! }
//! ```
//!
//! This lint is **warn** by default

use rustc::lint::*;
use syntax::ast;

use utils::{in_macro, snippet_block, span_lint_and_then};
use utils::sugg::Sugg;

/// **What it does:** This lint checks for nested `if`-statements which can be collapsed by
/// `&&`-combining their conditions and for `else { if .. }` expressions that can be collapsed to
/// `else if ..`.
///
/// **Why is this bad?** Each `if`-statement adds one level of nesting, which makes code look more complex than it really is.
///
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// if x {
///     if y {
///         …
///     }
/// }
///
/// // or
///
/// if x {
///     …
/// } else {
///     if y {
///         …
///     }
/// }
/// ```
///
/// Should be written:
///
/// ```rust
/// if x && y {
///     …
/// }
///
/// // or
///
/// if x {
///     …
/// } else if y {
///     …
/// }
/// ```
declare_lint! {
    pub COLLAPSIBLE_IF,
    Warn,
    "`if`s that can be collapsed (e.g. `if x { if y { ... } }` and `else { if x { ... } }`)"
}

#[derive(Copy,Clone)]
pub struct CollapsibleIf;

impl LintPass for CollapsibleIf {
    fn get_lints(&self) -> LintArray {
        lint_array!(COLLAPSIBLE_IF)
    }
}

impl EarlyLintPass for CollapsibleIf {
    fn check_expr(&mut self, cx: &EarlyContext, expr: &ast::Expr) {
        if !in_macro(cx, expr.span) {
            check_if(cx, expr)
        }
    }
}

fn check_if(cx: &EarlyContext, expr: &ast::Expr) {
    match expr.node {
        ast::ExprKind::If(ref check, ref then, ref else_) => {
            if let Some(ref else_) = *else_ {
                check_collapsible_maybe_if_let(cx, else_);
            } else {
                check_collapsible_no_if_let(cx, expr, check, then);
            }
        }
        ast::ExprKind::IfLet(_, _, _, Some(ref else_)) => {
            check_collapsible_maybe_if_let(cx, else_);
        }
        _ => (),
    }
}

fn check_collapsible_maybe_if_let(cx: &EarlyContext, else_: &ast::Expr) {
    if_let_chain! {[
        let ast::ExprKind::Block(ref block) = else_.node,
        let Some(ref else_) = expr_block(block),
        !in_macro(cx, else_.span),
    ], {
        match else_.node {
            ast::ExprKind::If(..) | ast::ExprKind::IfLet(..) => {
                span_lint_and_then(cx,
                                   COLLAPSIBLE_IF,
                                   block.span,
                                   "this `else { if .. }` block can be collapsed", |db| {
                    db.span_suggestion(block.span, "try", snippet_block(cx, else_.span, "..").into_owned());
                });
            }
            _ => (),
        }
    }}
}

fn check_collapsible_no_if_let(
    cx: &EarlyContext,
    expr: &ast::Expr,
    check: &ast::Expr,
    then: &ast::Block,
) {
    if_let_chain! {[
        let Some(inner) = expr_block(then),
        let ast::ExprKind::If(ref check_inner, ref content, None) = inner.node,
    ], {
        if expr.span.expn_id != inner.span.expn_id {
            return;
        }
        span_lint_and_then(cx, COLLAPSIBLE_IF, expr.span, "this if statement can be collapsed", |db| {
            let lhs = Sugg::ast(cx, check, "..");
            let rhs = Sugg::ast(cx, check_inner, "..");
            db.span_suggestion(expr.span,
                               "try",
                               format!("if {} {}",
                                       lhs.and(rhs),
                                       snippet_block(cx, content.span, "..")));
        });
    }}
}

/// If the block contains only one expression, returns it.
fn expr_block(block: &ast::Block) -> Option<&ast::Expr> {
    let mut it = block.stmts.iter();

    if let (Some(stmt), None) = (it.next(), it.next()) {
        match stmt.node {
            ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => Some(expr),
            _ => None,
        }
    } else {
        None
    }
}
<|endoftext|>


<commit_before>use super::generate;
use rules::range_set::Range;

#[test]
fn partial_overlap() {
    let mut set1 = generate(vec![('5', '9')]);
    set1.remove(Range('3', '6')); // beginnings different.

    let mut set2 = generate(vec![('5', '9')]);
    set2.remove(Range('5', '6')); // beginnings same.

    let mut set3 = generate(vec![('2', '6')]);
    set3.remove(Range('5', '9')); // end different.

    let mut set4 = generate(vec![('2', '6')]);
    set4.remove(Range('5', '6')); // end same.

    let other12 = generate(vec![('7', '9')]);
    let other34 = generate(vec![('2', '4')]);

    assert_eq!(set1, other12);
    assert_eq!(set2, other12);
    assert_eq!(set3, other34);
    assert_eq!(set4, other34);
}
#[test]
fn subset() {
    let mut set = generate(vec![('1', '9')]);
    set.remove(Range('3', '6'));

    let other = generate(vec![('1', '2'), ('7', '9')]);

    assert_eq!(set, other);
}
#[test]
fn superset() {
    let mut set = generate(vec![('5', '6')]);
    set.remove(Range('3', '8'));

    let other = generate(vec![]);
    assert_eq!(set, other);
}
#[test]
fn disjoint() {
    let mut set = generate(vec![('2', '3')]);
    set.remove(Range('6', '8'));

    let other = generate(vec![('2', '3')]);
    assert_eq!(set, other);
}
<commit_msg>Update range_set remove tests<commit_after>use super::generate;
use rules::range_set::Range;

#[test]
fn partial_overlap() {
    let mut left  = generate(vec![('4', '7')]);
    let mut right = left.clone();

    left.remove(Range('3', '5'));
    right.remove(Range('6', '9'));

    let other_left  = generate(vec![('6', '7')]);
    let other_right = generate(vec![('4', '5')]);

    assert_eq!(left,  other_left);
    assert_eq!(right, other_right);
}
#[test]
fn subset() {
    let mut inner  = generate(vec![('3', '8')]);
    let mut left   = inner.clone();
    let mut right  = inner.clone();
    let mut both   = inner.clone();

    inner.remove(Range('4', '7'));
    left .remove(Range('3', '7'));
    right.remove(Range('4', '8'));
    both .remove(Range('3', '8'));

    let other_inner = generate(vec![('3', '3'), ('8', '8')]);
    let other_left  = generate(vec![('8', '8')]);
    let other_right = generate(vec![('3', '3')]);
    let other_both  = generate(vec![]);

    assert_eq!(inner, other_inner);
    assert_eq!(left, other_left);
    assert_eq!(right, other_right);
    assert_eq!(both, other_both);
}
#[test]
fn superset() {
    let mut set = generate(vec![('5', '6')]);
    set.remove(Range('3', '8'));

    let other = generate(vec![]);
    assert_eq!(set, other);
}
#[test]
fn disjoint() {
    let mut set = generate(vec![('3', '4')]);
    set.remove(Range('6', '8'));
    set.remove(Range('1', '2'));

    let other = generate(vec![('3', '4')]);
    assert_eq!(set, other);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;

use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct Address {
    city: String,
    postcode: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct QueryParams {
    id: u8,
    name: String,
    address: Address,
    phone: u32,
    user_ids: Vec<u8>,
}

// Compares a map generated by hash_to_map with the map returned by
// qs::from_str. All types are inferred by the compiler.
macro_rules! map_test {
    ($string:expr, $($mapvars:tt)*) => {
        let expected_map = hash_to_map!(New $($mapvars)*);
        let testmap: HashMap<String, _> = qs::from_str($string).unwrap();
        assert_eq!(expected_map, testmap);
    }
}

// Macro used to quickly generate a nested HashMap from a string.
macro_rules! hash_to_map {
    // Base case: a map with no inputs, do nothing.
    ($map:expr, ) => ();
    //{}
    // This parses a single map entry, with a value explicitly an expression.
    ($map:expr, $k:tt[e $v:expr] $($rest:tt)*) => {{
        $map.insert($k.to_string(), $v.to_owned());
        hash_to_map!($map, $($rest)*);
    }};

    // This parses a single map entry, plus the rest of the values.
    ($map:expr, $k:tt[$v:tt] $($rest:tt)*) => {{
        $map.insert($k.to_string(), $v.to_owned());
        hash_to_map!($map, $($rest)*);
    }};

    // This parses the first entry as a nested entry, and tail calls the
    // remaining in rest.
    ($map:expr, $k:tt[$($inner:tt)*] $($rest:tt)*) => {{
        let mut inner_map = HashMap::new();
        hash_to_map!(inner_map, $($inner)*);
        $map.insert($k.to_string(), inner_map);
        hash_to_map!($map, $($rest)*);
    }};

    // Constructs the map and then runs the macro. This infers the types for the
    // hashmap.
    (New $($rest:tt)*) => {{
      let mut map = HashMap::new();
      hash_to_map!(map, $($rest)*);
      map
    }}

}

#[test]
fn deserialize_struct() {
    let params = QueryParams {
        id: 42,
        name: "Acme".to_string(),
        phone: 12345,
        address: Address {
            city: "Carrot City".to_string(),
            postcode: "12345".to_string(),
        },
        user_ids: vec![1, 2, 3, 4],
    };

    // standard parameters
    let rec_params: QueryParams = qs::from_str("\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        address[city]=Carrot+City&user_ids[0]=1&user_ids[1]=2&\
        user_ids[2]=3&user_ids[3]=4")
        .unwrap();
    assert_eq!(rec_params, params);

    // unindexed arrays
    let rec_params: QueryParams = qs::from_str("\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        address[city]=Carrot+City&user_ids[]=1&user_ids[]=2&\
        user_ids[]=3&user_ids[]=4")
        .unwrap();
    assert_eq!(rec_params, params);

    // ordering doesn't matter
    let rec_params: QueryParams = qs::from_str("\
        address[city]=Carrot+City&user_ids[]=1&user_ids[]=2&\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        user_ids[]=3&user_ids[]=4")
        .unwrap();
    assert_eq!(rec_params, params);

}

#[test]
fn qs_test_simple() {
    // test('parse()', function (t) {
    // t.test('parses a simple string', function (st) {
    // st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
    map_test!("0=foo", 0["foo"]);

    // st.deepEqual(qs.parse('foo=c++'), { foo: 'c  ' });
    map_test!("foo=c++", "foo"["c  "]);

    // st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } });
    map_test!("a[>=]=23", "a"[">="[23]]);

    // st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } });
    map_test!("a[<=>]==23", "a"["<=>"["=23"]]);

    // st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } });
    map_test!("a[==]=23", "a"["=="[23]]);

    // st.deepEqual(qs.parse('foo', { strictNullHandling: true }),
    // { foo: null });
    let none: Option<String> = Option::None;
    map_test!("foo", "foo"[none]);

    // st.deepEqual(qs.parse('foo'), { foo: '' });
    map_test!("foo", "foo"[""]);

    // st.deepEqual(qs.parse('foo='), { foo: '' });
    map_test!("foo=", "foo"[""]);

    // st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' });
    map_test!("foo=bar", "foo"["bar"]);

    // st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' });
    map_test!(" foo = bar = baz ", " foo "[" bar = baz "]);

    // st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' });
    map_test!("foo=bar=baz", "foo"["bar=baz"]);

    // st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' });
    map_test!("foo=bar&bar=baz", "foo"["bar"] "bar"["baz"]);

    // st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' });
    map_test!("foo2=bar2&baz2=", "foo2"["bar2"] "baz2"[""]);

    // st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), {
    // foo: 'bar', baz: null });
    map_test!("foo=bar&baz", "foo"[e Some("bar".to_string())] "baz"[e None]);

    // st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' });
    map_test!("foo=bar&baz", "foo"["bar"] "baz"[""]);

    // st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'),
    // {
    //     cht: 'p3',
    //     chd: 't:60,40',
    //     chs: '250x100',
    //     chl: 'Hello|World'
    // });
    map_test!("cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World",
      "cht"["p3"]
      "chd"["t:60,40"]
      "chs"["250x100"]
      "chl"["Hello|World"]
    );
    // st.end();
    // });
}

#[test]
fn qs_u32_map() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        map: HashMap<u32, String>
    }

    let query = {
        let mut map = HashMap::new();
        map.insert(10, "Hello".into());
        Query { map: map }
    };

    let params: Query = qs::from_str("map[10]=Hello").unwrap();
    assert_eq!(params, query)
}

#[test]
fn no_panic_on_parse_error() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        vec: Vec<u32>
    }

    let params: Result<Query, _> = qs::from_str("vec[]=a&vec[]=2");
    assert!(params.is_err())
}

#[test]
fn qs_nesting() {
    // t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single
    // nested string');
    map_test!("a[b]=c", "a"["b"["c"]]);

    // t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a
    // double nested string');
    map_test!("a[b][c]=d", "a"["b"["c"["d"]]]);
    // t.deepEqual(
    //     qs.parse('a[b][c][d][e][f][g][h]=i'),
    //     { a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } },
    //     'defaults to a depth of 5'
    // );
    // This looks like depth 6 to me? Tweaked test to make it 5.
    map_test!("a[b][c][d][e][f][g][h]=i",
              "a"["b"["c"["d"["e"["[f][g][h]"["i"]]]]]]);
}

#[test]
fn optional_seq() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        vec: Option<Vec<u8>>,
    }

    let params = "";
    let query = Query {
        vec: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "vec=";
    let query = Query {
        vec: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "vec[0]=1&vec[1]=2";
    let query = Query {
        vec: Some(vec![1,2]),
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);
}

#[test]
fn optional_struct() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        address: Option<Address>,
    }

    let params = "";
    let query = Query {
        address: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "address=";
    let query = Query {
        address: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "address[city]=Carrot+City&address[postcode]=12345";
    let query = Query {
        address: Some(Address {
            city: "Carrot City".to_string(),
            postcode: "12345".to_string(),
        },),
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);
}

#[test]
fn deserialize_enum_untagged() {
    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(untagged)]
    enum E {
        B(bool),
        S(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Query {
        e: E,
    }

    let params = "e=true";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, Query { e: E::S("true".to_string()) });
}

#[test]
fn deserialize_enum_adjacently() {
    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(tag = "type", content = "val")]
    enum E {
        B(bool),
        S(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(tag = "type", content = "val")]
    enum V {
        V1 { x: u8, y: u16 },
        V2(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Query {
        e: E,
        v: Option<V>
    }

    let params = "e[type]=B&e[val]=true&v[type]=V1&v[val][x]=12&v[val][y]=300";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params,
        Query { e: E::B(true), v: Some(V::V1 { x: 12, y: 300 }) }
    );

    let params = "e[type]=S&e[val]=other";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, Query { e: E::S("other".to_string()), v: None });
}
<commit_msg>Revert changes to tests to allow integer keys.<commit_after>#[macro_use]
extern crate serde_derive;
extern crate serde_qs as qs;

use std::collections::HashMap;

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct Address {
    city: String,
    postcode: String,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
struct QueryParams {
    id: u8,
    name: String,
    address: Address,
    phone: u32,
    user_ids: Vec<u8>,
}

// Compares a map generated by hash_to_map with the map returned by
// qs::from_str. All types are inferred by the compiler.
macro_rules! map_test {
    ($string:expr, $($mapvars:tt)*) => {
        let expected_map = hash_to_map!(New $($mapvars)*);
        let testmap: HashMap<_, _> = qs::from_str($string).unwrap();
        assert_eq!(expected_map, testmap);
    }
}

// Macro used to quickly generate a nested HashMap from a string.
macro_rules! hash_to_map {
    // Base case: a map with no inputs, do nothing.
    ($map:expr, ) => ();
    //{}
    // This parses a single map entry, with a value explicitly an expression.
    ($map:expr, $k:tt[e $v:expr] $($rest:tt)*) => {{
        $map.insert($k.to_owned(), $v.to_owned());
        hash_to_map!($map, $($rest)*);
    }};

    // This parses a single map entry, plus the rest of the values.
    ($map:expr, $k:tt[$v:tt] $($rest:tt)*) => {{
        $map.insert($k.to_owned(), $v.to_owned());
        hash_to_map!($map, $($rest)*);
    }};

    // This parses the first entry as a nested entry, and tail calls the
    // remaining in rest.
    ($map:expr, $k:tt[$($inner:tt)*] $($rest:tt)*) => {{
        let mut inner_map = HashMap::new();
        hash_to_map!(inner_map, $($inner)*);
        $map.insert($k.to_owned(), inner_map);
        hash_to_map!($map, $($rest)*);
    }};

    // Constructs the map and then runs the macro. This infers the types for the
    // hashmap.
    (New $($rest:tt)*) => {{
      let mut map = HashMap::new();
      hash_to_map!(map, $($rest)*);
      map
    }}

}

#[test]
fn deserialize_struct() {
    let params = QueryParams {
        id: 42,
        name: "Acme".to_string(),
        phone: 12345,
        address: Address {
            city: "Carrot City".to_string(),
            postcode: "12345".to_string(),
        },
        user_ids: vec![1, 2, 3, 4],
    };

    // standard parameters
    let rec_params: QueryParams = qs::from_str("\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        address[city]=Carrot+City&user_ids[0]=1&user_ids[1]=2&\
        user_ids[2]=3&user_ids[3]=4")
        .unwrap();
    assert_eq!(rec_params, params);

    // unindexed arrays
    let rec_params: QueryParams = qs::from_str("\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        address[city]=Carrot+City&user_ids[]=1&user_ids[]=2&\
        user_ids[]=3&user_ids[]=4")
        .unwrap();
    assert_eq!(rec_params, params);

    // ordering doesn't matter
    let rec_params: QueryParams = qs::from_str("\
        address[city]=Carrot+City&user_ids[]=1&user_ids[]=2&\
        name=Acme&id=42&phone=12345&address[postcode]=12345&\
        user_ids[]=3&user_ids[]=4")
        .unwrap();
    assert_eq!(rec_params, params);

}

#[test]
fn qs_test_simple() {
    // test('parse()', function (t) {
    // t.test('parses a simple string', function (st) {
    // st.deepEqual(qs.parse('0=foo'), { 0: 'foo' });
    map_test!("0=foo", 0["foo"]);

    // st.deepEqual(qs.parse('foo=c++'), { foo: 'c  ' });
    map_test!("foo=c++", "foo"["c  "]);

    // st.deepEqual(qs.parse('a[>=]=23'), { a: { '>=': '23' } });
    map_test!("a[>=]=23", "a"[">="[23]]);

    // st.deepEqual(qs.parse('a[<=>]==23'), { a: { '<=>': '=23' } });
    map_test!("a[<=>]==23", "a"["<=>"["=23"]]);

    // st.deepEqual(qs.parse('a[==]=23'), { a: { '==': '23' } });
    map_test!("a[==]=23", "a"["=="[23]]);

    // st.deepEqual(qs.parse('foo', { strictNullHandling: true }),
    // { foo: null });
    let none: Option<String> = Option::None;
    map_test!("foo", "foo"[none]);

    // st.deepEqual(qs.parse('foo'), { foo: '' });
    map_test!("foo", "foo"[""]);

    // st.deepEqual(qs.parse('foo='), { foo: '' });
    map_test!("foo=", "foo"[""]);

    // st.deepEqual(qs.parse('foo=bar'), { foo: 'bar' });
    map_test!("foo=bar", "foo"["bar"]);

    // st.deepEqual(qs.parse(' foo = bar = baz '), { ' foo ': ' bar = baz ' });
    map_test!(" foo = bar = baz ", " foo "[" bar = baz "]);

    // st.deepEqual(qs.parse('foo=bar=baz'), { foo: 'bar=baz' });
    map_test!("foo=bar=baz", "foo"["bar=baz"]);

    // st.deepEqual(qs.parse('foo=bar&bar=baz'), { foo: 'bar', bar: 'baz' });
    map_test!("foo=bar&bar=baz", "foo"["bar"] "bar"["baz"]);

    // st.deepEqual(qs.parse('foo2=bar2&baz2='), { foo2: 'bar2', baz2: '' });
    map_test!("foo2=bar2&baz2=", "foo2"["bar2"] "baz2"[""]);

    // st.deepEqual(qs.parse('foo=bar&baz', { strictNullHandling: true }), {
    // foo: 'bar', baz: null });
    map_test!("foo=bar&baz", "foo"[e Some("bar".to_string())] "baz"[e None]);

    // st.deepEqual(qs.parse('foo=bar&baz'), { foo: 'bar', baz: '' });
    map_test!("foo=bar&baz", "foo"["bar"] "baz"[""]);

    // st.deepEqual(qs.parse('cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World'),
    // {
    //     cht: 'p3',
    //     chd: 't:60,40',
    //     chs: '250x100',
    //     chl: 'Hello|World'
    // });
    map_test!("cht=p3&chd=t:60,40&chs=250x100&chl=Hello|World",
      "cht"["p3"]
      "chd"["t:60,40"]
      "chs"["250x100"]
      "chl"["Hello|World"]
    );
    // st.end();
    // });
}

#[test]
fn qs_u32_map() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        map: HashMap<u32, String>
    }

    let query = {
        let mut map = HashMap::new();
        map.insert(10, "Hello".into());
        Query { map: map }
    };

    let params: Query = qs::from_str("map[10]=Hello").unwrap();
    assert_eq!(params, query)
}

#[test]
fn no_panic_on_parse_error() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        vec: Vec<u32>
    }

    let params: Result<Query, _> = qs::from_str("vec[]=a&vec[]=2");
    assert!(params.is_err())
}

#[test]
fn qs_nesting() {
    // t.deepEqual(qs.parse('a[b]=c'), { a: { b: 'c' } }, 'parses a single
    // nested string');
    map_test!("a[b]=c", "a"["b"["c"]]);

    // t.deepEqual(qs.parse('a[b][c]=d'), { a: { b: { c: 'd' } } }, 'parses a
    // double nested string');
    map_test!("a[b][c]=d", "a"["b"["c"["d"]]]);
    // t.deepEqual(
    //     qs.parse('a[b][c][d][e][f][g][h]=i'),
    //     { a: { b: { c: { d: { e: { f: { '[g][h]': 'i' } } } } } } },
    //     'defaults to a depth of 5'
    // );
    // This looks like depth 6 to me? Tweaked test to make it 5.
    map_test!("a[b][c][d][e][f][g][h]=i",
              "a"["b"["c"["d"["e"["[f][g][h]"["i"]]]]]]);
}

#[test]
fn optional_seq() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        vec: Option<Vec<u8>>,
    }

    let params = "";
    let query = Query {
        vec: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "vec=";
    let query = Query {
        vec: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "vec[0]=1&vec[1]=2";
    let query = Query {
        vec: Some(vec![1,2]),
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);
}

#[test]
fn optional_struct() {
    #[derive(Debug,Serialize,Deserialize,PartialEq)]
    struct Query {
        address: Option<Address>,
    }

    let params = "";
    let query = Query {
        address: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "address=";
    let query = Query {
        address: None,
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);

    let params = "address[city]=Carrot+City&address[postcode]=12345";
    let query = Query {
        address: Some(Address {
            city: "Carrot City".to_string(),
            postcode: "12345".to_string(),
        },),
    };
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, query);
}

#[test]
fn deserialize_enum_untagged() {
    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(untagged)]
    enum E {
        B(bool),
        S(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Query {
        e: E,
    }

    let params = "e=true";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, Query { e: E::S("true".to_string()) });
}

#[test]
fn deserialize_enum_adjacently() {
    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(tag = "type", content = "val")]
    enum E {
        B(bool),
        S(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    #[serde(tag = "type", content = "val")]
    enum V {
        V1 { x: u8, y: u16 },
        V2(String),
    }

    #[derive(Deserialize, Debug, PartialEq)]
    struct Query {
        e: E,
        v: Option<V>
    }

    let params = "e[type]=B&e[val]=true&v[type]=V1&v[val][x]=12&v[val][y]=300";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params,
        Query { e: E::B(true), v: Some(V::V1 { x: 12, y: 300 }) }
    );

    let params = "e[type]=S&e[val]=other";
    let rec_params: Query = qs::from_str(params).unwrap();
    assert_eq!(rec_params, Query { e: E::S("other".to_string()), v: None });
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        self.sshfs_manager.mount()?;
        self.loop_manager.attach()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.unlock(&loop_device)?;
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        self.luks_manager.unmount()?;
        let loop_device = self.loop_manager.find()?;
        self.luks_manager.lock(&loop_device)?;
        self.loop_manager.detach()?;
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<commit_msg>apricot-auric: Add verbose logging<commit_after>use regex::Regex;
use std::path::PathBuf;
use std::result::Result;
use subprocess::{Exec, Redirection};

const LOOP_BACKING_DEVICE_BASENAME: &'static str = "flaglock6-home";
const LOOP_DEVICE_PARTITION_LABEL: &'static str = "fl6";
const RSYNC_DOT_NET_REMOTE_DIR: &'static str = "usw-s007.rsync.net:./";

#[derive(Debug)]
pub enum AuricError {
    Invocation(String),
    Io(String),
    Subprocess(String),
    NotFound,
}

enum AuricOperationMode {
    Mount,
    Unmount,
}

impl From<subprocess::PopenError> for AuricError {
    fn from(err: subprocess::PopenError) -> AuricError {
        AuricError::Subprocess(err.to_string())
    }
}

struct SshfsManager {
    mountpoint: PathBuf,
}

struct LoopManager {
    target: Regex,
    backing: PathBuf,
}

struct LuksManager {
    device: PathBuf,
}

struct AuricImpl {
    sshfs_manager: SshfsManager,
    loop_manager: LoopManager,
    luks_manager: LuksManager,
}

fn exec_with_stdout(command: &str, args: &[&str], error_hint: &str) -> Result<String, AuricError> {
    let result = Exec::cmd(command)
        .args(args)
        .stdout(Redirection::Pipe)
        .stderr(Redirection::Pipe)
        .capture()?;
    if !result.success() {
        return Err(AuricError::Subprocess(format!(
            "{} failed: ``{}''",
            error_hint,
            result.stderr_str(),
        )));
    }
    Ok(result.stdout_str())
}

fn exec(command: &str, args: &[&str], error_hint: &str) -> Result<(), AuricError> {
    exec_with_stdout(command, args, error_hint).map(|_stdout_str| ())
}

impl SshfsManager {
    pub fn new() -> Result<SshfsManager, AuricError> {
        let xdg_runtime_dir = match std::env::var("XDG_RUNTIME_DIR") {
            Ok(val) => val,
            Err(e) => {
                return Err(AuricError::Invocation(format!(
                    "fatal error getting ${{XDG_RUNTIME_DIR}}: ``{}''",
                    e
                )))
            }
        };
        let mut mountpoint = PathBuf::new();
        mountpoint.push(xdg_runtime_dir);
        mountpoint.push("rsync.net-mountpoint");

        Ok(SshfsManager {
            mountpoint: mountpoint,
        })
    }

    fn ensure_mountpoint_is_dir(&self) -> Result<(), AuricError> {
        if self.mountpoint.is_dir() {
            return Ok(());
        }
        std::fs::create_dir(self.mountpoint.as_path())
            .map_err(|e| AuricError::Io(format!("bad mountpoint: ``{}''", e)))
    }

    // Does _not_ ensure that backing device exists.
    pub fn loop_backing_device_path(&self) -> PathBuf {
        let mut result: PathBuf = self.mountpoint.clone();
        result.push(LOOP_BACKING_DEVICE_BASENAME);
        result
    }

    fn loop_backing_device_present(&self) -> bool {
        self.loop_backing_device_path().is_file()
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        self.ensure_mountpoint_is_dir()?;
        if self.loop_backing_device_present() {
            return Ok(());
        }

        // Invoke `sshfs`
        // *    with compression,
        // *    allowing other users to access the mounted filesystem
        //      (necessary to allow loop-mounts), and
        // *    attempting to reconnect if we lose the connection.
        exec(
            "sshfs",
            &[
                "-C",
                "-o",
                "allow_other,reconnect",
                RSYNC_DOT_NET_REMOTE_DIR,
                self.mountpoint.to_str().unwrap(),
            ],
            "sshfs mount",
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        if !self.loop_backing_device_present() {
            return Ok(());
        }
        exec(
            "fusermount",
            &["-u", self.mountpoint.to_str().unwrap()],
            "fusermount -u",
        )
    }
}

impl LoopManager {
    pub fn new(backing: &PathBuf) -> LoopManager {
        let target = format!(
            r"^(/dev/loop\d)(\s+\d+){{4}}\s+({})\s+",
            backing.to_str().unwrap()
        );
        LoopManager {
            target: Regex::new(&target).unwrap(),
            backing: backing.clone(),
        }
    }

    pub fn find(&self) -> Result<PathBuf, AuricError> {
        let losetup_stdout = exec_with_stdout("losetup", &["-l"], "loop device enumeration")?;
        for line in losetup_stdout.lines() {
            if let Some(captures) = self.target.captures(line) {
                return Ok(PathBuf::from(captures.get(1).unwrap().as_str()));
            }
        }
        Err(AuricError::NotFound)
    }

    pub fn attach(&self) -> Result<(), AuricError> {
        if let Ok(_attached_loop) = self.find() {
            return Ok(());
        }

        exec(
            "udisksctl",
            &["loop-setup", "-f", self.backing.to_str().unwrap()],
            "loop device attach",
        )
    }

    // Note that the loop device will linger if the volume remains
    // unlocked. Prefer not to call this before locking.
    pub fn detach(&self) -> Result<(), AuricError> {
        let attached_loop = match self.find() {
            Ok(path) => path,
            Err(e) => {
                if let AuricError::NotFound = e {
                    return Ok(());
                }
                return Err(e);
            }
        };
        exec(
            "udisksctl",
            &["loop-delete", "-b", attached_loop.to_str().unwrap()],
            "loop device detach",
        )
    }
}

impl LuksManager {
    pub fn new() -> LuksManager {
        let mut device = PathBuf::from("/dev/disk/by-label");
        device.push(LOOP_DEVICE_PARTITION_LABEL);
        LuksManager { device: device }
    }

    pub fn unlock(&self, locked_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unlock", "-b", locked_device.to_str().unwrap()],
            format!("unlocking {}", locked_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn lock(&self, underlying_device: &PathBuf) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["lock", "-b", underlying_device.to_str().unwrap()],
            format!("locking {}", underlying_device.to_str().unwrap()).as_str(),
        )
    }

    pub fn mount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["mount", "-b", self.device.to_str().unwrap()],
            format!("mounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }

    pub fn unmount(&self) -> Result<(), AuricError> {
        exec(
            "udisksctl",
            &["unmount", "-b", self.device.to_str().unwrap()],
            format!("unmounting {}", self.device.to_str().unwrap()).as_str(),
        )
    }
}

fn get_action() -> Result<AuricOperationMode, AuricError> {
    let mut args_iter = std::env::args();
    args_iter.next();
    if let Some(action) = args_iter.next() {
        match action.as_str() {
            "mount" => return Ok(AuricOperationMode::Mount),
            "unmount" => return Ok(AuricOperationMode::Unmount),
            _ => {
                return Err(AuricError::Invocation(format!(
                    "unknown action: ``{}''",
                    action
                )))
            }
        }
    }
    Err(AuricError::Invocation("no action given".to_string()))
}

impl AuricImpl {
    pub fn new() -> Result<AuricImpl, AuricError> {
        let sshfs_manager = SshfsManager::new()?;
        let loop_backing_device_path = sshfs_manager.loop_backing_device_path();
        Ok(AuricImpl {
            sshfs_manager: sshfs_manager,
            loop_manager: LoopManager::new(&loop_backing_device_path),
            luks_manager: LuksManager::new(),
        })
    }

    fn mount(&self) -> Result<(), AuricError> {
        println!("{}", "Mounting sshfs...");
        self.sshfs_manager.mount()?;
        println!("{}", "Attaching loop device...");
        self.loop_manager.attach()?;
        println!("{}", "Finding newly attached loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Unlocking volume...");
        self.luks_manager.unlock(&loop_device)?;
        println!("{}", "Mounting...");
        self.luks_manager.mount()
    }

    fn unmount(&self) -> Result<(), AuricError> {
        println!("{}", "Unmounting...");
        self.luks_manager.unmount()?;
        println!("{}", "Finding backing loop device...");
        let loop_device = self.loop_manager.find()?;
        println!("{}", "Locking volume...");
        self.luks_manager.lock(&loop_device)?;
        println!("{}", "Detaching loop device...");
        self.loop_manager.detach()?;
        println!("{}", "Unmounting sshfs...");
        self.sshfs_manager.unmount()
    }

    pub fn act(&self, mode: AuricOperationMode) -> Result<(), AuricError> {
        match mode {
            AuricOperationMode::Mount => return self.mount(),
            AuricOperationMode::Unmount => return self.unmount(),
        }
    }
}

fn main_impl() -> Result<(), AuricError> {
    let action = get_action()?;
    let auric_impl = AuricImpl::new()?;
    auric_impl.act(action)
}

fn main() {
    std::process::exit(match main_impl() {
        Ok(_) => 0,
        Err(e) => {
            eprintln!("{:?}", e);
            1
        }
    });
}
<|endoftext|>


<commit_before>extern crate config;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;

use futures::future::join_all;

use tokio::join;
use tokio::net::TcpListener;
use tokio::net::TcpSocket;
use tokio::net::TcpStream;
use tokio::task;

use std::collections::HashMap;

use std::io;

type HashAddressInfo = HashMap<String, HashMap<String, String>>;
type HashContainsYaml = HashMap<String, HashAddressInfo>;

async fn forward(mut stream_origin: TcpStream, output_address: &str) -> io::Result<()> {
    let socket_output = TcpSocket::new_v4()?;
    let mut stream_output = socket_output
        .connect(output_address.parse().expect("Unable parse socket address"))
        .await?;
    let (mut stream_origin_recv, mut stream_origin_send) = stream_origin.split();
    let (mut stream_output_recv, mut stream_output_send) = stream_output.split();
    let (stream_origin_bytes_copied, stream_output_bytes_copied) = join!(
        tokio::io::copy(&mut stream_origin_recv, &mut stream_output_send),
        tokio::io::copy(&mut stream_output_recv, &mut stream_origin_send),
    );

    if let Ok(count) = stream_origin_bytes_copied {
        info!("Stream origin transfered bytes: {}", count);
    } else {
        error!("Stream origin error transfering bytes");
    }

    if let Ok(count) = stream_output_bytes_copied {
        info!("Stream output transfered bytes: {}", count);
    } else {
        error!("Stream output eError transfering bytes");
    }

    Ok(())
}

pub struct Host {
    host: String,
    port: i32,
}

pub struct RuleConfig {
    name: String,
    input: Host,
    output: Host,
}

fn load_config(config_file: &str) -> Vec<RuleConfig> {
    let mut settings = config::Config::default();
    settings
        .merge(config::File::with_name(config_file))
        .unwrap();
    let mut doc = settings.try_into::<HashContainsYaml>().unwrap();

    let mut rules: Vec<RuleConfig> = Vec::new();
    for (name, value) in doc.iter_mut() {
        let address_input = value.clone().get_mut("input").map_or(
            Host {
                host: "".to_owned(),
                port: 0,
            },
            |v| Host {
                host: v["host"].clone(),
                port: v["port"].clone().parse::<i32>().unwrap(),
            },
        );
        let address_output = value.clone().get_mut("output").map_or(
            Host {
                host: "".to_owned(),
                port: 0,
            },
            |v| Host {
                host: v["host"].clone(),
                port: v["port"].clone().parse::<i32>().unwrap(),
            },
        );

        rules.push(RuleConfig {
            name: name.to_string(),
            input: address_input,
            output: address_output,
        });
    }

    return rules;
}

#[tokio::main]
async fn main() -> io::Result<()> {
    let config_file: &str = "/home/carlosb/rust-workspace/examples-rust/reverse-proxy/config.yml";
    let rule_configs = load_config(config_file);

    pretty_env_logger::init();

    let mut joins = Vec::new();
    for rule in rule_configs {
        let input_host = rule.input.host;
        let input_port = rule.input.port;
        let output_host = rule.output.host;
        let output_port = rule.output.port;

        let join = task::spawn(async move {
            let output_address = format!("{}:{}", output_host, output_port);
            let input_address = format!("{}:{}", input_host, input_port);
            let listener = TcpListener::bind(output_address.clone())
                .await
                .expect(format!("Error binding address {}", output_address).as_str());
            //TODO loop to wait for connection, is it needed this loop?
            loop {
                let (socket, _) = listener.accept().await.unwrap();
                forward(socket, input_address.as_str()).await.unwrap();
            }
        });
        joins.push(join);
    }
    join_all(joins).await;
    Ok(())
}
<commit_msg>Added flag option<commit_after>extern crate config;
extern crate pretty_env_logger;
#[macro_use]
extern crate log;

use futures::future::join_all;

use getopts::Options;
use std::env;

use tokio::join;
use tokio::net::TcpListener;
use tokio::net::TcpSocket;
use tokio::net::TcpStream;
use tokio::task;

use std::collections::HashMap;

use std::io;

type HashAddressInfo = HashMap<String, HashMap<String, String>>;
type HashContainsYaml = HashMap<String, HashAddressInfo>;

async fn forward(mut stream_origin: TcpStream, output_address: &str) -> io::Result<()> {
    let socket_output = TcpSocket::new_v4()?;
    let mut stream_output = socket_output
        .connect(output_address.parse().expect("Unable parse socket address"))
        .await?;
    let (mut stream_origin_recv, mut stream_origin_send) = stream_origin.split();
    let (mut stream_output_recv, mut stream_output_send) = stream_output.split();
    let (stream_origin_bytes_copied, stream_output_bytes_copied) = join!(
        tokio::io::copy(&mut stream_origin_recv, &mut stream_output_send),
        tokio::io::copy(&mut stream_output_recv, &mut stream_origin_send),
    );

    if let Ok(count) = stream_origin_bytes_copied {
        info!("Stream origin transfered bytes: {}", count);
    } else {
        error!("Stream origin error transfering bytes");
    }

    if let Ok(count) = stream_output_bytes_copied {
        info!("Stream output transfered bytes: {}", count);
    } else {
        error!("Stream output eError transfering bytes");
    }

    Ok(())
}

pub struct Host {
    host: String,
    port: i32,
}

pub struct RuleConfig {
    name: String,
    input: Host,
    output: Host,
}

fn load_config(config_file: &str) -> Vec<RuleConfig> {
    let mut settings = config::Config::default();
    settings
        .merge(config::File::with_name(config_file))
        .unwrap();
    let mut doc = settings.try_into::<HashContainsYaml>().unwrap();

    let mut rules: Vec<RuleConfig> = Vec::new();
    for (name, value) in doc.iter_mut() {
        let address_input = value.clone().get_mut("input").map_or(
            Host {
                host: "".to_owned(),
                port: 0,
            },
            |v| Host {
                host: v["host"].clone(),
                port: v["port"].clone().parse::<i32>().unwrap(),
            },
        );
        let address_output = value.clone().get_mut("output").map_or(
            Host {
                host: "".to_owned(),
                port: 0,
            },
            |v| Host {
                host: v["host"].clone(),
                port: v["port"].clone().parse::<i32>().unwrap(),
            },
        );

        rules.push(RuleConfig {
            name: name.to_string(),
            input: address_input,
            output: address_output,
        });
    }

    return rules;
}

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

#[tokio::main]
async fn main() -> io::Result<()> {
    let args: Vec<String> = env::args().collect();
    let mut opts = Options::new();
    opts.optflag("h", "help", "print this help menu");
    opts.optopt(
        "f",
        "config.yml",
        "specify configuration file for tcp rules",
        "CONFIG_FILE_PROXY",
    );

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => {
            panic!(f.to_string())
        }
    };
    let program = args[0].clone();
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }

    let config_file = matches.opt_str("f").unwrap();
    let rule_configs = load_config(config_file.as_str());

    pretty_env_logger::init();

    let mut joins = Vec::new();
    for rule in rule_configs {
        let input_host = rule.input.host;
        let input_port = rule.input.port;
        let output_host = rule.output.host;
        let output_port = rule.output.port;

        let join = task::spawn(async move {
            let output_address = format!("{}:{}", output_host, output_port);
            let input_address = format!("{}:{}", input_host, input_port);
            let listener = TcpListener::bind(output_address.clone())
                .await
                .expect(format!("Error binding address {}", output_address).as_str());
            //TODO loop to wait for connection, is it needed this loop?
            loop {
                let (socket, _) = listener.accept().await.unwrap();
                forward(socket, input_address.as_str()).await.unwrap();
            }
        });
        joins.push(join);
    }
    join_all(joins).await;
    Ok(())
}
<|endoftext|>


<commit_before>
#![feature(globs)]

extern crate graphics;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use opengl_graphics::{
    Gl,
    Texture,
};
use sdl2_game_window::GameWindowSDL2;
use graphics::*;
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindowSettings,
    Render,
};

#[start]
fn start(argc: int, argv: **u8) -> int {
    // Run gui on the main thread.
    native::start(argc, argv, main)
}

fn main() {
    let mut window = GameWindowSDL2::new(
        GameWindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
        }
    );

    let asset_store = AssetStore::from_folder("assets");

    let image = asset_store.path("rust-logo.png").unwrap();
    let image = Texture::from_path(&image).unwrap();
    let game_iter_settings = GameIteratorSettings {
            updates_per_second: 120,
            max_frames_per_second: 60,
        };
    let mut game_iter = GameIterator::new(&mut window, &game_iter_settings);
    let ref mut gl = Gl::new();
    loop {
        match game_iter.next() {
            None => { break },
            Some(e) => match e {
                Render(args) => {
                    gl.viewport(0, 0, args.width as i32, args.height as i32);

                    let c = Context::abs(args.width as f64, args.height as f64);
                    c.rgb(1.0, 1.0, 1.0).draw(gl);
                    c.image(&image).draw(gl);
                },
                _ => {},
            },
        }
    }
}

<commit_msg>Using for loop in 'image_iter' example<commit_after>
#![feature(globs)]

extern crate graphics;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;

use opengl_graphics::{
    Gl,
    Texture,
};
use sdl2_game_window::GameWindowSDL2;
use graphics::*;
use piston::{
    AssetStore,
    GameIterator,
    GameIteratorSettings,
    GameWindowSettings,
    Render,
};

#[start]
fn start(argc: int, argv: **u8) -> int {
    // Run gui on the main thread.
    native::start(argc, argv, main)
}

fn main() {
    let mut window = GameWindowSDL2::new(
        GameWindowSettings {
            title: "Image".to_string(),
            size: [300, 300],
            fullscreen: false,
            exit_on_esc: true,
        }
    );

    let asset_store = AssetStore::from_folder("assets");

    let image = asset_store.path("rust-logo.png").unwrap();
    let image = Texture::from_path(&image).unwrap();
    let game_iter_settings = GameIteratorSettings {
            updates_per_second: 120,
            max_frames_per_second: 60,
        };
    let ref mut gl = Gl::new();
    for e in GameIterator::new(&mut window, &game_iter_settings) {
        match e {
            Render(args) => {
                gl.viewport(0, 0, args.width as i32, args.height as i32);

                let c = Context::abs(args.width as f64, args.height as f64);
                c.rgb(1.0, 1.0, 1.0).draw(gl);
                    c.image(&image).draw(gl);
            },
            _ => {},
        }
    }
}

<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::Infallible;
use std::net::{Ipv4Addr, SocketAddr};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use aes_ctr::cipher::generic_array::GenericArray;
use aes_ctr::cipher::{NewStreamCipher, SyncStreamCipher};
use aes_ctr::Aes128Ctr;
use futures_core::Stream;
use hmac::{Hmac, Mac, NewMac};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, StatusCode};
use log::{debug, warn};
use serde_json::json;
use sha1::{Digest, Sha1};
use tokio::sync::{mpsc, oneshot};

use crate::core::authentication::Credentials;
use crate::core::config::DeviceType;
use crate::core::diffie_hellman::DhLocalKeys;

type Params<'a> = BTreeMap<Cow<'a, str>, Cow<'a, str>>;

pub struct Config {
    pub name: Cow<'static, str>,
    pub device_type: DeviceType,
    pub device_id: String,
}

struct RequestHandler {
    config: Config,
    keys: DhLocalKeys,
    tx: mpsc::UnboundedSender<Credentials>,
}

impl RequestHandler {
    fn new(config: Config) -> (Self, mpsc::UnboundedReceiver<Credentials>) {
        let (tx, rx) = mpsc::unbounded_channel();

        let discovery = Self {
            config,
            keys: DhLocalKeys::random(&mut rand::thread_rng()),
            tx,
        };

        (discovery, rx)
    }

    fn handle_get_info(&self) -> Response<hyper::Body> {
        let public_key = base64::encode(&self.keys.public_key());
        let device_type: &str = self.config.device_type.into();

        let body = json!({
            "status": 101,
            "statusString": "ERROR-OK",
            "spotifyError": 0,
            "version": "2.7.1",
            "deviceID": (self.config.device_id),
            "remoteName": (self.config.name),
            "activeUser": "",
            "publicKey": (public_key),
            "deviceType": (device_type),
            "libraryVersion": "0.1.0",
            "accountReq": "PREMIUM",
            "brandDisplayName": "librespot",
            "modelDisplayName": "librespot",
            "resolverVersion": "0",
            "groupStatus": "NONE",
            "voiceSupport": "NO",
        })
        .to_string();

        Response::new(Body::from(body))
    }

    fn handle_add_user(&self, params: &Params<'_>) -> Response<hyper::Body> {
        let username = params.get("userName").unwrap().as_ref();
        let encrypted_blob = params.get("blob").unwrap();
        let client_key = params.get("clientKey").unwrap();

        let encrypted_blob = base64::decode(encrypted_blob.as_bytes()).unwrap();

        let client_key = base64::decode(client_key.as_bytes()).unwrap();
        let shared_key = self.keys.shared_secret(&client_key);

        let iv = &encrypted_blob[0..16];
        let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
        let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];

        let base_key = Sha1::digest(&shared_key);
        let base_key = &base_key[..16];

        let checksum_key = {
            let mut h =
                Hmac::<Sha1>::new_from_slice(base_key).expect("HMAC can take key of any size");
            h.update(b"checksum");
            h.finalize().into_bytes()
        };

        let encryption_key = {
            let mut h =
                Hmac::<Sha1>::new_from_slice(base_key).expect("HMAC can take key of any size");
            h.update(b"encryption");
            h.finalize().into_bytes()
        };

        let mut h =
            Hmac::<Sha1>::new_from_slice(&checksum_key).expect("HMAC can take key of any size");
        h.update(encrypted);
        if h.verify(cksum).is_err() {
            warn!("Login error for user {:?}: MAC mismatch", username);
            let result = json!({
                "status": 102,
                "spotifyError": 1,
                "statusString": "ERROR-MAC"
            });

            let body = result.to_string();
            return Response::new(Body::from(body));
        }

        let decrypted = {
            let mut data = encrypted.to_vec();
            let mut cipher = Aes128Ctr::new(
                GenericArray::from_slice(&encryption_key[0..16]),
                GenericArray::from_slice(iv),
            );
            cipher.apply_keystream(&mut data);
            String::from_utf8(data).unwrap()
        };

        let credentials =
            Credentials::with_blob(username.to_string(), &decrypted, &self.config.device_id);

        self.tx.send(credentials).unwrap();

        let result = json!({
            "status": 101,
            "spotifyError": 0,
            "statusString": "ERROR-OK"
        });

        let body = result.to_string();
        Response::new(Body::from(body))
    }

    fn not_found(&self) -> Response<hyper::Body> {
        let mut res = Response::default();
        *res.status_mut() = StatusCode::NOT_FOUND;
        res
    }

    async fn handle(self: Arc<Self>, request: Request<Body>) -> hyper::Result<Response<Body>> {
        let mut params = Params::new();

        let (parts, body) = request.into_parts();

        if let Some(query) = parts.uri.query() {
            let query_params = form_urlencoded::parse(query.as_bytes());
            params.extend(query_params);
        }

        if parts.method != Method::GET {
            debug!("{:?} {:?} {:?}", parts.method, parts.uri.path(), params);
        }

        let body = hyper::body::to_bytes(body).await?;

        params.extend(form_urlencoded::parse(&body));

        let action = params.get("action").map(Cow::as_ref);

        Ok(match (parts.method, action) {
            (Method::GET, Some("getInfo")) => self.handle_get_info(),
            (Method::POST, Some("addUser")) => self.handle_add_user(&params),
            _ => self.not_found(),
        })
    }
}

pub struct DiscoveryServer {
    cred_rx: mpsc::UnboundedReceiver<Credentials>,
    _close_tx: oneshot::Sender<Infallible>,
}

impl DiscoveryServer {
    pub fn new(config: Config, port: &mut u16) -> hyper::Result<Self> {
        let (discovery, cred_rx) = RequestHandler::new(config);
        let discovery = Arc::new(discovery);

        let (close_tx, close_rx) = oneshot::channel();

        let address = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), *port);

        let make_service = make_service_fn(move |_| {
            let discovery = discovery.clone();
            async move {
                Ok::<_, hyper::Error>(service_fn(move |request| discovery.clone().handle(request)))
            }
        });

        let server = hyper::Server::try_bind(&address)?.serve(make_service);

        *port = server.local_addr().port();
        debug!("Zeroconf server listening on 0.0.0.0:{}", *port);

        tokio::spawn(async {
            let result = server
                .with_graceful_shutdown(async {
                    close_rx.await.unwrap_err();
                    debug!("Shutting down discovery server");
                })
                .await;

            if let Err(e) = result {
                warn!("Discovery server failed: {}", e);
            }
        });

        Ok(Self {
            cred_rx,
            _close_tx: close_tx,
        })
    }
}

impl Stream for DiscoveryServer {
    type Item = Credentials;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Credentials>> {
        self.cred_rx.poll_recv(cx)
    }
}
<commit_msg>Set correct libraryVersion<commit_after>use std::borrow::Cow;
use std::collections::BTreeMap;
use std::convert::Infallible;
use std::net::{Ipv4Addr, SocketAddr};
use std::pin::Pin;
use std::sync::Arc;
use std::task::{Context, Poll};

use aes_ctr::cipher::generic_array::GenericArray;
use aes_ctr::cipher::{NewStreamCipher, SyncStreamCipher};
use aes_ctr::Aes128Ctr;
use futures_core::Stream;
use hmac::{Hmac, Mac, NewMac};
use hyper::service::{make_service_fn, service_fn};
use hyper::{Body, Method, Request, Response, StatusCode};
use log::{debug, warn};
use serde_json::json;
use sha1::{Digest, Sha1};
use tokio::sync::{mpsc, oneshot};

use crate::core::authentication::Credentials;
use crate::core::config::DeviceType;
use crate::core::diffie_hellman::DhLocalKeys;

type Params<'a> = BTreeMap<Cow<'a, str>, Cow<'a, str>>;

pub struct Config {
    pub name: Cow<'static, str>,
    pub device_type: DeviceType,
    pub device_id: String,
}

struct RequestHandler {
    config: Config,
    keys: DhLocalKeys,
    tx: mpsc::UnboundedSender<Credentials>,
}

impl RequestHandler {
    fn new(config: Config) -> (Self, mpsc::UnboundedReceiver<Credentials>) {
        let (tx, rx) = mpsc::unbounded_channel();

        let discovery = Self {
            config,
            keys: DhLocalKeys::random(&mut rand::thread_rng()),
            tx,
        };

        (discovery, rx)
    }

    fn handle_get_info(&self) -> Response<hyper::Body> {
        let public_key = base64::encode(&self.keys.public_key());
        let device_type: &str = self.config.device_type.into();

        let body = json!({
            "status": 101,
            "statusString": "ERROR-OK",
            "spotifyError": 0,
            "version": "2.7.1",
            "deviceID": (self.config.device_id),
            "remoteName": (self.config.name),
            "activeUser": "",
            "publicKey": (public_key),
            "deviceType": (device_type),
            "libraryVersion": crate::core::version::SEMVER,
            "accountReq": "PREMIUM",
            "brandDisplayName": "librespot",
            "modelDisplayName": "librespot",
            "resolverVersion": "0",
            "groupStatus": "NONE",
            "voiceSupport": "NO",
        })
        .to_string();

        Response::new(Body::from(body))
    }

    fn handle_add_user(&self, params: &Params<'_>) -> Response<hyper::Body> {
        let username = params.get("userName").unwrap().as_ref();
        let encrypted_blob = params.get("blob").unwrap();
        let client_key = params.get("clientKey").unwrap();

        let encrypted_blob = base64::decode(encrypted_blob.as_bytes()).unwrap();

        let client_key = base64::decode(client_key.as_bytes()).unwrap();
        let shared_key = self.keys.shared_secret(&client_key);

        let iv = &encrypted_blob[0..16];
        let encrypted = &encrypted_blob[16..encrypted_blob.len() - 20];
        let cksum = &encrypted_blob[encrypted_blob.len() - 20..encrypted_blob.len()];

        let base_key = Sha1::digest(&shared_key);
        let base_key = &base_key[..16];

        let checksum_key = {
            let mut h =
                Hmac::<Sha1>::new_from_slice(base_key).expect("HMAC can take key of any size");
            h.update(b"checksum");
            h.finalize().into_bytes()
        };

        let encryption_key = {
            let mut h =
                Hmac::<Sha1>::new_from_slice(base_key).expect("HMAC can take key of any size");
            h.update(b"encryption");
            h.finalize().into_bytes()
        };

        let mut h =
            Hmac::<Sha1>::new_from_slice(&checksum_key).expect("HMAC can take key of any size");
        h.update(encrypted);
        if h.verify(cksum).is_err() {
            warn!("Login error for user {:?}: MAC mismatch", username);
            let result = json!({
                "status": 102,
                "spotifyError": 1,
                "statusString": "ERROR-MAC"
            });

            let body = result.to_string();
            return Response::new(Body::from(body));
        }

        let decrypted = {
            let mut data = encrypted.to_vec();
            let mut cipher = Aes128Ctr::new(
                GenericArray::from_slice(&encryption_key[0..16]),
                GenericArray::from_slice(iv),
            );
            cipher.apply_keystream(&mut data);
            String::from_utf8(data).unwrap()
        };

        let credentials =
            Credentials::with_blob(username.to_string(), &decrypted, &self.config.device_id);

        self.tx.send(credentials).unwrap();

        let result = json!({
            "status": 101,
            "spotifyError": 0,
            "statusString": "ERROR-OK"
        });

        let body = result.to_string();
        Response::new(Body::from(body))
    }

    fn not_found(&self) -> Response<hyper::Body> {
        let mut res = Response::default();
        *res.status_mut() = StatusCode::NOT_FOUND;
        res
    }

    async fn handle(self: Arc<Self>, request: Request<Body>) -> hyper::Result<Response<Body>> {
        let mut params = Params::new();

        let (parts, body) = request.into_parts();

        if let Some(query) = parts.uri.query() {
            let query_params = form_urlencoded::parse(query.as_bytes());
            params.extend(query_params);
        }

        if parts.method != Method::GET {
            debug!("{:?} {:?} {:?}", parts.method, parts.uri.path(), params);
        }

        let body = hyper::body::to_bytes(body).await?;

        params.extend(form_urlencoded::parse(&body));

        let action = params.get("action").map(Cow::as_ref);

        Ok(match (parts.method, action) {
            (Method::GET, Some("getInfo")) => self.handle_get_info(),
            (Method::POST, Some("addUser")) => self.handle_add_user(&params),
            _ => self.not_found(),
        })
    }
}

pub struct DiscoveryServer {
    cred_rx: mpsc::UnboundedReceiver<Credentials>,
    _close_tx: oneshot::Sender<Infallible>,
}

impl DiscoveryServer {
    pub fn new(config: Config, port: &mut u16) -> hyper::Result<Self> {
        let (discovery, cred_rx) = RequestHandler::new(config);
        let discovery = Arc::new(discovery);

        let (close_tx, close_rx) = oneshot::channel();

        let address = SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), *port);

        let make_service = make_service_fn(move |_| {
            let discovery = discovery.clone();
            async move {
                Ok::<_, hyper::Error>(service_fn(move |request| discovery.clone().handle(request)))
            }
        });

        let server = hyper::Server::try_bind(&address)?.serve(make_service);

        *port = server.local_addr().port();
        debug!("Zeroconf server listening on 0.0.0.0:{}", *port);

        tokio::spawn(async {
            let result = server
                .with_graceful_shutdown(async {
                    close_rx.await.unwrap_err();
                    debug!("Shutting down discovery server");
                })
                .await;

            if let Err(e) = result {
                warn!("Discovery server failed: {}", e);
            }
        });

        Ok(Self {
            cred_rx,
            _close_tx: close_tx,
        })
    }
}

impl Stream for DiscoveryServer {
    type Item = Credentials;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Credentials>> {
        self.cred_rx.poll_recv(cx)
    }
}
<|endoftext|>


<commit_before>use index::btree::NodeCellRef;
use index::EntryKey;
use smallvec::SmallVec;
use index::btree::search::mut_search;
use index::btree::node::read_node;
use index::Slice;
use index::btree::internal::InNode;
use index::btree::node::read_unchecked;
use std::fmt::Debug;
use index::btree::node::write_node;
use index::btree::BPlusTree;
use index::lsmtree::LEVEL_PAGE_DIFF_MULTIPLIER;
use index::btree::node::NodeWriteGuard;
use itertools::Itertools;
use index::btree::search::MutSearchResult;
use index::btree::node::write_key_page;
use index::btree::node::NodeData;
use index::btree::node::EmptyNode;

enum Selection<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    Selected(Vec<(EntryKey, NodeWriteGuard<KS, PS>)>),
    Innode(NodeCellRef)
}

enum PruningSearch {
    DeepestInnode,
    Innode(NodeCellRef),
}

fn select<KS, PS>(node: &NodeCellRef) -> Vec<(EntryKey, NodeWriteGuard<KS, PS>)>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let selection = read_node(node, |node_handler| {
        debug_assert!(!node_handler.is_ext());
        let innode: &InNode<KS, PS> = node_handler.innode();
        let first_node = innode.ptrs.as_slice_immute().first().unwrap();
        if read_unchecked::<KS, PS>(first_node).is_ext() {
            let mut keys = innode.keys.as_slice_immute().iter();
            keys.next(); // skip the first
            Selection::Selected(innode.ptrs
                .as_slice_immute()
                .iter()
                .zip(keys)
                .take(LEVEL_PAGE_DIFF_MULTIPLIER)
                .map(|(r, k)| (k.clone(), write_node::<KS, PS>(r)))
                .collect())
        } else {
            Selection::Innode(first_node.clone())
        }
    });
    match selection {
        Selection::Selected(res) => res,
        Selection::Innode(node) => select::<KS, PS>(&node)
    }
}

fn prune_selected<'a, KS, PS>(node: &NodeCellRef, mut keys: Vec<&'a EntryKey>) -> Vec<&'a EntryKey>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let first_search = mut_search::<KS, PS>(node, keys.first().unwrap());
    let pruning = match first_search {
        MutSearchResult::Internal(sub_node) => {
            if read_unchecked::<KS, PS>(&sub_node).is_ext() {
                PruningSearch::DeepestInnode
            } else {
                PruningSearch::Innode(sub_node)
            }
        },
        MutSearchResult::External => unreachable!()
    };
    // empty page references that will dealt with by upper level
    let mut empty_pages = vec![];
    match pruning {
        PruningSearch::DeepestInnode => {},
        PruningSearch::Innode(sub_node) => {
            keys = prune_selected::<KS, PS>(&sub_node, keys);
        }
    }
    // start delete
    let mut cursor_guard = write_node::<KS, PS>(node);
    for keys_to_del in keys {
        cursor_guard = write_key_page(cursor_guard, keys_to_del);
        let pos = cursor_guard.search(keys_to_del);
        cursor_guard.remove(pos);
        if cursor_guard.is_empty() {
            cursor_guard.make_empty_node();
            empty_pages.push(keys_to_del);
        }
    }
    empty_pages
}

pub fn level_merge<KSA, PSA, KSB, PSB>(src_tree: &BPlusTree<KSA, PSA>, dest_tree: &BPlusTree<KSB, PSB>)
    where KSA: Slice<EntryKey> + Debug + 'static,
          PSA: Slice<NodeCellRef> + 'static,
          KSB: Slice<EntryKey> + Debug + 'static,
          PSB: Slice<NodeCellRef> + 'static
{
    let left_most_leaf_guards = select::<KSA, PSA>(&src_tree.get_root());

    // merge to dest_tree
    {
        let keys: Vec<EntryKey> = left_most_leaf_guards.iter()
            .map(|(_, g)| g.keys())
            .flatten()
            .cloned()
            .collect_vec();
        dest_tree.merge_page(keys);
    }

    // cleanup upper level references
    {
        let page_keys = left_most_leaf_guards.iter()
            .map(|(k, _)| k)
            .collect_vec();
        prune_selected::<KSA, PSA>(&src_tree.get_root(), page_keys);
    }

    // adjust leaf left, right references
    let right_right_most = left_most_leaf_guards.last().unwrap().1.right_ref().unwrap().clone();
    let left_left_most = left_most_leaf_guards.first().unwrap().1.left_ref().unwrap().clone();
    for (_, mut g) in left_most_leaf_guards {
        *g = NodeData::Empty(box EmptyNode {
            left: Some(left_left_most.clone()),
            right: right_right_most.clone()
        })
    }
}<commit_msg>fix prune search<commit_after>use index::btree::NodeCellRef;
use index::EntryKey;
use smallvec::SmallVec;
use index::btree::search::mut_search;
use index::btree::node::read_node;
use index::Slice;
use index::btree::internal::InNode;
use index::btree::node::read_unchecked;
use std::fmt::Debug;
use index::btree::node::write_node;
use index::btree::BPlusTree;
use index::lsmtree::LEVEL_PAGE_DIFF_MULTIPLIER;
use index::btree::node::NodeWriteGuard;
use itertools::Itertools;
use index::btree::search::MutSearchResult;
use index::btree::node::write_key_page;
use index::btree::node::NodeData;
use index::btree::node::EmptyNode;

enum Selection<KS, PS>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    Selected(Vec<NodeWriteGuard<KS, PS>>),
    Innode(NodeCellRef)
}

enum PruningSearch {
    DeepestInnode,
    Innode(NodeCellRef),
}

fn select<KS, PS>(node: &NodeCellRef) -> Vec<NodeWriteGuard<KS, PS>>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let selection = read_node(node, |node_handler| {
        debug_assert!(!node_handler.is_ext());
        let innode: &InNode<KS, PS> = node_handler.innode();
        let first_node = innode.ptrs.as_slice_immute().first().unwrap();
        if read_unchecked::<KS, PS>(first_node).is_ext() {
            Selection::Selected(innode.ptrs
                .as_slice_immute()
                .iter()
                .take(LEVEL_PAGE_DIFF_MULTIPLIER)
                .map(|r| write_node::<KS, PS>(r))
                .collect())
        } else {
            Selection::Innode(first_node.clone())
        }
    });
    match selection {
        Selection::Selected(res) => res,
        Selection::Innode(node) => select::<KS, PS>(&node)
    }
}

fn prune_selected<'a, KS, PS>(node: &NodeCellRef, mut keys: Vec<&'a EntryKey>) -> Vec<&'a EntryKey>
    where KS: Slice<EntryKey> + Debug + 'static,
          PS: Slice<NodeCellRef> + 'static
{
    let first_search = mut_search::<KS, PS>(node, keys.first().unwrap());
    let pruning = match first_search {
        MutSearchResult::Internal(sub_node) => {
            if read_unchecked::<KS, PS>(&sub_node).is_ext() {
                PruningSearch::DeepestInnode
            } else {
                PruningSearch::Innode(sub_node)
            }
        },
        MutSearchResult::External => unreachable!()
    };
    // empty page references that will dealt with by upper level
    let mut empty_pages = vec![];
    match pruning {
        PruningSearch::DeepestInnode => {},
        PruningSearch::Innode(sub_node) => {
            keys = prune_selected::<KS, PS>(&sub_node, keys);
        }
    }
    // start delete
    let mut cursor_guard = write_node::<KS, PS>(node);
    for keys_to_del in keys {
        cursor_guard = write_key_page(cursor_guard, keys_to_del);
        let pos = cursor_guard.search(keys_to_del);
        cursor_guard.remove(pos);
        if cursor_guard.is_empty() {
            cursor_guard.make_empty_node();
            empty_pages.push(keys_to_del);
        }
    }
    empty_pages
}

pub fn level_merge<KSA, PSA, KSB, PSB>(src_tree: &BPlusTree<KSA, PSA>, dest_tree: &BPlusTree<KSB, PSB>)
    where KSA: Slice<EntryKey> + Debug + 'static,
          PSA: Slice<NodeCellRef> + 'static,
          KSB: Slice<EntryKey> + Debug + 'static,
          PSB: Slice<NodeCellRef> + 'static
{
    let left_most_leaf_guards = select::<KSA, PSA>(&src_tree.get_root());

    // merge to dest_tree
    {
        let keys: Vec<EntryKey> = left_most_leaf_guards.iter()
            .map(|g| g.keys())
            .flatten()
            .cloned()
            .collect_vec();
        dest_tree.merge_page(keys);
    }

    // cleanup upper level references
    {
        let page_keys = left_most_leaf_guards.iter()
            .filter(|g| !g.is_empty())
            .map(|g| g.first_key())
            .collect_vec();
        prune_selected::<KSA, PSA>(&src_tree.get_root(), page_keys);
    }

    // adjust leaf left, right references
    let right_right_most = left_most_leaf_guards.last().unwrap().right_ref().unwrap().clone();
    let left_left_most = left_most_leaf_guards.first().unwrap().left_ref().unwrap().clone();
    for mut g in left_most_leaf_guards {
        *g = NodeData::Empty(box EmptyNode {
            left: Some(left_left_most.clone()),
            right: right_right_most.clone()
        })
    }
}<|endoftext|>