<commit_before>macro_rules! float_tests {
    { $vector:ident, $scalar:ident } => {
        #[cfg(test)]
        mod $vector {
            use super::*;
            use helpers::lanewise::*;

            #[cfg(target_arch = "wasm32")]
            use wasm_bindgen_test::*;

            #[cfg(target_arch = "wasm32")]
            wasm_bindgen_test_configure!(run_in_browser);

            // TODO impl this as an associated fn on vectors
            fn from_slice(slice: &[$scalar]) -> core_simd::$vector {
                let mut value = core_simd::$vector::default();
                let value_slice: &mut [_] = value.as_mut();
                value_slice.copy_from_slice(&slice[0..value_slice.len()]);
                value
            }

            const A: [$scalar; 16] = [0.,   1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15.];
            const B: [$scalar; 16] = [16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31.];
            const C: [$scalar; 16] = [
                -0.0,
                0.0,
                -1.0,
                1.0,
                <$scalar>::MIN,
                <$scalar>::MAX,
                <$scalar>::INFINITY,
                -<$scalar>::INFINITY,
                <$scalar>::MIN_POSITIVE,
                -<$scalar>::MIN_POSITIVE,
                <$scalar>::EPSILON,
                -<$scalar>::EPSILON,
                0.0 / 0.0,
                -0.0 / 0.0,
                // Still not sure if wasm can have weird nans, or I'd check them
                // too. Until then
                1.0 / 3.0,
                -1.0 / 4.0
            ];

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
                a += b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
                a += b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
                a -= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
                a -= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
                a *= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
                a *= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
                a /= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
                a /= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
                a %= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
                a %= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn neg() {
                let v = from_slice(&A);
                let expected = apply_unary_lanewise(v, core::ops::Neg::neg);
                assert_biteq!(-v, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_negative() {
                let v = -from_slice(&A);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_positive() {
                let v = from_slice(&B);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_odd_floats() {
                let v = from_slice(&C);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }
        }
    }
}
<commit_msg>use NEG_INFINITY and NAN constants instead computing them<commit_after>macro_rules! float_tests {
    { $vector:ident, $scalar:ident } => {
        #[cfg(test)]
        mod $vector {
            use super::*;
            use helpers::lanewise::*;

            #[cfg(target_arch = "wasm32")]
            use wasm_bindgen_test::*;

            #[cfg(target_arch = "wasm32")]
            wasm_bindgen_test_configure!(run_in_browser);

            // TODO impl this as an associated fn on vectors
            fn from_slice(slice: &[$scalar]) -> core_simd::$vector {
                let mut value = core_simd::$vector::default();
                let value_slice: &mut [_] = value.as_mut();
                value_slice.copy_from_slice(&slice[0..value_slice.len()]);
                value
            }

            const A: [$scalar; 16] = [0.,   1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12., 13., 14., 15.];
            const B: [$scalar; 16] = [16., 17., 18., 19., 20., 21., 22., 23., 24., 25., 26., 27., 28., 29., 30., 31.];
            const C: [$scalar; 16] = [
                -0.0,
                0.0,
                -1.0,
                1.0,
                <$scalar>::MIN,
                <$scalar>::MAX,
                <$scalar>::INFINITY,
                <$scalar>::NEG_INFINITY,
                <$scalar>::MIN_POSITIVE,
                -<$scalar>::MIN_POSITIVE,
                <$scalar>::EPSILON,
                -<$scalar>::EPSILON,
                <$scalar>::NAN,
                -<$scalar>::NAN,
                // TODO: Would be nice to check sNaN...
                100.0 / 3.0,
                -100.0 / 3.0,
            ];

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Add::add);
                a += b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Add::add);
                assert_biteq!(a + b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn add_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Add::add);
                a += b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Sub::sub);
                a -= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Sub::sub);
                assert_biteq!(a - b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn sub_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Sub::sub);
                a -= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Mul::mul);
                a *= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Mul::mul);
                assert_biteq!(a * b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn mul_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Mul::mul);
                a *= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Div::div);
                a /= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Div::div);
                assert_biteq!(a / b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn div_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Div::div);
                a /= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem() {
                let a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_assign() {
                let mut a = from_slice(&A);
                let b = from_slice(&B);
                let expected = apply_binary_lanewise(a, b, core::ops::Rem::rem);
                a %= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_scalar_rhs() {
                let a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_scalar_lhs() {
                let a = 5.;
                let b = from_slice(&B);
                let expected = apply_binary_scalar_lhs_lanewise(a, b, core::ops::Rem::rem);
                assert_biteq!(a % b, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn rem_assign_scalar() {
                let mut a = from_slice(&A);
                let b = 5.;
                let expected = apply_binary_scalar_rhs_lanewise(a, b, core::ops::Rem::rem);
                a %= b;
                assert_biteq!(a, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn neg() {
                let v = from_slice(&A);
                let expected = apply_unary_lanewise(v, core::ops::Neg::neg);
                assert_biteq!(-v, expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_negative() {
                let v = -from_slice(&A);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_positive() {
                let v = from_slice(&B);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }

            #[test]
            #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)]
            fn abs_odd_floats() {
                let v = from_slice(&C);
                let expected = apply_unary_lanewise(v, <$scalar>::abs);
                assert_biteq!(v.abs(), expected);
            }
        }
    }
}
<|endoftext|>


<commit_before>use syntax::{
    ast::{self, edit::AstNodeEdit},
    AstNode, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_tuple
//
// Unwrap the tuple to different variables.
//
// ```
// # //- minicore: result
// fn main() {
//     $0let (foo, bar) = ("Foo", "Bar");
// }
// ```
// ->
// ```
// fn main() {
//     let foo = "Foo";
//     let bar = "Bar";
// }
// ```
pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
    let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
    let indent_level = let_stmt.indent_level().0 as usize;
    let pat = let_stmt.pat()?;
    let ty = let_stmt.ty();
    let init = let_stmt.initializer()?;

    // This only applies for tuple patterns, types, and initializers.
    let tuple_pat = match pat {
        ast::Pat::TuplePat(pat) => pat,
        _ => return None,
    };
    let tuple_ty = ty.and_then(|it| match it {
        ast::Type::TupleType(ty) => Some(ty),
        _ => None,
    });
    let tuple_init = match init {
        ast::Expr::TupleExpr(expr) => expr,
        _ => return None,
    };

    stdx::always!(
        tuple_pat.fields().count() == tuple_init.fields().count(),
        "Length of tuples in pattern and initializer do not match"
    );

    let parent = let_kw.parent()?;

    acc.add(
        AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
        "Unwrap tuple",
        let_kw.text_range(),
        |edit| {
            let indents = "    ".repeat(indent_level);

            // If there is an ascribed type, insert that type for each declaration,
            // otherwise, omit that type.
            if let Some(tys) = tuple_ty {
                stdx::always!(
                    tuple_pat.fields().count() == tys.fields().count(),
                    "Length of tuples in patterns and type do not match"
                );

                let mut zipped_decls = String::new();
                for (pat, ty, expr) in
                    itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
                {
                    zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
                }
                edit.replace(parent.text_range(), zipped_decls.trim());
            } else {
                let mut zipped_decls = String::new();
                for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
                    zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
                }
                edit.replace(parent.text_range(), zipped_decls.trim());
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::check_assist;

    use super::*;

    #[test]
    fn unwrap_tuples() {
        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar) = ("Foo", "Bar");
}
"#,
            r#"
fn main() {
    let foo = "Foo";
    let bar = "Bar";
}
"#,
        );

        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
}
"#,
            r#"
fn main() {
    let foo = "Foo";
    let bar = "Bar";
    let baz = "Baz";
}
"#,
        );
    }

    #[test]
    fn unwrap_tuple_with_types() {
        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar): (u8, i32) = (5, 10);
}
"#,
            r#"
fn main() {
    let foo: u8 = 5;
    let bar: i32 = 10;
}
"#,
        );

        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
}
"#,
            r#"
fn main() {
    let foo: u8 = 5;
    let bar: i32 = 10;
    let baz: f64 = 17.5;
}
"#,
        );
    }
}
<commit_msg>return None instead of assert<commit_after>use syntax::{
    ast::{self, edit::AstNodeEdit},
    AstNode, T,
};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: unwrap_tuple
//
// Unwrap the tuple to different variables.
//
// ```
// # //- minicore: result
// fn main() {
//     $0let (foo, bar) = ("Foo", "Bar");
// }
// ```
// ->
// ```
// fn main() {
//     let foo = "Foo";
//     let bar = "Bar";
// }
// ```
pub(crate) fn unwrap_tuple(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let let_kw = ctx.find_token_syntax_at_offset(T![let])?;
    let let_stmt = let_kw.parent().and_then(ast::LetStmt::cast)?;
    let indent_level = let_stmt.indent_level().0 as usize;
    let pat = let_stmt.pat()?;
    let ty = let_stmt.ty();
    let init = let_stmt.initializer()?;

    // This only applies for tuple patterns, types, and initializers.
    let tuple_pat = match pat {
        ast::Pat::TuplePat(pat) => pat,
        _ => return None,
    };
    let tuple_ty = ty.and_then(|it| match it {
        ast::Type::TupleType(ty) => Some(ty),
        _ => None,
    });
    let tuple_init = match init {
        ast::Expr::TupleExpr(expr) => expr,
        _ => return None,
    };

    if tuple_pat.fields().count() != tuple_init.fields().count() {
        return None;
    }
    if let Some(tys) = &tuple_ty {
        if tuple_pat.fields().count() != tys.fields().count() {
            return None;
        }
    }

    let parent = let_kw.parent()?;

    acc.add(
        AssistId("unwrap_tuple", AssistKind::RefactorRewrite),
        "Unwrap tuple",
        let_kw.text_range(),
        |edit| {
            let indents = "    ".repeat(indent_level);

            // If there is an ascribed type, insert that type for each declaration,
            // otherwise, omit that type.
            if let Some(tys) = tuple_ty {
                let mut zipped_decls = String::new();
                for (pat, ty, expr) in
                    itertools::izip!(tuple_pat.fields(), tys.fields(), tuple_init.fields())
                {
                    zipped_decls.push_str(&format!("{}let {pat}: {ty} = {expr};\n", indents))
                }
                edit.replace(parent.text_range(), zipped_decls.trim());
            } else {
                let mut zipped_decls = String::new();
                for (pat, expr) in itertools::izip!(tuple_pat.fields(), tuple_init.fields()) {
                    zipped_decls.push_str(&format!("{}let {pat} = {expr};\n", indents));
                }
                edit.replace(parent.text_range(), zipped_decls.trim());
            }
        },
    )
}

#[cfg(test)]
mod tests {
    use crate::tests::check_assist;

    use super::*;

    #[test]
    fn unwrap_tuples() {
        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar) = ("Foo", "Bar");
}
"#,
            r#"
fn main() {
    let foo = "Foo";
    let bar = "Bar";
}
"#,
        );

        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar, baz) = ("Foo", "Bar", "Baz");
}
"#,
            r#"
fn main() {
    let foo = "Foo";
    let bar = "Bar";
    let baz = "Baz";
}
"#,
        );
    }

    #[test]
    fn unwrap_tuple_with_types() {
        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar): (u8, i32) = (5, 10);
}
"#,
            r#"
fn main() {
    let foo: u8 = 5;
    let bar: i32 = 10;
}
"#,
        );

        check_assist(
            unwrap_tuple,
            r#"
fn main() {
    $0let (foo, bar, baz): (u8, i32, f64) = (5, 10, 17.5);
}
"#,
            r#"
fn main() {
    let foo: u8 = 5;
    let bar: i32 = 10;
    let baz: f64 = 17.5;
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>//! This module provides LSIF types. This module is a temporary solution
//! and it will go to its own repository in future

use lsp_types::FoldingRange;
use serde::{Deserialize, Serialize};

pub(crate) type RangeId = lsp_types::NumberOrString;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum LocationOrRangeId {
    Location(lsp_types::Location),
    RangeId(RangeId),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Entry {
    pub(crate) id: lsp_types::NumberOrString,
    #[serde(flatten)]
    pub(crate) data: Element,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub(crate) enum Element {
    Vertex(Vertex),
    Edge(Edge),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct ToolInfo {
    name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    args: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    version: Option<String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub(crate) enum Encoding {
    /// Currently only 'utf-16' is supported due to the limitations in LSP.
    #[serde(rename = "utf-16")]
    Utf16,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub(crate) enum Vertex {
    #[serde(rename_all = "camelCase")]
    MetaData {
        /// The version of the LSIF format using semver notation. See https://semver.org/. Please note
        /// the version numbers starting with 0 don't adhere to semver and adopters have to assume
        /// that each new version is breaking.
        version: String,

        /// The project root (in form of an URI) used to compute this dump.
        project_root: lsp_types::Url,

        /// The string encoding used to compute line and character values in
        /// positions and ranges.
        position_encoding: Encoding,

        /// Information about the tool that created the dump
        #[serde(skip_serializing_if = "Option::is_none")]
        tool_info: Option<ToolInfo>,
    },
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex
    Project(Project),
    Document(Document),
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#ranges
    Range(lsp_types::Range),
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set
    ResultSet(ResultSet),

    // FIXME: support all kind of results
    DefinitionResult {
        result: DefinitionResultType,
    },
    FoldingRangeResult {
        result: Vec<FoldingRange>,
    },
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub(crate) enum Edge {
    Contains(EdgeData),
    RefersTo(EdgeData),
    Item(Item),

    // Methods
    #[serde(rename = "textDocument/definition")]
    Definition(EdgeData),
    #[serde(rename = "textDocument/declaration")]
    Declaration(EdgeData),
    #[serde(rename = "textDocument/hover")]
    Hover(EdgeData),
    #[serde(rename = "textDocument/references")]
    References(EdgeData),
    #[serde(rename = "textDocument/implementation")]
    Implementation(EdgeData),
    #[serde(rename = "textDocument/typeDefinition")]
    TypeDefinition(EdgeData),
    #[serde(rename = "textDocument/foldingRange")]
    FoldingRange(EdgeData),
    #[serde(rename = "textDocument/documentLink")]
    DocumentLink(EdgeData),
    #[serde(rename = "textDocument/documentSymbol")]
    DocumentSymbol(EdgeData),
    #[serde(rename = "textDocument/diagnostic")]
    Diagnostic(EdgeData),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct EdgeData {
    pub(crate) in_v: lsp_types::NumberOrString,
    pub(crate) out_v: lsp_types::NumberOrString,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum DefinitionResultType {
    Scalar(LocationOrRangeId),
    Array(LocationOrRangeId),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "property")]
pub(crate) enum Item {
    Definition(EdgeData),
    Reference(EdgeData),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Document {
    pub(crate) uri: lsp_types::Url,
    pub(crate) language_id: Language,
}

/// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ResultSet {
    #[serde(skip_serializing_if = "Option::is_none")]
    key: Option<String>,
}

/// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Project {
    project_file: lsp_types::Url,
    language_id: Language,
}

/// https://github.com/Microsoft/language-server-protocol/issues/213
/// For examples, see: https://code.visualstudio.com/docs/languages/identifiers.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum Language {
    Rust,
    TypeScript,
    #[serde(other)]
    Other,
}

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

    #[test]
    fn metadata() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(1),
            data: Element::Vertex(Vertex::MetaData {
                version: String::from("0.4.3"),
                project_root: lsp_types::Url::from_file_path("/hello/world").unwrap(),
                position_encoding: Encoding::Utf16,
                tool_info: Some(ToolInfo {
                    name: String::from("lsif-tsc"),
                    args: Some(vec![String::from("-p"), String::from(".")]),
                    version: Some(String::from("0.7.2")),
                }),
            }),
        };
        let text = r#"{"id":1,"type":"vertex","label":"metaData","version":"0.4.3","projectRoot":"file:///hello/world","positionEncoding":"utf-16","toolInfo":{"name":"lsif-tsc","args":["-p","."],"version":"0.7.2"}}"#
            .replace(' ', "");
        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn document() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(1),
            data: Element::Vertex(Vertex::Document(Document {
                uri: lsp_types::Url::from_file_path("/Users/dirkb/sample.ts").unwrap(),
                language_id: Language::TypeScript,
            })),
        };

        let text = r#"{ "id": 1, "type": "vertex", "label": "document", "uri": "file:///Users/dirkb/sample.ts", "languageId": "typescript" }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn range() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(4),
            data: Element::Vertex(Vertex::Range(lsp_types::Range::new(
                lsp_types::Position::new(0, 9),
                lsp_types::Position::new(0, 12),
            ))),
        };

        let text = r#"{ "id": 4, "type": "vertex", "label": "range", "start": { "line": 0, "character": 9}, "end": { "line": 0, "character": 12 } }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn contains() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(5),
            data: Element::Edge(Edge::Contains(EdgeData {
                in_v: lsp_types::NumberOrString::Number(4),
                out_v: lsp_types::NumberOrString::Number(1),
            })),
        };

        let text = r#"{ "id": 5, "type": "edge", "label": "contains", "outV": 1, "inV": 4}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    #[test]
    fn refers_to() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(5),
            data: Element::Edge(Edge::RefersTo(EdgeData {
                in_v: lsp_types::NumberOrString::Number(2),
                out_v: lsp_types::NumberOrString::Number(3),
            })),
        };

        let text = r#"{ "id": 5, "type": "edge", "label": "refersTo", "outV": 3, "inV": 2}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    #[test]
    fn result_set() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(2),
            data: Element::Vertex(Vertex::ResultSet(ResultSet { key: None })),
        };

        let text = r#"{ "id": 2, "type": "vertex", "label": "resultSet" }"#.replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);

        let data = Entry {
            id: lsp_types::NumberOrString::Number(4),
            data: Element::Vertex(Vertex::ResultSet(ResultSet {
                key: Some(String::from("hello")),
            })),
        };

        let text = r#"{ "id": 4, "type": "vertex", "label": "resultSet", "key": "hello" }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn definition() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(21),
            data: Element::Edge(Edge::Item(Item::Definition(EdgeData {
                in_v: lsp_types::NumberOrString::Number(18),
                out_v: lsp_types::NumberOrString::Number(16),
            }))),
        };

        let text = r#"{ "id": 21, "type": "edge", "label": "item", "property": "definition", "outV": 16, "inV": 18}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    mod methods {
        use super::*;

        #[test]
        fn references() {
            let data = Entry {
                id: lsp_types::NumberOrString::Number(17),
                data: Element::Edge(Edge::References(EdgeData {
                    in_v: lsp_types::NumberOrString::Number(16),
                    out_v: lsp_types::NumberOrString::Number(15),
                })),
            };

            let text = r#"{ "id": 17, "type": "edge", "label": "textDocument/references", "outV": 15, "inV": 16 }"#;

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
                serde_json::to_value(&data).unwrap()
            );
        }

        #[test]
        fn definition() {
            let data = Entry {
                id: lsp_types::NumberOrString::Number(13),
                data: Element::Vertex(Vertex::DefinitionResult {
                    result: DefinitionResultType::Scalar(LocationOrRangeId::RangeId(
                        lsp_types::NumberOrString::Number(7),
                    )),
                }),
            };

            let text =
                r#"{ "id": 13, "type": "vertex", "label": "definitionResult", "result": 7 }"#;

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
                serde_json::to_value(&data).unwrap()
            );
        }
    }
}
<commit_msg>cfg unix for tests with unix path<commit_after>//! This module provides LSIF types. This module is a temporary solution
//! and it will go to its own repository in future

use lsp_types::FoldingRange;
use serde::{Deserialize, Serialize};

pub(crate) type RangeId = lsp_types::NumberOrString;

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum LocationOrRangeId {
    Location(lsp_types::Location),
    RangeId(RangeId),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Entry {
    pub(crate) id: lsp_types::NumberOrString,
    #[serde(flatten)]
    pub(crate) data: Element,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "type")]
pub(crate) enum Element {
    Vertex(Vertex),
    Edge(Edge),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub(crate) struct ToolInfo {
    name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    args: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    version: Option<String>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub(crate) enum Encoding {
    /// Currently only 'utf-16' is supported due to the limitations in LSP.
    #[serde(rename = "utf-16")]
    Utf16,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub(crate) enum Vertex {
    #[serde(rename_all = "camelCase")]
    MetaData {
        /// The version of the LSIF format using semver notation. See https://semver.org/. Please note
        /// the version numbers starting with 0 don't adhere to semver and adopters have to assume
        /// that each new version is breaking.
        version: String,

        /// The project root (in form of an URI) used to compute this dump.
        project_root: lsp_types::Url,

        /// The string encoding used to compute line and character values in
        /// positions and ranges.
        position_encoding: Encoding,

        /// Information about the tool that created the dump
        #[serde(skip_serializing_if = "Option::is_none")]
        tool_info: Option<ToolInfo>,
    },
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex
    Project(Project),
    Document(Document),
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#ranges
    Range(lsp_types::Range),
    /// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set
    ResultSet(ResultSet),

    // FIXME: support all kind of results
    DefinitionResult {
        result: DefinitionResultType,
    },
    FoldingRangeResult {
        result: Vec<FoldingRange>,
    },
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "label")]
pub(crate) enum Edge {
    Contains(EdgeData),
    RefersTo(EdgeData),
    Item(Item),

    // Methods
    #[serde(rename = "textDocument/definition")]
    Definition(EdgeData),
    #[serde(rename = "textDocument/declaration")]
    Declaration(EdgeData),
    #[serde(rename = "textDocument/hover")]
    Hover(EdgeData),
    #[serde(rename = "textDocument/references")]
    References(EdgeData),
    #[serde(rename = "textDocument/implementation")]
    Implementation(EdgeData),
    #[serde(rename = "textDocument/typeDefinition")]
    TypeDefinition(EdgeData),
    #[serde(rename = "textDocument/foldingRange")]
    FoldingRange(EdgeData),
    #[serde(rename = "textDocument/documentLink")]
    DocumentLink(EdgeData),
    #[serde(rename = "textDocument/documentSymbol")]
    DocumentSymbol(EdgeData),
    #[serde(rename = "textDocument/diagnostic")]
    Diagnostic(EdgeData),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct EdgeData {
    pub(crate) in_v: lsp_types::NumberOrString,
    pub(crate) out_v: lsp_types::NumberOrString,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub(crate) enum DefinitionResultType {
    Scalar(LocationOrRangeId),
    Array(LocationOrRangeId),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[serde(tag = "property")]
pub(crate) enum Item {
    Definition(EdgeData),
    Reference(EdgeData),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Document {
    pub(crate) uri: lsp_types::Url,
    pub(crate) language_id: Language,
}

/// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#result-set
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct ResultSet {
    #[serde(skip_serializing_if = "Option::is_none")]
    key: Option<String>,
}

/// https://github.com/Microsoft/language-server-protocol/blob/master/indexFormat/specification.md#the-project-vertex
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Project {
    project_file: lsp_types::Url,
    language_id: Language,
}

/// https://github.com/Microsoft/language-server-protocol/issues/213
/// For examples, see: https://code.visualstudio.com/docs/languages/identifiers.
#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub(crate) enum Language {
    Rust,
    TypeScript,
    #[serde(other)]
    Other,
}

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

    #[test]
    #[cfg(unix)]
    fn metadata() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(1),
            data: Element::Vertex(Vertex::MetaData {
                version: String::from("0.4.3"),
                project_root: lsp_types::Url::from_file_path("/hello/world").unwrap(),
                position_encoding: Encoding::Utf16,
                tool_info: Some(ToolInfo {
                    name: String::from("lsif-tsc"),
                    args: Some(vec![String::from("-p"), String::from(".")]),
                    version: Some(String::from("0.7.2")),
                }),
            }),
        };
        let text = r#"{"id":1,"type":"vertex","label":"metaData","version":"0.4.3","projectRoot":"file:///hello/world","positionEncoding":"utf-16","toolInfo":{"name":"lsif-tsc","args":["-p","."],"version":"0.7.2"}}"#
            .replace(' ', "");
        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    #[cfg(unix)]
    fn document() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(1),
            data: Element::Vertex(Vertex::Document(Document {
                uri: lsp_types::Url::from_file_path("/Users/dirkb/sample.ts").unwrap(),
                language_id: Language::TypeScript,
            })),
        };

        let text = r#"{ "id": 1, "type": "vertex", "label": "document", "uri": "file:///Users/dirkb/sample.ts", "languageId": "typescript" }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn range() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(4),
            data: Element::Vertex(Vertex::Range(lsp_types::Range::new(
                lsp_types::Position::new(0, 9),
                lsp_types::Position::new(0, 12),
            ))),
        };

        let text = r#"{ "id": 4, "type": "vertex", "label": "range", "start": { "line": 0, "character": 9}, "end": { "line": 0, "character": 12 } }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn contains() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(5),
            data: Element::Edge(Edge::Contains(EdgeData {
                in_v: lsp_types::NumberOrString::Number(4),
                out_v: lsp_types::NumberOrString::Number(1),
            })),
        };

        let text = r#"{ "id": 5, "type": "edge", "label": "contains", "outV": 1, "inV": 4}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    #[test]
    fn refers_to() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(5),
            data: Element::Edge(Edge::RefersTo(EdgeData {
                in_v: lsp_types::NumberOrString::Number(2),
                out_v: lsp_types::NumberOrString::Number(3),
            })),
        };

        let text = r#"{ "id": 5, "type": "edge", "label": "refersTo", "outV": 3, "inV": 2}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    #[test]
    fn result_set() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(2),
            data: Element::Vertex(Vertex::ResultSet(ResultSet { key: None })),
        };

        let text = r#"{ "id": 2, "type": "vertex", "label": "resultSet" }"#.replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);

        let data = Entry {
            id: lsp_types::NumberOrString::Number(4),
            data: Element::Vertex(Vertex::ResultSet(ResultSet {
                key: Some(String::from("hello")),
            })),
        };

        let text = r#"{ "id": 4, "type": "vertex", "label": "resultSet", "key": "hello" }"#
            .replace(' ', "");

        assert_eq!(serde_json::to_string(&data).unwrap(), text);
        assert_eq!(serde_json::from_str::<Entry>(&text).unwrap(), data);
    }

    #[test]
    fn definition() {
        let data = Entry {
            id: lsp_types::NumberOrString::Number(21),
            data: Element::Edge(Edge::Item(Item::Definition(EdgeData {
                in_v: lsp_types::NumberOrString::Number(18),
                out_v: lsp_types::NumberOrString::Number(16),
            }))),
        };

        let text = r#"{ "id": 21, "type": "edge", "label": "item", "property": "definition", "outV": 16, "inV": 18}"#
            .replace(' ', "");

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&text).unwrap(),
            serde_json::to_value(&data).unwrap()
        );
    }

    mod methods {
        use super::*;

        #[test]
        fn references() {
            let data = Entry {
                id: lsp_types::NumberOrString::Number(17),
                data: Element::Edge(Edge::References(EdgeData {
                    in_v: lsp_types::NumberOrString::Number(16),
                    out_v: lsp_types::NumberOrString::Number(15),
                })),
            };

            let text = r#"{ "id": 17, "type": "edge", "label": "textDocument/references", "outV": 15, "inV": 16 }"#;

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
                serde_json::to_value(&data).unwrap()
            );
        }

        #[test]
        fn definition() {
            let data = Entry {
                id: lsp_types::NumberOrString::Number(13),
                data: Element::Vertex(Vertex::DefinitionResult {
                    result: DefinitionResultType::Scalar(LocationOrRangeId::RangeId(
                        lsp_types::NumberOrString::Number(7),
                    )),
                }),
            };

            let text =
                r#"{ "id": 13, "type": "vertex", "label": "definitionResult", "result": 7 }"#;

            assert_eq!(
                serde_json::from_str::<serde_json::Value>(&text).unwrap(),
                serde_json::to_value(&data).unwrap()
            );
        }
    }
}
<|endoftext|>


<commit_before>extern crate handlebars;
extern crate itertools;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

use handlebars::Handlebars;
use itertools::Itertools;
use serde_json::Value;
use std::collections::BTreeMap;

trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

#[derive(Debug, Deserialize)]
pub struct AMQProtocolDefinition {
    pub name:          String,
    #[serde(rename="major-version")]
    pub major_version: u8,
    #[serde(rename="minor-version")]
    pub minor_version: u8,
    pub revision:      u8,
    pub port:          u32,
    pub copyright:     Vec<String>,
    pub domains:       Vec<AMQPDomain>,
    pub constants:     Vec<AMQPConstant>,
    pub classes:       Vec<AMQPClass>,
}

impl AMQProtocolDefinition {
    pub fn codegen(&self, templates: &AMQPTemplates) -> String {
        let handlebars = register_templates(templates);
        let mut data   = BTreeMap::new();

        data.insert("name".to_string(),          self.name.clone());
        data.insert("major_version".to_string(), format!("{}", self.major_version));
        data.insert("minor_version".to_string(), format!("{}", self.minor_version));
        data.insert("revision".to_string(),      format!("{}", self.revision));
        data.insert("port".to_string(),          format!("{}", self.port));
        data.insert("copyright".to_string(),     self.copyright.iter().join("\n"));
        data.insert("domains".to_string(),       self.domains.iter().map(|domain| domain.codegen(&handlebars)).join("\n"));
        data.insert("constants".to_string(),     self.constants.iter().map(|constant| constant.codegen(&handlebars)).join("\n"));
        data.insert("classes".to_string(),       self.classes.iter().map(|klass| klass.codegen(&handlebars)).join("\n"));

        handlebars.render("main", &data).expect("Failed to render main template")
    }
}

fn register_templates(templates: &AMQPTemplates) -> Handlebars {
    let mut handlebars = Handlebars::new();

    handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
    handlebars.register_template_string("domain",   &templates.main).expect("Failed to register domain template");
    handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
    handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
    handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
    handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
    handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

    handlebars
}

pub struct AMQPTemplates {
    pub main:     String,
    pub domain:   String,
    pub constant: String,
    pub klass:    String,
    pub method:   String,
    pub argument: String,
    pub property: String,
}

#[derive(Debug, Deserialize)]
pub enum AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl AMQPType {
    fn to_string(&self) -> String {
        match *self {
            AMQPType::Bit       => "bit",
            AMQPType::Octet     => "octet",
            AMQPType::Short     => "short",
            AMQPType::Long      => "long",
            AMQPType::LongLong  => "longlong",
            AMQPType::ShortStr  => "shortstr",
            AMQPType::LongStr   => "longstr",
            AMQPType::Table     => "table",
            AMQPType::Timestamp => "timestamp",
        }.to_string()
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPDomain(String, AMQPType);

impl Codegen for AMQPDomain {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(), self.0.clone());
        data.insert("type".to_string(), self.1.to_string());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPConstant {
    pub name:  String,
    pub value: u16,
    #[serde(rename="class")]
    pub klass: Option<String>,
}

impl Codegen for AMQPConstant {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(),  self.name.clone());
        data.insert("value".to_string(), format!("{}", self.value));
        data.insert("class".to_string(), self.klass.clone().unwrap_or("".to_string())); /* FIXME: we might want to handle None another way */

        handlebars.render("constant", &data).expect("Failed to render constant template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPClass {
    pub id:         u8,
    pub methods:    Vec<AMQPMethod>,
    pub name:       String,
    pub properties: Option<Vec<AMQPProperty>>,
}

impl Codegen for AMQPClass {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),        format!("{}", self.id));
        data.insert("methods".to_sting(),    self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"))
        data.insert("name".to_string(),      self.name.clone());
        data.insert("properties".to_sting(), self.properties.iter().map(|prop| prop.codegen(&handlebars)).join("\n"))

        handlebars.render("class", &data).expect("Failed to render class template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPMethod {
    pub id:          u8,
    pub arguments:   Vec<AMQPArgument>,
    pub name:        String,
    pub synchronous: Option<bool>,
}

impl Codegen for AMQPMethod {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),          format!("{}", self.id));
        /* TODO: arguments */
        data.insert("name".to_string(),        self.name.clone());
        data.insert("synchronous".to_string(), format!("{}", self.synchronous.unwrap_or(false)));

        handlebars.render("method", &data).expect("Failed to render method template");
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPArgument {
    #[serde(rename="type")]
    pub amqp_type:     Option<AMQPType>,
    pub name:          String,
    #[serde(rename="default-value")]
    pub default_value: Option<Value>,
    pub domain:        Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct AMQPProperty {
    #[serde(rename="type")]
    pub amqp_type: AMQPType,
    pub name:      String,
}

impl Codegen for AMQPProperty {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("type".to_string(), self.amqp_type.to_string());
        data.insert("name".to_string(), self.name.clone());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

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

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     Vec::new(),
            domains:       Vec::new(),
            constants:     Vec::new(),
            classes:       Vec::new(),
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     "{{name}} - {{major_version}}.{{minor_version}}.{{revision}}".to_string(),
            domain:   String::new(),
            constant: String::new(),
            klass:    String::new(),
            method:   String::new(),
            argument: String::new(),
            property: String::new(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().codegen(&templates()), "AMQP - 0.9.1");
    }
}
<commit_msg>codegen: write everything to templates<commit_after>extern crate handlebars;
extern crate itertools;
extern crate serde;
#[macro_use] extern crate serde_derive;
extern crate serde_json;

use handlebars::Handlebars;
use itertools::Itertools;
use serde_json::Value;
use std::collections::BTreeMap;

trait Codegen {
    fn codegen(&self, handlebars: &Handlebars) -> String;
}

#[derive(Debug, Deserialize)]
pub struct AMQProtocolDefinition {
    pub name:          String,
    #[serde(rename="major-version")]
    pub major_version: u8,
    #[serde(rename="minor-version")]
    pub minor_version: u8,
    pub revision:      u8,
    pub port:          u32,
    pub copyright:     Vec<String>,
    pub domains:       Vec<AMQPDomain>,
    pub constants:     Vec<AMQPConstant>,
    pub classes:       Vec<AMQPClass>,
}

impl AMQProtocolDefinition {
    pub fn codegen(&self, templates: &AMQPTemplates) -> String {
        let handlebars = register_templates(templates);
        let mut data   = BTreeMap::new();

        data.insert("name".to_string(),          self.name.clone());
        data.insert("major_version".to_string(), format!("{}", self.major_version));
        data.insert("minor_version".to_string(), format!("{}", self.minor_version));
        data.insert("revision".to_string(),      format!("{}", self.revision));
        data.insert("port".to_string(),          format!("{}", self.port));
        data.insert("copyright".to_string(),     self.copyright.iter().join("\n"));
        data.insert("domains".to_string(),       self.domains.iter().map(|domain| domain.codegen(&handlebars)).join("\n"));
        data.insert("constants".to_string(),     self.constants.iter().map(|constant| constant.codegen(&handlebars)).join("\n"));
        data.insert("classes".to_string(),       self.classes.iter().map(|klass| klass.codegen(&handlebars)).join("\n"));

        handlebars.render("main", &data).expect("Failed to render main template")
    }
}

fn register_templates(templates: &AMQPTemplates) -> Handlebars {
    let mut handlebars = Handlebars::new();

    handlebars.register_template_string("main",     &templates.main).expect("Failed to register main template");
    handlebars.register_template_string("domain",   &templates.main).expect("Failed to register domain template");
    handlebars.register_template_string("constant", &templates.constant).expect("Failed to register constant template");
    handlebars.register_template_string("class",    &templates.klass).expect("Failed to register class template");
    handlebars.register_template_string("method",   &templates.method).expect("Failed to register method template");
    handlebars.register_template_string("argument", &templates.argument).expect("Failed to register argument template");
    handlebars.register_template_string("property", &templates.property).expect("Failed to register property template");

    handlebars
}

pub struct AMQPTemplates {
    pub main:     String,
    pub domain:   String,
    pub constant: String,
    pub klass:    String,
    pub method:   String,
    pub argument: String,
    pub property: String,
}

#[derive(Debug, Deserialize)]
pub enum AMQPType {
    #[serde(rename="bit")]
    Bit,
    #[serde(rename="octet")]
    Octet,
    #[serde(rename="short")]
    Short,
    #[serde(rename="long")]
    Long,
    #[serde(rename="longlong")]
    LongLong,
    #[serde(rename="shortstr")]
    ShortStr,
    #[serde(rename="longstr")]
    LongStr,
    #[serde(rename="table")]
    Table,
    #[serde(rename="timestamp")]
    Timestamp,
}

impl AMQPType {
    fn to_string(&self) -> String {
        match *self {
            AMQPType::Bit       => "bit",
            AMQPType::Octet     => "octet",
            AMQPType::Short     => "short",
            AMQPType::Long      => "long",
            AMQPType::LongLong  => "longlong",
            AMQPType::ShortStr  => "shortstr",
            AMQPType::LongStr   => "longstr",
            AMQPType::Table     => "table",
            AMQPType::Timestamp => "timestamp",
        }.to_string()
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPDomain(String, AMQPType);

impl Codegen for AMQPDomain {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(), self.0.clone());
        data.insert("type".to_string(), self.1.to_string());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPConstant {
    pub name:  String,
    pub value: u16,
    #[serde(rename="class")]
    pub klass: Option<String>,
}

impl Codegen for AMQPConstant {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("name".to_string(),  self.name.clone());
        data.insert("value".to_string(), format!("{}", self.value));
        if let Some(ref klass) = self.klass {
            data.insert("class".to_string(), klass);
        }

        handlebars.render("constant", &data).expect("Failed to render constant template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPClass {
    pub id:         u8,
    pub methods:    Vec<AMQPMethod>,
    pub name:       String,
    pub properties: Option<Vec<AMQPProperty>>,
}

impl Codegen for AMQPClass {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),      format!("{}", self.id));
        data.insert("methods".to_string(), self.methods.iter().map(|method| method.codegen(&handlebars)).join("\n"));
        data.insert("name".to_string(),    self.name.clone());
        if let Some(ref properties) = self.properties {
            data.insert("properties".to_string(), properties.iter().map(|prop| prop.codegen(&handlebars)).join("\n"));
        }

        handlebars.render("class", &data).expect("Failed to render class template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPMethod {
    pub id:          u8,
    pub arguments:   Vec<AMQPArgument>,
    pub name:        String,
    pub synchronous: Option<bool>,
}

impl Codegen for AMQPMethod {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("id".to_string(),          format!("{}", self.id));
        data.insert("arguments".to_string(),   self.arguments.iter().map(|arg| arg.codegen(&handlebars)).join("\n"));
        data.insert("name".to_string(),        self.name.clone());
        data.insert("synchronous".to_string(), format!("{}", self.synchronous.unwrap_or(false)));

        handlebars.render("method", &data).expect("Failed to render method template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPArgument {
    #[serde(rename="type")]
    pub amqp_type:     Option<AMQPType>,
    pub name:          String,
    #[serde(rename="default-value")]
    pub default_value: Option<Value>,
    pub domain:        Option<String>,
}

impl Codegen for AMQPArgument {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        if let Some(ref amqp_type) = self.amqp_type {
            data.insert("type".to_string(), amqp_type.to_string());
        }
        if let Some(ref default_value) = self.default_value {
            data.insert("default_value".to_string(), default_value.to_string());
        }
        data.insert("name".to_string(),   self.name.clone());
        if let Some(ref domain) = self.domain {
            data.insert("domain".to_string(), domain.clone());
        }

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

#[derive(Debug, Deserialize)]
pub struct AMQPProperty {
    #[serde(rename="type")]
    pub amqp_type: AMQPType,
    pub name:      String,
}

impl Codegen for AMQPProperty {
    fn codegen(&self, handlebars: &Handlebars) -> String {
        let mut data = BTreeMap::new();

        data.insert("type".to_string(), self.amqp_type.to_string());
        data.insert("name".to_string(), self.name.clone());

        handlebars.render("domain", &data).expect("Failed to render domain template")
    }
}

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

    fn specs() -> AMQProtocolDefinition {
        AMQProtocolDefinition {
            name:          "AMQP".to_string(),
            major_version: 0,
            minor_version: 9,
            revision:      1,
            port:          5672,
            copyright:     Vec::new(),
            domains:       Vec::new(),
            constants:     Vec::new(),
            classes:       Vec::new(),
        }
    }

    fn templates() -> AMQPTemplates {
        AMQPTemplates {
            main:     "{{name}} - {{major_version}}.{{minor_version}}.{{revision}}".to_string(),
            domain:   String::new(),
            constant: String::new(),
            klass:    String::new(),
            method:   String::new(),
            argument: String::new(),
            property: String::new(),
        }
    }

    #[test]
    fn main_template() {
        assert_eq!(specs().codegen(&templates()), "AMQP - 0.9.1");
    }
}
<|endoftext|>


<commit_before>//! The shared module shall give binary and library high level functionality
//! to be used by both the same way.
use crate::{
    common::paths::SearchPath,
    ctrl::UiUpdateMsg,
    data::{
        self, audio_info::Container, collection::Collection, ipc::IPC,
        IFInternalCollectionOutputData,
    },
    net::Net,
};
use crossbeam::{
    channel::{Receiver as CReceiver, Sender as CSender},
    sync::WaitGroup,
};
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    error::Error,
    sync::{Arc, Mutex},
};

/// High level function to search path
pub fn collection_search(
    collection_handler: Arc<Mutex<Collection>>,
    search_path: Arc<Mutex<SearchPath>>,
    sender_handler: Arc<Mutex<CSender<UiUpdateMsg>>>,
    has_ui: bool,
) -> IFInternalCollectionOutputData {
    let output_data = IFInternalCollectionOutputData::new();
    let output_data_handle = Arc::new(Mutex::new(output_data));
    let output_data_handle2 = output_data_handle.clone();

    let handle_container = Arc::new(Mutex::new(Container::new()));

    let current_search_path = search_path.lock().unwrap().read();
    // start the parallel search threads with rayon, each path its own
    &current_search_path
        .par_iter()
        .enumerate()
        .for_each(|(index, elem)| {
            let sender_loop = sender_handler.clone();
            let collection_data_in_iterator = collection_handler.clone();
            let single_path_collection_data = data::search_in_single_path(
                has_ui,
                handle_container.clone(),
                collection_data_in_iterator,
                sender_loop,
                index,
                elem,
            );
            // accumulate data
            {
                let mut locker = output_data_handle.lock().unwrap();
                locker.nr_found_songs += single_path_collection_data.nr_found_songs;
                locker.nr_internal_duplicates += single_path_collection_data.nr_internal_duplicates;
                locker.nr_searched_files += single_path_collection_data.nr_searched_files;
            }
        });
    let out = &*output_data_handle2.lock().unwrap();
    out.clone()
}

/// High level function to startup net functionality
pub async fn net_search(
    wait_net: WaitGroup,
    net_system_messages: Option<CSender<UiUpdateMsg>>,
    ipc_receive: CReceiver<IPC>,
) -> Result<(), Box<dyn Error>> {
    // - can be terminated by ui message
    // - collector finished (depending on definition)

    info!("net started!!");
    let mut network = Net {};

    // startup net thread synchronization
    wait_net.wait();

    network.lookup(net_system_messages, ipc_receive).await
}
<commit_msg>[refactor] rename crossbeam sender/receiver to normal<commit_after>//! The shared module shall give binary and library high level functionality
//! to be used by both the same way.
use crate::{
    common::paths::SearchPath,
    ctrl::UiUpdateMsg,
    data::{
        self, audio_info::Container, collection::Collection, ipc::IPC,
        IFInternalCollectionOutputData,
    },
    net::Net,
};
use crossbeam::{
    channel::{Receiver, Sender},
    sync::WaitGroup,
};
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use std::{
    error::Error,
    sync::{Arc, Mutex},
};

/// High level function to search path
pub fn collection_search(
    collection_handler: Arc<Mutex<Collection>>,
    search_path: Arc<Mutex<SearchPath>>,
    sender_handler: Arc<Mutex<Sender<UiUpdateMsg>>>,
    has_ui: bool,
) -> IFInternalCollectionOutputData {
    let output_data = IFInternalCollectionOutputData::new();
    let output_data_handle = Arc::new(Mutex::new(output_data));
    let output_data_handle2 = output_data_handle.clone();

    let handle_container = Arc::new(Mutex::new(Container::new()));

    let current_search_path = search_path.lock().unwrap().read();
    // start the parallel search threads with rayon, each path its own
    &current_search_path
        .par_iter()
        .enumerate()
        .for_each(|(index, elem)| {
            let sender_loop = sender_handler.clone();
            let collection_data_in_iterator = collection_handler.clone();
            let single_path_collection_data = data::search_in_single_path(
                has_ui,
                handle_container.clone(),
                collection_data_in_iterator,
                sender_loop,
                index,
                elem,
            );
            // accumulate data
            {
                let mut locker = output_data_handle.lock().unwrap();
                locker.nr_found_songs += single_path_collection_data.nr_found_songs;
                locker.nr_internal_duplicates += single_path_collection_data.nr_internal_duplicates;
                locker.nr_searched_files += single_path_collection_data.nr_searched_files;
            }
        });
    let out = &*output_data_handle2.lock().unwrap();
    out.clone()
}

/// High level function to startup net functionality
pub async fn net_search(
    wait_net: WaitGroup,
    net_system_messages: Option<Sender<UiUpdateMsg>>,
    ipc_receive: Receiver<IPC>,
) -> Result<(), Box<dyn Error>> {
    // - can be terminated by ui message
    // - collector finished (depending on definition)

    info!("net started!!");
    let mut network = Net {};

    // startup net thread synchronization
    wait_net.wait();

    network.lookup(net_system_messages, ipc_receive).await
}
<|endoftext|>