<commit_before>//! FIXME: write short doc here

use ra_syntax::{ast, SyntaxKind};
use test_utils::mark;

use crate::completion::{
    CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
};

pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    // complete keyword "crate" in use stmt
    let source_range = ctx.source_range();
    match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
        (Some(_), None) => {
            CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("crate::")
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "self")
                .kind(CompletionItemKind::Keyword)
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("super::")
                .add_to(acc);
        }
        (Some(_), Some(_)) => {
            CompletionItem::new(CompletionKind::Keyword, source_range, "self")
                .kind(CompletionItemKind::Keyword)
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("super::")
                .add_to(acc);
        }
        _ => {}
    }

    // Suggest .await syntax for types that implement Future trait
    if let Some(receiver) = &ctx.dot_receiver {
        if let Some(ty) = ctx.sema.type_of_expr(receiver) {
            if ty.impls_future(ctx.db) {
                CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
                    .kind(CompletionItemKind::Keyword)
                    .detail("expr.await")
                    .insert_text("await")
                    .add_to(acc);
            }
        };
    }
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.token.kind() == SyntaxKind::COMMENT {
        mark::hit!(no_keyword_completion_in_comments);
        return;
    }

    let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
    if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
        add_keyword(ctx, acc, "where", "where ");
        return;
    }
    if ctx.unsafe_is_prev {
        if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
            add_keyword(ctx, acc, "fn", "fn $0() {}")
        }

        if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
            || ctx.block_expr_parent
        {
            add_keyword(ctx, acc, "trait", "trait $0 {}");
            add_keyword(ctx, acc, "impl", "impl $0 {}");
        }

        return;
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
        add_keyword(ctx, acc, "fn", "fn $0() {}");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "use", "use ");
        add_keyword(ctx, acc, "impl", "impl $0 {}");
        add_keyword(ctx, acc, "trait", "trait $0 {}");
    }

    if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
        add_keyword(ctx, acc, "enum", "enum $0 {}");
        add_keyword(ctx, acc, "struct", "struct $0 {}");
        add_keyword(ctx, acc, "union", "union $0 {}");
    }

    if ctx.can_be_expr {
        add_keyword(ctx, acc, "match", "match $0 {}");
        add_keyword(ctx, acc, "while", "while $0 {}");
        add_keyword(ctx, acc, "loop", "loop {$0}");
        add_keyword(ctx, acc, "if", "if ");
        add_keyword(ctx, acc, "if let", "if let ");
    }

    if ctx.if_is_prev || ctx.block_expr_parent {
        add_keyword(ctx, acc, "let", "let ");
    }

    if ctx.after_if {
        add_keyword(ctx, acc, "else", "else {$0}");
        add_keyword(ctx, acc, "else if", "else if $0 {}");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "mod", "mod $0 {}");
    }
    if ctx.bind_pat_parent || ctx.ref_pat_parent {
        add_keyword(ctx, acc, "mut", "mut ");
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
        add_keyword(ctx, acc, "const", "const ");
        add_keyword(ctx, acc, "type", "type ");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "static", "static ");
    };
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "extern", "extern ");
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
        add_keyword(ctx, acc, "unsafe", "unsafe ");
    }
    if ctx.in_loop_body {
        if ctx.can_be_stmt {
            add_keyword(ctx, acc, "continue", "continue;");
            add_keyword(ctx, acc, "break", "break;");
        } else {
            add_keyword(ctx, acc, "continue", "continue");
            add_keyword(ctx, acc, "break", "break");
        }
    }
    if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
        add_keyword(ctx, acc, "pub", "pub ")
    }

    if !ctx.is_trivial_path {
        return;
    }
    let fn_def = match &ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
}

fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
    let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
        .kind(CompletionItemKind::Keyword);

    match ctx.config.snippet_cap {
        Some(cap) => res.insert_snippet(cap, snippet),
        _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
    }
    .build()
}

fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
    acc.add(keyword(ctx, kw, snippet));
}

fn complete_return(
    ctx: &CompletionContext,
    fn_def: &ast::FnDef,
    can_be_stmt: bool,
) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword(ctx, "return", snip))
}

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

    use crate::completion::{
        test_utils::{check_edit, completion_list},
        CompletionKind,
    };
    use test_utils::mark;

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

    #[test]
    fn test_keywords_in_use_stmt() {
        check(
            r"use <|>",
            expect![[r#"
                kw crate::
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::<|>",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::{b, <|>}",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );
    }

    #[test]
    fn test_keywords_at_source_file_level() {
        check(
            r"m<|>",
            expect![[r#"
                kw const
                kw enum
                kw extern
                kw fn
                kw impl
                kw mod
                kw pub
                kw static
                kw struct
                kw trait
                kw type
                kw union
                kw unsafe
                kw use
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_function() {
        check(
            r"fn quux() { <|> }",
            expect![[r#"
                kw const
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_inside_block() {
        check(
            r"fn quux() { if true { <|> } }",
            expect![[r#"
                kw const
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_if() {
        check(
            r#"fn quux() { if true { () } <|> }"#,
            expect![[r#"
                kw const
                kw else
                kw else if
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
        check_edit(
            "else",
            r#"fn quux() { if true { () } <|> }"#,
            r#"fn quux() { if true { () } else {$0} }"#,
        );
    }

    #[test]
    fn test_keywords_in_match_arm() {
        check(
            r#"
fn quux() -> i32 {
    match () { () => <|> }
}
"#,
            expect![[r#"
                kw if
                kw if let
                kw loop
                kw match
                kw return
                kw unsafe
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_trait_def() {
        check(
            r"trait My { <|> }",
            expect![[r#"
                kw const
                kw fn
                kw type
                kw unsafe
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_impl_def() {
        check(
            r"impl My { <|> }",
            expect![[r#"
                kw const
                kw fn
                kw pub
                kw type
                kw unsafe
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_loop() {
        check(
            r"fn my() { loop { <|> } }",
            expect![[r#"
                kw break
                kw const
                kw continue
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_unsafe_in_item_list() {
        check(
            r"unsafe <|>",
            expect![[r#"
                kw fn
                kw impl
                kw trait
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_unsafe_in_block_expr() {
        check(
            r"fn my_fn() { unsafe <|> }",
            expect![[r#"
                kw fn
                kw impl
                kw trait
            "#]],
        );
    }

    #[test]
    fn test_mut_in_ref_and_in_fn_parameters_list() {
        check(
            r"fn my_fn(&<|>) {}",
            expect![[r#"
                kw mut
            "#]],
        );
        check(
            r"fn my_fn(<|>) {}",
            expect![[r#"
                kw mut
            "#]],
        );
        check(
            r"fn my_fn() { let &<|> }",
            expect![[r#"
                kw mut
            "#]],
        );
    }

    #[test]
    fn test_where_keyword() {
        check(
            r"trait A <|>",
            expect![[r#"
                kw where
            "#]],
        );
        check(
            r"impl A <|>",
            expect![[r#"
                kw where
            "#]],
        );
    }

    #[test]
    fn no_keyword_completion_in_comments() {
        mark::check!(no_keyword_completion_in_comments);
        check(
            r#"
fn test() {
    let x = 2; // A comment<|>
}
"#,
            expect![[""]],
        );
        check(
            r#"
/*
Some multi-line comment<|>
*/
"#,
            expect![[""]],
        );
        check(
            r#"
/// Some doc comment
/// let test<|> = 1
"#,
            expect![[""]],
        );
    }

    #[test]
    fn test_completion_await_impls_future() {
        check(
            r#"
//- /main.rs
use std::future::*;
struct A {}
impl Future for A {}
fn foo(a: A) { a.<|> }

//- /std/lib.rs
pub mod future {
    #[lang = "future_trait"]
    pub trait Future {}
}
"#,
            expect![[r#"
                kw await expr.await
            "#]],
        )
    }

    #[test]
    fn after_let() {
        check(
            r#"fn main() { let _ = <|> }"#,
            expect![[r#"
                kw if
                kw if let
                kw loop
                kw match
                kw return
                kw while
            "#]],
        )
    }
}
<commit_msg>Don't add braces after struct<commit_after>//! FIXME: write short doc here

use ra_syntax::{ast, SyntaxKind};
use test_utils::mark;

use crate::completion::{
    CompletionContext, CompletionItem, CompletionItemKind, CompletionKind, Completions,
};

pub(super) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    // complete keyword "crate" in use stmt
    let source_range = ctx.source_range();
    match (ctx.use_item_syntax.as_ref(), ctx.path_prefix.as_ref()) {
        (Some(_), None) => {
            CompletionItem::new(CompletionKind::Keyword, source_range, "crate::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("crate::")
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "self")
                .kind(CompletionItemKind::Keyword)
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("super::")
                .add_to(acc);
        }
        (Some(_), Some(_)) => {
            CompletionItem::new(CompletionKind::Keyword, source_range, "self")
                .kind(CompletionItemKind::Keyword)
                .add_to(acc);
            CompletionItem::new(CompletionKind::Keyword, source_range, "super::")
                .kind(CompletionItemKind::Keyword)
                .insert_text("super::")
                .add_to(acc);
        }
        _ => {}
    }

    // Suggest .await syntax for types that implement Future trait
    if let Some(receiver) = &ctx.dot_receiver {
        if let Some(ty) = ctx.sema.type_of_expr(receiver) {
            if ty.impls_future(ctx.db) {
                CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), "await")
                    .kind(CompletionItemKind::Keyword)
                    .detail("expr.await")
                    .insert_text("await")
                    .add_to(acc);
            }
        };
    }
}

pub(super) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.token.kind() == SyntaxKind::COMMENT {
        mark::hit!(no_keyword_completion_in_comments);
        return;
    }

    let has_trait_or_impl_parent = ctx.has_impl_parent || ctx.has_trait_parent;
    if ctx.trait_as_prev_sibling || ctx.impl_as_prev_sibling {
        add_keyword(ctx, acc, "where", "where ");
        return;
    }
    if ctx.unsafe_is_prev {
        if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
            add_keyword(ctx, acc, "fn", "fn $0() {}")
        }

        if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
            || ctx.block_expr_parent
        {
            add_keyword(ctx, acc, "trait", "trait $0 {}");
            add_keyword(ctx, acc, "impl", "impl $0 {}");
        }

        return;
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
        add_keyword(ctx, acc, "fn", "fn $0() {}");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "use", "use ");
        add_keyword(ctx, acc, "impl", "impl $0 {}");
        add_keyword(ctx, acc, "trait", "trait $0 {}");
    }

    if ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent {
        add_keyword(ctx, acc, "enum", "enum $0 {}");
        add_keyword(ctx, acc, "struct", "struct $0");
        add_keyword(ctx, acc, "union", "union $0 {}");
    }

    if ctx.can_be_expr {
        add_keyword(ctx, acc, "match", "match $0 {}");
        add_keyword(ctx, acc, "while", "while $0 {}");
        add_keyword(ctx, acc, "loop", "loop {$0}");
        add_keyword(ctx, acc, "if", "if ");
        add_keyword(ctx, acc, "if let", "if let ");
    }

    if ctx.if_is_prev || ctx.block_expr_parent {
        add_keyword(ctx, acc, "let", "let ");
    }

    if ctx.after_if {
        add_keyword(ctx, acc, "else", "else {$0}");
        add_keyword(ctx, acc, "else if", "else if $0 {}");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "mod", "mod $0 {}");
    }
    if ctx.bind_pat_parent || ctx.ref_pat_parent {
        add_keyword(ctx, acc, "mut", "mut ");
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent {
        add_keyword(ctx, acc, "const", "const ");
        add_keyword(ctx, acc, "type", "type ");
    }
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "static", "static ");
    };
    if (ctx.has_item_list_or_source_file_parent && !has_trait_or_impl_parent)
        || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "extern", "extern ");
    }
    if ctx.has_item_list_or_source_file_parent || ctx.block_expr_parent || ctx.is_match_arm {
        add_keyword(ctx, acc, "unsafe", "unsafe ");
    }
    if ctx.in_loop_body {
        if ctx.can_be_stmt {
            add_keyword(ctx, acc, "continue", "continue;");
            add_keyword(ctx, acc, "break", "break;");
        } else {
            add_keyword(ctx, acc, "continue", "continue");
            add_keyword(ctx, acc, "break", "break");
        }
    }
    if ctx.has_item_list_or_source_file_parent && !ctx.has_trait_parent {
        add_keyword(ctx, acc, "pub", "pub ")
    }

    if !ctx.is_trivial_path {
        return;
    }
    let fn_def = match &ctx.function_syntax {
        Some(it) => it,
        None => return,
    };
    acc.add_all(complete_return(ctx, &fn_def, ctx.can_be_stmt));
}

fn keyword(ctx: &CompletionContext, kw: &str, snippet: &str) -> CompletionItem {
    let res = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
        .kind(CompletionItemKind::Keyword);

    match ctx.config.snippet_cap {
        Some(cap) => res.insert_snippet(cap, snippet),
        _ => res.insert_text(if snippet.contains('$') { kw } else { snippet }),
    }
    .build()
}

fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
    acc.add(keyword(ctx, kw, snippet));
}

fn complete_return(
    ctx: &CompletionContext,
    fn_def: &ast::FnDef,
    can_be_stmt: bool,
) -> Option<CompletionItem> {
    let snip = match (can_be_stmt, fn_def.ret_type().is_some()) {
        (true, true) => "return $0;",
        (true, false) => "return;",
        (false, true) => "return $0",
        (false, false) => "return",
    };
    Some(keyword(ctx, "return", snip))
}

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

    use crate::completion::{
        test_utils::{check_edit, completion_list},
        CompletionKind,
    };
    use test_utils::mark;

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

    #[test]
    fn test_keywords_in_use_stmt() {
        check(
            r"use <|>",
            expect![[r#"
                kw crate::
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::<|>",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::{b, <|>}",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );
    }

    #[test]
    fn test_keywords_at_source_file_level() {
        check(
            r"m<|>",
            expect![[r#"
                kw const
                kw enum
                kw extern
                kw fn
                kw impl
                kw mod
                kw pub
                kw static
                kw struct
                kw trait
                kw type
                kw union
                kw unsafe
                kw use
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_function() {
        check(
            r"fn quux() { <|> }",
            expect![[r#"
                kw const
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_inside_block() {
        check(
            r"fn quux() { if true { <|> } }",
            expect![[r#"
                kw const
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_if() {
        check(
            r#"fn quux() { if true { () } <|> }"#,
            expect![[r#"
                kw const
                kw else
                kw else if
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
        check_edit(
            "else",
            r#"fn quux() { if true { () } <|> }"#,
            r#"fn quux() { if true { () } else {$0} }"#,
        );
    }

    #[test]
    fn test_keywords_in_match_arm() {
        check(
            r#"
fn quux() -> i32 {
    match () { () => <|> }
}
"#,
            expect![[r#"
                kw if
                kw if let
                kw loop
                kw match
                kw return
                kw unsafe
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_trait_def() {
        check(
            r"trait My { <|> }",
            expect![[r#"
                kw const
                kw fn
                kw type
                kw unsafe
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_impl_def() {
        check(
            r"impl My { <|> }",
            expect![[r#"
                kw const
                kw fn
                kw pub
                kw type
                kw unsafe
            "#]],
        );
    }

    #[test]
    fn test_keywords_in_loop() {
        check(
            r"fn my() { loop { <|> } }",
            expect![[r#"
                kw break
                kw const
                kw continue
                kw extern
                kw fn
                kw if
                kw if let
                kw impl
                kw let
                kw loop
                kw match
                kw mod
                kw return
                kw static
                kw trait
                kw type
                kw unsafe
                kw use
                kw while
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_unsafe_in_item_list() {
        check(
            r"unsafe <|>",
            expect![[r#"
                kw fn
                kw impl
                kw trait
            "#]],
        );
    }

    #[test]
    fn test_keywords_after_unsafe_in_block_expr() {
        check(
            r"fn my_fn() { unsafe <|> }",
            expect![[r#"
                kw fn
                kw impl
                kw trait
            "#]],
        );
    }

    #[test]
    fn test_mut_in_ref_and_in_fn_parameters_list() {
        check(
            r"fn my_fn(&<|>) {}",
            expect![[r#"
                kw mut
            "#]],
        );
        check(
            r"fn my_fn(<|>) {}",
            expect![[r#"
                kw mut
            "#]],
        );
        check(
            r"fn my_fn() { let &<|> }",
            expect![[r#"
                kw mut
            "#]],
        );
    }

    #[test]
    fn test_where_keyword() {
        check(
            r"trait A <|>",
            expect![[r#"
                kw where
            "#]],
        );
        check(
            r"impl A <|>",
            expect![[r#"
                kw where
            "#]],
        );
    }

    #[test]
    fn no_keyword_completion_in_comments() {
        mark::check!(no_keyword_completion_in_comments);
        check(
            r#"
fn test() {
    let x = 2; // A comment<|>
}
"#,
            expect![[""]],
        );
        check(
            r#"
/*
Some multi-line comment<|>
*/
"#,
            expect![[""]],
        );
        check(
            r#"
/// Some doc comment
/// let test<|> = 1
"#,
            expect![[""]],
        );
    }

    #[test]
    fn test_completion_await_impls_future() {
        check(
            r#"
//- /main.rs
use std::future::*;
struct A {}
impl Future for A {}
fn foo(a: A) { a.<|> }

//- /std/lib.rs
pub mod future {
    #[lang = "future_trait"]
    pub trait Future {}
}
"#,
            expect![[r#"
                kw await expr.await
            "#]],
        )
    }

    #[test]
    fn after_let() {
        check(
            r#"fn main() { let _ = <|> }"#,
            expect![[r#"
                kw if
                kw if let
                kw loop
                kw match
                kw return
                kw while
            "#]],
        )
    }
}
<|endoftext|>


<commit_before>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addresser: Option<Regex>
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addresser: None
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by, if present
    pub fn get_addresser(&self) -> Option<&Regex> {
        self.addresser.as_ref()
    }

    /// Turns on the requirement of addressing the bot before a handler is triggered
    pub fn address_by_name(&mut self) {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", self.get_name());
        let addresser = Regex::new(addresser_str.as_ref());

        self.addresser = addresser.ok();
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Distribute to handlers
            for handler in &self.handlers {
                let msg_str = msg.get_contents();

                if handler.can_handle(msg_str) {
                    // When addresser is present, ignore handlers that don't directly call the
                    // bot's name
                    if let Some(ref addresser) = self.addresser {
                        if !addresser.is_match(msg_str) {
                            continue;
                        }
                    }

                    match handler.handle(&msg) {
                        Err(e) => {
                            println!("Error in handler `{}`", handler.name());
                            println!("{:?}", e);
                            println!("The incoming message was {}", msg_str);

                            // TODO remove handler?
                        },
                        _ => ()
                    }
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_by_name() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
    }

    #[test]
    fn test_chatbot_address_matches() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
        let addresser = bot.get_addresser().unwrap();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<commit_msg>Always create addresser regex with initialization<commit_after>use regex::Regex;
use std::sync::mpsc::channel;

use adapter::ChatAdapter;
use handler::MessageHandler;

/// The Chatbot is the central data structure of the chatbot platform. It contains a `run` method
/// which listens for messages from adapters and routes them to handlers. Any program which uses
/// chatbot will need to minimally create a Chatbot, add an adapter, add a handler, and call Chatbot
/// [`run`](chatbot/struct.Chatbot.html#method.run).
pub struct Chatbot {
    name: String,
    adapters: Vec<Box<ChatAdapter>>,
    handlers: Vec<Box<MessageHandler>>,
    addresser: Regex
}

impl Chatbot {
    /// Create a new chatbot instance.
    ///
    /// The name provided here will be used for message filtering and in handlers for whatever they
    /// want. You'll want to make your binding mutable so you can call `add_adapter` and
    /// `add_handler`.
    pub fn new(name: &str) -> Chatbot {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", name);
        let addresser = Regex::new(addresser_str.as_ref());

        Chatbot {
            name: name.to_owned(),
            adapters: Vec::new(),
            handlers: Vec::new(),
            addresser: addresser.unwrap()
        }
    }

    /// Return the name provided in initialization
    pub fn get_name(&self) -> &str {
        self.name.as_ref()
    }

    /// Return the regular expression of what the bot will be addressed by, if present
    pub fn get_addresser(&self) -> Option<&Regex> {
        self.addresser.as_ref()
    }

    /// Turns on the requirement of addressing the bot before a handler is triggered
    pub fn address_by_name(&mut self) {
        let addresser_str = format!(r"^\s*@?{}[:,\s]\s*", self.get_name());
        let addresser = Regex::new(addresser_str.as_ref());

        self.addresser = addresser.ok();
    }

    /// Add a ChatAdapter to the bot
    ///
    /// Add as many adapters as you like. The IncomingMessages sent by adapters are made available
    /// to all handlers regardless of how many adapters exist. The IncomingMessage.reply method
    /// makes sure the response is sent back to the adapter from whence the message came.
    pub fn add_adapter<T>(&mut self, adapter: T)
        where T: ChatAdapter + 'static
    {
        println!("Adding adapter {}", adapter.get_name());
        self.adapters.push(Box::new(adapter))
    }

    /// Add a MessageHandler to the bot
    ///
    /// The more handlers you have the more useful your bot becomes (for potentially loose
    /// definitions of useful :P). Check out the handler! macro for making simple handlers and also
    /// see the `MessageHandler` implementors section for a list of built in handlers.
    pub fn add_handler<T>(&mut self, handler: T)
        where T: MessageHandler + 'static
    {
        println!("Adding handler {}", handler.name());
        self.handlers.push(Box::new(handler))
    }

    /// Start processing messages
    ///
    /// Call process_events on all of the adapters and `recv` on the `IncomingMessage` channel.
    /// Distribute IncomingMessages to list of handlers.
    pub fn run(&self) {

        assert!(self.adapters.len() > 0);
        assert!(self.handlers.len() > 0);

        println!("Chatbot: {} adapters", self.adapters.len());
        println!("Chatbot: {} handlers", self.handlers.len());

        let (incoming_tx, incoming_rx) = channel();

        for adapter in &self.adapters {
            adapter.process_events(self, incoming_tx.clone());
        }

        loop {
            // Get message from adapter
            let msg = match incoming_rx.recv() {
                Ok(msg) => msg,
                Err(_) => break
            };

            // Distribute to handlers
            for handler in &self.handlers {
                let msg_str = msg.get_contents();

                if handler.can_handle(msg_str) {
                    // When addresser is present, ignore handlers that don't directly call the
                    // bot's name
                    if let Some(ref addresser) = self.addresser {
                        if !addresser.is_match(msg_str) {
                            continue;
                        }
                    }

                    match handler.handle(&msg) {
                        Err(e) => {
                            println!("Error in handler `{}`", handler.name());
                            println!("{:?}", e);
                            println!("The incoming message was {}", msg_str);

                            // TODO remove handler?
                        },
                        _ => ()
                    }
                }
            }
        }

        println!("chatbot shutting down");
    }
}

#[cfg(test)]
mod tests {
    use chatbot::Chatbot;
    use adapter::CliAdapter;

    #[test]
    fn test_create_chatbot() {
        let name = "testbot";
        let bot = Chatbot::new("testbot");
        assert_eq!(bot.get_name(), name);
    }

    #[test]
    fn test_chatbot_add_adapter() {
        let mut bot = Chatbot::new("testbot");
        let cli = CliAdapter::new();
        bot.add_adapter(cli);
    }

    #[test]
    fn test_chatbot_add_handler() {
        let mut bot = Chatbot::new("testbot");
        let echo = handler!("EchoHandler", r"echo .+", |_, msg| {
            Some(msg.to_owned())
        });
        bot.add_handler(echo);
    }

    #[test]
    fn test_chatbot_address_by_name() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
    }

    #[test]
    fn test_chatbot_address_matches() {
        let mut bot = Chatbot::new("testbot");
        bot.address_by_name();
        let addresser = bot.get_addresser().unwrap();

        assert!(!addresser.is_match("testbotping"),
                "Shouldn't match bot name without a break (space, colon, comma)");
        assert!(!addresser.is_match("@testbotping"),
                "Shouldn't match bot name with '@' sign minus a break (space, colon, comma)");

        // Space separation
        assert!(addresser.is_match("testbot ping"), "Should match bot name with space");
        assert!(addresser.is_match("@testbot ping"),
                "Should match bot name with '@' sign plus '@' space");

        // Colon separation
        assert!(addresser.is_match("testbot:ping"), "Should match bot name with colon");
        assert!(addresser.is_match("testbot: ping"), "Should match bot name with colon and space");
        assert!(addresser.is_match("@testbot:ping"),
                "Should match bot name with '@' sign plus colon");
        assert!(addresser.is_match("@testbot: ping"),
                "Should match bot name with '@' sign plus colon and space");

        // Comma separation
        assert!(addresser.is_match("testbot,ping"), "Should match bot name with comma");
        assert!(addresser.is_match("testbot, ping"), "Should match bot name with comma and space");
        assert!(addresser.is_match("@testbot,ping"),
                "Should match bot name with '@' sign plus comma");
        assert!(addresser.is_match("@testbot, ping"),
                "Should match bot name with '@' sign plus comma and space");
    }
}
<|endoftext|>


<commit_before>#[derive(Default, Debug, Clone)]
#[non_exhaustive]
pub struct Raw {}

impl Raw {
    pub fn new() -> Self {
        Self {}
    }

    pub fn has_theme(&self, _name: &str) -> bool {
        false
    }

    pub fn themes(&self) -> impl Iterator<Item = String> + '_ {
        vec![].into_iter()
    }

    pub fn syntaxes(&self) -> impl Iterator<Item = String> + '_ {
        vec![].into_iter()
    }

    pub fn format(&self, code: &str, lang: Option<&str>, _theme: Option<&str>) -> String {
        let code = html_escape(code);
        if let Some(ref lang) = lang {
            format!(
                "
{}
\n",
                lang, code
            )
        } else {
            format!("
{}
\n", code)
        }
    }
}

// The code is taken from Liquid which was adapted from
// https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/escape.rs
// Retrieved 2016-11-19.
fn html_escape(input: &str) -> String {
    let mut result = String::new();
    let mut last = 0;
    let mut skip = 0;
    for (i, c) in input.chars().enumerate() {
        if skip > 0 {
            skip -= 1;
            continue;
        }
        let c: char = c;
        match c {
            '<' | '>' | '\'' | '"' | '&' => {
                result.push_str(&input[last..i]);
                last = i + 1;
                let escaped = match c {
                    '<' => "<",
                    '>' => ">",
                    '\'' => "'",
                    '"' => """,
                    '&' => "&",
                    _ => unreachable!(),
                };
                result.push_str(escaped);
            }
            _ => {}
        }
    }
    if last < input.len() {
        result.push_str(&input[last..]);
    }
    result
}

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

    const CODEBLOCK: &str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
";

    const CODEBLOCK_RENDERED: &str = r#"
mod test {
        fn hello(arg: int) -> bool {
            true
        }
    }

"#;

    #[test]
    fn codeblock_renders_rust() {
        let syntax = Raw::new();
        let output = syntax.format(CODEBLOCK, Some("rust"), Some(""));
        assert_eq!(output, CODEBLOCK_RENDERED.to_string());
    }
}
<commit_msg>add load_custom_syntaxes method to engarde::Raw<commit_after>use std::path::Path;

#[derive(Default, Debug, Clone)]
#[non_exhaustive]
pub struct Raw {}

impl Raw {
    pub fn new() -> Self {
        Self {}
    }

    pub fn load_custom_syntaxes(&mut self, _syntaxes_path: &Path) {}

    pub fn has_theme(&self, _name: &str) -> bool {
        false
    }

    pub fn themes(&self) -> impl Iterator<Item = String> + '_ {
        vec![].into_iter()
    }

    pub fn syntaxes(&self) -> impl Iterator<Item = String> + '_ {
        vec![].into_iter()
    }

    pub fn format(&self, code: &str, lang: Option<&str>, _theme: Option<&str>) -> String {
        let code = html_escape(code);
        if let Some(ref lang) = lang {
            format!(
                "
{}
\n",
                lang, code
            )
        } else {
            format!("
{}
\n", code)
        }
    }
}

// The code is taken from Liquid which was adapted from
// https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/escape.rs
// Retrieved 2016-11-19.
fn html_escape(input: &str) -> String {
    let mut result = String::new();
    let mut last = 0;
    let mut skip = 0;
    for (i, c) in input.chars().enumerate() {
        if skip > 0 {
            skip -= 1;
            continue;
        }
        let c: char = c;
        match c {
            '<' | '>' | '\'' | '"' | '&' => {
                result.push_str(&input[last..i]);
                last = i + 1;
                let escaped = match c {
                    '<' => "<",
                    '>' => ">",
                    '\'' => "'",
                    '"' => """,
                    '&' => "&",
                    _ => unreachable!(),
                };
                result.push_str(escaped);
            }
            _ => {}
        }
    }
    if last < input.len() {
        result.push_str(&input[last..]);
    }
    result
}

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

    const CODEBLOCK: &str = "mod test {
        fn hello(arg: int) -> bool {
            \
                                      true
        }
    }
";

    const CODEBLOCK_RENDERED: &str = r#"
mod test {
        fn hello(arg: int) -> bool {
            true
        }
    }

"#;

    #[test]
    fn codeblock_renders_rust() {
        let syntax = Raw::new();
        let output = syntax.format(CODEBLOCK, Some("rust"), Some(""));
        assert_eq!(output, CODEBLOCK_RENDERED.to_string());
    }
}
<|endoftext|>


<commit_before>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read, path::PathBuf};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T> = std::result::Result<T, Box<dyn Error + Send + Sync>>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = Command::from_args()?;
    match command {
        Command::Parse { no_dump } => {
            let _p = profile("parsing");
            let file = file()?;
            if !no_dump {
                println!("{:#?}", file.syntax());
            }
            std::mem::forget(file);
        }
        Command::Symbols => {
            let file = file()?;
            for s in file_structure(&file) {
                println!("{:?}", s);
            }
        }
        Command::Highlight { rainbow } => {
            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
            let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
            println!("{}", html);
        }
        Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
            analysis_stats::run(
                verbosity,
                memory_usage,
                path.as_ref(),
                only.as_ref().map(String::as_ref),
                with_deps,
                randomize,
            )?;
        }
        Command::Bench { verbosity, path, op } => {
            analysis_bench::run(verbosity, path.as_ref(), op)?;
        }
        Command::HelpPrinted => (),
    }

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        verbosity: Verbosity,
        path: PathBuf,
        op: analysis_bench::Op,
    },
    HelpPrinted,
}

impl Command {
    fn from_args() -> Result<Command> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
        };

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Command::HelpPrinted);
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Command::HelpPrinted);
                }

                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbosity, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Command::HelpPrinted);
            }
        };
        Ok(command)
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<commit_msg>More precise types<commit_after>//! FIXME: write short doc here

mod load_cargo;
mod analysis_stats;
mod analysis_bench;
mod progress_report;

use std::{error::Error, fmt::Write, io::Read, path::PathBuf};

use pico_args::Arguments;
use ra_ide::{file_structure, Analysis};
use ra_prof::profile;
use ra_syntax::{AstNode, SourceFile};

type Result<T, E = Box<dyn Error + Send + Sync>> = std::result::Result<T, E>;

fn main() -> Result<()> {
    env_logger::try_init()?;

    let command = match Command::from_args()? {
        Ok(it) => it,
        Err(HelpPrinted) => return Ok(()),
    };
    match command {
        Command::Parse { no_dump } => {
            let _p = profile("parsing");
            let file = file()?;
            if !no_dump {
                println!("{:#?}", file.syntax());
            }
            std::mem::forget(file);
        }
        Command::Symbols => {
            let file = file()?;
            for s in file_structure(&file) {
                println!("{:?}", s);
            }
        }
        Command::Highlight { rainbow } => {
            let (analysis, file_id) = Analysis::from_single_file(read_stdin()?);
            let html = analysis.highlight_as_html(file_id, rainbow).unwrap();
            println!("{}", html);
        }
        Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path } => {
            analysis_stats::run(
                verbosity,
                memory_usage,
                path.as_ref(),
                only.as_ref().map(String::as_ref),
                with_deps,
                randomize,
            )?;
        }
        Command::Bench { verbosity, path, op } => {
            analysis_bench::run(verbosity, path.as_ref(), op)?;
        }
    }

    Ok(())
}

#[derive(Clone, Copy)]
pub enum Verbosity {
    Spammy,
    Verbose,
    Normal,
    Quiet,
}

impl Verbosity {
    fn is_verbose(self) -> bool {
        match self {
            Verbosity::Verbose | Verbosity::Spammy => true,
            _ => false,
        }
    }
    fn is_spammy(self) -> bool {
        match self {
            Verbosity::Spammy => true,
            _ => false,
        }
    }
}

enum Command {
    Parse {
        no_dump: bool,
    },
    Symbols,
    Highlight {
        rainbow: bool,
    },
    Stats {
        verbosity: Verbosity,
        randomize: bool,
        memory_usage: bool,
        only: Option<String>,
        with_deps: bool,
        path: PathBuf,
    },
    Bench {
        verbosity: Verbosity,
        path: PathBuf,
        op: analysis_bench::Op,
    },
}

struct HelpPrinted;

impl Command {
    fn from_args() -> Result<Result<Command, HelpPrinted>> {
        let mut matches = Arguments::from_env();
        let subcommand = matches.subcommand()?.unwrap_or_default();

        let verbosity = match (
            matches.contains(["-vv", "--spammy"]),
            matches.contains(["-v", "--verbose"]),
            matches.contains(["-q", "--quiet"]),
        ) {
            (true, _, true) => Err("Invalid flags: -q conflicts with -vv")?,
            (true, _, false) => Verbosity::Spammy,
            (false, false, false) => Verbosity::Normal,
            (false, false, true) => Verbosity::Quiet,
            (false, true, false) => Verbosity::Verbose,
            (false, true, true) => Err("Invalid flags: -q conflicts with -v")?,
        };

        let command = match subcommand.as_str() {
            "parse" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-parse

USAGE:
    ra_cli parse [FLAGS]

FLAGS:
    -h, --help       Prints help inforamtion
        --no-dump"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let no_dump = matches.contains("--no-dump");
                matches.finish().or_else(handle_extra_flags)?;
                Command::Parse { no_dump }
            }
            "symbols" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-symbols

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help    Prints help inforamtion"
                    );
                    return Ok(Err(HelpPrinted));
                }

                matches.finish().or_else(handle_extra_flags)?;

                Command::Symbols
            }
            "highlight" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-highlight

USAGE:
    ra_cli highlight [FLAGS]

FLAGS:
    -h, --help       Prints help information
    -r, --rainbow"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let rainbow = matches.contains(["-r", "--rainbow"]);
                matches.finish().or_else(handle_extra_flags)?;
                Command::Highlight { rainbow }
            }
            "analysis-stats" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra-cli-analysis-stats

USAGE:
    ra_cli analysis-stats [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help            Prints help information
        --memory-usage
    -v, --verbose
    -q, --quiet

OPTIONS:
    -o <ONLY>

ARGS:
    <PATH>"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let randomize = matches.contains("--randomize");
                let memory_usage = matches.contains("--memory-usage");
                let only: Option<String> = matches.opt_value_from_str(["-o", "--only"])?;
                let with_deps: bool = matches.contains("--with-deps");
                let path = {
                    let mut trailing = matches.free()?;
                    if trailing.len() != 1 {
                        Err("Invalid flags")?;
                    }
                    trailing.pop().unwrap().into()
                };

                Command::Stats { verbosity, randomize, memory_usage, only, with_deps, path }
            }
            "analysis-bench" => {
                if matches.contains(["-h", "--help"]) {
                    eprintln!(
                        "\
ra_cli-analysis-bench

USAGE:
    ra_cli analysis-bench [FLAGS] [OPTIONS] [PATH]

FLAGS:
    -h, --help        Prints help information
    -v, --verbose

OPTIONS:
    --complete <PATH:LINE:COLUMN>    Compute completions at this location
    --highlight <PATH>               Hightlight this file

ARGS:
    <PATH>    Project to analyse"
                    );
                    return Ok(Err(HelpPrinted));
                }

                let path: PathBuf = matches.opt_value_from_str("--path")?.unwrap_or_default();
                let highlight_path: Option<String> = matches.opt_value_from_str("--highlight")?;
                let complete_path: Option<String> = matches.opt_value_from_str("--complete")?;
                let goto_def_path: Option<String> = matches.opt_value_from_str("--goto-def")?;
                let op = match (highlight_path, complete_path, goto_def_path) {
                    (Some(path), None, None) => analysis_bench::Op::Highlight { path: path.into() },
                    (None, Some(position), None) => analysis_bench::Op::Complete(position.parse()?),
                    (None, None, Some(position)) => analysis_bench::Op::GotoDef(position.parse()?),
                    _ => panic!(
                        "exactly one of  `--highlight`, `--complete` or `--goto-def` must be set"
                    ),
                };
                Command::Bench { verbosity, path, op }
            }
            _ => {
                eprintln!(
                    "\
ra-cli

USAGE:
    ra_cli <SUBCOMMAND>

FLAGS:
    -h, --help        Prints help information

SUBCOMMANDS:
    analysis-bench
    analysis-stats
    highlight
    parse
    symbols"
                );
                return Ok(Err(HelpPrinted));
            }
        };
        Ok(Ok(command))
    }
}

fn handle_extra_flags(e: pico_args::Error) -> Result<()> {
    if let pico_args::Error::UnusedArgsLeft(flags) = e {
        let mut invalid_flags = String::new();
        for flag in flags {
            write!(&mut invalid_flags, "{}, ", flag)?;
        }
        let (invalid_flags, _) = invalid_flags.split_at(invalid_flags.len() - 2);
        Err(format!("Invalid flags: {}", invalid_flags).into())
    } else {
        Err(e.to_string().into())
    }
}

fn file() -> Result<SourceFile> {
    let text = read_stdin()?;
    Ok(SourceFile::parse(&text).tree())
}

fn read_stdin() -> Result<String> {
    let mut buff = String::new();
    std::io::stdin().read_to_string(&mut buff)?;
    Ok(buff)
}
<|endoftext|>


<commit_before>use std::ascii::AsciiExt;

use direction::Direction;
use platform;

#[derive(Debug)]
pub enum PrimaryCommand {
  Go(Direction),
  Look,
  Quit,
}

type HelpInfo = &'static [(char, &'static str)];

pub trait CommandProcessor<T> {
  fn from_char(c: char) -> Option<T>;

  fn get_help() -> HelpInfo;

  fn prompt() -> &'static str { "> " }

  fn get() -> Option<T> {
    platform::show_prompt(Self::prompt());

    match platform::read_input() {
      Some(input) => {
        match input.chars().next() {
          Some(k) => {
            let k = k.to_ascii_lowercase();
            if k == 'h' || k == '?' {
              println!("Available commands:");

              for &(ch, desc) in Self::get_help().iter() {
                println!("{} - {}", ch, desc);
              }
              return None;
            } else if let Some(cmd) = Self::from_char(k) {
              return Some(cmd);
            }
          },
          None => {}
        }
        println!("I have no idea what you're talking about.");
        return None;
      },
      None => {
        return None;
      }
    }
  }
}

static PRIMARY_COMMAND_HELP: HelpInfo = &[
  ('n', "go north"),
  ('s', "go south"),
  ('e', "go east"),
  ('w', "go west"),
  ('l', "look around"),
  ('q', "quit"),
];

impl CommandProcessor<PrimaryCommand> for PrimaryCommand {
  fn get_help() -> HelpInfo { PRIMARY_COMMAND_HELP }

  fn from_char(c: char) -> Option<PrimaryCommand> {
    match c {
      'n' => { return Some(PrimaryCommand::Go(Direction::North)); },
      's' => { return Some(PrimaryCommand::Go(Direction::South)); },
      'e' => { return Some(PrimaryCommand::Go(Direction::East)); },
      'w' => { return Some(PrimaryCommand::Go(Direction::West)); },
      'l' => { return Some(PrimaryCommand::Look); },
      'q' => { return Some(PrimaryCommand::Quit); },
      _ => { return None; },
    }
  }
}

<commit_msg>Add up/down commands.<commit_after>use std::ascii::AsciiExt;

use direction::Direction;
use platform;

#[derive(Debug)]
pub enum PrimaryCommand {
  Go(Direction),
  Look,
  Quit,
}

type HelpInfo = &'static [(char, &'static str)];

pub trait CommandProcessor<T> {
  fn from_char(c: char) -> Option<T>;

  fn get_help() -> HelpInfo;

  fn prompt() -> &'static str { "> " }

  fn get() -> Option<T> {
    platform::show_prompt(Self::prompt());

    match platform::read_input() {
      Some(input) => {
        match input.chars().next() {
          Some(k) => {
            let k = k.to_ascii_lowercase();
            if k == 'h' || k == '?' {
              println!("Available commands:");

              for &(ch, desc) in Self::get_help().iter() {
                println!("{} - {}", ch, desc);
              }
              return None;
            } else if let Some(cmd) = Self::from_char(k) {
              return Some(cmd);
            }
          },
          None => {}
        }
        println!("I have no idea what you're talking about.");
        return None;
      },
      None => {
        return None;
      }
    }
  }
}

static PRIMARY_COMMAND_HELP: HelpInfo = &[
  ('n', "go north"),
  ('s', "go south"),
  ('e', "go east"),
  ('w', "go west"),
  ('u', "go up"),
  ('d', "go down"),
  ('l', "look around"),
  ('q', "quit"),
];

impl CommandProcessor<PrimaryCommand> for PrimaryCommand {
  fn get_help() -> HelpInfo { PRIMARY_COMMAND_HELP }

  fn from_char(c: char) -> Option<PrimaryCommand> {
    match c {
      'n' => { return Some(PrimaryCommand::Go(Direction::North)); },
      's' => { return Some(PrimaryCommand::Go(Direction::South)); },
      'e' => { return Some(PrimaryCommand::Go(Direction::East)); },
      'w' => { return Some(PrimaryCommand::Go(Direction::West)); },
      'u' => { return Some(PrimaryCommand::Go(Direction::Up)); },
      'd' => { return Some(PrimaryCommand::Go(Direction::Down)); },
      'l' => { return Some(PrimaryCommand::Look); },
      'q' => { return Some(PrimaryCommand::Quit); },
      _ => { return None; },
    }
  }
}

<|endoftext|>


<commit_before>use std::ascii::AsciiExt;

use platform;

pub struct HelpInfo {
  key: char,
  desc: String,
}

impl HelpInfo {
  pub fn list<T: AsRef<str>>(v: Vec<(char, T)>) -> Vec<Self> {
    v.into_iter().map(|(key, desc)| {
      // TODO: We might be making unnecessary copies of strings here.
      Self {key: key, desc: String::from(desc.as_ref())}
    }).collect()
  }
}

pub trait CommandProcessor<T> {
  fn from_char(c: char) -> Option<T>;

  fn get_help() -> Vec<HelpInfo>;

  fn prompt() -> &'static str { "What do you want to do? " }

  fn show_help() {
    for info in Self::get_help().iter() {
      println!("  {} - {}", info.key, info.desc);
    }
  }

  fn get_from_input(input: String) -> Option<T> {
    platform::hide_prompt();
    match input.chars().next() {
      Some(k) => {
        let k = k.to_ascii_lowercase();
        if k == 'h' || k == '?' {
          println!("Here's what I understand right now:\n");
          Self::show_help();
          println!("");
          return None;
        } else if let Some(cmd) = Self::from_char(k) {
          return Some(cmd);
        }
      },
      None => {}
    }
    println!("I have no idea what you're talking about.");
    return None;
  }

  fn get() -> Option<T> {
    platform::show_prompt(Self::prompt());

    match platform::read_input() {
      Some(input) => Self::get_from_input(input),
      None => None
    }
  }
}
<commit_msg>Get rid of CommandProcessor::get().<commit_after>use std::ascii::AsciiExt;

use platform;

pub struct HelpInfo {
  key: char,
  desc: String,
}

impl HelpInfo {
  pub fn list<T: AsRef<str>>(v: Vec<(char, T)>) -> Vec<Self> {
    v.into_iter().map(|(key, desc)| {
      // TODO: We might be making unnecessary copies of strings here.
      Self {key: key, desc: String::from(desc.as_ref())}
    }).collect()
  }
}

pub trait CommandProcessor<T> {
  fn from_char(c: char) -> Option<T>;

  fn get_help() -> Vec<HelpInfo>;

  fn prompt() -> &'static str { "What do you want to do? " }

  fn show_help() {
    for info in Self::get_help().iter() {
      println!("  {} - {}", info.key, info.desc);
    }
  }

  fn get_from_input(input: String) -> Option<T> {
    platform::hide_prompt();
    match input.chars().next() {
      Some(k) => {
        let k = k.to_ascii_lowercase();
        if k == 'h' || k == '?' {
          println!("Here's what I understand right now:\n");
          Self::show_help();
          println!("");
          return None;
        } else if let Some(cmd) = Self::from_char(k) {
          return Some(cmd);
        }
      },
      None => {}
    }
    println!("I have no idea what you're talking about.");
    return None;
  }
}
<|endoftext|>


<commit_before>use cocoa::base::id;
use cocoa::foundation::NSString;
use metal_sys::{MTLArgument, MTLArgumentAccess, MTLArgumentType, MTLDataType};
use objc::runtime::YES;
use std::borrow::Cow;
use std::ffi::CStr;
use std::mem;

pub struct Argument(id);

impl Argument {
    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn access(&self) -> ArgumentAccess {
        unsafe { self.0.access().into() }
    }

    pub fn is_active(&self) -> bool {
        unsafe { self.0.active() == YES }
    }

    pub fn index(&self) -> usize {
        unsafe { self.0.index() as usize }
    }

    pub fn get_type(&self) -> ArgumentType {
        unsafe { self.0.getType().into() }
    }

    pub fn buffer_alignment(&self) -> usize {
        unsafe { self.0.bufferAlignment() as usize }
    }

    pub fn buffer_data_size(&self) -> usize {
        unsafe { self.0.bufferDataSize() as usize }
    }

    pub fn buffer_struct_type(&self) -> ! {
        unimplemented!()
    }

    pub fn texture_data_type(&self) -> ! {
        unimplemented!()
    }

    pub fn threadgroup_memory_alignment(&self) -> usize {
        // TODO(George): Error checking
        unsafe { self.0.threadgroupMemoryAlignment() as usize }
    }

    pub fn threadgroup_memory_data_size(&self) -> usize {
        // TODO(George): Error checking
        unsafe { self.0.threadgroupMemoryDataSize() as usize }
    }
}

#[repr(usize)]
pub enum ArgumentAccess {
    ReadOnly,
    ReadWrite,
    WriteOnly
}

impl From<MTLArgumentAccess> for ArgumentAccess {
    fn from(argument_access: MTLArgumentAccess) -> Self {
        unsafe { mem::transmute(argument_access) }
    }
}

impl Into<MTLArgumentAccess> for ArgumentAccess {
    fn into(self) -> MTLArgumentAccess {
        unsafe { mem::transmute(self) }
    }
}

#[repr(usize)]
pub enum ArgumentType {
    Buffer,
    ThreadgroupMemory,
    Texture,
    Sampler
}

impl From<MTLArgumentType> for ArgumentType {
    fn from(argument_type: MTLArgumentType) -> Self {
        unsafe { mem::transmute(argument_type) }
    }
}

impl Into<MTLArgumentType> for ArgumentType {
    fn into(self) -> MTLArgumentType {
        unsafe { mem::transmute(self) }
    }
}

#[repr(usize)]
pub enum DataType {
    None,
    Struct,
    Array,

    Float,
    Float2,
    Float3,
    Float4,

    Float2x2,
    Float2x3,
    Float2x4,

    Float3x2,
    Float3x3,
    Float3x4,
    Float4x2,
    Float4x3,
    Float4x4,

    Half,
    Half2,
    Half3,
    Half4,

    Half2x2,
    Half2x3,
    Half2x4,

    Half3x2,
    Half3x3,
    Half3x4,

    Half4x2,
    Half4x3,
    Half4x4,

    Int,
    Int2,
    Int3,
    Int4,
    UInt,
    UInt2,
    UInt3,
    UInt4,

    Short,
    Short2,
    Short3,
    Short4,

    UShort,
    UShort2,
    UShort3,
    UShort4,

    Char,
    Char2,
    Char3,
    Char4,

    UChar,
    UChar2,
    UChar3,
    UChar4,

    Bool,
    Bool2,
    Bool3,
    Bool4
}

impl From<MTLDataType> for DataType {
    fn from(data_type: MTLDataType) -> Self {
        unsafe { mem::transmute(data_type) }
    }
}

impl Into<MTLDataType> for DataType {
    fn into(self) -> MTLDataType {
        unsafe { mem::transmute(self) }
    }
}
<commit_msg>Add validation to Argument methods<commit_after>use cocoa::base::id;
use cocoa::foundation::NSString;
use metal_sys::{MTLArgument, MTLArgumentAccess, MTLArgumentType, MTLDataType};
use objc::runtime::YES;
use std::borrow::Cow;
use std::ffi::CStr;
use std::mem;

pub struct Argument(id);

impl Argument {
    pub fn name(&self) -> Cow<str> {
        unsafe { CStr::from_ptr(self.0.name().UTF8String()).to_string_lossy() }
    }

    pub fn access(&self) -> ArgumentAccess {
        unsafe { self.0.access().into() }
    }

    pub fn is_active(&self) -> bool {
        unsafe { self.0.active() == YES }
    }

    pub fn index(&self) -> usize {
        unsafe { self.0.index() as usize }
    }

    pub fn get_type(&self) -> ArgumentType {
        unsafe { self.0.getType().into() }
    }

    pub fn buffer_alignment(&self) -> Option<usize> {
        if self.get_type() == ArgumentType::Buffer {
            unsafe { Some(self.0.bufferAlignment() as usize) }
        } else {
            None
        }
    }

    pub fn buffer_data_size(&self) -> Option<usize> {
        if self.get_type() == ArgumentType::Buffer {
            unsafe { Some(self.0.bufferDataSize() as usize) }
        } else {
            None
        }
    }

    pub fn buffer_struct_type(&self) -> ! {
        unimplemented!()
    }

    pub fn texture_data_type(&self) -> ! {
        unimplemented!()
    }

    pub fn threadgroup_memory_alignment(&self) -> Option<usize> {
        if self.get_type() == ArgumentType::ThreadgroupMemory {
            unsafe { Some(self.0.threadgroupMemoryAlignment() as usize) }
        } else {
            None
        }
    }

    pub fn threadgroup_memory_data_size(&self) -> Option<usize> {
        if self.get_type() == ArgumentType::ThreadgroupMemory {
            unsafe { Some(self.0.threadgroupMemoryDataSize() as usize) }
        } else {
            None
        }
    }
}

#[repr(usize)]
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub enum ArgumentAccess {
    ReadOnly,
    ReadWrite,
    WriteOnly
}

impl From<MTLArgumentAccess> for ArgumentAccess {
    fn from(argument_access: MTLArgumentAccess) -> Self {
        unsafe { mem::transmute(argument_access) }
    }
}

impl Into<MTLArgumentAccess> for ArgumentAccess {
    fn into(self) -> MTLArgumentAccess {
        unsafe { mem::transmute(self) }
    }
}

#[repr(usize)]
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub enum ArgumentType {
    Buffer,
    ThreadgroupMemory,
    Texture,
    Sampler
}

impl From<MTLArgumentType> for ArgumentType {
    fn from(argument_type: MTLArgumentType) -> Self {
        unsafe { mem::transmute(argument_type) }
    }
}

impl Into<MTLArgumentType> for ArgumentType {
    fn into(self) -> MTLArgumentType {
        unsafe { mem::transmute(self) }
    }
}

#[repr(usize)]
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub enum DataType {
    None,
    Struct,
    Array,

    Float,
    Float2,
    Float3,
    Float4,

    Float2x2,
    Float2x3,
    Float2x4,

    Float3x2,
    Float3x3,
    Float3x4,
    Float4x2,
    Float4x3,
    Float4x4,

    Half,
    Half2,
    Half3,
    Half4,

    Half2x2,
    Half2x3,
    Half2x4,

    Half3x2,
    Half3x3,
    Half3x4,

    Half4x2,
    Half4x3,
    Half4x4,

    Int,
    Int2,
    Int3,
    Int4,
    UInt,
    UInt2,
    UInt3,
    UInt4,

    Short,
    Short2,
    Short3,
    Short4,

    UShort,
    UShort2,
    UShort3,
    UShort4,

    Char,
    Char2,
    Char3,
    Char4,

    UChar,
    UChar2,
    UChar3,
    UChar4,

    Bool,
    Bool2,
    Bool3,
    Bool4
}

impl From<MTLDataType> for DataType {
    fn from(data_type: MTLDataType) -> Self {
        unsafe { mem::transmute(data_type) }
    }
}

impl Into<MTLDataType> for DataType {
    fn into(self) -> MTLDataType {
        unsafe { mem::transmute(self) }
    }
}
<|endoftext|>


<commit_before>use crate::cli::Command;
use mprovision as mp;
use std::env;
use std::io::{self, Write};
use std::path::Path;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};

mod cli;

fn main() {
    if let Err(e) = cli::parse(env::args()).and_then(run) {
        e.exit();
    }
}

fn run(command: cli::Command) -> Result<(), cli::Error> {
    match command {
        Command::List(cli::ListParams {
            text,
            expire_in_days,
            directory,
        }) => list(&text, expire_in_days, directory),
        Command::ShowUuid(cli::ShowUuidParams { uuid, directory }) => show_uuid(&uuid, directory),
        Command::ShowFile(cli::ShowFileParams { file }) => show_file(&file),
        Command::Remove(cli::RemoveParams { ids, directory }) => remove(&ids, directory),
        Command::Clean(cli::CleanParams { directory }) => clean(directory),
    }
}

fn list(
    text: &Option<String>,
    expires_in_days: Option<u64>,
    directory: Option<PathBuf>,
) -> Result<(), cli::Error> {
    let dir = mp::with_directory(directory)?;
    let entries = mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>())?;
    let date =
        expires_in_days.map(|days| SystemTime::now() + Duration::from_secs(days * 24 * 60 * 60));
    let filter_string = text.as_ref();
    let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
        (Some(date), Some(string)) => {
            profile.info.expiration_date <= date && profile.info.contains(string)
        }
        (Some(date), _) => profile.info.expiration_date <= date,
        (_, Some(string)) => profile.info.contains(string),
        (_, _) => true,
    });
    profiles.sort_by(|a, b| a.info.creation_date.cmp(&b.info.creation_date));
    if profiles.is_empty() {
        Ok(())
    } else {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        for profile in &profiles {
            writeln!(&mut stdout, "{}\n", profile.info.description())?;
        }
        Ok(())
    }
}

fn show_uuid(uuid: &str, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    let dir = mp::with_directory(directory)?;
    let profile = mp::find_by_uuid(&dir, &uuid)?;
    let xml = mp::show(&profile.path)?;
    writeln!(io::stdout(), "{}", xml)?;
    Ok(())
}

fn show_file(path: &Path) -> Result<(), cli::Error> {
    let xml = mp::show(&path)?;
    writeln!(io::stdout(), "{}", xml)?;
    Ok(())
}

fn remove(ids: &[String], directory: Option<PathBuf>) -> Result<(), cli::Error> {
    mp::with_directory(directory)
        .and_then(|directory| {
            mp::find_by_ids(&directory, &ids).and_then(|profiles| {
                for profile in profiles {
                    if mp::remove(&profile.path).is_ok() {
                        println!("\nRemoved: {}", profile.info.description());
                    } else {
                        println!("\nError while removing '{}'", profile.info.uuid);
                    }
                }
                Ok(())
            })
        })
        .map_err(|err| err.into())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
    fn concat(mut s1: String, s2: &str) -> String {
        s1.push_str(&s2);
        s1
    }

    let date = SystemTime::now();
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| mp::filter(entries, |profile| profile.info.expiration_date <= date))
        .and_then(|profiles| {
            if profiles.is_empty() {
                println!("All provisioning profiles are valid");
                Ok(())
            } else {
                profiles
                    .iter()
                    .map(|profile| {
                        std::fs::remove_file(&profile.path)
                            .map(|_| format!("'{}' was removed\n", profile.info.uuid))
                            .map_err(|err| format!("'{}' {}\n", profile.info.uuid, err))
                    })
                    .fold(Ok(String::new()), |acc, s| match (acc, s) {
                        (Ok(acc), Ok(s)) => Ok(concat(acc, &s)),
                        (Ok(acc), Err(s)) | (Err(acc), Ok(s)) | (Err(acc), Err(s)) => {
                            Err(concat(acc, &s))
                        }
                    })
                    .map(|s| println!("{}", s))
                    .map_err(cli::Error::Custom)
            }
        })
}
<commit_msg>Refactor out chaining combinators for the remove command<commit_after>use crate::cli::Command;
use mprovision as mp;
use std::env;
use std::io::{self, Write};
use std::path::Path;
use std::path::PathBuf;
use std::time::{Duration, SystemTime};

mod cli;

fn main() {
    if let Err(e) = cli::parse(env::args()).and_then(run) {
        e.exit();
    }
}

fn run(command: cli::Command) -> Result<(), cli::Error> {
    match command {
        Command::List(cli::ListParams {
            text,
            expire_in_days,
            directory,
        }) => list(&text, expire_in_days, directory),
        Command::ShowUuid(cli::ShowUuidParams { uuid, directory }) => show_uuid(&uuid, directory),
        Command::ShowFile(cli::ShowFileParams { file }) => show_file(&file),
        Command::Remove(cli::RemoveParams { ids, directory }) => remove(&ids, directory),
        Command::Clean(cli::CleanParams { directory }) => clean(directory),
    }
}

fn list(
    text: &Option<String>,
    expires_in_days: Option<u64>,
    directory: Option<PathBuf>,
) -> Result<(), cli::Error> {
    let dir = mp::with_directory(directory)?;
    let entries = mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>())?;
    let date =
        expires_in_days.map(|days| SystemTime::now() + Duration::from_secs(days * 24 * 60 * 60));
    let filter_string = text.as_ref();
    let mut profiles = mp::filter(entries, |profile| match (date, filter_string) {
        (Some(date), Some(string)) => {
            profile.info.expiration_date <= date && profile.info.contains(string)
        }
        (Some(date), _) => profile.info.expiration_date <= date,
        (_, Some(string)) => profile.info.contains(string),
        (_, _) => true,
    });
    profiles.sort_by(|a, b| a.info.creation_date.cmp(&b.info.creation_date));
    if profiles.is_empty() {
        Ok(())
    } else {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        for profile in &profiles {
            writeln!(&mut stdout, "{}\n", profile.info.description())?;
        }
        Ok(())
    }
}

fn show_uuid(uuid: &str, directory: Option<PathBuf>) -> Result<(), cli::Error> {
    let dir = mp::with_directory(directory)?;
    let profile = mp::find_by_uuid(&dir, &uuid)?;
    let xml = mp::show(&profile.path)?;
    writeln!(io::stdout(), "{}", xml)?;
    Ok(())
}

fn show_file(path: &Path) -> Result<(), cli::Error> {
    let xml = mp::show(&path)?;
    writeln!(io::stdout(), "{}", xml)?;
    Ok(())
}

fn remove(ids: &[String], directory: Option<PathBuf>) -> Result<(), cli::Error> {
    let dir = mp::with_directory(directory)?;
    let profiles = mp::find_by_ids(&dir, &ids)?;
    for profile in profiles {
        if mp::remove(&profile.path).is_ok() {
            println!("\nRemoved: {}", profile.info.description());
        } else {
            println!("\nError while removing '{}'", profile.info.uuid);
        }
    }
    Ok(())
}

fn clean(directory: Option<PathBuf>) -> Result<(), cli::Error> {
    fn concat(mut s1: String, s2: &str) -> String {
        s1.push_str(&s2);
        s1
    }

    let date = SystemTime::now();
    mp::with_directory(directory)
        .and_then(|dir| mp::entries(&dir).map(|entries| entries.collect::<Vec<_>>()))
        .map_err(|err| err.into())
        .map(|entries| mp::filter(entries, |profile| profile.info.expiration_date <= date))
        .and_then(|profiles| {
            if profiles.is_empty() {
                println!("All provisioning profiles are valid");
                Ok(())
            } else {
                profiles
                    .iter()
                    .map(|profile| {
                        std::fs::remove_file(&profile.path)
                            .map(|_| format!("'{}' was removed\n", profile.info.uuid))
                            .map_err(|err| format!("'{}' {}\n", profile.info.uuid, err))
                    })
                    .fold(Ok(String::new()), |acc, s| match (acc, s) {
                        (Ok(acc), Ok(s)) => Ok(concat(acc, &s)),
                        (Ok(acc), Err(s)) | (Err(acc), Ok(s)) | (Err(acc), Err(s)) => {
                            Err(concat(acc, &s))
                        }
                    })
                    .map(|s| println!("{}", s))
                    .map_err(cli::Error::Custom)
            }
        })
}
<|endoftext|>