<commit_before>//! Complete fields in record literals and patterns.
use ide_db::SymbolKind;
use syntax::{ast::Expr, T};

use crate::{
    patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
    CompletionRelevance, CompletionRelevancePostfixMatch, Completions,
};

pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let missing_fields = match &ctx.completion_location {
        Some(
            ImmediateLocation::RecordExpr(record_expr)
            | ImmediateLocation::RecordExprUpdate(record_expr),
        ) => {
            let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));

            if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) {
                // ctx.sema.record_literal_missing_fields will always return
                // an empty Vec on a union literal. This is normally
                // reasonable, but here we'd like to present the full list
                // of fields if the literal is empty.
                let were_fields_specified = record_expr
                    .record_expr_field_list()
                    .and_then(|fl| fl.fields().next())
                    .is_some();

                match were_fields_specified {
                    false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
                    true => vec![],
                }
            } else {
                let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);

                let default_trait = ctx.famous_defs().core_default_Default();
                let impl_default_trait =
                    default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| {
                        ty.original.impls_trait(ctx.db, default_trait, &[])
                    });

                if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
                    let completion_text = "..Default::default()";
                    let mut item =
                        CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
                    let completion_text =
                        completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
                    item.insert_text(completion_text).set_relevance(CompletionRelevance {
                        postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
                        ..Default::default()
                    });
                    item.add_to(acc);
                }
                if ctx.previous_token_is(T![.]) {
                    let mut item =
                        CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
                    item.insert_text(".");
                    item.add_to(acc);
                    return None;
                }
                missing_fields
            }
        }
        Some(ImmediateLocation::RecordPat(record_pat)) => {
            ctx.sema.record_pattern_missing_fields(record_pat)
        }
        _ => return None,
    };

    for (field, ty) in missing_fields {
        acc.add_field(ctx, None, field, &ty);
    }

    Some(())
}

pub(crate) fn complete_record_literal(
    acc: &mut Completions,
    ctx: &CompletionContext,
) -> Option<()> {
    if !ctx.expects_expression() {
        return None;
    }

    match ctx.expected_type.as_ref()?.as_adt()? {
        hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
            let path = ctx
                .module
                .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
                .filter(|it| it.len() > 1);

            acc.add_struct_literal(ctx, strukt, path, None);
        }
        hir::Adt::Union(un) if ctx.path_qual().is_none() => {
            let path = ctx
                .module
                .find_use_path(ctx.db, hir::ModuleDef::from(un))
                .filter(|it| it.len() > 1);

            acc.add_union_literal(ctx, un, path, None);
        }
        _ => {}
    };

    Some(())
}

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

    #[test]
    fn literal_struct_completion_edit() {
        check_edit(
            "FooDesc {…}",
            r#"
struct FooDesc { pub bar: bool }

fn create_foo(foo_desc: &FooDesc) -> () { () }

fn baz() {
    let foo = create_foo(&$0);
}
            "#,
            r#"
struct FooDesc { pub bar: bool }

fn create_foo(foo_desc: &FooDesc) -> () { () }

fn baz() {
    let foo = create_foo(&FooDesc { bar: ${1:()} }$0);
}
            "#,
        )
    }

    #[test]
    fn literal_struct_completion_from_sub_modules() {
        check_edit(
            "submod::Struct {…}",
            r#"
mod submod {
    pub struct Struct {
        pub a: u64,
    }
}

fn f() -> submod::Struct {
    Stru$0
}
            "#,
            r#"
mod submod {
    pub struct Struct {
        pub a: u64,
    }
}

fn f() -> submod::Struct {
    submod::Struct { a: ${1:()} }$0
}
            "#,
        )
    }

    #[test]
    fn literal_struct_complexion_module() {
        check_edit(
            "FooDesc {…}",
            r#"
mod _69latrick {
    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}

fn baz() {
    use _69latrick::*;

    let foo = create_foo(&$0);
}
            "#,
            r#"
mod _69latrick {
    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}

fn baz() {
    use _69latrick::*;

    let foo = create_foo(&FooDesc { six: ${1:()}, neuf: ${2:()}, bar: ${3:()} }$0);
}
            "#,
        );
    }

    #[test]
    fn default_completion_edit() {
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        .$0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        $0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..$0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
    }
}
<commit_msg>Auto merge of #12123 - rainy-me:feat/impl-self-completion, r=Veykril<commit_after>//! Complete fields in record literals and patterns.
use ide_db::SymbolKind;
use syntax::{ast::Expr, T};

use crate::{
    patterns::ImmediateLocation, CompletionContext, CompletionItem, CompletionItemKind,
    CompletionRelevance, CompletionRelevancePostfixMatch, Completions,
};

pub(crate) fn complete_record(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
    let missing_fields = match &ctx.completion_location {
        Some(
            ImmediateLocation::RecordExpr(record_expr)
            | ImmediateLocation::RecordExprUpdate(record_expr),
        ) => {
            let ty = ctx.sema.type_of_expr(&Expr::RecordExpr(record_expr.clone()));

            if let Some(hir::Adt::Union(un)) = ty.as_ref().and_then(|t| t.original.as_adt()) {
                // ctx.sema.record_literal_missing_fields will always return
                // an empty Vec on a union literal. This is normally
                // reasonable, but here we'd like to present the full list
                // of fields if the literal is empty.
                let were_fields_specified = record_expr
                    .record_expr_field_list()
                    .and_then(|fl| fl.fields().next())
                    .is_some();

                match were_fields_specified {
                    false => un.fields(ctx.db).into_iter().map(|f| (f, f.ty(ctx.db))).collect(),
                    true => vec![],
                }
            } else {
                let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);

                let default_trait = ctx.famous_defs().core_default_Default();
                let impl_default_trait =
                    default_trait.zip(ty.as_ref()).map_or(false, |(default_trait, ty)| {
                        ty.original.impls_trait(ctx.db, default_trait, &[])
                    });

                if impl_default_trait && !missing_fields.is_empty() && ctx.path_qual().is_none() {
                    let completion_text = "..Default::default()";
                    let mut item =
                        CompletionItem::new(SymbolKind::Field, ctx.source_range(), completion_text);
                    let completion_text =
                        completion_text.strip_prefix(ctx.token.text()).unwrap_or(completion_text);
                    item.insert_text(completion_text).set_relevance(CompletionRelevance {
                        postfix_match: Some(CompletionRelevancePostfixMatch::Exact),
                        ..Default::default()
                    });
                    item.add_to(acc);
                }
                if ctx.previous_token_is(T![.]) {
                    let mut item =
                        CompletionItem::new(CompletionItemKind::Snippet, ctx.source_range(), "..");
                    item.insert_text(".");
                    item.add_to(acc);
                    return None;
                }
                missing_fields
            }
        }
        Some(ImmediateLocation::RecordPat(record_pat)) => {
            ctx.sema.record_pattern_missing_fields(record_pat)
        }
        _ => return None,
    };

    for (field, ty) in missing_fields {
        acc.add_field(ctx, None, field, &ty);
    }

    Some(())
}

pub(crate) fn complete_record_literal(
    acc: &mut Completions,
    ctx: &CompletionContext,
) -> Option<()> {
    if !ctx.expects_expression() {
        return None;
    }

    match ctx.expected_type.as_ref()?.as_adt()? {
        hir::Adt::Struct(strukt) if ctx.path_qual().is_none() => {
            let path = ctx
                .module
                .find_use_path(ctx.db, hir::ModuleDef::from(strukt))
                .filter(|it| it.len() > 1);

            acc.add_struct_literal(ctx, strukt, path, None);

            let impl_ = ctx.impl_def.as_ref()?;
            let impl_adt = ctx.sema.to_def(impl_)?.self_ty(ctx.db).as_adt()?;
            if hir::Adt::Struct(strukt) == impl_adt {
                acc.add_struct_literal(ctx, strukt, None, Some(hir::known::SELF_TYPE));
            }
        }
        hir::Adt::Union(un) if ctx.path_qual().is_none() => {
            let path = ctx
                .module
                .find_use_path(ctx.db, hir::ModuleDef::from(un))
                .filter(|it| it.len() > 1);

            acc.add_union_literal(ctx, un, path, None);
        }
        _ => {}
    };

    Some(())
}

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

    #[test]
    fn literal_struct_completion_edit() {
        check_edit(
            "FooDesc {…}",
            r#"
struct FooDesc { pub bar: bool }

fn create_foo(foo_desc: &FooDesc) -> () { () }

fn baz() {
    let foo = create_foo(&$0);
}
            "#,
            r#"
struct FooDesc { pub bar: bool }

fn create_foo(foo_desc: &FooDesc) -> () { () }

fn baz() {
    let foo = create_foo(&FooDesc { bar: ${1:()} }$0);
}
            "#,
        )
    }

    #[test]
    fn literal_struct_impl_self_completion() {
        check_edit(
            "Self {…}",
            r#"
struct Foo {
    bar: u64,
}

impl Foo {
    fn new() -> Foo {
        Self$0
    }
}
            "#,
            r#"
struct Foo {
    bar: u64,
}

impl Foo {
    fn new() -> Foo {
        Self { bar: ${1:()} }$0
    }
}
            "#,
        );

        check_edit(
            "Self(…)",
            r#"
mod submod {
    pub struct Foo(pub u64);
}

impl submod::Foo {
    fn new() -> submod::Foo {
        Self$0
    }
}
            "#,
            r#"
mod submod {
    pub struct Foo(pub u64);
}

impl submod::Foo {
    fn new() -> submod::Foo {
        Self(${1:()})$0
    }
}
            "#,
        )
    }

    #[test]
    fn literal_struct_completion_from_sub_modules() {
        check_edit(
            "submod::Struct {…}",
            r#"
mod submod {
    pub struct Struct {
        pub a: u64,
    }
}

fn f() -> submod::Struct {
    Stru$0
}
            "#,
            r#"
mod submod {
    pub struct Struct {
        pub a: u64,
    }
}

fn f() -> submod::Struct {
    submod::Struct { a: ${1:()} }$0
}
            "#,
        )
    }

    #[test]
    fn literal_struct_complexion_module() {
        check_edit(
            "FooDesc {…}",
            r#"
mod _69latrick {
    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}

fn baz() {
    use _69latrick::*;

    let foo = create_foo(&$0);
}
            "#,
            r#"
mod _69latrick {
    pub struct FooDesc { pub six: bool, pub neuf: Vec<String>, pub bar: bool }
    pub fn create_foo(foo_desc: &FooDesc) -> () { () }
}

fn baz() {
    use _69latrick::*;

    let foo = create_foo(&FooDesc { six: ${1:()}, neuf: ${2:()}, bar: ${3:()} }$0);
}
            "#,
        );
    }

    #[test]
    fn default_completion_edit() {
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        .$0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        $0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
        check_edit(
            "..Default::default()",
            r#"
//- minicore: default
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..$0
    };
}
"#,
            r#"
struct Struct { foo: u32, bar: usize }

impl Default for Struct {
    fn default() -> Self {}
}

fn foo() {
    let other = Struct {
        foo: 5,
        ..Default::default()
    };
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>use std::{iter::once, ops::RangeInclusive};

use syntax::{
    algo::replace_children,
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
        make,
    },
    AstNode,
    SyntaxKind::{FN, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
    SyntaxNode,
};

use crate::{
    assist_context::{AssistContext, Assists},
    utils::invert_boolean_expression,
    AssistId, AssistKind,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     $0if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let cond = if_expr.condition()?;

    // Check if there is an IfLet that we can handle.
    let if_let_pat = match cond.pat() {
        None => None, // No IfLet, supported.
        Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
            let path = pat.path()?;
            match path.qualifier() {
                None => {
                    let bound_ident = pat.fields().next().unwrap();
                    Some((path, bound_ident))
                }
                Some(_) => return None,
            }
        }
        Some(_) => return None, // Unsupported IfLet.
    };

    let cond_expr = cond.expr()?;
    let then_block = if_expr.then_branch()?;

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;

    if parent_block.tail_expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?;

    let early_expression: ast::Expr = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
        FN => make::expr_return(None),
        _ => return None,
    };

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;

    let target = if_expr.syntax().text_range();
    acc.add(
        AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
        "Convert to guarded return",
        target,
        |edit| {
            let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
            let new_block = match if_let_pat {
                None => {
                    // If.
                    let new_expr = {
                        let then_branch =
                            make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                        let cond = invert_boolean_expression(&ctx.sema, cond_expr);
                        make::expr_if(make::condition(cond, None), then_branch, None)
                            .indent(if_indent_level)
                    };
                    replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
                }
                Some((path, bound_ident)) => {
                    // If-let.
                    let match_expr = {
                        let happy_arm = {
                            let pat = make::tuple_struct_pat(
                                path,
                                once(make::ident_pat(make::name("it")).into()),
                            );
                            let expr = {
                                let name_ref = make::name_ref("it");
                                let segment = make::path_segment(name_ref);
                                let path = make::path_unqualified(segment);
                                make::expr_path(path)
                            };
                            make::match_arm(once(pat.into()), expr)
                        };

                        let sad_arm = make::match_arm(
                            // FIXME: would be cool to use `None` or `Err(_)` if appropriate
                            once(make::wildcard_pat().into()),
                            early_expression,
                        );

                        make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
                    };

                    let let_stmt = make::let_stmt(
                        make::ident_pat(make::name(&bound_ident.syntax().to_string())).into(),
                        Some(match_expr),
                    );
                    let let_stmt = let_stmt.indent(if_indent_level);
                    replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
                }
            };
            edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());

            fn replace(
                new_expr: &SyntaxNode,
                then_block: &ast::BlockExpr,
                parent_block: &ast::BlockExpr,
                if_expr: &ast::IfExpr,
            ) -> SyntaxNode {
                let then_block_items = then_block.dedent(IndentLevel(1));
                let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
                let end_of_then =
                    if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                        end_of_then.prev_sibling_or_token().unwrap()
                    } else {
                        end_of_then
                    };
                let mut then_statements = new_expr.children_with_tokens().chain(
                    then_block_items
                        .syntax()
                        .children_with_tokens()
                        .skip(1)
                        .take_while(|i| *i != end_of_then),
                );
                replace_children(
                    &parent_block.syntax(),
                    RangeInclusive::new(
                        if_expr.clone().syntax().clone().into(),
                        if_expr.syntax().clone().into(),
                    ),
                    &mut then_statements,
                )
            }
        },
    )
}

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

    use super::*;

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if$0 true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if$0 let Some(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                let n = match n {
                    Some(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_if_let_result() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 let Ok(x) = Err(92) {
                    foo(x);
                }
            }
            "#,
            r#"
            fn main() {
                let x = match Err(92) {
                    Ok(it) => it,
                    _ => return,
                };
                foo(x);
            }
            "#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if$0 let Ok(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                let n = match n {
                    Ok(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if$0 true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if$0 let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    let n = match n {
                        Some(it) => it,
                        _ => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    let n = match n {
                        Some(it) => it,
                        _ => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if$0 true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<commit_msg>correct `convert to guard return` let_stmt<commit_after>use std::{iter::once, ops::RangeInclusive};

use syntax::{
    algo::replace_children,
    ast::{
        self,
        edit::{AstNodeEdit, IndentLevel},
        make,
    },
    AstNode,
    SyntaxKind::{FN, LOOP_EXPR, L_CURLY, R_CURLY, WHILE_EXPR, WHITESPACE},
    SyntaxNode,
};

use crate::{
    assist_context::{AssistContext, Assists},
    utils::invert_boolean_expression,
    AssistId, AssistKind,
};

// Assist: convert_to_guarded_return
//
// Replace a large conditional with a guarded return.
//
// ```
// fn main() {
//     $0if cond {
//         foo();
//         bar();
//     }
// }
// ```
// ->
// ```
// fn main() {
//     if !cond {
//         return;
//     }
//     foo();
//     bar();
// }
// ```
pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let if_expr: ast::IfExpr = ctx.find_node_at_offset()?;
    if if_expr.else_branch().is_some() {
        return None;
    }

    let cond = if_expr.condition()?;

    // Check if there is an IfLet that we can handle.
    let if_let_pat = match cond.pat() {
        None => None, // No IfLet, supported.
        Some(ast::Pat::TupleStructPat(pat)) if pat.fields().count() == 1 => {
            let path = pat.path()?;
            match path.qualifier() {
                None => {
                    let bound_ident = pat.fields().next().unwrap();
                    if ast::IdentPat::can_cast(bound_ident.syntax().kind()) {
                        Some((path, bound_ident))
                    } else {
                        return None;
                    }
                }
                Some(_) => return None,
            }
        }
        Some(_) => return None, // Unsupported IfLet.
    };

    let cond_expr = cond.expr()?;
    let then_block = if_expr.then_branch()?;

    let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?;

    if parent_block.tail_expr()? != if_expr.clone().into() {
        return None;
    }

    // check for early return and continue
    let first_in_then_block = then_block.syntax().first_child()?;
    if ast::ReturnExpr::can_cast(first_in_then_block.kind())
        || ast::ContinueExpr::can_cast(first_in_then_block.kind())
        || first_in_then_block
            .children()
            .any(|x| ast::ReturnExpr::can_cast(x.kind()) || ast::ContinueExpr::can_cast(x.kind()))
    {
        return None;
    }

    let parent_container = parent_block.syntax().parent()?;

    let early_expression: ast::Expr = match parent_container.kind() {
        WHILE_EXPR | LOOP_EXPR => make::expr_continue(),
        FN => make::expr_return(None),
        _ => return None,
    };

    if then_block.syntax().first_child_or_token().map(|t| t.kind() == L_CURLY).is_none() {
        return None;
    }

    then_block.syntax().last_child_or_token().filter(|t| t.kind() == R_CURLY)?;

    let target = if_expr.syntax().text_range();
    acc.add(
        AssistId("convert_to_guarded_return", AssistKind::RefactorRewrite),
        "Convert to guarded return",
        target,
        |edit| {
            let if_indent_level = IndentLevel::from_node(&if_expr.syntax());
            let new_block = match if_let_pat {
                None => {
                    // If.
                    let new_expr = {
                        let then_branch =
                            make::block_expr(once(make::expr_stmt(early_expression).into()), None);
                        let cond = invert_boolean_expression(&ctx.sema, cond_expr);
                        make::expr_if(make::condition(cond, None), then_branch, None)
                            .indent(if_indent_level)
                    };
                    replace(new_expr.syntax(), &then_block, &parent_block, &if_expr)
                }
                Some((path, bound_ident)) => {
                    // If-let.
                    let match_expr = {
                        let happy_arm = {
                            let pat = make::tuple_struct_pat(
                                path,
                                once(make::ident_pat(make::name("it")).into()),
                            );
                            let expr = {
                                let name_ref = make::name_ref("it");
                                let segment = make::path_segment(name_ref);
                                let path = make::path_unqualified(segment);
                                make::expr_path(path)
                            };
                            make::match_arm(once(pat.into()), expr)
                        };

                        let sad_arm = make::match_arm(
                            // FIXME: would be cool to use `None` or `Err(_)` if appropriate
                            once(make::wildcard_pat().into()),
                            early_expression,
                        );

                        make::expr_match(cond_expr, make::match_arm_list(vec![happy_arm, sad_arm]))
                    };

                    let let_stmt = make::let_stmt(bound_ident, Some(match_expr));
                    let let_stmt = let_stmt.indent(if_indent_level);
                    replace(let_stmt.syntax(), &then_block, &parent_block, &if_expr)
                }
            };
            edit.replace_ast(parent_block, ast::BlockExpr::cast(new_block).unwrap());

            fn replace(
                new_expr: &SyntaxNode,
                then_block: &ast::BlockExpr,
                parent_block: &ast::BlockExpr,
                if_expr: &ast::IfExpr,
            ) -> SyntaxNode {
                let then_block_items = then_block.dedent(IndentLevel(1));
                let end_of_then = then_block_items.syntax().last_child_or_token().unwrap();
                let end_of_then =
                    if end_of_then.prev_sibling_or_token().map(|n| n.kind()) == Some(WHITESPACE) {
                        end_of_then.prev_sibling_or_token().unwrap()
                    } else {
                        end_of_then
                    };
                let mut then_statements = new_expr.children_with_tokens().chain(
                    then_block_items
                        .syntax()
                        .children_with_tokens()
                        .skip(1)
                        .take_while(|i| *i != end_of_then),
                );
                replace_children(
                    &parent_block.syntax(),
                    RangeInclusive::new(
                        if_expr.clone().syntax().clone().into(),
                        if_expr.syntax().clone().into(),
                    ),
                    &mut then_statements,
                )
            }
        },
    )
}

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

    use super::*;

    #[test]
    fn convert_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                bar();
                if$0 true {
                    foo();

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main() {
                bar();
                if !true {
                    return;
                }
                foo();

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if$0 let Some(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                let n = match n {
                    Some(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_if_let_result() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 let Ok(x) = Err(92) {
                    foo(x);
                }
            }
            "#,
            r#"
            fn main() {
                let x = match Err(92) {
                    Ok(it) => it,
                    _ => return,
                };
                foo(x);
            }
            "#,
        );
    }

    #[test]
    fn convert_let_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if$0 let Some(n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                let n = match n {
                    Some(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_mut_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<String>) {
                bar();
                if$0 let Some(mut n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<String>) {
                bar();
                let mut n = match n {
                    Some(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_let_ref_ok_inside_fn() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main(n: Option<&str>) {
                bar();
                if$0 let Some(ref n) = n {
                    foo(n);

                    //comment
                    bar();
                }
            }
            "#,
            r#"
            fn main(n: Option<&str>) {
                bar();
                let ref n = match n {
                    Some(it) => it,
                    _ => return,
                };
                foo(n);

                //comment
                bar();
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if$0 true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    if !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_while() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                while true {
                    if$0 let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                while true {
                    let n = match n {
                        Some(it) => it,
                        _ => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 true {
                        foo();
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    if !true {
                        continue;
                    }
                    foo();
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn convert_let_inside_loop() {
        check_assist(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 let Some(n) = n {
                        foo(n);
                        bar();
                    }
                }
            }
            "#,
            r#"
            fn main() {
                loop {
                    let n = match n {
                        Some(it) => it,
                        _ => continue,
                    };
                    foo(n);
                    bar();
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    return;
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_already_converted_loop() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                loop {
                    if$0 true {
                        continue;
                    }
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_return() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    return
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_else_branch() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    foo();
                } else {
                    bar()
                }
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_aftert_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if$0 true {
                    foo();
                }
                bar();
            }
            "#,
        );
    }

    #[test]
    fn ignore_statements_inside_if() {
        check_assist_not_applicable(
            convert_to_guarded_return,
            r#"
            fn main() {
                if false {
                    if$0 true {
                        foo();
                    }
                }
            }
            "#,
        );
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate nom;

pub mod tree;
pub mod parser;

use std::str::{self, FromStr};
use std::path::{PathBuf, Path};
use std::fs::File;
use std::io::Read;
use std::cmp::Ordering;
use std::borrow::Borrow;

use nom::{IResult, ErrorKind, Needed, FindSubstring, digit, space, multispace, line_ending};

use parser::escape_c_string;

named!(pub parse_include<String>, preceded!(
    tag!("/include/"),
    preceded!( multispace,
        delimited!(
            char!('"'),
            escape_c_string,
            char!('"')
        ))
));

named!(pub find_include<(&[u8], String)>, do_parse!(
    pre: take_until!("/include/") >>
    path: parse_include >>
    (pre, path)
));

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncludeBounds {
    pub path: PathBuf,
    pub global_start: usize,
    pub child_start: usize,
    pub len: usize,
    pub method: IncludeMethod,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IncludeMethod {
    DTS,
    CPP,
}

impl PartialOrd for IncludeBounds {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for IncludeBounds {
    fn cmp(&self, other: &Self) -> Ordering {
        self.global_start.cmp(&other.global_start)
    }
}

impl IncludeBounds {
    pub fn global_end(&self) -> usize {
        self.global_start + self.len
    }

    pub fn split_bounds(bounds: &mut Vec<IncludeBounds>, start: usize, end: usize, offset: usize) {
        let mut remainders: Vec<IncludeBounds> = Vec::new();

        // println!("split s: {} e: {} off: {}", start, end, offset);

        for b in bounds.iter_mut() {
            // println!("g_start: {} g_end: {}", b.global_start, b.global_end());
            if b.global_start < start && b.global_end() >= start {
                // global_start -- start -- global_end
                let remainder = b.global_end() - start;

                // println!("remainder: {}", remainder);

                remainders.push(IncludeBounds {
                    path: b.path.clone(),
                    global_start: end,
                    child_start: b.child_start + start - b.global_start + offset,
                    len: remainder, // - offset,
                    method: b.method.clone(),
                });

                b.len = start - b.global_start;
            } else if b.global_start == start {
                // split is at begining of the bound
                // offset the start
                {
                    let offset = end - start;
                    b.global_start += offset;
                }
                // shrink the len by the offset
                b.len -= offset;
            }
        }

        bounds.extend_from_slice(&remainders);
        bounds.sort();
    }
}

#[derive(Debug, PartialEq)]
pub struct Linemarker {
    child_line: usize,
    path: PathBuf,
    flag: Option<LinemarkerFlag>,
}

#[derive(Debug, PartialEq)]
pub enum LinemarkerFlag {
    Start,
    Return,
    System,
    Extern,
}

named!(pub parse_linemarker<Linemarker>,
    complete!(do_parse!(
        tag!("#") >>
        opt!(tag!("line")) >>
        space >>
        line: map_res!(map_res!(digit, str::from_utf8), usize::from_str) >>
        space >>
        path: delimited!(
            char!('"'),
            map!(escape_c_string, PathBuf::from),
            char!('"')
        ) >>
        flag: opt!(preceded!(space, map_res!(map_res!(digit, str::from_utf8), u64::from_str))) >>
        line_ending >>
        (Linemarker {
            child_line: line,
            path: path,
            flag: flag.map(|f| match f {
                1 => LinemarkerFlag::Start,
                2 => LinemarkerFlag::Return,
                3 => LinemarkerFlag::System,
                4 => LinemarkerFlag::Extern,
                _ => unreachable!(),
            }),
        })
    ))
);

fn find_linemarker_start(input: &[u8]) -> IResult<&[u8], &[u8]> {
    if "# ".len() > input.len() {
        IResult::Incomplete(Needed::Size("# ".len()))
    } else {
        match input.find_substring("# ").iter().chain(input.find_substring("#line ").iter()).min() {
            None => {
                IResult::Error(error_position!(ErrorKind::TakeUntil, input))
            },
            Some(index) => {
                IResult::Done(&input[*index..], &input[0..*index])
            },
        }
    }
}

named!(find_linemarker<(&[u8], Linemarker)>, do_parse!(
    pre: find_linemarker_start >>
    marker: parse_linemarker >>
    (pre, marker)
));

fn parse_linemarkers(buf: &[u8], bounds: &mut Vec<IncludeBounds>, global_offset: usize) {
    let end_offset = global_offset + buf.len();
    // println!("{}", str::from_utf8(buf).unwrap());
    let mut buf = buf;
    println!("parsing linemarkers");
    loop {
        // look for linemarker
        if let IResult::Done(rem, (pre, marker)) = find_linemarker(buf) {
            // println!("{}", str::from_utf8(line).unwrap());
            // println!("{:?}", marker);
            // println!("pre.len() {}", pre.len());

            // double check that last bound was from a linemarker
            match bounds.last() {
                Some(&IncludeBounds { method: IncludeMethod::CPP, .. }) => {}
                _ => {
                    println!("{:#?}", bounds);
                    panic!("Linemarker found within DTS include")
                }
            }

            // end last
            bounds.last_mut().unwrap().len = pre.len();

            // start at new line
            let new_bound = IncludeBounds {
                path: marker.path.clone(),
                global_start: end_offset - rem.len(),
                child_start: File::open(&marker.path)
                                .map(|f| f.bytes().filter_map(|e| e.ok()))
                                .map(|b| line_to_byte_offset(b, marker.child_line).unwrap()) //TODO: unwraping is bad, SOK?
                                .unwrap_or(0),
                len: rem.len(),
                method: IncludeMethod::CPP,
            };

            bounds.push(new_bound);

            buf = rem;
        } else {
            return;
        }
    }
}

pub fn include_files(path: &Path,
                     main_offset: usize)
                     -> Result<(Vec<u8>, Vec<IncludeBounds>), String> {
    // TODO: check from parent directory of root file
    let mut file = File::open(path).unwrap();
    let mut buffer: Vec<u8> = Vec::new();
    let mut bounds: Vec<IncludeBounds> = Vec::new();

    let mut string_buffer = String::new();
    file.read_to_string(&mut string_buffer).map_err(|_| "IO Error".to_string())?;

    let mut buf = string_buffer.as_bytes();

    named!(first_linemarker<(&[u8], Linemarker)>,
        do_parse!(
            marker: peek!(parse_linemarker) >>
            line: recognize!(parse_linemarker) >>
            (line, marker)
        )
    );

    let start_bound = if let IResult::Done(rem, (line, marker)) = first_linemarker(buf) {
        let bound = IncludeBounds {
            path: marker.path.clone(),
            global_start: buf.len() - rem.len(),
            // TODO: check from parent directory of root file
            child_start: File::open(&marker.path)
                             .map(|f| f.bytes().filter_map(|e| e.ok()))
                             .map(|b| line_to_byte_offset(b, marker.child_line).unwrap()) //TODO: unwraping is bad, SOK?
                             .unwrap_or(0),
            len: File::open(&marker.path).unwrap().bytes().count(),
            method: IncludeMethod::CPP,
        };

        buffer.extend_from_slice(line);
        buf = rem;

        bound
    } else {
        // println!("main_offset {}", main_offset);
        IncludeBounds {
            path: path.to_path_buf(),
            global_start: main_offset,
            child_start: 0,
            // TODO: check from parent directory of root file
            len: File::open(path).unwrap().bytes().count(),
            method: IncludeMethod::DTS,
        }
    };
    bounds.push(start_bound);

    loop {
        // go until /include/
        if let IResult::Done(rem, (pre, file)) = find_include(&buf[..]) {
            parse_linemarkers(pre, &mut bounds, buffer.len());
            buffer.extend_from_slice(pre);

            let offset = pre.len();
            // println!("{}", file);
            // println!("Offset: {}", offset);
            // println!("{}", include_tree);

            let included_path = Path::new(&file);
            let total_len = buffer.len() + main_offset; // - 1;
            let (sub_buf, sub_bounds) = include_files(included_path, total_len)?;
            buffer.extend(sub_buf);

            let inc_start = sub_bounds.first()
                                      .map(|b| b.global_start)
                                      .expect(&format!("No bounds returned: {}",
                                                      included_path.to_string_lossy()));
            let inc_end = sub_bounds.last()
                                    .map(|b| b.global_end())
                                    .expect(&format!("No bounds returned: {}",
                                                    included_path.to_string_lossy()));
            let eaten_len = (buf.len() - offset) - rem.len();
            //include_tree.offset_after_location(inc_start,
            //                                   inc_end as isize - inc_start as isize -
            //                                   eaten_len as isize);
            // println!("After offset");
            // println!("{}", include_tree);
            IncludeBounds::split_bounds(&mut bounds, inc_start, inc_end, eaten_len);
            bounds.extend_from_slice(&sub_bounds);
            bounds.sort();

            // println!("After split");
            // println!("{}", include_tree);

            buf = rem;
        } else {
            parse_linemarkers(buf, &mut bounds, buffer.len());
            // no more includes, just add the rest and return
            buffer.extend(buf);
            return Ok((buffer, bounds));
        }
    }
}

pub fn line_to_byte_offset<K, I>(bytes: I, line: usize) -> Result<usize, String>
    where K: Borrow<u8> + Eq,
          I: Iterator<Item = K>
{
    if line == 1 {
        Ok(0)
    } else {
        bytes.enumerate()
            .filter(|&(_, ref byte)| byte.borrow() == &b'\n')
            .nth(line - 2)
            .map(|(offset, _)| offset)
            .ok_or_else(|| "Failed converting from line to byte offset".to_string())
    }
}

pub fn byte_offset_to_line_col<K, I>(bytes: I, offset: usize) -> (usize, usize)
    where K: Borrow<u8> + Eq,
          I: Iterator<Item = K>
{
    let opt = bytes.enumerate()
        .filter(|&(off, ref byte)| off <= offset && byte.borrow() == &b'\n')
        .map(|(start, _)| start)
        .enumerate()
        .last();

    match opt {
        Some((line, start)) => (line + 2, offset - start),
        None => (1, offset + 1),
    }
}

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

    #[test]
    fn lines_to_bytes() {
        let string = "Howdy\nHow goes it\nI'm doing fine\n";
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 1).unwrap(),
                   0);
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 2).unwrap(),
                   5);
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 3).unwrap(),
                   17);
    }

    #[test]
    fn bytes_to_lines() {
        let string = "Howdy\nHow goes it\nI'm doing fine\n";
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 0),
                   (1, 1));
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 8),
                   (2, 3));
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 20),
                   (3, 3));
    }

    #[test]
    fn linemarker_no_flag() {
        let input = b"# 1 \"<built-in>\"\n";
        assert_eq!(
            parse_linemarker(input),
            IResult::Done(
                &b""[..],
                Linemarker {
                    child_line: 1,
                    path: PathBuf::from("<built-in>"),
                    flag: None,
                }
            )
        );
    }

    #[test]
    fn linemarker_flag() {
        let input = b"# 12 \"am33xx.dtsi\" 2\n";
        assert_eq!(
            parse_linemarker(input),
            IResult::Done(
                &b""[..],
                Linemarker {
                    child_line: 12,
                    path: PathBuf::from("am33xx.dtsi"),
                    flag: Some(LinemarkerFlag::Return),
                }
            )
        );
    }
}
<commit_msg>Fix a two off-by-one issues with offset <-> line, col conversion<commit_after>#[macro_use]
extern crate nom;

pub mod tree;
pub mod parser;

use std::str::{self, FromStr};
use std::path::{PathBuf, Path};
use std::fs::File;
use std::io::Read;
use std::cmp::Ordering;
use std::borrow::Borrow;

use nom::{IResult, ErrorKind, Needed, FindSubstring, digit, space, multispace, line_ending};

use parser::escape_c_string;

named!(pub parse_include<String>, preceded!(
    tag!("/include/"),
    preceded!( multispace,
        delimited!(
            char!('"'),
            escape_c_string,
            char!('"')
        ))
));

named!(pub find_include<(&[u8], String)>, do_parse!(
    pre: take_until!("/include/") >>
    path: parse_include >>
    (pre, path)
));

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct IncludeBounds {
    pub path: PathBuf,
    pub global_start: usize,
    pub child_start: usize,
    pub len: usize,
    pub method: IncludeMethod,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum IncludeMethod {
    DTS,
    CPP,
}

impl PartialOrd for IncludeBounds {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        Some(self.cmp(other))
    }
}

impl Ord for IncludeBounds {
    fn cmp(&self, other: &Self) -> Ordering {
        self.global_start.cmp(&other.global_start)
    }
}

impl IncludeBounds {
    pub fn global_end(&self) -> usize {
        self.global_start + self.len
    }

    pub fn split_bounds(bounds: &mut Vec<IncludeBounds>, start: usize, end: usize, offset: usize) {
        let mut remainders: Vec<IncludeBounds> = Vec::new();

        // println!("split s: {} e: {} off: {}", start, end, offset);

        for b in bounds.iter_mut() {
            // println!("g_start: {} g_end: {}", b.global_start, b.global_end());
            if b.global_start < start && b.global_end() >= start {
                // global_start -- start -- global_end
                let remainder = b.global_end() - start;

                // println!("remainder: {}", remainder);

                remainders.push(IncludeBounds {
                    path: b.path.clone(),
                    global_start: end,
                    child_start: b.child_start + start - b.global_start + offset,
                    len: remainder, // - offset,
                    method: b.method.clone(),
                });

                b.len = start - b.global_start;
            } else if b.global_start == start {
                // split is at begining of the bound
                // offset the start
                {
                    let offset = end - start;
                    b.global_start += offset;
                }
                // shrink the len by the offset
                b.len -= offset;
            }
        }

        bounds.extend_from_slice(&remainders);
        bounds.sort();
    }
}

#[derive(Debug, PartialEq)]
pub struct Linemarker {
    child_line: usize,
    path: PathBuf,
    flag: Option<LinemarkerFlag>,
}

#[derive(Debug, PartialEq)]
pub enum LinemarkerFlag {
    Start,
    Return,
    System,
    Extern,
}

named!(pub parse_linemarker<Linemarker>,
    complete!(do_parse!(
        tag!("#") >>
        opt!(tag!("line")) >>
        space >>
        line: map_res!(map_res!(digit, str::from_utf8), usize::from_str) >>
        space >>
        path: delimited!(
            char!('"'),
            map!(escape_c_string, PathBuf::from),
            char!('"')
        ) >>
        flag: opt!(preceded!(space, map_res!(map_res!(digit, str::from_utf8), u64::from_str))) >>
        line_ending >>
        (Linemarker {
            child_line: line,
            path: path,
            flag: flag.map(|f| match f {
                1 => LinemarkerFlag::Start,
                2 => LinemarkerFlag::Return,
                3 => LinemarkerFlag::System,
                4 => LinemarkerFlag::Extern,
                _ => unreachable!(),
            }),
        })
    ))
);

fn find_linemarker_start(input: &[u8]) -> IResult<&[u8], &[u8]> {
    if "# ".len() > input.len() {
        IResult::Incomplete(Needed::Size("# ".len()))
    } else {
        match input.find_substring("# ").iter().chain(input.find_substring("#line ").iter()).min() {
            None => {
                IResult::Error(error_position!(ErrorKind::TakeUntil, input))
            },
            Some(index) => {
                IResult::Done(&input[*index..], &input[0..*index])
            },
        }
    }
}

named!(find_linemarker<(&[u8], Linemarker)>, do_parse!(
    pre: find_linemarker_start >>
    marker: parse_linemarker >>
    (pre, marker)
));

fn parse_linemarkers(buf: &[u8], bounds: &mut Vec<IncludeBounds>, global_offset: usize) {
    let end_offset = global_offset + buf.len();
    // println!("{}", str::from_utf8(buf).unwrap());
    let mut buf = buf;
    println!("parsing linemarkers");
    loop {
        // look for linemarker
        if let IResult::Done(rem, (pre, marker)) = find_linemarker(buf) {
            // println!("{}", str::from_utf8(line).unwrap());
            // println!("{:?}", marker);
            // println!("pre.len() {}", pre.len());

            // double check that last bound was from a linemarker
            match bounds.last() {
                Some(&IncludeBounds { method: IncludeMethod::CPP, .. }) => {}
                _ => {
                    println!("{:#?}", bounds);
                    panic!("Linemarker found within DTS include")
                }
            }

            // end last
            bounds.last_mut().unwrap().len = pre.len();

            // start at new line
            let new_bound = IncludeBounds {
                path: marker.path.clone(),
                global_start: end_offset - rem.len(),
                child_start: File::open(&marker.path)
                                .map(|f| f.bytes().filter_map(|e| e.ok()))
                                .map(|b| line_to_byte_offset(b, marker.child_line).unwrap()) //TODO: unwraping is bad, SOK?
                                .unwrap_or(0),
                len: rem.len(),
                method: IncludeMethod::CPP,
            };

            bounds.push(new_bound);

            buf = rem;
        } else {
            return;
        }
    }
}

pub fn include_files(path: &Path,
                     main_offset: usize)
                     -> Result<(Vec<u8>, Vec<IncludeBounds>), String> {
    // TODO: check from parent directory of root file
    let mut file = File::open(path).unwrap();
    let mut buffer: Vec<u8> = Vec::new();
    let mut bounds: Vec<IncludeBounds> = Vec::new();

    let mut string_buffer = String::new();
    file.read_to_string(&mut string_buffer).map_err(|_| "IO Error".to_string())?;

    let mut buf = string_buffer.as_bytes();

    named!(first_linemarker<(&[u8], Linemarker)>,
        do_parse!(
            marker: peek!(parse_linemarker) >>
            line: recognize!(parse_linemarker) >>
            (line, marker)
        )
    );

    let start_bound = if let IResult::Done(rem, (line, marker)) = first_linemarker(buf) {
        let bound = IncludeBounds {
            path: marker.path.clone(),
            global_start: buf.len() - rem.len(),
            // TODO: check from parent directory of root file
            child_start: File::open(&marker.path)
                             .map(|f| f.bytes().filter_map(|e| e.ok()))
                             .map(|b| line_to_byte_offset(b, marker.child_line).unwrap()) //TODO: unwraping is bad, SOK?
                             .unwrap_or(0),
            len: File::open(&marker.path).unwrap().bytes().count(),
            method: IncludeMethod::CPP,
        };

        buffer.extend_from_slice(line);
        buf = rem;

        bound
    } else {
        // println!("main_offset {}", main_offset);
        IncludeBounds {
            path: path.to_path_buf(),
            global_start: main_offset,
            child_start: 0,
            // TODO: check from parent directory of root file
            len: File::open(path).unwrap().bytes().count(),
            method: IncludeMethod::DTS,
        }
    };
    bounds.push(start_bound);

    loop {
        // go until /include/
        if let IResult::Done(rem, (pre, file)) = find_include(&buf[..]) {
            parse_linemarkers(pre, &mut bounds, buffer.len());
            buffer.extend_from_slice(pre);

            let offset = pre.len();
            // println!("{}", file);
            // println!("Offset: {}", offset);
            // println!("{}", include_tree);

            let included_path = Path::new(&file);
            let total_len = buffer.len() + main_offset; // - 1;
            let (sub_buf, sub_bounds) = include_files(included_path, total_len)?;
            buffer.extend(sub_buf);

            let inc_start = sub_bounds.first()
                                      .map(|b| b.global_start)
                                      .expect(&format!("No bounds returned: {}",
                                                      included_path.to_string_lossy()));
            let inc_end = sub_bounds.last()
                                    .map(|b| b.global_end())
                                    .expect(&format!("No bounds returned: {}",
                                                    included_path.to_string_lossy()));
            let eaten_len = (buf.len() - offset) - rem.len();
            //include_tree.offset_after_location(inc_start,
            //                                   inc_end as isize - inc_start as isize -
            //                                   eaten_len as isize);
            // println!("After offset");
            // println!("{}", include_tree);
            IncludeBounds::split_bounds(&mut bounds, inc_start, inc_end, eaten_len);
            bounds.extend_from_slice(&sub_bounds);
            bounds.sort();

            // println!("After split");
            // println!("{}", include_tree);

            buf = rem;
        } else {
            parse_linemarkers(buf, &mut bounds, buffer.len());
            // no more includes, just add the rest and return
            buffer.extend(buf);
            return Ok((buffer, bounds));
        }
    }
}

pub fn line_to_byte_offset<K, I>(bytes: I, line: usize) -> Result<usize, String>
    where K: Borrow<u8> + Eq,
          I: Iterator<Item = K>
{
    if line == 1 {
        Ok(0)
    } else {
        bytes.enumerate()
            .filter(|&(_, ref byte)| byte.borrow() == &b'\n')
            .nth(line - 2)
            .map(|(offset, _)| offset + 1)
            .ok_or_else(|| "Failed converting from line to byte offset".to_string())
    }
}

pub fn byte_offset_to_line_col<K, I>(bytes: I, offset: usize) -> (usize, usize)
    where K: Borrow<u8> + Eq,
          I: Iterator<Item = K>
{
    let opt = bytes.enumerate()
        .filter(|&(off, ref byte)| off < offset && byte.borrow() == &b'\n')
        .map(|(start, _)| start)
        .enumerate()
        .last();

    match opt {
        Some((line, start)) => (line + 2, offset - start),
        None => (1, offset + 1),
    }
}

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

    #[test]
    fn lines_to_bytes() {
        let string = "Howdy\nHow goes it\n\nI'm doing fine\n";
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 1).unwrap(),
                   0);
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 2).unwrap(),
                   6);
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 3).unwrap(),
                   18);
        assert_eq!(line_to_byte_offset(string.as_bytes().iter(), 4).unwrap(),
                   19);
    }

    #[test]
    fn bytes_to_lines() {
        let string = "Howdy\nHow goes it\n\nI'm doing fine\n";
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 0),
                   (1, 1));
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 8),
                   (2, 3));
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 20),
                   (4, 2));
        assert_eq!(byte_offset_to_line_col(string.as_bytes().iter(), 18),
                   (3, 1));
    }

    #[test]
    fn linemarker_no_flag() {
        let input = b"# 1 \"<built-in>\"\n";
        assert_eq!(
            parse_linemarker(input),
            IResult::Done(
                &b""[..],
                Linemarker {
                    child_line: 1,
                    path: PathBuf::from("<built-in>"),
                    flag: None,
                }
            )
        );
    }

    #[test]
    fn linemarker_flag() {
        let input = b"# 12 \"am33xx.dtsi\" 2\n";
        assert_eq!(
            parse_linemarker(input),
            IResult::Done(
                &b""[..],
                Linemarker {
                    child_line: 12,
                    path: PathBuf::from("am33xx.dtsi"),
                    flag: Some(LinemarkerFlag::Return),
                }
            )
        );
    }
}
<|endoftext|>


<commit_before>use std::env;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::str;

use label;
use rustfmt_lib;

fn main() {
    // Gather all command line and environment settings
    let options = parse_args();

    // Gather a list of all formattable targets
    let targets = query_rustfmt_targets(&options);

    // Run rustfmt on these targets
    apply_rustfmt(&options, &targets);
}

/// Perform a `bazel` query to determine a list of Bazel targets which are to be formatted.
fn query_rustfmt_targets(options: &Config) -> Vec<String> {
    // Determine what packages to query
    let scope = match options.packages.is_empty() {
        true => "//...:all".to_owned(),
        false => {
            // Check to see if all the provided packages are actually targets
            let is_all_targets = options
                .packages
                .iter()
                .all(|pkg| match label::analyze(pkg) {
                    Ok(tgt) => tgt.name != "all",
                    Err(_) => false,
                });

            // Early return if a list of targets and not packages were provided
            if is_all_targets {
                return options.packages.clone();
            }

            options.packages.join(" + ")
        }
    };

    let query_args = vec![
        "query".to_owned(),
        format!(
            r#"kind('{types}', {scope}) except attr(tags, 'norustfmt', kind('{types}', {scope}))"#,
            types = "^rust_",
            scope = scope
        ),
    ];

    let child = Command::new(&options.bazel)
        .current_dir(&options.workspace)
        .args(query_args)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn bazel query command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        std::process::exit(output.status.code().unwrap_or(1));
    }

    str::from_utf8(&output.stdout)
        .expect("Invalid stream from command")
        .split("\n")
        .filter(|line| !line.is_empty())
        .map(|line| line.to_string())
        .collect()
}

/// Build a list of Bazel targets using the `rustfmt_aspect` to produce the
/// arguments to use when formatting the sources of those targets.
fn generate_rustfmt_target_manifests(options: &Config, targets: &Vec<String>) {
    let build_args = vec![
        "build",
        "--aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect",
        "--output_groups=rustfmt_manifest",
    ];

    let child = Command::new(&options.bazel)
        .current_dir(&options.workspace)
        .args(build_args)
        .args(targets)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        std::process::exit(output.status.code().unwrap_or(1));
    }
}

/// Run rustfmt on a set of Bazel targets
fn apply_rustfmt(options: &Config, targets: &Vec<String>) {
    // Ensure the targets are first built and a manifest containing `rustfmt`
    // arguments are generated before formatting source files.
    generate_rustfmt_target_manifests(&options, &targets);

    for target in targets.iter() {
        // Replace any `:` characters and strip leading slashes
        let target_path = target.replace(":", "/").trim_start_matches("/").to_owned();

        // Find a manifest for the current target. Not all targets will have one
        let manifest = options.workspace.join("bazel-bin").join(format!(
            "{}.{}",
            &target_path,
            rustfmt_lib::RUSTFMT_MANIFEST_EXTENSION,
        ));

        if !manifest.exists() {
            continue;
        }

        // Load the manifest containing rustfmt arguments
        let rustfmt_config = rustfmt_lib::parse_rustfmt_manifest(&manifest);

        // Ignore any targets which do not have source files. This can
        // occur in cases where all source files are generated.
        if rustfmt_config.sources.is_empty() {
            continue;
        }

        // Run rustfmt
        let status = Command::new(&options.rustfmt_config.rustfmt)
            .current_dir(&options.workspace)
            .arg("--edition")
            .arg(rustfmt_config.edition)
            .arg("--config-path")
            .arg(&options.rustfmt_config.config)
            .args(rustfmt_config.sources)
            .status()
            .expect("Failed to run rustfmt");

        if !status.success() {
            std::process::exit(status.code().unwrap_or(1));
        }
    }
}

/// A struct containing details used for executing rustfmt.
#[derive(Debug)]
struct Config {
    /// The path of the Bazel workspace root.
    pub workspace: PathBuf,

    /// The Bazel executable to use for builds and queries.
    pub bazel: PathBuf,

    /// Information about the current rustfmt binary to run.
    pub rustfmt_config: rustfmt_lib::RustfmtConfig,

    /// Optionally, users can pass a list of targets/packages/scopes
    /// (eg `//my:target` or `//my/pkg/...`) to control the targets
    /// to be formatted. If empty, all targets in the workspace will
    /// be formatted.
    pub packages: Vec<String>,
}

/// Parse command line arguments and environment variables to
/// produce config data for running rustfmt.
fn parse_args() -> Config {
    Config{
        workspace: PathBuf::from(
            env::var("BUILD_WORKSPACE_DIRECTORY")
            .expect("The environment variable BUILD_WORKSPACE_DIRECTORY is required for finding the workspace root")
        ),
        bazel: PathBuf::from(
            env::var("BAZEL_REAL")
            .unwrap_or_else(|_| "bazel".to_owned())
        ),
        rustfmt_config: rustfmt_lib::parse_rustfmt_config(),
        packages: env::args().skip(1).collect(),
    }
}
<commit_msg>Fix clippy warnings for //tools/rustfmt (#745)<commit_after>use std::env;
use std::path::PathBuf;
use std::process::{Command, Stdio};
use std::str;

fn main() {
    // Gather all command line and environment settings
    let options = parse_args();

    // Gather a list of all formattable targets
    let targets = query_rustfmt_targets(&options);

    // Run rustfmt on these targets
    apply_rustfmt(&options, &targets);
}

/// Perform a `bazel` query to determine a list of Bazel targets which are to be formatted.
fn query_rustfmt_targets(options: &Config) -> Vec<String> {
    // Determine what packages to query
    let scope = match options.packages.is_empty() {
        true => "//...:all".to_owned(),
        false => {
            // Check to see if all the provided packages are actually targets
            let is_all_targets = options
                .packages
                .iter()
                .all(|pkg| match label::analyze(pkg) {
                    Ok(tgt) => tgt.name != "all",
                    Err(_) => false,
                });

            // Early return if a list of targets and not packages were provided
            if is_all_targets {
                return options.packages.clone();
            }

            options.packages.join(" + ")
        }
    };

    let query_args = vec![
        "query".to_owned(),
        format!(
            r#"kind('{types}', {scope}) except attr(tags, 'norustfmt', kind('{types}', {scope}))"#,
            types = "^rust_",
            scope = scope
        ),
    ];

    let child = Command::new(&options.bazel)
        .current_dir(&options.workspace)
        .args(query_args)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn bazel query command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        std::process::exit(output.status.code().unwrap_or(1));
    }

    str::from_utf8(&output.stdout)
        .expect("Invalid stream from command")
        .split('\n')
        .filter(|line| !line.is_empty())
        .map(|line| line.to_string())
        .collect()
}

/// Build a list of Bazel targets using the `rustfmt_aspect` to produce the
/// arguments to use when formatting the sources of those targets.
fn generate_rustfmt_target_manifests(options: &Config, targets: &[String]) {
    let build_args = vec![
        "build",
        "--aspects=@rules_rust//rust:defs.bzl%rustfmt_aspect",
        "--output_groups=rustfmt_manifest",
    ];

    let child = Command::new(&options.bazel)
        .current_dir(&options.workspace)
        .args(build_args)
        .args(targets)
        .stdout(Stdio::piped())
        .stderr(Stdio::inherit())
        .spawn()
        .expect("Failed to spawn command");

    let output = child
        .wait_with_output()
        .expect("Failed to wait on spawned command");

    if !output.status.success() {
        std::process::exit(output.status.code().unwrap_or(1));
    }
}

/// Run rustfmt on a set of Bazel targets
fn apply_rustfmt(options: &Config, targets: &[String]) {
    // Ensure the targets are first built and a manifest containing `rustfmt`
    // arguments are generated before formatting source files.
    generate_rustfmt_target_manifests(&options, &targets);

    for target in targets.iter() {
        // Replace any `:` characters and strip leading slashes
        let target_path = target.replace(":", "/").trim_start_matches('/').to_owned();

        // Find a manifest for the current target. Not all targets will have one
        let manifest = options.workspace.join("bazel-bin").join(format!(
            "{}.{}",
            &target_path,
            rustfmt_lib::RUSTFMT_MANIFEST_EXTENSION,
        ));

        if !manifest.exists() {
            continue;
        }

        // Load the manifest containing rustfmt arguments
        let rustfmt_config = rustfmt_lib::parse_rustfmt_manifest(&manifest);

        // Ignore any targets which do not have source files. This can
        // occur in cases where all source files are generated.
        if rustfmt_config.sources.is_empty() {
            continue;
        }

        // Run rustfmt
        let status = Command::new(&options.rustfmt_config.rustfmt)
            .current_dir(&options.workspace)
            .arg("--edition")
            .arg(rustfmt_config.edition)
            .arg("--config-path")
            .arg(&options.rustfmt_config.config)
            .args(rustfmt_config.sources)
            .status()
            .expect("Failed to run rustfmt");

        if !status.success() {
            std::process::exit(status.code().unwrap_or(1));
        }
    }
}

/// A struct containing details used for executing rustfmt.
#[derive(Debug)]
struct Config {
    /// The path of the Bazel workspace root.
    pub workspace: PathBuf,

    /// The Bazel executable to use for builds and queries.
    pub bazel: PathBuf,

    /// Information about the current rustfmt binary to run.
    pub rustfmt_config: rustfmt_lib::RustfmtConfig,

    /// Optionally, users can pass a list of targets/packages/scopes
    /// (eg `//my:target` or `//my/pkg/...`) to control the targets
    /// to be formatted. If empty, all targets in the workspace will
    /// be formatted.
    pub packages: Vec<String>,
}

/// Parse command line arguments and environment variables to
/// produce config data for running rustfmt.
fn parse_args() -> Config {
    Config{
        workspace: PathBuf::from(
            env::var("BUILD_WORKSPACE_DIRECTORY")
            .expect("The environment variable BUILD_WORKSPACE_DIRECTORY is required for finding the workspace root")
        ),
        bazel: PathBuf::from(
            env::var("BAZEL_REAL")
            .unwrap_or_else(|_| "bazel".to_owned())
        ),
        rustfmt_config: rustfmt_lib::parse_rustfmt_config(),
        packages: env::args().skip(1).collect(),
    }
}
<|endoftext|>


<commit_before>use std::ops::{Add, Sub, Mul};

macro_rules! wrapping_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, v: &Self) -> Self {
                <$t>::$method(*self, *v)
            }
        }
    };
    ($trait_name:ident, $method:ident, $t:ty, $rhs:ty) => {
        impl $trait_name<$rhs> for $t {
            #[inline]
            fn $method(&self, v: &$rhs) -> Self {
                <$t>::$method(*self, *v)
            }
        }
    }
}

/// Performs addition that wrapps around on overflow.
pub trait WrappingAdd: Sized + Add<Self, Output=Self> {
    /// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of
    /// the type.
    fn wrapping_add(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingAdd, wrapping_add, u8);
wrapping_impl!(WrappingAdd, wrapping_add, u16);
wrapping_impl!(WrappingAdd, wrapping_add, u32);
wrapping_impl!(WrappingAdd, wrapping_add, u64);
wrapping_impl!(WrappingAdd, wrapping_add, usize);

wrapping_impl!(WrappingAdd, wrapping_add, i8);
wrapping_impl!(WrappingAdd, wrapping_add, i16);
wrapping_impl!(WrappingAdd, wrapping_add, i32);
wrapping_impl!(WrappingAdd, wrapping_add, i64);
wrapping_impl!(WrappingAdd, wrapping_add, isize);

/// Performs subtraction that wrapps around on overflow.
pub trait WrappingSub: Sized + Sub<Self, Output=Self> {
    /// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary
    /// of the type.
    fn wrapping_sub(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingSub, wrapping_sub, u8);
wrapping_impl!(WrappingSub, wrapping_sub, u16);
wrapping_impl!(WrappingSub, wrapping_sub, u32);
wrapping_impl!(WrappingSub, wrapping_sub, u64);
wrapping_impl!(WrappingSub, wrapping_sub, usize);

wrapping_impl!(WrappingSub, wrapping_sub, i8);
wrapping_impl!(WrappingSub, wrapping_sub, i16);
wrapping_impl!(WrappingSub, wrapping_sub, i32);
wrapping_impl!(WrappingSub, wrapping_sub, i64);
wrapping_impl!(WrappingSub, wrapping_sub, isize);

/// Performs multiplication that wrapps around on overflow.
pub trait WrappingMul: Sized + Mul<Self, Output=Self> {
    /// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary
    /// of the type.
    fn wrapping_mul(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingMul, wrapping_mul, u8);
wrapping_impl!(WrappingMul, wrapping_mul, u16);
wrapping_impl!(WrappingMul, wrapping_mul, u32);
wrapping_impl!(WrappingMul, wrapping_mul, u64);
wrapping_impl!(WrappingMul, wrapping_mul, usize);

wrapping_impl!(WrappingMul, wrapping_mul, i8);
wrapping_impl!(WrappingMul, wrapping_mul, i16);
wrapping_impl!(WrappingMul, wrapping_mul, i32);
wrapping_impl!(WrappingMul, wrapping_mul, i64);
wrapping_impl!(WrappingMul, wrapping_mul, isize);
<commit_msg>Correct typos.<commit_after>use std::ops::{Add, Sub, Mul};

macro_rules! wrapping_impl {
    ($trait_name:ident, $method:ident, $t:ty) => {
        impl $trait_name for $t {
            #[inline]
            fn $method(&self, v: &Self) -> Self {
                <$t>::$method(*self, *v)
            }
        }
    };
    ($trait_name:ident, $method:ident, $t:ty, $rhs:ty) => {
        impl $trait_name<$rhs> for $t {
            #[inline]
            fn $method(&self, v: &$rhs) -> Self {
                <$t>::$method(*self, *v)
            }
        }
    }
}

/// Performs addition that wraps around on overflow.
pub trait WrappingAdd: Sized + Add<Self, Output=Self> {
    /// Wrapping (modular) addition. Computes `self + other`, wrapping around at the boundary of
    /// the type.
    fn wrapping_add(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingAdd, wrapping_add, u8);
wrapping_impl!(WrappingAdd, wrapping_add, u16);
wrapping_impl!(WrappingAdd, wrapping_add, u32);
wrapping_impl!(WrappingAdd, wrapping_add, u64);
wrapping_impl!(WrappingAdd, wrapping_add, usize);

wrapping_impl!(WrappingAdd, wrapping_add, i8);
wrapping_impl!(WrappingAdd, wrapping_add, i16);
wrapping_impl!(WrappingAdd, wrapping_add, i32);
wrapping_impl!(WrappingAdd, wrapping_add, i64);
wrapping_impl!(WrappingAdd, wrapping_add, isize);

/// Performs subtraction that wraps around on overflow.
pub trait WrappingSub: Sized + Sub<Self, Output=Self> {
    /// Wrapping (modular) subtraction. Computes `self - other`, wrapping around at the boundary
    /// of the type.
    fn wrapping_sub(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingSub, wrapping_sub, u8);
wrapping_impl!(WrappingSub, wrapping_sub, u16);
wrapping_impl!(WrappingSub, wrapping_sub, u32);
wrapping_impl!(WrappingSub, wrapping_sub, u64);
wrapping_impl!(WrappingSub, wrapping_sub, usize);

wrapping_impl!(WrappingSub, wrapping_sub, i8);
wrapping_impl!(WrappingSub, wrapping_sub, i16);
wrapping_impl!(WrappingSub, wrapping_sub, i32);
wrapping_impl!(WrappingSub, wrapping_sub, i64);
wrapping_impl!(WrappingSub, wrapping_sub, isize);

/// Performs multiplication that wraps around on overflow.
pub trait WrappingMul: Sized + Mul<Self, Output=Self> {
    /// Wrapping (modular) multiplication. Computes `self * other`, wrapping around at the boundary
    /// of the type.
    fn wrapping_mul(&self, v: &Self) -> Self;
}

wrapping_impl!(WrappingMul, wrapping_mul, u8);
wrapping_impl!(WrappingMul, wrapping_mul, u16);
wrapping_impl!(WrappingMul, wrapping_mul, u32);
wrapping_impl!(WrappingMul, wrapping_mul, u64);
wrapping_impl!(WrappingMul, wrapping_mul, usize);

wrapping_impl!(WrappingMul, wrapping_mul, i8);
wrapping_impl!(WrappingMul, wrapping_mul, i16);
wrapping_impl!(WrappingMul, wrapping_mul, i32);
wrapping_impl!(WrappingMul, wrapping_mul, i64);
wrapping_impl!(WrappingMul, wrapping_mul, isize);
<|endoftext|>


<commit_before>//! This sub module contains extra structs used as widget properties.

use std::{collections::HashSet, fmt::Debug, path::PathBuf};

use dces::prelude::{Component, ComponentStore, Entity};

pub use self::layout::*;
pub use self::widget::*;
use crate::{into_property_source, render, theming, utils};

mod layout;
mod widget;

/// Get the property of a widget.
pub fn get_property<T>(key: &str, entity: Entity, store: &ComponentStore) -> T
where
    T: Clone + Component,
{
    store.get::<T>(key, entity).map(|r| r.clone()).unwrap()
}

/// Handles the key of a given entity.
/// It returns its property, if it does exits. If not, it returns the value of the key.
pub fn get_property_or_value<T>(key: &str, entity: Entity, store: &ComponentStore, value: T) -> T
where
    T: Clone + Component,
{
    if let Ok(property) = store.get::<T>(key, entity).map(|r| r.clone()) {
        return property;
    }
    value
}

/// Use to build a property or to share it.
#[derive(PartialEq, Debug)]
pub enum PropertySource
 {
    Source(Entity),
    KeySource(String, Entity),
    Value(P),
}

impl
 From<Entity> for PropertySource
 {
    fn from(entity: Entity) -> Self {
        PropertySource::Source(entity)
    }
}

/// Used to convert components / properties into a PropertySource object.
pub trait IntoPropertySource
 {
    fn into_source(self) -> PropertySource
;
}

/// Used ot generate attached properties.
pub struct AttachedProperty

where
    P: Component + Debug,
{
    pub key: String,
    pub property_source: PropertySource
,
}

impl
 AttachedProperty

where
    P: Component + Debug,
{
    /// Create a new attached property.
    pub fn new(key: impl Into<String>, property_source: impl IntoPropertySource
) -> Self {
        AttachedProperty {
            key: key.into(),
            property_source: property_source.into_source(),
        }
    }
}

// Implementation of PropertySource for default types
into_property_source!(bool);
into_property_source!(char);
into_property_source!(String: &str, utils::Value);
into_property_source!(usize);
into_property_source!(u32);
into_property_source!(f32: utils::Value);
into_property_source!(f64: i32, f32, utils::Value);
into_property_source!(i32);
into_property_source!(i64);

into_property_source!(PathBuf);

// Implementation of PropertySource for utils types
into_property_source!(utils::Alignment: &str);
into_property_source!(utils::Brush: &str, utils::Color, utils::Value);
into_property_source!(utils::Orientation: &str);
into_property_source!(utils::Point: f64, i32, (i32, i32), (f64, f64));
into_property_source!(utils::Size: f64, i32, (i32, i32), (f64, f64));
into_property_source!(
    utils::Rectangle: (i32, i32, i32, i32),
    (f64, f64, f64, f64),
    (utils::Point, utils::Size)
);
into_property_source!(
    utils::Thickness: i32,
    f64,
    (i32, i32),
    (f64, f64),
    (i32, i32, i32, i32),
    (f64, f64, f64, f64),
    utils::Value
);
into_property_source!(utils::SelectionMode: &str);
into_property_source!(utils::Visibility: &str);
into_property_source!(Vec<String>);
into_property_source!(utils::Filter: &str, String, Vec<String>, Vec<&str>);

// Implementation of css types
into_property_source!(theming::Selector: &str, String);
into_property_source!(theming::Theme);

// Implementation of render property types
into_property_source!(render::Image: &str, String, (u32, u32, Vec<u32>));

// Implementation of custom property types
into_property_source!(Blocks: BlocksBuilder, &str, String);
into_property_source!(utils::Constraint: utils::ConstraintBuilder);
into_property_source!(DefaultRenderPipeline);
into_property_source!(ScrollViewerMode: (&str, &str));
into_property_source!(SelectedEntities: HashSet<Entity>);
into_property_source!(SelectedIndices: HashSet<usize>);
into_property_source!(TextSelection: (usize, usize));
into_property_source!(FocusState);
into_property_source!(KeyboardState);
<commit_msg>orbtk_core: update mod documentation<commit_after>//! This sub module contains extra structs used as widget properties.

use std::{collections::HashSet, fmt::Debug, path::PathBuf};

use dces::prelude::{Component, ComponentStore, Entity};

pub use self::layout::*;
pub use self::widget::*;
use crate::{into_property_source, render, theming, utils};

mod layout;
mod widget;

/// Get the property of a widget.
pub fn get_property<T>(key: &str, entity: Entity, store: &ComponentStore) -> T
where
    T: Clone + Component,
{
    store.get::<T>(key, entity).map(|r| r.clone()).unwrap()
}

/// Handles the key of a given entity.
/// It returns its property, if it does exits. If not, it returns the value of the key.
pub fn get_property_or_value<T>(key: &str, entity: Entity, store: &ComponentStore, value: T) -> T
where
    T: Clone + Component,
{
    if let Ok(property) = store.get::<T>(key, entity).map(|r| r.clone()) {
        return property;
    }
    value
}

/// Used to build or share a property.
#[derive(PartialEq, Debug)]
pub enum PropertySource
 {
    Source(Entity),
    KeySource(String, Entity),
    Value(P),
}

impl
 From<Entity> for PropertySource
 {
    fn from(entity: Entity) -> Self {
        PropertySource::Source(entity)
    }
}

/// Used to convert components / properties into a PropertySource object.
pub trait IntoPropertySource
 {
    fn into_source(self) -> PropertySource
;
}

/// Used ot generate attached properties.
pub struct AttachedProperty

where
    P: Component + Debug,
{
    pub key: String,
    pub property_source: PropertySource
,
}

impl
 AttachedProperty

where
    P: Component + Debug,
{
    /// Create a new attached property.
    pub fn new(key: impl Into<String>, property_source: impl IntoPropertySource
) -> Self {
        AttachedProperty {
            key: key.into(),
            property_source: property_source.into_source(),
        }
    }
}

// Implementation of PropertySource for default types
into_property_source!(bool);
into_property_source!(char);
into_property_source!(String: &str, utils::Value);
into_property_source!(usize);
into_property_source!(u32);
into_property_source!(f32: utils::Value);
into_property_source!(f64: i32, f32, utils::Value);
into_property_source!(i32);
into_property_source!(i64);

into_property_source!(PathBuf);

// Implementation of PropertySource for utils types
into_property_source!(utils::Alignment: &str);
into_property_source!(utils::Brush: &str, utils::Color, utils::Value);
into_property_source!(utils::Orientation: &str);
into_property_source!(utils::Point: f64, i32, (i32, i32), (f64, f64));
into_property_source!(utils::Size: f64, i32, (i32, i32), (f64, f64));
into_property_source!(
    utils::Rectangle: (i32, i32, i32, i32),
    (f64, f64, f64, f64),
    (utils::Point, utils::Size)
);
into_property_source!(
    utils::Thickness: i32,
    f64,
    (i32, i32),
    (f64, f64),
    (i32, i32, i32, i32),
    (f64, f64, f64, f64),
    utils::Value
);
into_property_source!(utils::SelectionMode: &str);
into_property_source!(utils::Visibility: &str);
into_property_source!(Vec<String>);
into_property_source!(utils::Filter: &str, String, Vec<String>, Vec<&str>);

// Implementation of css types
into_property_source!(theming::Selector: &str, String);
into_property_source!(theming::Theme);

// Implementation of render property types
into_property_source!(render::Image: &str, String, (u32, u32, Vec<u32>));

// Implementation of custom property types
into_property_source!(Blocks: BlocksBuilder, &str, String);
into_property_source!(utils::Constraint: utils::ConstraintBuilder);
into_property_source!(DefaultRenderPipeline);
into_property_source!(ScrollViewerMode: (&str, &str));
into_property_source!(SelectedEntities: HashSet<Entity>);
into_property_source!(SelectedIndices: HashSet<usize>);
into_property_source!(TextSelection: (usize, usize));
into_property_source!(FocusState);
into_property_source!(KeyboardState);
<|endoftext|>