<commit_before>//! Completes keywords.

use syntax::SyntaxKind;
use test_utils::mark;

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

pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    // complete keyword "crate" in use stmt
    let source_range = ctx.source_range();

    if ctx.use_item_syntax.is_some() {
        if ctx.path_qual.is_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);
    }

    // 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(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.token.kind() == SyntaxKind::COMMENT {
        mark::hit!(no_keyword_completion_in_comments);
        return;
    }
    if ctx.record_lit_syntax.is_some() {
        mark::hit!(no_keyword_completion_in_record_lit);
        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) || 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 || has_trait_or_impl_parent || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "fn", "fn $0() {}");
    }
    if (ctx.has_item_list_or_source_file_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 {
        add_keyword(ctx, acc, "enum", "enum $0 {}");
        add_keyword(ctx, acc, "struct", "struct $0");
        add_keyword(ctx, acc, "union", "union $0 {}");
    }

    if ctx.is_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 $0 {}");
        add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
    }

    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) || 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 || has_trait_or_impl_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) || ctx.block_expr_parent {
        add_keyword(ctx, acc, "static", "static ");
    };
    if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
        add_keyword(ctx, acc, "extern", "extern ");
    }
    if ctx.has_item_list_or_source_file_parent
        || has_trait_or_impl_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_impl_parent | ctx.has_field_list_parent {
        add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
        add_keyword(ctx, acc, "pub", "pub ");
    }

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

    add_keyword(
        ctx,
        acc,
        "return",
        match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
            (true, true) => "return $0;",
            (true, false) => "return;",
            (false, true) => "return $0",
            (false, false) => "return",
        },
    )
}

fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
    let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
        .kind(CompletionItemKind::Keyword);
    let builder = match ctx.config.snippet_cap {
        Some(cap) => {
            let tmp;
            let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
                mark::hit!(let_semi);
                tmp = format!("{};", snippet);
                &tmp
            } else {
                snippet
            };
            builder.insert_snippet(cap, snippet)
        }
        None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
    };
    acc.add(builder.build());
}

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

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

    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 $0",
            expect![[r#"
                kw crate::
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::$0",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );

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

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

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

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

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

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

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

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

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

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

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

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

    #[test]
    fn test_where_keyword() {
        check(
            r"trait A $0",
            expect![[r#"
                kw where
            "#]],
        );
        check(
            r"impl A $0",
            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$0
}
"#,
            expect![[""]],
        );
        check(
            r#"
/*
Some multi-line comment$0
*/
"#,
            expect![[""]],
        );
        check(
            r#"
/// Some doc comment
/// let test$0 = 1
"#,
            expect![[""]],
        );
    }

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

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

        check(
            r#"
//- /main.rs crate:main deps:std
use std::future::*;
fn foo() {
    let a = async {};
    a.$0
}

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

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

    #[test]
    fn before_field() {
        check(
            r#"
struct Foo {
    $0
    pub f: i32,
}
"#,
            expect![[r#"
                kw pub(crate)
                kw pub
            "#]],
        )
    }

    #[test]
    fn skip_struct_initializer() {
        mark::check!(no_keyword_completion_in_record_lit);
        check(
            r#"
struct Foo {
    pub f: i32,
}
fn foo() {
    Foo {
        $0
    }
}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn struct_initializer_field_expr() {
        check(
            r#"
struct Foo {
    pub f: i32,
}
fn foo() {
    Foo {
        f: $0
    }
}
"#,
            expect![[r#"
                kw match
                kw while
                kw loop
                kw if
                kw if let
                kw return
            "#]],
        );
    }

    #[test]
    fn let_semi() {
        mark::check!(let_semi);
        check_edit(
            "match",
            r#"
fn main() { let x = $0 }
"#,
            r#"
fn main() { let x = match $0 {}; }
"#,
        );

        check_edit(
            "if",
            r#"
fn main() {
    let x = $0
    let y = 92;
}
"#,
            r#"
fn main() {
    let x = if $0 {};
    let y = 92;
}
"#,
        );

        check_edit(
            "loop",
            r#"
fn main() {
    let x = $0
    bar();
}
"#,
            r#"
fn main() {
    let x = loop {$0};
    bar();
}
"#,
        );
    }
}
<commit_msg>Less annoying `mod` completion<commit_after>//! Completes keywords.

use syntax::SyntaxKind;
use test_utils::mark;

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

pub(crate) fn complete_use_tree_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    // complete keyword "crate" in use stmt
    let source_range = ctx.source_range();

    if ctx.use_item_syntax.is_some() {
        if ctx.path_qual.is_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);
    }

    // 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(crate) fn complete_expr_keyword(acc: &mut Completions, ctx: &CompletionContext) {
    if ctx.token.kind() == SyntaxKind::COMMENT {
        mark::hit!(no_keyword_completion_in_comments);
        return;
    }
    if ctx.record_lit_syntax.is_some() {
        mark::hit!(no_keyword_completion_in_record_lit);
        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) || 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 || has_trait_or_impl_parent || ctx.block_expr_parent
    {
        add_keyword(ctx, acc, "fn", "fn $0() {}");
    }
    if (ctx.has_item_list_or_source_file_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 {
        add_keyword(ctx, acc, "enum", "enum $0 {}");
        add_keyword(ctx, acc, "struct", "struct $0");
        add_keyword(ctx, acc, "union", "union $0 {}");
    }

    if ctx.is_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 $0 {}");
        add_keyword(ctx, acc, "if let", "if let $1 = $0 {}");
    }

    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) || 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 || has_trait_or_impl_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) || ctx.block_expr_parent {
        add_keyword(ctx, acc, "static", "static ");
    };
    if (ctx.has_item_list_or_source_file_parent) || ctx.block_expr_parent {
        add_keyword(ctx, acc, "extern", "extern ");
    }
    if ctx.has_item_list_or_source_file_parent
        || has_trait_or_impl_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_impl_parent | ctx.has_field_list_parent {
        add_keyword(ctx, acc, "pub(crate)", "pub(crate) ");
        add_keyword(ctx, acc, "pub", "pub ");
    }

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

    add_keyword(
        ctx,
        acc,
        "return",
        match (ctx.can_be_stmt, fn_def.ret_type().is_some()) {
            (true, true) => "return $0;",
            (true, false) => "return;",
            (false, true) => "return $0",
            (false, false) => "return",
        },
    )
}

fn add_keyword(ctx: &CompletionContext, acc: &mut Completions, kw: &str, snippet: &str) {
    let builder = CompletionItem::new(CompletionKind::Keyword, ctx.source_range(), kw)
        .kind(CompletionItemKind::Keyword);
    let builder = match ctx.config.snippet_cap {
        Some(cap) => {
            let tmp;
            let snippet = if snippet.ends_with('}') && ctx.incomplete_let {
                mark::hit!(let_semi);
                tmp = format!("{};", snippet);
                &tmp
            } else {
                snippet
            };
            builder.insert_snippet(cap, snippet)
        }
        None => builder.insert_text(if snippet.contains('$') { kw } else { snippet }),
    };
    acc.add(builder.build());
}

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

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

    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 $0",
            expect![[r#"
                kw crate::
                kw self
                kw super::
            "#]],
        );

        check(
            r"use a::$0",
            expect![[r#"
                kw self
                kw super::
            "#]],
        );

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

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

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

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

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

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

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

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

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

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

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

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

    #[test]
    fn test_where_keyword() {
        check(
            r"trait A $0",
            expect![[r#"
                kw where
            "#]],
        );
        check(
            r"impl A $0",
            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$0
}
"#,
            expect![[""]],
        );
        check(
            r#"
/*
Some multi-line comment$0
*/
"#,
            expect![[""]],
        );
        check(
            r#"
/// Some doc comment
/// let test$0 = 1
"#,
            expect![[""]],
        );
    }

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

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

        check(
            r#"
//- /main.rs crate:main deps:std
use std::future::*;
fn foo() {
    let a = async {};
    a.$0
}

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

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

    #[test]
    fn before_field() {
        check(
            r#"
struct Foo {
    $0
    pub f: i32,
}
"#,
            expect![[r#"
                kw pub(crate)
                kw pub
            "#]],
        )
    }

    #[test]
    fn skip_struct_initializer() {
        mark::check!(no_keyword_completion_in_record_lit);
        check(
            r#"
struct Foo {
    pub f: i32,
}
fn foo() {
    Foo {
        $0
    }
}
"#,
            expect![[r#""#]],
        );
    }

    #[test]
    fn struct_initializer_field_expr() {
        check(
            r#"
struct Foo {
    pub f: i32,
}
fn foo() {
    Foo {
        f: $0
    }
}
"#,
            expect![[r#"
                kw match
                kw while
                kw loop
                kw if
                kw if let
                kw return
            "#]],
        );
    }

    #[test]
    fn let_semi() {
        mark::check!(let_semi);
        check_edit(
            "match",
            r#"
fn main() { let x = $0 }
"#,
            r#"
fn main() { let x = match $0 {}; }
"#,
        );

        check_edit(
            "if",
            r#"
fn main() {
    let x = $0
    let y = 92;
}
"#,
            r#"
fn main() {
    let x = if $0 {};
    let y = 92;
}
"#,
        );

        check_edit(
            "loop",
            r#"
fn main() {
    let x = $0
    bar();
}
"#,
            r#"
fn main() {
    let x = loop {$0};
    bar();
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-test linked failure
// error-pattern:explicit failure
// Testing that runtime failure doesn't cause callbacks to abort abnormally.
// Instead the failure will be delivered after the callbacks return.
#![feature(std_misc, libc)]

extern crate libc;
use std::task;

mod rustrt {
    extern crate libc;

    extern {
        pub fn rust_dbg_call(cb: *u8, data: libc::uintptr_t)
                             -> libc::uintptr_t;
    }
}

extern fn cb(data: libc::uintptr_t) -> libc::uintptr_t {
    if data == 1 {
        data
    } else {
        count(data - 1) + count(data - 1)
    }
}

fn count(n: usize) -> usize {
    unsafe {
        task::deschedule();
        rustrt::rust_dbg_call(cb, n)
    }
}

fn main() {
    for _ in 0..10 {
        task::spawn(move|| {
            let result = count(5);
            println!("result = %?", result);
            panic!();
        });
    }
}
<commit_msg>Remove old runtime panic test<commit_after><|endoftext|>


<commit_before>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Result as StoreResult;
use libimagstore::storeid::StoreId;
use libimagutil::trace::trace_error;

mod ui;
mod viewer;

use ui::build_ui;
use viewer::Viewer;
use viewer::ViewInformation;
use viewer::stdout::StdoutViewer;

fn main() {
    let name = "imag-view";
    let version = &version!()[..];
    let about = "View entries (readonly)";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    rt.init_logger();

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    info!("No implementation yet");

    let entry_id = rt.cli().value_of("id").unwrap(); // enforced by clap

    if rt.cli().is_present("versions") {
        view_versions_of(entry_id, &rt);
    } else {
        let entry_version   = rt.cli().value_of("version");
        let view_header     = rt.cli().is_present("view-header");
        let view_content    = rt.cli().is_present("view-content");
        let view_copy       = rt.cli().is_present("view-copy");
        let keep_copy       = rt.cli().is_present("keep-copy");

        let scmd = rt.cli().subcommand_matches("view-in");
        if scmd.is_none() {
            debug!("No commandline call");
            exit(1);
        }
        let scmd = scmd.unwrap();

        let viewer = build_viewer(scmd);
        let entry = load_entry(entry_id, entry_version, &rt);
        if entry.is_err() {
            trace_error(&entry.err().unwrap());
            exit(1);
        }
        let entry = entry.unwrap();

        let view_info = ViewInformation {
            entry:          entry,
            view_header:    view_header,
            view_content:   view_content,
            view_copy:      view_copy,
            keep_copy:      keep_copy,
        };

        let viewer = StdoutViewer::new();
        viewer.view(view_info);
    }
}

// TODO: This is a shameless adaption of imag-store/src/util.rs
fn load_entry<'a>(id: &str,
                  version: Option<&str>,
                  rt: &'a Runtime)
    -> StoreResult<FileLockEntry<'a>>
{
    use std::ops::Deref;

    debug!("Checking path element for version");

    let version = {
        version.unwrap_or_else(|| {
            id.split("~").last().unwrap_or_else(|| {
                warn!("No version");
                exit(1);
            })
        })
    };

    debug!("Building path from {:?} and {:?}", id, version);
    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~{}", &id[1..id.len()], version));
    } else {
        path.push(format!("{}~{}", id, version));
    }

    // the above is the adaption...

    rt.store().retrieve(path)
}

fn view_versions_of(id: &str, rt: &Runtime) {
    use glob::glob;

    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~*", &id[1..id.len()]));
    } else {
        path.push(format!("{}~*", id));
    }

    if let Some(path) = path.to_str() {
        match glob(path) {
            Ok(paths) =>
                for entry in paths {
                    match entry {
                        Ok(path) => println!("{}", path.file_name().and_then(|s| s.to_str()).unwrap()),
                        Err(e)   => trace_error(e.error()),
                    }
                },
            Err(e) => {
                // trace_error(&e); // error seems not to be implemented
                debug!("Error in pattern");
                exit(1);
            },
        }
    } else {
        warn!("Could not build glob() argument!");
    }
}

<commit_msg>Print error in case of GlobError<commit_after>extern crate clap;
extern crate glob;
#[macro_use] extern crate log;
extern crate semver;
extern crate toml;
#[macro_use] extern crate version;

extern crate libimagrt;
extern crate libimagstore;
extern crate libimagutil;

use std::process::exit;

use clap::ArgMatches;

use libimagrt::runtime::Runtime;
use libimagstore::store::Entry;
use libimagstore::store::FileLockEntry;
use libimagstore::store::Result as StoreResult;
use libimagstore::storeid::StoreId;
use libimagutil::trace::trace_error;

mod ui;
mod viewer;

use ui::build_ui;
use viewer::Viewer;
use viewer::ViewInformation;
use viewer::stdout::StdoutViewer;

fn main() {
    let name = "imag-view";
    let version = &version!()[..];
    let about = "View entries (readonly)";
    let ui = build_ui(Runtime::get_default_cli_builder(name, version, about));
    let rt = {
        let rt = Runtime::new(ui);
        if rt.is_ok() {
            rt.unwrap()
        } else {
            println!("Could not set up Runtime");
            println!("{:?}", rt.err().unwrap());
            exit(1);
        }
    };

    rt.init_logger();

    debug!("Hello. Logging was just enabled");
    debug!("I already set up the Runtime object and build the commandline interface parser.");
    debug!("Lets get rollin' ...");

    info!("No implementation yet");

    let entry_id = rt.cli().value_of("id").unwrap(); // enforced by clap

    if rt.cli().is_present("versions") {
        view_versions_of(entry_id, &rt);
    } else {
        let entry_version   = rt.cli().value_of("version");
        let view_header     = rt.cli().is_present("view-header");
        let view_content    = rt.cli().is_present("view-content");
        let view_copy       = rt.cli().is_present("view-copy");
        let keep_copy       = rt.cli().is_present("keep-copy");

        let scmd = rt.cli().subcommand_matches("view-in");
        if scmd.is_none() {
            debug!("No commandline call");
            exit(1);
        }
        let scmd = scmd.unwrap();

        let viewer = build_viewer(scmd);
        let entry = load_entry(entry_id, entry_version, &rt);
        if entry.is_err() {
            trace_error(&entry.err().unwrap());
            exit(1);
        }
        let entry = entry.unwrap();

        let view_info = ViewInformation {
            entry:          entry,
            view_header:    view_header,
            view_content:   view_content,
            view_copy:      view_copy,
            keep_copy:      keep_copy,
        };

        let viewer = StdoutViewer::new();
        viewer.view(view_info);
    }
}

// TODO: This is a shameless adaption of imag-store/src/util.rs
fn load_entry<'a>(id: &str,
                  version: Option<&str>,
                  rt: &'a Runtime)
    -> StoreResult<FileLockEntry<'a>>
{
    use std::ops::Deref;

    debug!("Checking path element for version");

    let version = {
        version.unwrap_or_else(|| {
            id.split("~").last().unwrap_or_else(|| {
                warn!("No version");
                exit(1);
            })
        })
    };

    debug!("Building path from {:?} and {:?}", id, version);
    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~{}", &id[1..id.len()], version));
    } else {
        path.push(format!("{}~{}", id, version));
    }

    // the above is the adaption...

    rt.store().retrieve(path)
}

fn view_versions_of(id: &str, rt: &Runtime) {
    use glob::glob;

    let mut path = rt.store().path().clone();

    if id.chars().next() == Some('/') {
        path.push(format!("{}~*", &id[1..id.len()]));
    } else {
        path.push(format!("{}~*", id));
    }

    if let Some(path) = path.to_str() {
        match glob(path) {
            Ok(paths) =>
                for entry in paths {
                    match entry {
                        Ok(path) => println!("{}", path.file_name().and_then(|s| s.to_str()).unwrap()),
                        Err(e)   => trace_error(e.error()),
                    }
                },
            Err(e) => {
                warn!("{}", e); // trace_error(&e); // error seems not to be implemented
                debug!("Error in pattern");
                exit(1);
            },
        }
    } else {
        warn!("Could not build glob() argument!");
    }
}

<|endoftext|>


<commit_before>use crate::{ids, runtime};

use super::auto::{AsAny, HasInner};
use super::container::MaybeContainer;
use super::control::MaybeControl;
use super::closeable::MaybeCloseable;
use super::has_native_id::{HasNativeId, HasNativeIdInner};
use super::has_size::MaybeHasSize;
use super::has_visibility::MaybeHasVisibility;
use super::window::MaybeWindow;
use super::message::MaybeMessage;
use super::tray::MaybeTray;
use super::seal::Sealed;

#[cfg(feature = "type_check")]
use std::any::TypeId;

use std::any::Any;
use std::borrow::Cow;
use std::marker::PhantomData;
use std::rc::Rc;

pub trait Member: HasNativeId + AsAny + Sealed
        + MaybeControl + MaybeContainer + MaybeHasSize + MaybeHasVisibility + MaybeCloseable
        + MaybeWindow + MaybeTray + MaybeMessage {
    fn id(&self) -> ids::Id;
    fn tag(&self) -> Option<Cow<str>>;
    fn set_tag(&mut self, tag: Option<Cow<str>>);

    #[cfg(feature = "type_check")]
    unsafe fn type_id(&self) -> TypeId;

    fn as_member(&self) -> &dyn Member;
    fn as_member_mut(&mut self) -> &mut dyn Member;
    fn into_member(self: Box<Self>) -> Box<dyn Member>;
}

pub trait MemberInner: HasNativeIdInner + Sized + 'static {}

impl<II: MemberInner, T: HasInner<I = II> + HasNativeIdInner> MemberInner for T {}

#[repr(C)]
pub struct MemberBase {
    id: ids::Id,
    functions: MemberFunctions,
    app: usize,
    tag: Option<String>,

    _no_threads: PhantomData<Rc<()>>,
}
#[repr(C)]
pub struct AMember<T: MemberInner> {
    pub base: MemberBase,
    pub inner: T,
}

#[repr(C)]
pub struct MemberFunctions {
    _as_any: unsafe fn(&MemberBase) -> &dyn Any,
    _as_any_mut: unsafe fn(&mut MemberBase) -> &mut dyn Any,
    _as_member: unsafe fn(&MemberBase) -> &dyn Member,
    _as_member_mut: unsafe fn(&mut MemberBase) -> &mut dyn Member,
}
impl MemberFunctions {
    #[inline]
    pub fn new(_as_any: unsafe fn(&MemberBase) -> &dyn Any, _as_any_mut: unsafe fn(&mut MemberBase) -> &mut dyn Any, _as_member: unsafe fn(&MemberBase) -> &dyn Member, _as_member_mut: unsafe fn(&mut MemberBase) -> &mut dyn Member) -> Self {
        MemberFunctions {
            _as_any,
            _as_any_mut,
            _as_member,
            _as_member_mut,
        }
    }
}

impl MemberBase {
    #[inline]
    pub fn with_functions(functions: MemberFunctions) -> Self {
        MemberBase {
            id: ids::Id::next(),
            functions: functions,
            app: runtime::APPLICATION.with(|a| *a.borrow()),
            tag: None,
            _no_threads: PhantomData,
        }
    }
    pub fn id(&self) -> ids::Id {
        self.id
    }
    pub fn tag(&self) -> Option<Cow<str>> {
        self.tag.as_ref().map(|t| t.as_str().into())
    }
    pub fn set_tag(&mut self, tag: Option<Cow<str>>) {
        self.tag = tag.map(|t| t.into());
    }
    #[inline]
    pub fn as_any(&self) -> &dyn Any {
        unsafe { (self.functions._as_any)(self) }
    }
    #[inline]
    pub fn as_any_mut(&mut self) -> &mut dyn Any {
        unsafe { (self.functions._as_any_mut)(self) }
    }
    #[inline]
    pub fn as_member(&self) -> &dyn Member {
        unsafe { (self.functions._as_member)(self) }
    }
    #[inline]
    pub fn as_member_mut(&mut self) -> &mut dyn Member {
        unsafe { (self.functions._as_member_mut)(self) }
    }
}
impl<T: MemberInner> HasNativeId for AMember<T> {
    #[inline]
    unsafe fn native_id(&self) -> usize {
        self.inner.native_id().into()
    }
}
impl<T: MemberInner> Member for AMember<T> {
    #[inline]
    fn id(&self) -> ids::Id {
        self.base.id
    }
    fn tag(&self) -> Option<Cow<str>> {
        self.base.tag()
    }
    fn set_tag(&mut self, tag: Option<Cow<str>>) {
        self.base.set_tag(tag)
    }
    #[cfg(feature = "type_check")]
    unsafe fn type_id(&self) -> TypeId {
        self.inner.native_id().type_id()
    }

    #[inline]
    fn as_member(&self) -> &dyn Member {
        self
    }
    #[inline]
    fn as_member_mut(&mut self) -> &mut dyn Member {
        self
    }
    #[inline]
    fn into_member(self: Box<Self>) -> Box<dyn Member> {
        self
    }
}
impl<T: MemberInner> AsAny for AMember<T> {
    #[inline]
    fn as_any(&self) -> &dyn Any {
        self
    }
    #[inline]
    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
    #[inline]
    fn into_any(self: Box<Self>) -> Box<dyn Any> {
        self
    }
}
impl<T: MemberInner> AMember<T> {
    #[inline]
    pub fn with_inner(inner: T, params: MemberFunctions) -> Self {
        AMember {
            inner: inner,
            base: MemberBase::with_functions(params),
        }
    }
}

impl<T: MemberInner> HasInner for AMember<T> {
    type I = T;

    #[inline]
    fn inner(&self) -> &Self::I {
        &self.inner
    }
    #[inline]
    fn inner_mut(&mut self) -> &mut Self::I {
        &mut self.inner
    }
    #[inline]
    fn into_inner(self) -> Self::I {
        self.inner
    }
}

impl<T: MemberInner> Sealed for AMember<T> {}
<commit_msg>Even more maybes<commit_after>use crate::{ids, runtime};

use super::auto::{AsAny, HasInner};
use super::container::MaybeContainer;
use super::control::MaybeControl;
use super::closeable::MaybeCloseable;
use super::clickable::MaybeClickable;
use super::has_native_id::{HasNativeId, HasNativeIdInner};
use super::has_size::MaybeHasSize;
use super::has_visibility::MaybeHasVisibility;
use super::window::MaybeWindow;
use super::message::MaybeMessage;
use super::tray::MaybeTray;
use super::seal::Sealed;
use super::has_image::MaybeHasImage;
use super::has_layout::MaybeHasLayout;
use super::has_label::MaybeHasLabel;
use super::has_progress::MaybeHasProgress;

#[cfg(feature = "type_check")]
use std::any::TypeId;

use std::any::Any;
use std::borrow::Cow;
use std::marker::PhantomData;
use std::rc::Rc;

pub trait Member: HasNativeId + AsAny + Sealed
        + MaybeControl + MaybeContainer + MaybeHasSize + MaybeHasVisibility + MaybeHasImage + MaybeHasLayout + MaybeHasLabel + MaybeHasProgress + MaybeCloseable + MaybeClickable
        + MaybeWindow + MaybeTray + MaybeMessage {
    fn id(&self) -> ids::Id;
    fn tag(&self) -> Option<Cow<str>>;
    fn set_tag(&mut self, tag: Option<Cow<str>>);

    #[cfg(feature = "type_check")]
    unsafe fn type_id(&self) -> TypeId;

    fn as_member(&self) -> &dyn Member;
    fn as_member_mut(&mut self) -> &mut dyn Member;
    fn into_member(self: Box<Self>) -> Box<dyn Member>;
}

pub trait MemberInner: HasNativeIdInner + Sized + 'static {}

impl<II: MemberInner, T: HasInner<I = II> + HasNativeIdInner> MemberInner for T {}

#[repr(C)]
pub struct MemberBase {
    id: ids::Id,
    functions: MemberFunctions,
    app: usize,
    tag: Option<String>,

    _no_threads: PhantomData<Rc<()>>,
}
#[repr(C)]
pub struct AMember<T: MemberInner> {
    pub base: MemberBase,
    pub inner: T,
}

#[repr(C)]
pub struct MemberFunctions {
    _as_any: unsafe fn(&MemberBase) -> &dyn Any,
    _as_any_mut: unsafe fn(&mut MemberBase) -> &mut dyn Any,
    _as_member: unsafe fn(&MemberBase) -> &dyn Member,
    _as_member_mut: unsafe fn(&mut MemberBase) -> &mut dyn Member,
}
impl MemberFunctions {
    #[inline]
    pub fn new(_as_any: unsafe fn(&MemberBase) -> &dyn Any, _as_any_mut: unsafe fn(&mut MemberBase) -> &mut dyn Any, _as_member: unsafe fn(&MemberBase) -> &dyn Member, _as_member_mut: unsafe fn(&mut MemberBase) -> &mut dyn Member) -> Self {
        MemberFunctions {
            _as_any,
            _as_any_mut,
            _as_member,
            _as_member_mut,
        }
    }
}

impl MemberBase {
    #[inline]
    pub fn with_functions(functions: MemberFunctions) -> Self {
        MemberBase {
            id: ids::Id::next(),
            functions: functions,
            app: runtime::APPLICATION.with(|a| *a.borrow()),
            tag: None,
            _no_threads: PhantomData,
        }
    }
    pub fn id(&self) -> ids::Id {
        self.id
    }
    pub fn tag(&self) -> Option<Cow<str>> {
        self.tag.as_ref().map(|t| t.as_str().into())
    }
    pub fn set_tag(&mut self, tag: Option<Cow<str>>) {
        self.tag = tag.map(|t| t.into());
    }
    #[inline]
    pub fn as_any(&self) -> &dyn Any {
        unsafe { (self.functions._as_any)(self) }
    }
    #[inline]
    pub fn as_any_mut(&mut self) -> &mut dyn Any {
        unsafe { (self.functions._as_any_mut)(self) }
    }
    #[inline]
    pub fn as_member(&self) -> &dyn Member {
        unsafe { (self.functions._as_member)(self) }
    }
    #[inline]
    pub fn as_member_mut(&mut self) -> &mut dyn Member {
        unsafe { (self.functions._as_member_mut)(self) }
    }
}
impl<T: MemberInner> HasNativeId for AMember<T> {
    #[inline]
    unsafe fn native_id(&self) -> usize {
        self.inner.native_id().into()
    }
}
impl<T: MemberInner> Member for AMember<T> {
    #[inline]
    fn id(&self) -> ids::Id {
        self.base.id
    }
    fn tag(&self) -> Option<Cow<str>> {
        self.base.tag()
    }
    fn set_tag(&mut self, tag: Option<Cow<str>>) {
        self.base.set_tag(tag)
    }
    #[cfg(feature = "type_check")]
    unsafe fn type_id(&self) -> TypeId {
        self.inner.native_id().type_id()
    }

    #[inline]
    fn as_member(&self) -> &dyn Member {
        self
    }
    #[inline]
    fn as_member_mut(&mut self) -> &mut dyn Member {
        self
    }
    #[inline]
    fn into_member(self: Box<Self>) -> Box<dyn Member> {
        self
    }
}
impl<T: MemberInner> AsAny for AMember<T> {
    #[inline]
    fn as_any(&self) -> &dyn Any {
        self
    }
    #[inline]
    fn as_any_mut(&mut self) -> &mut dyn Any {
        self
    }
    #[inline]
    fn into_any(self: Box<Self>) -> Box<dyn Any> {
        self
    }
}
impl<T: MemberInner> AMember<T> {
    #[inline]
    pub fn with_inner(inner: T, params: MemberFunctions) -> Self {
        AMember {
            inner: inner,
            base: MemberBase::with_functions(params),
        }
    }
}

impl<T: MemberInner> HasInner for AMember<T> {
    type I = T;

    #[inline]
    fn inner(&self) -> &Self::I {
        &self.inner
    }
    #[inline]
    fn inner_mut(&mut self) -> &mut Self::I {
        &mut self.inner
    }
    #[inline]
    fn into_inner(self) -> Self::I {
        self.inner
    }
}

impl<T: MemberInner> Sealed for AMember<T> {}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::collections::HashMap;

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    let (qtx, qrx) = mpsc::channel();

    for (k, nwk) in config.irc_networks
                          .iter()
                          .filter_map(|cfg| {
                              match ircp::IrcServer::from_config(cfg.clone()) {
                                  Ok(conn) => Some(conn),
                                  Err(ref err) => {
                                      println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                      None
                                  }
                              }
                          })
                          .enumerate() {
        start_worker(nwk,
                     tx.clone(),
                     config.owners.clone(),
                     config.command_prefix.clone(),
                     qtx.clone(),
                     format!("{}", k));
    }
    util::ignore(qrx.recv());
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    },
    Raw {
        msg: String
    },
    Multiline {
        lang: String,
        code: String
    },
    CancelMultiline { lang: String }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if let Some(x) = try_cmd(&message, cmd_prefix, is_owner) {
            return Some(IrcMessage { sender: sender, data: x });
        }

        let message = message.trim();
        let tok: Vec<&str> = message.splitn(2, |c| c == '>' || c == '#' || c == '$').collect();
        if tok.len() < 2 {
            return None;
        }

        Some(IrcMessage {
            sender: sender,
            data: match &message[tok[0].len() .. tok[0].len() + 1] {
                "$" => MessageData::Multiline { lang: tok[0].to_owned(), code: tok[1].to_owned() },
                ">" => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: true },
                "#" if is_owner => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: false },
                _ => return None
            }
        })
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str, owner: bool) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" if owner => Some(MessageData::Quit),
            "rehash" if owner => Some(MessageData::Rehash),
            "restart" if owner && !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            "raw" if owner && !args.is_empty() => {
                Some(MessageData::Raw {
                    msg: {
                        let mut m = args.join(" ");
                        m.push_str("\r\n");
                        m
                    }
                })
            },
            "cancel" if !args.is_empty() => Some(MessageData::CancelMultiline { lang: args[0].to_owned() }),
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } | MessageData::Rehash | MessageData::Restart { .. } | MessageData::Multiline { .. } | MessageData::CancelMultiline { .. } => {
                        let conn = conn.clone();
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Raw { msg: raw_msg } => {
                        match raw_msg.parse::<ircp::Message>() {
                            Ok(raw_msg) => {
                                println!("{} sent raw: {:?}", msg.sender.sender, raw_msg);
                                if let Err(err) = conn.send(raw_msg) {
                                    send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err))
                                }
                            }
                            Err(err) => send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err)),
                        };
                    }
                    MessageData::Quit => {
                        util::ignore(quit_handle.send(()));
                        break 'connection;
                    }
                }
            }
        }
    }
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    let mut mlbufs: HashMap<(MessageSource, String), String> = HashMap::new();
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        macro_rules! key {
            ($x:expr) => { (msg.sender.clone(), $x.clone()) }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let code = match mlbufs.remove(&key!(lang)) {
                    Some(mut buf) => { buf.push_str(&code); buf }
                    None => code
                };
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            MessageData::Multiline { lang, code } => {
                let key = key!(lang);
                if let Some(buf) = mlbufs.get_mut(&key) {
                    buf.push_str(&code);
                    continue;
                }
                mlbufs.insert(key, code);
            }
            MessageData::CancelMultiline { lang } => {
                match mlbufs.remove(&key!(lang)) {
                    Some(_) => reply!(&format!("{}: OK, cleared {} buffer", msg.sender.sender, lang)),
                    None => reply!(&format!("{}: no buffer for {}", msg.sender.sender, lang))
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<commit_msg>Add newline after each multiline part<commit_after>#![feature(plugin)]
#![plugin(clippy)]

extern crate irc as irclib;
extern crate evalbotlib as backend;
extern crate rustc_serialize;

use std::thread;
use std::time::Duration;
use std::sync::mpsc;
use std::collections::HashMap;

use backend::{EvalSvc, EvalSvcCfg, Response, util};

use self::irclib::client::server::NetIrcServer;
use self::irclib::client::prelude as ircp;
use self::irclib::client::prelude::{Server, ServerExt};

macro_rules! send_msg {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_privmsg($dest, $line) {
            println!("failed to send message: {}", x)
        }
    };
}

macro_rules! send_notice {
    ($conn:expr, $dest:expr, $line:expr) => {
        if let Err(x) = $conn.send_notice($dest, $line) {
            println!("failed to send notice: {}", x)
        }
    };
}

type EvalWorkerSender = mpsc::Sender<(IrcMessage, SendMsgFnBox)>;
type EvalWorkerReceiver = mpsc::Receiver<(IrcMessage, SendMsgFnBox)>;
type SendMsgFnBox = Box<Fn(&str, &str) + Send>;

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct IrcCfg {
    output: OutputCfg,
    command_prefix: String,
    owners: Vec<String>,
    irc_networks: Vec<ircp::Config>
}

#[derive(Clone, RustcDecodable, Default, PartialEq, Debug)]
struct OutputCfg {
    chan_output_prefix: String,
    max_channel_lines: usize,
    max_channel_line_len: usize,
    max_priv_lines: usize
}

fn main() {
    let config = match util::decode::<IrcCfg>("evalbot.irc.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.irc.toml: {:?}", x);
            return;
        }
    };
    let (tx, rx) = mpsc::channel();

    {
        let svc = match start_evalsvc() {
            Ok(x) => x,
            Err(()) => return,
        };
        let ocfg = config.output.clone();
        thread::spawn(move || eval_worker(svc, rx, ocfg));
    }

    let (qtx, qrx) = mpsc::channel();

    for (k, nwk) in config.irc_networks
                          .iter()
                          .filter_map(|cfg| {
                              match ircp::IrcServer::from_config(cfg.clone()) {
                                  Ok(conn) => Some(conn),
                                  Err(ref err) => {
                                      println!("failed to create IRC connection from Config {:#?}: {:?}", cfg, err);
                                      None
                                  }
                              }
                          })
                          .enumerate() {
        start_worker(nwk,
                     tx.clone(),
                     config.owners.clone(),
                     config.command_prefix.clone(),
                     qtx.clone(),
                     format!("{}", k));
    }
    util::ignore(qrx.recv());
}

fn start_evalsvc() -> Result<EvalSvc, ()> {
    let evalcfg = match util::decode::<EvalSvcCfg>("evalbot.toml") {
        Ok(x) => x,
        Err(x) => {
            println!("failed to read evalbot.toml: {:?}", x);
            return Err(());
        }
    };
    Ok(EvalSvc::new(evalcfg))
}

fn start_worker(conn: NetIrcServer,
    svc: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String)
                -> thread::JoinHandle<()> {
    thread::spawn(move || {
        worker(conn, svc, owners, cmd_prefix, quit_handle, conn_hash);
    })
}

#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct MessageSource {
    chan: Option<String>,
    sender: String,
    conn_hash: String
}

impl MessageSource {
    fn reply_to(&self) -> &str {
        if let Some(ref chan) = self.chan { chan } else { &self.sender }
    }

    fn is_chan(&self) -> bool {
        self.chan.is_some()
    }
}

#[derive(Debug)]
struct IrcMessage {
    sender: MessageSource,
    data: MessageData
}

#[derive(Debug)]
enum MessageData {
    EvalReq {
        lang: String,
        code: String,
        timeout: bool
    },
    Quit,
    Rehash,
    Restart {
        lang: String
    },
    Raw {
        msg: String
    },
    Multiline {
        lang: String,
        code: String
    },
    CancelMultiline { lang: String }
}

unsafe impl Sync for IrcMessage {}

fn parse_msg(conn_hash: &str, message: &ircp::Message, owners: &[String], cmd_prefix: &str) -> Option<IrcMessage> {
    let sender = message.source_nickname().unwrap_or("").to_owned();
    if let ircp::Command::PRIVMSG(ref target, ref message) = message.command {
        let target = target.to_owned();
        let is_owner = owners.iter().any(|x| *x == sender);
        let sender = {
            let in_channel = target.starts_with('#');
            if !in_channel && message.contains('\x01') {
                // ignore CTCP
                return None;
            }

            MessageSource {
                chan: if in_channel { Some(target) } else { None },
                sender: sender,
                conn_hash: conn_hash.to_owned()
            }
        };

        if let Some(x) = try_cmd(&message, cmd_prefix, is_owner) {
            return Some(IrcMessage { sender: sender, data: x });
        }

        let message = message.trim();
        let tok: Vec<&str> = message.splitn(2, |c| c == '>' || c == '#' || c == '$').collect();
        if tok.len() < 2 {
            return None;
        }

        Some(IrcMessage {
            sender: sender,
            data: match &message[tok[0].len() .. tok[0].len() + 1] {
                "$" => MessageData::Multiline { lang: tok[0].to_owned(), code: tok[1].to_owned() },
                ">" => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: true },
                "#" if is_owner => MessageData::EvalReq { lang: tok[0].to_owned(), code: tok[1].to_owned(), timeout: false },
                _ => return None
            }
        })
    } else {
        None
    }
}

fn try_cmd(msg: &str, cmd_prefix: &str, owner: bool) -> Option<MessageData> {
    if let Some((cmd, args)) = parse_cmd(msg, cmd_prefix) {
        match &cmd as &str {
            "quit" if owner => Some(MessageData::Quit),
            "rehash" if owner => Some(MessageData::Rehash),
            "restart" if owner && !args.is_empty() => Some(MessageData::Restart { lang: args[0].to_owned() }),
            "raw" if owner && !args.is_empty() => {
                Some(MessageData::Raw {
                    msg: {
                        let mut m = args.join(" ");
                        m.push_str("\r\n");
                        m
                    }
                })
            },
            "cancel" if !args.is_empty() => Some(MessageData::CancelMultiline { lang: args[0].to_owned() }),
            _ => None,
        }
    } else {
        None
    }
}

fn parse_cmd(msg: &str, cmd_prefix: &str) -> Option<(String, Vec<String>)> {
    let tok: Vec<&str> = match msg.splitn(2, cmd_prefix).nth(1).map(|x| x.split(' ').collect()) {
        Some(x) => x,
        None => return None,
    };

    if tok.is_empty() {
        return None;
    }

    Some((tok[0].to_owned(),
          tok.into_iter().skip(1).map(|x| x.to_owned()).collect::<Vec<String>>()))
}

fn worker(conn: NetIrcServer,
    tx: EvalWorkerSender,
    owners: Vec<String>,
    cmd_prefix: String,
    quit_handle: mpsc::Sender<()>,
    conn_hash: String) {
    'connection: loop {
        println!("connecting to to {:?} as {:?}", conn.config().server, conn.config().nickname);
        while let Err(x) = conn.identify() {
            println!("error while identify()ing; retrying: {:?}", x);
            thread::sleep(Duration::new(1, 0));
        }
        println!("connected to {:?} as {:?}", conn.config().server, conn.config().nickname);
        for msg in conn.iter() {
            let msg = match msg {
                Ok(x) => x,
                Err(_) => continue,
            };

            if let Some(msg) = parse_msg(&conn_hash, &msg, &owners, &cmd_prefix) {
                println!("M: {:?}", msg);
                match msg.data {
                    MessageData::EvalReq { .. } | MessageData::Rehash | MessageData::Restart { .. } | MessageData::Multiline { .. } | MessageData::CancelMultiline { .. } => {
                        let conn = conn.clone();
                        util::ignore(tx.send((msg, Box::new(move |to, r| send_msg!(conn, to, r)))))
                    }
                    MessageData::Raw { msg: raw_msg } => {
                        match raw_msg.parse::<ircp::Message>() {
                            Ok(raw_msg) => {
                                println!("{} sent raw: {:?}", msg.sender.sender, raw_msg);
                                if let Err(err) = conn.send(raw_msg) {
                                    send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err))
                                }
                            }
                            Err(err) => send_notice!(conn, &msg.sender.sender, &format!("error: {:?}", err)),
                        };
                    }
                    MessageData::Quit => {
                        util::ignore(quit_handle.send(()));
                        break 'connection;
                    }
                }
            }
        }
    }
}

fn eval_worker(mut svc: EvalSvc, rx: EvalWorkerReceiver, ocfg: OutputCfg) {
    let mut mlbufs: HashMap<(MessageSource, String), String> = HashMap::new();
    while let Ok((msg, callback)) = rx.recv() {
        macro_rules! reply {
            ($x:expr) => { callback(msg.sender.reply_to(), $x); }
        }
        macro_rules! key {
            ($x:expr) => { (msg.sender.clone(), $x.clone()) }
        }
        match msg.data {
            MessageData::EvalReq { lang, code, timeout } => {
                let code = match mlbufs.remove(&key!(lang)) {
                    Some(mut buf) => { buf.push_str(&code); buf.push_str("\n"); buf }
                    None => code
                };
                let ocfg = ocfg.clone();
                let sender = msg.sender.clone();
                svc.eval(&lang,
                         code,
                         timeout,
                         Some(format!("{}{}", msg.sender.conn_hash, msg.sender.reply_to())),
                         Box::new(move |resp| respond(resp, ocfg, sender, callback)))
            }
            MessageData::Rehash => {
                if let Ok(nsvc) = start_evalsvc() {
                    reply!("rehashed");
                    svc = nsvc;
                } else {
                    reply!("read config failed; check logs");
                }
            }
            MessageData::Restart { lang } => {
                match svc.restart(&lang) {
                    Ok(_) => reply!(&format!("restarted {}", lang)),
                    Err(_) => reply!(&format!("no such language {}", lang)),
                }
            }
            MessageData::Multiline { lang, code } => {
                let key = key!(lang);
                if let Some(buf) = mlbufs.get_mut(&key) {
                    buf.push_str(&code);
                    buf.push_str("\n");
                    continue;
                }
                mlbufs.insert(key, code);
            }
            MessageData::CancelMultiline { lang } => {
                match mlbufs.remove(&key!(lang)) {
                    Some(_) => reply!(&format!("{}: OK, cleared {} buffer", msg.sender.sender, lang)),
                    None => reply!(&format!("{}: no buffer for {}", msg.sender.sender, lang))
                }
            }
            _ => println!("invalid thing sent to eval_worker?"),
        };
    }
}

fn sanitise_output(input: &str, prefix: Option<&str>, max_len: usize, max_lines: usize) -> Vec<String> {
    let (mut ret, initial_lines): (Vec<String>, usize) = {
        let med = input.lines()
                       .map(|l| l.trim())
                       .filter(|l| !l.is_empty())
                       .flat_map(|l| l.split(util::LengthPattern(max_len)))
                       .collect::<Vec<_>>();

        (if let Some(prefix) = prefix {
            med.iter().take(max_lines).map(|l| prefix.to_owned() + l).collect()
        } else {
            med.iter().take(max_lines).map(|l| (*l).to_owned()).collect()
        },
         med.len())
    };
    if ret.is_empty() {
        ret.push("(ok, no output)".to_owned());
    } else if ret.len() < initial_lines {
        if let Some(ref mut l) = ret.last_mut() {
            l.push_str("... (truncated)");
        }
    }
    ret
}

#[allow(boxed_local)] // send_msg is already boxed anyway
fn respond(resp: Response, cfg: OutputCfg, to: MessageSource, send_msg: SendMsgFnBox) {
    let (response, error) = match resp {
        Response::Success(x) => (x, false),
        Response::Error(x) => (x, true),
        _ => return,
    };
    let output = sanitise_output(&response,
                                 if to.is_chan() && !error { Some(&cfg.chan_output_prefix) } else { None },
                                 if to.is_chan() { cfg.max_channel_line_len } else { 425 },
                                 if to.is_chan() { cfg.max_channel_lines } else { cfg.max_priv_lines });
    for line in &output {
        send_msg(to.reply_to(), line);
    }
}
<|endoftext|>


<commit_before>use getopts::Options;

#[derive(Debug)]
pub struct Config {
    /// The directory documentation output was generated in
    pub doc_dir: String,
    /// The file documentation was generated for, with docck commands to check
    pub template: String,
}

pub fn parse_config(args: Vec<String>) -> Config {
    let mut opts = Options::new();
    opts.reqopt("", "doc-dir", "Path to the documentation directory", "PATH")
        .reqopt("", "template", "Path to the template file", "PATH")
        .optflag("h", "help", "show this message");

    let (argv0, args_) = args.split_first().unwrap();
    if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
        let message = format!("Usage: {} <doc-dir> <template>", argv0);
        println!("{}", opts.usage(&message));
        println!();
        panic!()
    }

    let matches = opts.parse(args_).unwrap();

    if matches.opt_present("h") || matches.opt_present("help") {
        let message = format!("Usage: {} <doc-dir> <template>", argv0);
        println!("{}", opts.usage(&message));
        std::process::exit(1);
    }

    Config {
        doc_dir: matches.opt_str("doc-dir").unwrap(),
        template: matches.opt_str("template").unwrap(),
    }
}
<commit_msg>Shift another panic into an exit<commit_after>use getopts::Options;

#[derive(Debug)]
pub struct Config {
    /// The directory documentation output was generated in
    pub doc_dir: String,
    /// The file documentation was generated for, with docck commands to check
    pub template: String,
}

pub fn parse_config(args: Vec<String>) -> Config {
    let mut opts = Options::new();
    opts.reqopt("", "doc-dir", "Path to the documentation directory", "PATH")
        .reqopt("", "template", "Path to the template file", "PATH")
        .optflag("h", "help", "show this message");

    let (argv0, args_) = args.split_first().unwrap();
    if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
        let message = format!("Usage: {} <doc-dir> <template>", argv0);
        println!("{}", opts.usage(&message));
        std::process::exit(1);
    }

    let matches = opts.parse(args_).unwrap();

    if matches.opt_present("h") || matches.opt_present("help") {
        let message = format!("Usage: {} <doc-dir> <template>", argv0);
        println!("{}", opts.usage(&message));
        std::process::exit(1);
    }

    Config {
        doc_dir: matches.opt_str("doc-dir").unwrap(),
        template: matches.opt_str("template").unwrap(),
    }
}
<|endoftext|>


<commit_before>use std::{mem, ptr, time::Duration};
use wlroots_sys::{timespec, wlr_output, wlr_output_damage, wlr_output_damage_add,
                  wlr_output_damage_add_box, wlr_output_damage_add_whole,
                  wlr_output_damage_create, wlr_output_damage_destroy,
                  wlr_output_damage_make_current, wlr_output_damage_swap_buffers,
                  pixman_region32_fini, pixman_region32_init, pixman_region32_t};

use Area;

/// A pixman region, used for damage tracking.
pub struct PixmanRegion {
    pub region: pixman_region32_t
}

impl PixmanRegion {
    /// Make a new pixman region.
    pub fn new() -> Self {
        unsafe {
            // NOTE Rational for uninitialized memory:
            // We are automatically filling it in with pixman_region32_init.
            let mut region = mem::uninitialized();
            pixman_region32_init(&mut region);
            PixmanRegion { region }
        }
    }
}

impl Drop for PixmanRegion {
    fn drop(&mut self) {
        unsafe { pixman_region32_fini(&mut self.region) }
    }
}

#[derive(Debug)]
/// Tracks damage for an output.
///
/// When a `frame` event is emitted, `make_current` should be
/// called. If necessary, the output should be repainted and
/// `swap_buffers` should be called.
///
/// No rendering should happen outside a `frame` event handler.
pub struct OutputDamage {
    damage: *mut wlr_output_damage
}

impl OutputDamage {
    /// Makes a new `OutputDamage` bound to the given Output.
    ///
    /// # Safety
    /// This function is unsafe because the `OutputDamage` should not outlive the
    /// past in `Output`.
    pub(crate) unsafe fn new(output: *mut wlr_output) -> Self {
        unsafe {
            let damage = wlr_output_damage_create(output);
            if damage.is_null() {
                panic!("Damage was none")
            }
            OutputDamage { damage }
        }
    }

    pub(crate) unsafe fn from_ptr(damage: *mut wlr_output_damage) -> Self {
        OutputDamage { damage }
    }

    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_output_damage {
        self.damage
    }

    /// Just like `std::clone::Clone` but unsafe.
    ///
    /// # Unsafety
    /// You can create a reference leak using this method very easily,
    /// and it could make it so that weak handles to an output are dangling.
    ///
    /// This exists due to an issue in output_manager.rs that might be fixed
    /// with NLL, so if this is no longer necessary it should be removed asap.
    pub(crate) unsafe fn clone(&self) -> Self {
        OutputDamage { damage: self.damage }
    }

    /// Makes the output rendering context current.
    /// Returns `true` if `wlr_output_damage_swap_buffers` needs to be called.
    ///
    ///The region of the output that needs to be repainted is added to `damage`.
    pub fn make_current(&mut self, damage: Option<&mut PixmanRegion>) -> bool {
        unsafe {
            let mut res = false;
            let damage = match damage {
                Some(region) => &mut region.region as *mut _,
                None => ptr::null_mut()
            };
            wlr_output_damage_make_current(self.damage, &mut res, damage);
            res
        }
    }

    /// Swaps the output buffers.
    ///
    /// If the time of the frame isn't known, set `when` to `None`.
    ///
    /// Swapping buffers schedules a `frame` event.
    pub fn swap_buffers(&mut self,
                        when: Option<Duration>,
                        damage: Option<&mut PixmanRegion>)
                        -> bool {
        unsafe {
            let when = when.map(|duration| {
                                    timespec { tv_sec: duration.as_secs() as i64,
                                               tv_nsec: duration.subsec_nanos() as i64 }
                                });
            let when_ptr =
                when.map(|mut duration| &mut duration as *mut _).unwrap_or_else(|| ptr::null_mut());
            let damage = match damage {
                Some(region) => &mut region.region as *mut _,
                None => ptr::null_mut()
            };
            wlr_output_damage_swap_buffers(self.damage, when_ptr, damage)
        }
    }

    /// Accumulates damage and schedules a `frame` event.
    pub fn add(&mut self, damage: &mut PixmanRegion) {
        unsafe {
            wlr_output_damage_add(self.damage, &mut damage.region);
        }
    }

    /// Damages the whole output and schedules a `frame` event.
    pub fn add_whole(&mut self) {
        unsafe { wlr_output_damage_add_whole(self.damage) }
    }

    /// Accumulates damage from an `Area` and schedules a `frame` event.
    pub fn add_area(&mut self, mut area: Area) {
        unsafe { wlr_output_damage_add_box(self.damage, &mut area.0) }
    }
}

impl Drop for OutputDamage {
    fn drop(&mut self) {
        wlr_log!(L_DEBUG, "Dropped OutputDamage {:p}", self.damage);
        unsafe {
            wlr_output_damage_destroy(self.damage);
        }
    }
}
<commit_msg>Added PixmanRegion::rectangle<commit_after>use libc::{c_int, c_uint};
use std::{mem, ptr, time::Duration};
use wlroots_sys::{timespec, wlr_output, wlr_output_damage, wlr_output_damage_add,
                  wlr_output_damage_add_box, wlr_output_damage_add_whole,
                  wlr_output_damage_create, wlr_output_damage_destroy,
                  wlr_output_damage_make_current, wlr_output_damage_swap_buffers,
                  pixman_region32_fini, pixman_region32_init, pixman_region32_t,
                  pixman_region32_union_rect};

use Area;

/// A pixman region, used for damage tracking.
#[derive(Debug)]
pub struct PixmanRegion {
    pub region: pixman_region32_t
}

impl PixmanRegion {
    /// Make a new pixman region.
    pub fn new() -> Self {
        unsafe {
            // NOTE Rational for uninitialized memory:
            // We are automatically filling it in with pixman_region32_init.
            let mut region = mem::uninitialized();
            pixman_region32_init(&mut region);
            PixmanRegion { region }
        }
    }

    pub fn rectangle(&mut self, x: c_int, y: c_int, width: c_uint, height: c_uint) {
        unsafe {
            let region_ptr = &mut self.region as *mut _;
            pixman_region32_union_rect(region_ptr, region_ptr, x, y, width, height);
        }
    }
}

impl Drop for PixmanRegion {
    fn drop(&mut self) {
        unsafe { pixman_region32_fini(&mut self.region) }
    }
}

#[derive(Debug)]
/// Tracks damage for an output.
///
/// When a `frame` event is emitted, `make_current` should be
/// called. If necessary, the output should be repainted and
/// `swap_buffers` should be called.
///
/// No rendering should happen outside a `frame` event handler.
pub struct OutputDamage {
    damage: *mut wlr_output_damage
}

impl OutputDamage {
    /// Makes a new `OutputDamage` bound to the given Output.
    ///
    /// # Safety
    /// This function is unsafe because the `OutputDamage` should not outlive the
    /// past in `Output`.
    pub(crate) unsafe fn new(output: *mut wlr_output) -> Self {
        unsafe {
            let damage = wlr_output_damage_create(output);
            if damage.is_null() {
                panic!("Damage was none")
            }
            OutputDamage { damage }
        }
    }

    pub(crate) unsafe fn from_ptr(damage: *mut wlr_output_damage) -> Self {
        OutputDamage { damage }
    }

    pub(crate) unsafe fn as_ptr(&self) -> *mut wlr_output_damage {
        self.damage
    }

    /// Just like `std::clone::Clone` but unsafe.
    ///
    /// # Unsafety
    /// You can create a reference leak using this method very easily,
    /// and it could make it so that weak handles to an output are dangling.
    ///
    /// This exists due to an issue in output_manager.rs that might be fixed
    /// with NLL, so if this is no longer necessary it should be removed asap.
    pub(crate) unsafe fn clone(&self) -> Self {
        OutputDamage { damage: self.damage }
    }

    /// Makes the output rendering context current.
    /// Returns `true` if `wlr_output_damage_swap_buffers` needs to be called.
    ///
    ///The region of the output that needs to be repainted is added to `damage`.
    pub fn make_current(&mut self, damage: Option<&mut PixmanRegion>) -> bool {
        unsafe {
            let mut res = false;
            let damage = match damage {
                Some(region) => &mut region.region as *mut _,
                None => ptr::null_mut()
            };
            wlr_output_damage_make_current(self.damage, &mut res, damage);
            res
        }
    }

    /// Swaps the output buffers.
    ///
    /// If the time of the frame isn't known, set `when` to `None`.
    ///
    /// Swapping buffers schedules a `frame` event.
    pub fn swap_buffers(&mut self,
                        when: Option<Duration>,
                        damage: Option<&mut PixmanRegion>)
                        -> bool {
        unsafe {
            let when = when.map(|duration| {
                                    timespec { tv_sec: duration.as_secs() as i64,
                                               tv_nsec: duration.subsec_nanos() as i64 }
                                });
            let when_ptr =
                when.map(|mut duration| &mut duration as *mut _).unwrap_or_else(|| ptr::null_mut());
            let damage = match damage {
                Some(region) => &mut region.region as *mut _,
                None => ptr::null_mut()
            };
            wlr_output_damage_swap_buffers(self.damage, when_ptr, damage)
        }
    }

    /// Accumulates damage and schedules a `frame` event.
    pub fn add(&mut self, damage: &mut PixmanRegion) {
        unsafe {
            wlr_output_damage_add(self.damage, &mut damage.region);
        }
    }

    /// Damages the whole output and schedules a `frame` event.
    pub fn add_whole(&mut self) {
        unsafe { wlr_output_damage_add_whole(self.damage) }
    }

    /// Accumulates damage from an `Area` and schedules a `frame` event.
    pub fn add_area(&mut self, mut area: Area) {
        unsafe { wlr_output_damage_add_box(self.damage, &mut area.0) }
    }
}

impl Drop for OutputDamage {
    fn drop(&mut self) {
        wlr_log!(L_DEBUG, "Dropped OutputDamage {:p}", self.damage);
        unsafe {
            wlr_output_damage_destroy(self.damage);
        }
    }
}
<|endoftext|>