<commit_before>use hir::Semantics;
use ide_db::{
    base_db::FilePosition,
    defs::Definition,
    helpers::pick_best_token,
    search::{FileReference, ReferenceAccess, SearchScope},
    RootDatabase,
};
use syntax::{ast, match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, WalkEvent, T};

use crate::{display::TryToNav, references, NavigationTarget};

pub struct HighlightedRange {
    pub range: TextRange,
    pub access: Option<ReferenceAccess>,
}

// Feature: Highlight Related
//
// Highlights constructs related to the thing under the cursor:
// - if on an identifier, highlights all references to that identifier in the current file
// - if on an `async` or `await token, highlights all yield points for that async context
// - if on a `return` token, `?` character or `->` return type arrow, highlights all exit points for that context
pub(crate) fn highlight_related(
    sema: &Semantics<RootDatabase>,
    position: FilePosition,
) -> Option<Vec<HighlightedRange>> {
    let _p = profile::span("highlight_related");
    let syntax = sema.parse(position.file_id).syntax().clone();

    let token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
        T![?] => 2, // prefer `?` when the cursor is sandwiched like `await$0?`
        T![await] | T![async] | T![return] | T![->] => 1,
        _ => 0,
    })?;

    match token.kind() {
        T![return] | T![?] | T![->] => highlight_exit_points(sema, token),
        T![await] | T![async] => highlight_yield_points(token),
        _ => highlight_references(sema, &syntax, position),
    }
}

fn highlight_references(
    sema: &Semantics<RootDatabase>,
    syntax: &SyntaxNode,
    FilePosition { offset, file_id }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
    let def = references::find_def(sema, syntax, offset)?;
    let usages = def.usages(sema).set_scope(Some(SearchScope::single_file(file_id))).all();

    let declaration = match def {
        Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
            Some(NavigationTarget::from_module_to_decl(sema.db, module))
        }
        def => def.try_to_nav(sema.db),
    }
    .filter(|decl| decl.file_id == file_id)
    .and_then(|decl| {
        let range = decl.focus_range?;
        let access = references::decl_access(&def, syntax, range);
        Some(HighlightedRange { range, access })
    });

    let file_refs = usages.references.get(&file_id).map_or(&[][..], Vec::as_slice);
    let mut res = Vec::with_capacity(file_refs.len() + 1);
    res.extend(declaration);
    res.extend(
        file_refs
            .iter()
            .map(|&FileReference { access, range, .. }| HighlightedRange { range, access }),
    );
    Some(res)
}

fn highlight_exit_points(
    sema: &Semantics<RootDatabase>,
    token: SyntaxToken,
) -> Option<Vec<HighlightedRange>> {
    fn hl(
        sema: &Semantics<RootDatabase>,
        body: Option<ast::Expr>,
    ) -> Option<Vec<HighlightedRange>> {
        let mut highlights = Vec::new();
        let body = body?;
        walk(&body, &mut |expr| {
            match expr {
                ast::Expr::ReturnExpr(expr) => {
                    if let Some(token) = expr.return_token() {
                        highlights
                            .push(HighlightedRange { access: None, range: token.text_range() });
                    }
                }
                ast::Expr::TryExpr(try_) => {
                    if let Some(token) = try_.question_mark_token() {
                        highlights
                            .push(HighlightedRange { access: None, range: token.text_range() });
                    }
                }
                ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroCall(_) => {
                    if sema.type_of_expr(&expr).map_or(false, |ty| ty.is_never()) {
                        highlights.push(HighlightedRange {
                            access: None,
                            range: expr.syntax().text_range(),
                        });
                    }
                }
                ast::Expr::EffectExpr(effect) => {
                    return effect.async_token().is_some() || effect.try_token().is_some()
                }
                ast::Expr::ClosureExpr(_) => return true,
                _ => (),
            }
            false
        });
        let tail = match body {
            ast::Expr::BlockExpr(b) => b.tail_expr(),
            e => Some(e),
        };

        if let Some(tail) = tail {
            highlights.push(HighlightedRange { access: None, range: tail.syntax().text_range() })
        }
        Some(highlights)
    }
    for anc in token.ancestors() {
        return match_ast! {
            match anc {
                ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
                ast::ClosureExpr(closure) => hl(sema, closure.body()),
                ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) {
                    hl(sema, effect.block_expr().map(ast::Expr::BlockExpr))
                } else {
                    continue;
                },
                _ => continue,
            }
        };
    }
    None
}

fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
    fn hl(
        async_token: Option<SyntaxToken>,
        body: Option<ast::Expr>,
    ) -> Option<Vec<HighlightedRange>> {
        let mut highlights = Vec::new();
        highlights.push(HighlightedRange { access: None, range: async_token?.text_range() });
        if let Some(body) = body {
            walk(&body, &mut |expr| {
                match expr {
                    ast::Expr::AwaitExpr(expr) => {
                        if let Some(token) = expr.await_token() {
                            highlights
                                .push(HighlightedRange { access: None, range: token.text_range() });
                        }
                    }
                    // All the following are different contexts so skip them
                    ast::Expr::EffectExpr(effect) => {
                        return matches!(
                            effect.effect(),
                            ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_)
                        )
                    }
                    ast::Expr::ClosureExpr(__) => return true,
                    _ => (),
                }
                false
            });
        }
        Some(highlights)
    }
    for anc in token.ancestors() {
        return match_ast! {
            match anc {
                ast::Fn(fn_) => hl(fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)),
                ast::EffectExpr(effect) => hl(effect.async_token(), effect.block_expr().map(ast::Expr::BlockExpr)),
                ast::ClosureExpr(closure) => hl(closure.async_token(), closure.body()),
                _ => continue,
            }
        };
    }
    None
}

/// Preorder walk the expression node skipping a node's subtrees if the callback returns `true` for the node.
fn walk(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr) -> bool) {
    let mut preorder = expr.syntax().preorder();
    while let Some(event) = preorder.next() {
        let node = match event {
            WalkEvent::Enter(node) => node,
            WalkEvent::Leave(_) => continue,
        };
        match ast::Stmt::cast(node.clone()) {
            Some(ast::Stmt::LetStmt(l)) => {
                if let Some(expr) = l.initializer() {
                    walk(&expr, cb);
                }
            }
            // Don't skip subtree since we want to process the expression behind this next
            Some(ast::Stmt::ExprStmt(_)) => continue,
            // skip inner items which might have their own expressions
            Some(ast::Stmt::Item(_)) => (),
            None => {
                if let Some(expr) = ast::Expr::cast(node) {
                    if !cb(expr) {
                        continue;
                    }
                }
            }
        }
        preorder.skip_subtree();
    }
}

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

    use super::*;

    fn check(ra_fixture: &str) {
        let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
        let hls = analysis.highlight_related(pos).unwrap().unwrap();

        let mut expected = annotations
            .into_iter()
            .map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
            .collect::<Vec<_>>();

        let mut actual = hls
            .into_iter()
            .map(|hl| {
                (
                    hl.range,
                    hl.access.map(|it| {
                        match it {
                            ReferenceAccess::Read => "read",
                            ReferenceAccess::Write => "write",
                        }
                        .to_string()
                    }),
                )
            })
            .collect::<Vec<_>>();
        actual.sort_by_key(|(range, _)| range.start());
        expected.sort_by_key(|(range, _)| range.start());

        assert_eq!(expected, actual);
    }

    #[test]
    fn test_hl_module() {
        check(
            r#"
//- /lib.rs
mod foo$0;
 // ^^^
//- /foo.rs
struct Foo;
"#,
        );
    }

    #[test]
    fn test_hl_self_in_crate_root() {
        check(
            r#"
use self$0;
"#,
        );
    }

    #[test]
    fn test_hl_self_in_module() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
use self$0;
"#,
        );
    }

    #[test]
    fn test_hl_local() {
        check(
            r#"
fn foo() {
    let mut bar = 3;
         // ^^^ write
    bar$0;
 // ^^^ read
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_points() {
        check(
            r#"
pub async fn foo() {
 // ^^^^^
    let x = foo()
        .await$0
      // ^^^^^
        .await;
      // ^^^^^
    || { 0.await };
    (async { 0.await }).await
                     // ^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_points2() {
        check(
            r#"
pub async$0 fn foo() {
 // ^^^^^
    let x = foo()
        .await
      // ^^^^^
        .await;
      // ^^^^^
    || { 0.await };
    (async { 0.await }).await
                     // ^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_nested_fn() {
        check(
            r#"
async fn foo() {
    async fn foo2() {
 // ^^^^^
        async fn foo3() {
            0.await
        }
        0.await$0
       // ^^^^^
    }
    0.await
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_nested_async_blocks() {
        check(
            r#"
async fn foo() {
    (async {
  // ^^^^^
        (async {
           0.await
        }).await$0 }
        // ^^^^^
    ).await;
}
"#,
        );
    }

    #[test]
    fn test_hl_exit_points() {
        check(
            r#"
fn foo() -> u32 {
    if true {
        return$0 0;
     // ^^^^^^
    }

    0?;
  // ^
    0xDEAD_BEEF
 // ^^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_exit_points2() {
        check(
            r#"
fn foo() ->$0 u32 {
    if true {
        return 0;
     // ^^^^^^
    }

    0?;
  // ^
    0xDEAD_BEEF
 // ^^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_prefer_ref_over_tail_exit() {
        check(
            r#"
fn foo() -> u32 {
// ^^^
    if true {
        return 0;
    }

    0?;

    foo$0()
 // ^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_never_call_is_exit_point() {
        check(
            r#"
struct Never;
impl Never {
    fn never(self) -> ! { loop {} }
}
macro_rules! never {
    () => { never() }
}
fn never() -> ! { loop {} }
fn foo() ->$0 u32 {
    never();
 // ^^^^^^^
    never!();
 // FIXME sema doesn't give us types for macrocalls

    Never.never();
 // ^^^^^^^^^^^^^

    0
 // ^
}
"#,
        );
    }
}
<commit_msg>Refine tail exit point highlighting to highlight inner tails<commit_after>use hir::Semantics;
use ide_db::{
    base_db::FilePosition,
    defs::Definition,
    helpers::pick_best_token,
    search::{FileReference, ReferenceAccess, SearchScope},
    RootDatabase,
};
use syntax::{
    ast::{self, LoopBodyOwner},
    match_ast, AstNode, SyntaxNode, SyntaxToken, TextRange, WalkEvent, T,
};

use crate::{display::TryToNav, references, NavigationTarget};

pub struct HighlightedRange {
    pub range: TextRange,
    pub access: Option<ReferenceAccess>,
}

// Feature: Highlight Related
//
// Highlights constructs related to the thing under the cursor:
// - if on an identifier, highlights all references to that identifier in the current file
// - if on an `async` or `await token, highlights all yield points for that async context
// - if on a `return` token, `?` character or `->` return type arrow, highlights all exit points for that context
pub(crate) fn highlight_related(
    sema: &Semantics<RootDatabase>,
    position: FilePosition,
) -> Option<Vec<HighlightedRange>> {
    let _p = profile::span("highlight_related");
    let syntax = sema.parse(position.file_id).syntax().clone();

    let token = pick_best_token(syntax.token_at_offset(position.offset), |kind| match kind {
        T![?] => 2, // prefer `?` when the cursor is sandwiched like `await$0?`
        T![await] | T![async] | T![return] | T![->] => 1,
        _ => 0,
    })?;

    match token.kind() {
        T![return] | T![?] | T![->] => highlight_exit_points(sema, token),
        T![await] | T![async] => highlight_yield_points(token),
        _ => highlight_references(sema, &syntax, position),
    }
}

fn highlight_references(
    sema: &Semantics<RootDatabase>,
    syntax: &SyntaxNode,
    FilePosition { offset, file_id }: FilePosition,
) -> Option<Vec<HighlightedRange>> {
    let def = references::find_def(sema, syntax, offset)?;
    let usages = def.usages(sema).set_scope(Some(SearchScope::single_file(file_id))).all();

    let declaration = match def {
        Definition::ModuleDef(hir::ModuleDef::Module(module)) => {
            Some(NavigationTarget::from_module_to_decl(sema.db, module))
        }
        def => def.try_to_nav(sema.db),
    }
    .filter(|decl| decl.file_id == file_id)
    .and_then(|decl| {
        let range = decl.focus_range?;
        let access = references::decl_access(&def, syntax, range);
        Some(HighlightedRange { range, access })
    });

    let file_refs = usages.references.get(&file_id).map_or(&[][..], Vec::as_slice);
    let mut res = Vec::with_capacity(file_refs.len() + 1);
    res.extend(declaration);
    res.extend(
        file_refs
            .iter()
            .map(|&FileReference { access, range, .. }| HighlightedRange { range, access }),
    );
    Some(res)
}

fn highlight_exit_points(
    sema: &Semantics<RootDatabase>,
    token: SyntaxToken,
) -> Option<Vec<HighlightedRange>> {
    fn hl(
        sema: &Semantics<RootDatabase>,
        body: Option<ast::Expr>,
    ) -> Option<Vec<HighlightedRange>> {
        let mut highlights = Vec::new();
        let body = body?;
        walk(&body, &mut |expr| match expr {
            ast::Expr::ReturnExpr(expr) => {
                if let Some(token) = expr.return_token() {
                    highlights.push(HighlightedRange { access: None, range: token.text_range() });
                }
            }
            ast::Expr::TryExpr(try_) => {
                if let Some(token) = try_.question_mark_token() {
                    highlights.push(HighlightedRange { access: None, range: token.text_range() });
                }
            }
            ast::Expr::MethodCallExpr(_) | ast::Expr::CallExpr(_) | ast::Expr::MacroCall(_) => {
                if sema.type_of_expr(&expr).map_or(false, |ty| ty.is_never()) {
                    highlights
                        .push(HighlightedRange { access: None, range: expr.syntax().text_range() });
                }
            }
            _ => (),
        });
        let tail = match body {
            ast::Expr::BlockExpr(b) => b.tail_expr(),
            e => Some(e),
        };

        if let Some(tail) = tail {
            for_each_inner_tail(&tail, &mut |tail| {
                highlights
                    .push(HighlightedRange { access: None, range: tail.syntax().text_range() })
            });
        }
        Some(highlights)
    }
    for anc in token.ancestors() {
        return match_ast! {
            match anc {
                ast::Fn(fn_) => hl(sema, fn_.body().map(ast::Expr::BlockExpr)),
                ast::ClosureExpr(closure) => hl(sema, closure.body()),
                ast::EffectExpr(effect) => if matches!(effect.effect(), ast::Effect::Async(_) | ast::Effect::Try(_)| ast::Effect::Const(_)) {
                    hl(sema, effect.block_expr().map(ast::Expr::BlockExpr))
                } else {
                    continue;
                },
                _ => continue,
            }
        };
    }
    None
}

fn highlight_yield_points(token: SyntaxToken) -> Option<Vec<HighlightedRange>> {
    fn hl(
        async_token: Option<SyntaxToken>,
        body: Option<ast::Expr>,
    ) -> Option<Vec<HighlightedRange>> {
        let mut highlights = Vec::new();
        highlights.push(HighlightedRange { access: None, range: async_token?.text_range() });
        if let Some(body) = body {
            walk(&body, &mut |expr| {
                if let ast::Expr::AwaitExpr(expr) = expr {
                    if let Some(token) = expr.await_token() {
                        highlights
                            .push(HighlightedRange { access: None, range: token.text_range() });
                    }
                }
            });
        }
        Some(highlights)
    }
    for anc in token.ancestors() {
        return match_ast! {
            match anc {
                ast::Fn(fn_) => hl(fn_.async_token(), fn_.body().map(ast::Expr::BlockExpr)),
                ast::EffectExpr(effect) => hl(effect.async_token(), effect.block_expr().map(ast::Expr::BlockExpr)),
                ast::ClosureExpr(closure) => hl(closure.async_token(), closure.body()),
                _ => continue,
            }
        };
    }
    None
}

/// Preorder walk all the expression's child expressions
fn walk(expr: &ast::Expr, cb: &mut dyn FnMut(ast::Expr)) {
    let mut preorder = expr.syntax().preorder();
    while let Some(event) = preorder.next() {
        let node = match event {
            WalkEvent::Enter(node) => node,
            WalkEvent::Leave(_) => continue,
        };
        match ast::Stmt::cast(node.clone()) {
            // recursively walk the initializer, skipping potential const pat expressions
            // lets statements aren't usually nested too deeply so this is fine to recurse on
            Some(ast::Stmt::LetStmt(l)) => {
                if let Some(expr) = l.initializer() {
                    walk(&expr, cb);
                }
                preorder.skip_subtree();
            }
            // Don't skip subtree since we want to process the expression child next
            Some(ast::Stmt::ExprStmt(_)) => (),
            // skip inner items which might have their own expressions
            Some(ast::Stmt::Item(_)) => preorder.skip_subtree(),
            None => {
                if let Some(expr) = ast::Expr::cast(node) {
                    let is_different_context = match &expr {
                        ast::Expr::EffectExpr(effect) => {
                            matches!(
                                effect.effect(),
                                ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_)
                            )
                        }
                        ast::Expr::ClosureExpr(__) => true,
                        _ => false,
                    };
                    cb(expr);
                    if is_different_context {
                        preorder.skip_subtree();
                    }
                } else {
                    preorder.skip_subtree();
                }
            }
        }
    }
}

// FIXME: doesn't account for labeled breaks in labeled blocks
fn for_each_inner_tail(expr: &ast::Expr, cb: &mut dyn FnMut(&ast::Expr)) {
    match expr {
        ast::Expr::BlockExpr(b) => {
            if let Some(e) = b.tail_expr() {
                for_each_inner_tail(&e, cb);
            }
        }
        ast::Expr::EffectExpr(e) => match e.effect() {
            ast::Effect::Label(_) | ast::Effect::Unsafe(_) => {
                if let Some(e) = e.block_expr().and_then(|b| b.tail_expr()) {
                    for_each_inner_tail(&e, cb);
                }
            }
            ast::Effect::Async(_) | ast::Effect::Try(_) | ast::Effect::Const(_) => cb(expr),
        },
        ast::Expr::IfExpr(if_) => {
            if_.blocks().for_each(|block| for_each_inner_tail(&ast::Expr::BlockExpr(block), cb))
        }
        ast::Expr::LoopExpr(l) => for_each_break(l, cb),
        ast::Expr::MatchExpr(m) => {
            if let Some(arms) = m.match_arm_list() {
                arms.arms().filter_map(|arm| arm.expr()).for_each(|e| for_each_inner_tail(&e, cb));
            }
        }
        ast::Expr::ArrayExpr(_)
        | ast::Expr::AwaitExpr(_)
        | ast::Expr::BinExpr(_)
        | ast::Expr::BoxExpr(_)
        | ast::Expr::BreakExpr(_)
        | ast::Expr::CallExpr(_)
        | ast::Expr::CastExpr(_)
        | ast::Expr::ClosureExpr(_)
        | ast::Expr::ContinueExpr(_)
        | ast::Expr::FieldExpr(_)
        | ast::Expr::ForExpr(_)
        | ast::Expr::IndexExpr(_)
        | ast::Expr::Literal(_)
        | ast::Expr::MacroCall(_)
        | ast::Expr::MacroStmts(_)
        | ast::Expr::MethodCallExpr(_)
        | ast::Expr::ParenExpr(_)
        | ast::Expr::PathExpr(_)
        | ast::Expr::PrefixExpr(_)
        | ast::Expr::RangeExpr(_)
        | ast::Expr::RecordExpr(_)
        | ast::Expr::RefExpr(_)
        | ast::Expr::ReturnExpr(_)
        | ast::Expr::TryExpr(_)
        | ast::Expr::TupleExpr(_)
        | ast::Expr::WhileExpr(_)
        | ast::Expr::YieldExpr(_) => cb(expr),
    }
}

fn for_each_break(l: &ast::LoopExpr, cb: &mut dyn FnMut(&ast::Expr)) {
    let label = l.label().and_then(|lbl| lbl.lifetime());
    let mut depth = 0;
    if let Some(b) = l.loop_body() {
        let preorder = &mut b.syntax().preorder();
        let ev_as_expr = |ev| match ev {
            WalkEvent::Enter(it) => Some(WalkEvent::Enter(ast::Expr::cast(it)?)),
            WalkEvent::Leave(it) => Some(WalkEvent::Leave(ast::Expr::cast(it)?)),
        };
        let eq_label = |lt: Option<ast::Lifetime>| {
            lt.zip(label.as_ref()).map_or(false, |(lt, lbl)| lt.text() == lbl.text())
        };
        while let Some(node) = preorder.find_map(ev_as_expr) {
            match node {
                WalkEvent::Enter(expr) => match &expr {
                    ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
                        depth += 1
                    }
                    ast::Expr::EffectExpr(e) if e.label().is_some() => depth += 1,
                    ast::Expr::BreakExpr(b) if depth == 0 || eq_label(b.lifetime()) => {
                        cb(&expr);
                    }
                    _ => (),
                },
                WalkEvent::Leave(expr) => match expr {
                    ast::Expr::LoopExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::ForExpr(_) => {
                        depth -= 1
                    }
                    ast::Expr::EffectExpr(e) if e.label().is_some() => depth -= 1,
                    _ => (),
                },
            }
        }
    }
}

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

    use super::*;

    fn check(ra_fixture: &str) {
        let (analysis, pos, annotations) = fixture::annotations(ra_fixture);
        let hls = analysis.highlight_related(pos).unwrap().unwrap();

        let mut expected = annotations
            .into_iter()
            .map(|(r, access)| (r.range, (!access.is_empty()).then(|| access)))
            .collect::<Vec<_>>();

        let mut actual = hls
            .into_iter()
            .map(|hl| {
                (
                    hl.range,
                    hl.access.map(|it| {
                        match it {
                            ReferenceAccess::Read => "read",
                            ReferenceAccess::Write => "write",
                        }
                        .to_string()
                    }),
                )
            })
            .collect::<Vec<_>>();
        actual.sort_by_key(|(range, _)| range.start());
        expected.sort_by_key(|(range, _)| range.start());

        assert_eq!(expected, actual);
    }

    #[test]
    fn test_hl_module() {
        check(
            r#"
//- /lib.rs
mod foo$0;
 // ^^^
//- /foo.rs
struct Foo;
"#,
        );
    }

    #[test]
    fn test_hl_self_in_crate_root() {
        check(
            r#"
use self$0;
"#,
        );
    }

    #[test]
    fn test_hl_self_in_module() {
        check(
            r#"
//- /lib.rs
mod foo;
//- /foo.rs
use self$0;
"#,
        );
    }

    #[test]
    fn test_hl_local() {
        check(
            r#"
fn foo() {
    let mut bar = 3;
         // ^^^ write
    bar$0;
 // ^^^ read
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_points() {
        check(
            r#"
pub async fn foo() {
 // ^^^^^
    let x = foo()
        .await$0
      // ^^^^^
        .await;
      // ^^^^^
    || { 0.await };
    (async { 0.await }).await
                     // ^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_points2() {
        check(
            r#"
pub async$0 fn foo() {
 // ^^^^^
    let x = foo()
        .await
      // ^^^^^
        .await;
      // ^^^^^
    || { 0.await };
    (async { 0.await }).await
                     // ^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_nested_fn() {
        check(
            r#"
async fn foo() {
    async fn foo2() {
 // ^^^^^
        async fn foo3() {
            0.await
        }
        0.await$0
       // ^^^^^
    }
    0.await
}
"#,
        );
    }

    #[test]
    fn test_hl_yield_nested_async_blocks() {
        check(
            r#"
async fn foo() {
    (async {
  // ^^^^^
        (async {
           0.await
        }).await$0 }
        // ^^^^^
    ).await;
}
"#,
        );
    }

    #[test]
    fn test_hl_exit_points() {
        check(
            r#"
fn foo() -> u32 {
    if true {
        return$0 0;
     // ^^^^^^
    }

    0?;
  // ^
    0xDEAD_BEEF
 // ^^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_exit_points2() {
        check(
            r#"
fn foo() ->$0 u32 {
    if true {
        return 0;
     // ^^^^^^
    }

    0?;
  // ^
    0xDEAD_BEEF
 // ^^^^^^^^^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_prefer_ref_over_tail_exit() {
        check(
            r#"
fn foo() -> u32 {
// ^^^
    if true {
        return 0;
    }

    0?;

    foo$0()
 // ^^^
}
"#,
        );
    }

    #[test]
    fn test_hl_never_call_is_exit_point() {
        check(
            r#"
struct Never;
impl Never {
    fn never(self) -> ! { loop {} }
}
macro_rules! never {
    () => { never() }
}
fn never() -> ! { loop {} }
fn foo() ->$0 u32 {
    never();
 // ^^^^^^^
    never!();
 // FIXME sema doesn't give us types for macrocalls

    Never.never();
 // ^^^^^^^^^^^^^

    0
 // ^
}
"#,
        );
    }

    #[test]
    fn test_hl_inner_tail_exit_points() {
        check(
            r#"
fn foo() ->$0 u32 {
    if true {
        unsafe {
            return 5;
         // ^^^^^^
            5
         // ^
        }
    } else {
        match 5 {
            6 => 100,
              // ^^^
            7 => loop {
                break 5;
             // ^^^^^^^
            }
            8 => 'a: loop {
                'b: loop {
                    break 'a 5;
                 // ^^^^^^^^^^
                    break 'b 5;
                    break 5;
                };
            }
            //
            _ => 500,
              // ^^^
        }
    }
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>use super::{Frame, FrameRange, Allocator};
use ::{AllocResult, AllocErr, Layout};
use params::{InitParams, mem};
use memory::{Page, PAGE_SIZE, PAddr};

use core::iter::Step;
use core::convert::From;
/// A simple area allocator.
///
/// This is based on the memory area allocation scheme described
/// by Phil Oppermann at [http://os.phil-opp.com/allocating-frames.html].
///
/// This is Not A Good Allocation Scheme, as it does not currently support
/// reallocation of freed frames. The plan is that it will only be used
/// initially, and after we've allocated everything once, we'll switch over
/// to a better allocator.
pub struct MemMapAllocator<'a> { next_free: Frame
                               , current_area: Option<&'a mem::Area>
                               , areas: mem::Map<'a>
                               , kernel_frames: FrameRange
                               , mb_frames: FrameRange
                               }
impl<'a> MemMapAllocator<'a> {
    fn next_area(&mut self) {
        // println!("In next_area");
        self.current_area
            = self.areas
                  .clone()
                  .filter(|a|
                      Frame::containing(a.end_addr) >= self.next_free)
                  .min_by_key(|a| a.start_addr)
                  .map(|area| {
                      let start = Frame::containing(area.start_addr);
                      if self.next_free > start { self.next_free = start };
                      area
                  })
    }

}

impl<'a> From<&'a InitParams> for MemMapAllocator<'a> {
    fn from(params: &'a InitParams) -> Self {
        let mut new_allocator = MemMapAllocator {
              next_free: Frame::containing(PAddr::new(0x0))
            , current_area: None
            , areas: params.mem_map()
            , kernel_frames: params.kernel_frames()
            // TODO: handle non-multiboot case
            , mb_frames: Frame::containing(params.multiboot_start()) ..
                         Frame::containing(params.multiboot_end())
            };
        new_allocator.next_area();
        new_allocator
    }
}

impl<'a> Allocator for MemMapAllocator<'a> {
    // type Frame = Frame;

    unsafe fn allocate(&mut self) -> AllocResult<Frame> {
        // // println!("In alloc method");
        if let Some(area) = self.current_area {
            match self.next_free {
                // all frames in the current memory area are in use
                f if f > Frame::containing(area.end_addr) => {
                    // so we advance to the next free area

                    // println!("All frames in current area in use.");
                    self.next_area();
                    // println!("...and returning None");
                }
              , // this frame is in use by the kernel.
                f if self.kernel_frames.contains(f) => {
                    // skip ahead to the end of the kernel
                    // println!("In kernel frame, skipping.");
                    self.next_free = self.kernel_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is part of the multiboot info.
                f if self.mb_frames.contains(f) => {
                    // skip ahead to the end of the multiboot info.
                    // println!("In multiboot frame, skipping...");
                    self.next_free = self.mb_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is free.
                frame => {
                    // advance the next free frame and return this frame.
                    // println!("In free frame, advancing...");
                    self.next_free = self.next_free.add_one();
                    // println!("...and returning {:?}", frame);
                    return Ok(frame)
                }
            };
            self.allocate()
        } else {
            // println!("No free frames remain!");
            Err(AllocErr::Exhausted {
                    request: Layout::from_size_align( PAGE_SIZE as usize, PAGE_SIZE as usize)
            })
        }
    }

    /// Deallocate a frame
    unsafe fn deallocate(&mut self, frame: Frame) {
        // just leak it
    }

    /// Allocate a range of frames
    unsafe fn allocate_range(&mut self, num: usize) -> AllocResult<FrameRange> {
        unimplemented!()
    }
    /// Deallocate a range of frames
    unsafe fn deallocate_range(&mut self, range: FrameRange) {
        //just leak it
    }
}
<commit_msg>docs(alloc::frame::mem_map): add missing module docs<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (eliza@elizas.website)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! A simple allocator using the [`InitParams`] memory map.
//!
//! This is basically just a bump pointer allocator for frames; since
//! it doesn't support deallocating frames.
use super::{Frame, FrameRange, Allocator};
use ::{AllocResult, AllocErr, Layout};
use params::{InitParams, mem};
use memory::{Page, PAGE_SIZE, PAddr};

use core::iter::Step;
use core::convert::From;
/// A simple area allocator.
///
/// This is based on the memory area allocation scheme described
/// by Phil Oppermann at [http://os.phil-opp.com/allocating-frames.html].
///
/// This is Not A Good Allocation Scheme, as it does not currently support
/// reallocation of freed frames. The plan is that it will only be used
/// initially, and after we've allocated everything once, we'll switch over
/// to a better allocator.
pub struct MemMapAllocator<'a> { next_free: Frame
                               , current_area: Option<&'a mem::Area>
                               , areas: mem::Map<'a>
                               , kernel_frames: FrameRange
                               , mb_frames: FrameRange
                               }
impl<'a> MemMapAllocator<'a> {
    fn next_area(&mut self) {
        // println!("In next_area");
        self.current_area
            = self.areas
                  .clone()
                  .filter(|a|
                      Frame::containing(a.end_addr) >= self.next_free)
                  .min_by_key(|a| a.start_addr)
                  .map(|area| {
                      let start = Frame::containing(area.start_addr);
                      if self.next_free > start { self.next_free = start };
                      area
                  })
    }

}

impl<'a> From<&'a InitParams> for MemMapAllocator<'a> {
    fn from(params: &'a InitParams) -> Self {
        let mut new_allocator = MemMapAllocator {
              next_free: Frame::containing(PAddr::new(0x0))
            , current_area: None
            , areas: params.mem_map()
            , kernel_frames: params.kernel_frames()
            // TODO: handle non-multiboot case
            , mb_frames: Frame::containing(params.multiboot_start()) ..
                         Frame::containing(params.multiboot_end())
            };
        new_allocator.next_area();
        new_allocator
    }
}

impl<'a> Allocator for MemMapAllocator<'a> {
    // type Frame = Frame;

    unsafe fn allocate(&mut self) -> AllocResult<Frame> {
        // // println!("In alloc method");
        if let Some(area) = self.current_area {
            match self.next_free {
                // all frames in the current memory area are in use
                f if f > Frame::containing(area.end_addr) => {
                    // so we advance to the next free area

                    // println!("All frames in current area in use.");
                    self.next_area();
                    // println!("...and returning None");
                }
              , // this frame is in use by the kernel.
                f if self.kernel_frames.contains(f) => {
                    // skip ahead to the end of the kernel
                    // println!("In kernel frame, skipping.");
                    self.next_free = self.kernel_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is part of the multiboot info.
                f if self.mb_frames.contains(f) => {
                    // skip ahead to the end of the multiboot info.
                    // println!("In multiboot frame, skipping...");
                    self.next_free = self.mb_frames.end.add_one();
                    // println!("...and returning None");
                }
              , // this frame is free.
                frame => {
                    // advance the next free frame and return this frame.
                    // println!("In free frame, advancing...");
                    self.next_free = self.next_free.add_one();
                    // println!("...and returning {:?}", frame);
                    return Ok(frame)
                }
            };
            self.allocate()
        } else {
            // println!("No free frames remain!");
            Err(AllocErr::Exhausted {
                    request: Layout::from_size_align( PAGE_SIZE as usize, PAGE_SIZE as usize)
            })
        }
    }

    /// Deallocate a frame
    unsafe fn deallocate(&mut self, frame: Frame) {
        // just leak it
    }

    /// Allocate a range of frames
    unsafe fn allocate_range(&mut self, num: usize) -> AllocResult<FrameRange> {
        unimplemented!()
    }
    /// Deallocate a range of frames
    unsafe fn deallocate_range(&mut self, range: FrameRange) {
        //just leak it
    }
}
<|endoftext|>


<commit_before>extern crate odbc;
use odbc::*;

#[test]
fn list_tables() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let ds = DataSource::with_parent(&env).unwrap();
    let mut ds = ds.connect("TestDataSource", "", "").unwrap();
    // scope is required (for now) to close statement before disconnecting
    {
        let statement = Statement::with_parent(&mut ds).unwrap();
        let statement = statement.tables().unwrap();
        assert_eq!(statement.num_result_cols().unwrap(), 5);
    }
    ds.disconnect().unwrap();
}

#[test]
fn not_read_only() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap();
    let conn = conn.connect("TestDataSource", "", "").unwrap();

    assert!(!conn.read_only().unwrap());
    conn.disconnect().unwrap();
}

#[test]
fn implicit_disconnect() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap();
    conn.connect("TestDataSource", "", "").unwrap();

    // if there would be no implicit disconnect, all the drops would panic with function sequence
    // error
}

#[test]
fn invalid_connection_string() {

    let expected = if cfg!(target_os = "windows") {
        "State: IM002, Native error: 0, Message: [Microsoft][ODBC Driver Manager] Data source \
            name not found and no default driver specified"
    } else {
        "State: IM002, Native error: 0, Message: [unixODBC][Driver Manager]Data source name not \
            found, and no default driver specified"
    };

    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&environment).unwrap();
    let result = conn.connect_with_connection_string("bla");
    let message = format!("{}", result.err().unwrap());
    assert_eq!(expected, message);
}

#[test]
fn test_connection_string() {

    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&environment).unwrap();
    let conn = conn.connect_with_connection_string("dsn=TestDataSource;Uid=;Pwd=;")
        .unwrap();
    conn.disconnect().unwrap();
}

#[test]
fn test_direct_select() {
    let env = Environment::new().unwrap().set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap().connect("TestDataSource", "", "").unwrap();
    let stmt = Statement::with_parent(&conn).unwrap();

    let mut stmt = stmt.exec_direct("SELECT TITLE, YEAR FROM MOVIES ORDER BY YEAR").unwrap();
    assert_eq!(stmt.num_result_cols().unwrap(), 2);

    #[derive(PartialEq)]
    struct Movie {
        title: String,
        year: String,
    }

    let mut actual = Vec::new();
    while let Some(mut cursor) = stmt.fetch().unwrap() {
        actual.push(Movie {
            title: cursor.get_data(1).unwrap().unwrap(),
            year: cursor.get_data(2).unwrap().unwrap(),
        })
    }

    assert!(actual ==
            vec![Movie {
                     title: "2001: A Space Odyssey".to_owned(),
                     year: "1968".to_owned(),
                 },
                 Movie {
                     title: "Jurassic Park".to_owned(),
                     year: "1993".to_owned(),
                 }]);
}

// These tests query the results of catalog functions. These results are only likely to match the
// expectation on the travis.ci build on linux. Therefore we limit compilation and execution of
// these tests to this platform.
#[cfg(unix)]
#[test]
fn list_drivers() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let drivers = environment.drivers()
        .expect("Drivers can be iterated over");
    println!("{:?}", drivers);

    let expected = ["PostgreSQL ANSI", "PostgreSQL Unicode", "SQLite", "SQLite3"];
    assert!(drivers.iter().map(|d| &d.description).eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected = [DataSourceInfo {
                        server_name: "PostgreSQL".to_owned(),
                        description: "PostgreSQL Unicode".to_owned(),
                    }];
    assert!(sources.iter().eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_user_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.user_data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected = [DataSourceInfo {
                        server_name: "PostgreSQL".to_owned(),
                        description: "PostgreSQL Unicode".to_owned(),
                    }];
    assert!(sources.iter().eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_system_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.system_data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected: [DataSourceInfo; 0] = [];
    assert!(sources.iter().eq(expected.iter()));
}
<commit_msg>add missing Datasource to list_data_sources<commit_after>extern crate odbc;
use odbc::*;

#[test]
fn list_tables() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let ds = DataSource::with_parent(&env).unwrap();
    let mut ds = ds.connect("TestDataSource", "", "").unwrap();
    // scope is required (for now) to close statement before disconnecting
    {
        let statement = Statement::with_parent(&mut ds).unwrap();
        let statement = statement.tables().unwrap();
        assert_eq!(statement.num_result_cols().unwrap(), 5);
    }
    ds.disconnect().unwrap();
}

#[test]
fn not_read_only() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap();
    let conn = conn.connect("TestDataSource", "", "").unwrap();

    assert!(!conn.read_only().unwrap());
    conn.disconnect().unwrap();
}

#[test]
fn implicit_disconnect() {

    let env = Environment::new().unwrap();
    let env = env.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap();
    conn.connect("TestDataSource", "", "").unwrap();

    // if there would be no implicit disconnect, all the drops would panic with function sequence
    // error
}

#[test]
fn invalid_connection_string() {

    let expected = if cfg!(target_os = "windows") {
        "State: IM002, Native error: 0, Message: [Microsoft][ODBC Driver Manager] Data source \
            name not found and no default driver specified"
    } else {
        "State: IM002, Native error: 0, Message: [unixODBC][Driver Manager]Data source name not \
            found, and no default driver specified"
    };

    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&environment).unwrap();
    let result = conn.connect_with_connection_string("bla");
    let message = format!("{}", result.err().unwrap());
    assert_eq!(expected, message);
}

#[test]
fn test_connection_string() {

    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&environment).unwrap();
    let conn = conn.connect_with_connection_string("dsn=TestDataSource;Uid=;Pwd=;")
        .unwrap();
    conn.disconnect().unwrap();
}

#[test]
fn test_direct_select() {
    let env = Environment::new().unwrap().set_odbc_version_3().unwrap();
    let conn = DataSource::with_parent(&env).unwrap().connect("TestDataSource", "", "").unwrap();
    let stmt = Statement::with_parent(&conn).unwrap();

    let mut stmt = stmt.exec_direct("SELECT TITLE, YEAR FROM MOVIES ORDER BY YEAR").unwrap();
    assert_eq!(stmt.num_result_cols().unwrap(), 2);

    #[derive(PartialEq)]
    struct Movie {
        title: String,
        year: String,
    }

    let mut actual = Vec::new();
    while let Some(mut cursor) = stmt.fetch().unwrap() {
        actual.push(Movie {
            title: cursor.get_data(1).unwrap().unwrap(),
            year: cursor.get_data(2).unwrap().unwrap(),
        })
    }

    assert!(actual ==
            vec![Movie {
                     title: "2001: A Space Odyssey".to_owned(),
                     year: "1968".to_owned(),
                 },
                 Movie {
                     title: "Jurassic Park".to_owned(),
                     year: "1993".to_owned(),
                 }]);
}

// These tests query the results of catalog functions. These results are only likely to match the
// expectation on the travis.ci build on linux. Therefore we limit compilation and execution of
// these tests to this platform.
#[cfg(unix)]
#[test]
fn list_drivers() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let drivers = environment.drivers()
        .expect("Drivers can be iterated over");
    println!("{:?}", drivers);

    let expected = ["PostgreSQL ANSI", "PostgreSQL Unicode", "SQLite", "SQLite3"];
    assert!(drivers.iter().map(|d| &d.description).eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected = [DataSourceInfo {
                        server_name: "PostgreSQL".to_owned(),
                        description: "PostgreSQL Unicode".to_owned(),
                    },
                    DataSourceInfo {
                        server_name: "TestDataSource".to_owned(),
                        description: "Test database for odbc-rs".to_owned(),
                    };
    assert!(sources.iter().eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_user_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.user_data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected = [DataSourceInfo {
                        server_name: "PostgreSQL".to_owned(),
                        description: "PostgreSQL Unicode".to_owned(),
                    },
                    DataSourceInfo {
                        server_name: "TestDataSource".to_owned(),
                        description: "Test database for odbc-rs".to_owned(),
                    }];
    assert!(sources.iter().eq(expected.iter()));
}

#[cfg(unix)]
#[test]
fn list_system_data_sources() {
    let environment = Environment::new().unwrap();
    let environment = environment.set_odbc_version_3().unwrap();
    let sources = environment.system_data_sources()
        .expect("Data sources can be iterated over");
    println!("{:?}", sources);

    let expected: [DataSourceInfo; 0] = [];
    assert!(sources.iter().eq(expected.iter()));
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::Value;
use clap::App;

use error::RuntimeErrorKind as REK;
use libimagerror::into::IntoError;

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        TOMLParserError => "TOML Parsing error",
        NoConfigFileFound   => "No config file found",

        ConfigOverrideError => "Config override error",
        ConfigOverrideKeyNotAvailable => "Key not available",
        ConfigOverrideTypeNotMatching => "Configuration Type not matching"

    );
);

pub use self::error::{ConfigError, ConfigErrorKind};

/// Result type of this module. Either `T` or `ConfigError`
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    /// Get a new configuration object built from the given toml value.
    pub fn with_value(value: Value) -> Configuration {
        Configuration{
            verbosity: get_verbosity(&value),
            editor: get_editor(&value),
            editor_opts: get_editor_opts(&value),
            config: value,
        }
    }

    /// Get the Editor setting from the configuration
    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    /// Get the underlying configuration TOML object
    pub fn config(&self) -> &Value {
        &self.config
    }

    /// Get the configuration of the store, if any.
    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

    /// Override the configuration.
    /// The `v` parameter is expected to contain 'key=value' pairs where the key is a path in the
    /// TOML tree, the value to be an appropriate value.
    ///
    /// The override fails if the configuration which is about to be overridden does not exist or
    /// the `value` part cannot be converted to the type of the configuration value.
    ///
    /// If `v` is empty, this is considered to be a successful `override_config()` call.
    pub fn override_config(&mut self, v: Vec<String>) -> Result<()> {
        use libimagutil::key_value_split::*;
        use libimagutil::iter::*;
        use self::error::ConfigErrorKind as CEK;
        use self::error::MapErrInto;
        use libimagerror::into::IntoError;
        use libimagstore::toml_ext::TomlValueExt;

        v.into_iter()
            .map(|s| { debug!("Trying to process '{}'", s); s })
            .filter_map(|s| match s.into_kv() {
                Some(kv) => Some(kv.into()),
                None => {
                    warn!("Could split at '=' - will be ignore override");
                    None
                }
            })
            .map(|(k, v)| self
                 .config
                 .read(&k[..])
                 .map_err_into(CEK::TOMLParserError)
                 .map(|toml| match toml {
                    Some(value) => match into_value(value, v) {
                        Some(v) => {
                            info!("Successfully overridden: {} = {}", k, v);
                            Ok(v)
                        },
                        None => Err(CEK::ConfigOverrideTypeNotMatching.into_error()),
                    },
                    None => Err(CEK::ConfigOverrideKeyNotAvailable.into_error()),
                })
            )
            .fold_result(|i| i)
            .map_err(Box::new)
            .map_err(|e| CEK::ConfigOverrideError.into_error_with_cause(e))
    }
}

/// Tries to convert the String `s` into the same type as `value`.
///
/// Returns None if string cannot be converted.
///
/// Arrays and Tables are not supported and will yield `None`.
fn into_value(value: Value, s: String) -> Option<Value> {
    use std::str::FromStr;

    match value {
        Value::String(_)  => Some(Value::String(s)),
        Value::Integer(_) => FromStr::from_str(&s[..]).ok().map(Value::Integer),
        Value::Float(_)   => FromStr::from_str(&s[..]).ok().map(Value::Float),
        Value::Boolean(_) => {
            if s == "true" { Some(Value::Boolean(true)) }
            else if s == "false" { Some(Value::Boolean(false)) }
            else { None }
        }
        Value::Datetime(_) => Value::try_from(s).ok(),
        _ => None,
    }
}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| is_match!(v, &Value::Boolean(true))),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/// Helper to fetch the config file
///
/// Tests several variants for the config file path and uses the first one which works.
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;
    use libimagerror::trace::trace_error;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![rtp.clone()],
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .map(|path| {
            let content = {
                let mut s = String::new();
                let f = File::open(path);
                if f.is_err() {
                    return None
                }
                let mut f = f.unwrap();
                f.read_to_string(&mut s).ok();
                s
            };

            match ::toml::de::from_str(&content[..]) {
                Ok(res) => res,
                Err(e) => {
                    write!(stderr(), "Config file parser error:").ok();
                    trace_error(&e);
                    None
                }
            }
        })
        .filter(|loaded| loaded.is_some())
        .nth(0)
        .map(|inner| Value::Table(inner.unwrap()))
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

pub trait InternalConfiguration {
    fn enable_logging(&self) -> bool {
        true
    }

    fn use_inmemory_fs(&self) -> bool {
        false
    }
}

impl<'a> InternalConfiguration for App<'a, 'a> {}
<commit_msg>Remove error types from runtime configuration file<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use std::path::PathBuf;
use std::result::Result as RResult;
use std::ops::Deref;

use toml::Value;
use clap::App;

generate_error_module!(
    generate_error_types!(ConfigError, ConfigErrorKind,
        TOMLParserError => "TOML Parsing error",
        NoConfigFileFound   => "No config file found",

        ConfigOverrideError => "Config override error",
        ConfigOverrideKeyNotAvailable => "Key not available",
        ConfigOverrideTypeNotMatching => "Configuration Type not matching"

    );
);

pub use self::error::{ConfigError, ConfigErrorKind};

/// Result type of this module. Either `T` or `ConfigError`
pub type Result<T> = RResult<T, ConfigError>;

/// `Configuration` object
///
/// Holds all config variables which are globally available plus the configuration object from the
/// config parser, which can be accessed.
#[derive(Debug)]
pub struct Configuration {

    /// The plain configuration object for direct access if necessary
    config: Value,

    /// The verbosity the program should run with
    verbosity: bool,

    /// The editor which should be used
    editor: Option<String>,

    ///The options the editor should get when opening some file
    editor_opts: String,
}

impl Configuration {

    /// Get a new configuration object.
    ///
    /// The passed runtimepath is used for searching the configuration file, whereas several file
    /// names are tested. If that does not work, the home directory and the XDG basedir are tested
    /// with all variants.
    ///
    /// If that doesn't work either, an error is returned.
    pub fn new(rtp: &PathBuf) -> Result<Configuration> {
        fetch_config(&rtp).map(|cfg| {
            let verbosity   = get_verbosity(&cfg);
            let editor      = get_editor(&cfg);
            let editor_opts = get_editor_opts(&cfg);

            debug!("Building configuration");
            debug!("  - verbosity  : {:?}", verbosity);
            debug!("  - editor     : {:?}", editor);
            debug!("  - editor-opts: {}", editor_opts);

            Configuration {
                config: cfg,
                verbosity: verbosity,
                editor: editor,
                editor_opts: editor_opts,
            }
        })
    }

    /// Get a new configuration object built from the given toml value.
    pub fn with_value(value: Value) -> Configuration {
        Configuration{
            verbosity: get_verbosity(&value),
            editor: get_editor(&value),
            editor_opts: get_editor_opts(&value),
            config: value,
        }
    }

    /// Get the Editor setting from the configuration
    pub fn editor(&self) -> Option<&String> {
        self.editor.as_ref()
    }

    /// Get the underlying configuration TOML object
    pub fn config(&self) -> &Value {
        &self.config
    }

    /// Get the configuration of the store, if any.
    pub fn store_config(&self) -> Option<&Value> {
        match self.config {
            Value::Table(ref tabl) => tabl.get("store"),
            _ => None,
        }
    }

    /// Override the configuration.
    /// The `v` parameter is expected to contain 'key=value' pairs where the key is a path in the
    /// TOML tree, the value to be an appropriate value.
    ///
    /// The override fails if the configuration which is about to be overridden does not exist or
    /// the `value` part cannot be converted to the type of the configuration value.
    ///
    /// If `v` is empty, this is considered to be a successful `override_config()` call.
    pub fn override_config(&mut self, v: Vec<String>) -> Result<()> {
        use libimagutil::key_value_split::*;
        use libimagutil::iter::*;
        use self::error::ConfigErrorKind as CEK;
        use self::error::MapErrInto;
        use libimagerror::into::IntoError;
        use libimagstore::toml_ext::TomlValueExt;

        v.into_iter()
            .map(|s| { debug!("Trying to process '{}'", s); s })
            .filter_map(|s| match s.into_kv() {
                Some(kv) => Some(kv.into()),
                None => {
                    warn!("Could split at '=' - will be ignore override");
                    None
                }
            })
            .map(|(k, v)| self
                 .config
                 .read(&k[..])
                 .map_err_into(CEK::TOMLParserError)
                 .map(|toml| match toml {
                    Some(value) => match into_value(value, v) {
                        Some(v) => {
                            info!("Successfully overridden: {} = {}", k, v);
                            Ok(v)
                        },
                        None => Err(CEK::ConfigOverrideTypeNotMatching.into_error()),
                    },
                    None => Err(CEK::ConfigOverrideKeyNotAvailable.into_error()),
                })
            )
            .fold_result(|i| i)
            .map_err(Box::new)
            .map_err(|e| CEK::ConfigOverrideError.into_error_with_cause(e))
    }
}

/// Tries to convert the String `s` into the same type as `value`.
///
/// Returns None if string cannot be converted.
///
/// Arrays and Tables are not supported and will yield `None`.
fn into_value(value: Value, s: String) -> Option<Value> {
    use std::str::FromStr;

    match value {
        Value::String(_)  => Some(Value::String(s)),
        Value::Integer(_) => FromStr::from_str(&s[..]).ok().map(Value::Integer),
        Value::Float(_)   => FromStr::from_str(&s[..]).ok().map(Value::Float),
        Value::Boolean(_) => {
            if s == "true" { Some(Value::Boolean(true)) }
            else if s == "false" { Some(Value::Boolean(false)) }
            else { None }
        }
        Value::Datetime(_) => Value::try_from(s).ok(),
        _ => None,
    }
}

impl Deref for Configuration {
    type Target = Value;

    fn deref(&self) -> &Value {
        &self.config
    }

}

fn get_verbosity(v: &Value) -> bool {
    match *v {
        Value::Table(ref t) => t.get("verbose")
                .map_or(false, |v| is_match!(v, &Value::Boolean(true))),
        _ => false,
    }
}

fn get_editor(v: &Value) -> Option<String> {
    match *v {
        Value::Table(ref t) => t.get("editor")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, }),
        _ => None,
    }
}

fn get_editor_opts(v: &Value) -> String {
    match *v {
        Value::Table(ref t) => t.get("editor-opts")
                .and_then(|v| match *v { Value::String(ref s) => Some(s.clone()), _ => None, })
                .unwrap_or_default(),
        _ => String::new(),
    }
}

/// Helper to fetch the config file
///
/// Tests several variants for the config file path and uses the first one which works.
fn fetch_config(rtp: &PathBuf) -> Result<Value> {
    use std::env;
    use std::fs::File;
    use std::io::Read;
    use std::io::Write;
    use std::io::stderr;

    use xdg_basedir;
    use itertools::Itertools;

    use libimagutil::variants::generate_variants as gen_vars;
    use libimagerror::trace::trace_error;

    let variants = vec!["config", "config.toml", "imagrc", "imagrc.toml"];
    let modifier = |base: &PathBuf, v: &'static str| {
        let mut base = base.clone();
        base.push(String::from(v));
        base
    };

    vec![
        vec![rtp.clone()],
        gen_vars(rtp.clone(), variants.clone(), &modifier),

        env::var("HOME").map(|home| gen_vars(PathBuf::from(home), variants.clone(), &modifier))
                        .unwrap_or(vec![]),

        xdg_basedir::get_data_home().map(|data_dir| gen_vars(data_dir, variants.clone(), &modifier))
                                    .unwrap_or(vec![]),
    ].iter()
        .flatten()
        .filter(|path| path.exists() && path.is_file())
        .map(|path| {
            let content = {
                let mut s = String::new();
                let f = File::open(path);
                if f.is_err() {
                    return None
                }
                let mut f = f.unwrap();
                f.read_to_string(&mut s).ok();
                s
            };

            match ::toml::de::from_str(&content[..]) {
                Ok(res) => res,
                Err(e) => {
                    write!(stderr(), "Config file parser error:").ok();
                    trace_error(&e);
                    None
                }
            }
        })
        .filter(|loaded| loaded.is_some())
        .nth(0)
        .map(|inner| Value::Table(inner.unwrap()))
        .ok_or(ConfigErrorKind::NoConfigFileFound.into())
}

pub trait InternalConfiguration {
    fn enable_logging(&self) -> bool {
        true
    }

    fn use_inmemory_fs(&self) -> bool {
        false
    }
}

impl<'a> InternalConfiguration for App<'a, 'a> {}
<|endoftext|>


<commit_before>extern crate desync;
extern crate futures;

use desync::*;
use futures::stream;
use futures::executor;
use futures::sink::{SinkExt};
use futures::stream::{StreamExt};
use futures::channel::mpsc;

use std::sync::*;
use std::thread;
use std::time::Duration;

#[test]
fn pipe_in_simple_stream() {
    // Create a stream
    let stream  = vec![1, 2, 3];
    let stream  = stream::iter(stream);

    // Create an object for the stream to be piped into
    let obj     = Arc::new(Desync::new(vec![]));

    // Pipe the stream into the object
    pipe_in(Arc::clone(&obj), stream, |core: &mut Vec<Result<i32, ()>>, item| core.push(Ok(item)));

    // Delay to allow the messages to be processed on the stream
    thread::sleep(Duration::from_millis(10));

    // Once the stream is drained, the core should contain Ok(1), Ok(2), Ok(3)
    assert!(obj.sync(|core| core.clone()) == vec![Ok(1), Ok(2), Ok(3)])
}

#[test]
fn pipe_in_mpsc_receiver() {
    // Create a channel to send to the object
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object
    let obj = Arc::new(Desync::new(vec![]));

    // Add anything received to the vector via a pipe
    pipe_in(Arc::clone(&obj), receiver, |core, item| core.push(item));

    // Initially empty
    assert!(obj.sync(|core| core.clone()) == vec![]);

    // Send some values
    let send_values = async {
        sender.send(1).await.unwrap();
        sender.send(2).await.unwrap();
    };
    executor::block_on(send_values);

    // Delay to allow the messages to be processed on the stream
    // TODO: fix so this isn't needed. This happens because there's a race between when 'poll'
    // is called in the pipe and the 'async' call
    thread::sleep(Duration::from_millis(10));

    // Should be available on the core
    assert!(obj.sync(|core| core.clone()) == vec![1, 2]);
}

#[test]
fn pipe_through() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(10);

    // Create an object to pipe through
    let obj             = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item| item + *core);

    // Start things running
    executor::block_on(async {
        sender.send(2).await.unwrap();
        assert!(pipe_out.next().await == Some(3));

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));

        // Changing the value should change the output
        obj.desync(|core| *core = 2);

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));
    });
}

#[test]
fn pipe_through_stream_closes() {
    let mut pipe_out_with_closed_stream = {
        // Create a channel we'll use to send data to the pipe
        let (mut sender, receiver) = mpsc::channel(10);

        // Create an object to pipe through
        let obj = Arc::new(Desync::new(1));

        // Create a pipe that adds values from the stream to the value in the object
        let mut pipe_out = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

        // Start things running
        executor::block_on(async {
            sender.send(2).await.unwrap();
            assert!(pipe_out.next().await == Some(3))
        });

        pipe_out
    };

    executor::block_on(async {
        // The sender is now closed (the sender and receiver are dropped after the block above), so the pipe should close too
        assert!(pipe_out_with_closed_stream.next().await == None);
    });
}

#[test]
fn pipe_through_produces_backpressure() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object to pipe through
    let obj = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

    // Set the backpressure depth to 3
    pipe_out.set_backpressure_depth(3);

    // Start things running. We never read from this pipe here
    executor::block_on(async {
        // Send 3 events to the pipe. Wait a bit between them to allow for processing time
        for _x in 0..3 {
            assert!(sender.try_send(1) == Ok(()));

            // The wait here allows the message to flow through to the pipe (if we call try_send again before the pipe has a chance to accept the input)
            thread::sleep(Duration::from_millis(5));
        }

        // This will stick in the channel (pipe should not be accepting more input)
        assert!(sender.try_send(2) == Ok(()));
        thread::sleep(Duration::from_millis(5));

        // Channel will push back on this one
        let channel_full = sender.try_send(3);
        assert!(channel_full.is_err());
        assert!(channel_full.unwrap_err().is_full());
    });
}
<commit_msg>Oops, failed to change this test properly<commit_after>extern crate desync;
extern crate futures;

use desync::*;
use futures::stream;
use futures::executor;
use futures::sink::{SinkExt};
use futures::stream::{StreamExt};
use futures::channel::mpsc;

use std::sync::*;
use std::thread;
use std::time::Duration;

#[test]
fn pipe_in_simple_stream() {
    // Create a stream
    let stream  = vec![1, 2, 3];
    let stream  = stream::iter(stream);

    // Create an object for the stream to be piped into
    let obj     = Arc::new(Desync::new(vec![]));

    // Pipe the stream into the object
    pipe_in(Arc::clone(&obj), stream, |core: &mut Vec<Result<i32, ()>>, item| core.push(Ok(item)));

    // Delay to allow the messages to be processed on the stream
    thread::sleep(Duration::from_millis(10));

    // Once the stream is drained, the core should contain Ok(1), Ok(2), Ok(3)
    assert!(obj.sync(|core| core.clone()) == vec![Ok(1), Ok(2), Ok(3)])
}

#[test]
fn pipe_in_mpsc_receiver() {
    // Create a channel to send to the object
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object
    let obj = Arc::new(Desync::new(vec![]));

    // Add anything received to the vector via a pipe
    pipe_in(Arc::clone(&obj), receiver, |core, item| core.push(item));

    // Initially empty
    assert!(obj.sync(|core| core.clone()) == vec![]);

    // Send some values
    let send_values = async {
        sender.send(1).await.unwrap();
        sender.send(2).await.unwrap();
    };
    executor::block_on(send_values);

    // Delay to allow the messages to be processed on the stream
    // TODO: fix so this isn't needed. This happens because there's a race between when 'poll'
    // is called in the pipe and the 'async' call
    thread::sleep(Duration::from_millis(10));

    // Should be available on the core
    assert!(obj.sync(|core| core.clone()) == vec![1, 2]);
}

#[test]
fn pipe_through() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(10);

    // Create an object to pipe through
    let obj             = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item| item + *core);

    // Start things running
    executor::block_on(async {
        sender.send(2).await.unwrap();
        assert!(pipe_out.next().await == Some(3));

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(43));

        // Changing the value should change the output
        obj.desync(|core| *core = 2);

        sender.send(42).await.unwrap();
        assert!(pipe_out.next().await == Some(44));
    });
}

#[test]
fn pipe_through_stream_closes() {
    let mut pipe_out_with_closed_stream = {
        // Create a channel we'll use to send data to the pipe
        let (mut sender, receiver) = mpsc::channel(10);

        // Create an object to pipe through
        let obj = Arc::new(Desync::new(1));

        // Create a pipe that adds values from the stream to the value in the object
        let mut pipe_out = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

        // Start things running
        executor::block_on(async {
            sender.send(2).await.unwrap();
            assert!(pipe_out.next().await == Some(3))
        });

        pipe_out
    };

    executor::block_on(async {
        // The sender is now closed (the sender and receiver are dropped after the block above), so the pipe should close too
        assert!(pipe_out_with_closed_stream.next().await == None);
    });
}

#[test]
fn pipe_through_produces_backpressure() {
    // Create a channel we'll use to send data to the pipe
    let (mut sender, receiver) = mpsc::channel(0);

    // Create an object to pipe through
    let obj = Arc::new(Desync::new(1));

    // Create a pipe that adds values from the stream to the value in the object
    let mut pipe_out    = pipe(Arc::clone(&obj), receiver, |core, item: i32| item + *core);

    // Set the backpressure depth to 3
    pipe_out.set_backpressure_depth(3);

    // Start things running. We never read from this pipe here
    executor::block_on(async {
        // Send 3 events to the pipe. Wait a bit between them to allow for processing time
        for _x in 0..3 {
            assert!(sender.try_send(1) == Ok(()));

            // The wait here allows the message to flow through to the pipe (if we call try_send again before the pipe has a chance to accept the input)
            thread::sleep(Duration::from_millis(5));
        }

        // This will stick in the channel (pipe should not be accepting more input)
        assert!(sender.try_send(2) == Ok(()));
        thread::sleep(Duration::from_millis(5));

        // Channel will push back on this one
        let channel_full = sender.try_send(3);
        assert!(channel_full.is_err());
        assert!(channel_full.unwrap_err().is_full());
    });
}
<|endoftext|>


<commit_before>extern crate funzzy;
mod cli;

#[warn(unused_imports)]
use std::io::prelude::*;
use std::fs::{ File, remove_file };

#[test]
fn it_returns_some_command() {
   let mut args = funzzy::cli::Args::new();
   args.cmd_init = true;
   assert!(funzzy::cli::command(&args).is_some())
}

#[test]
fn it_returns_no_command() {
   let args = funzzy::cli::Args::new();
   assert!(funzzy::cli::command(&args).is_none())
}

#[test]
fn it_returns_watch_command() {
   setup();
   let mut args = funzzy::cli::Args::new();
   args.cmd_watch = true;
   assert!(funzzy::cli::command(&args).is_some())
}

#[test]
fn it_returns_watch_command_with_arbitrary_command() {
   let mut args = funzzy::cli::Args::new();
   args.cmd_watch = true;
   args.flag_c = true;
   args.arg_command = vec![String::from("cargo build")];
   assert!(funzzy::cli::command(&args).is_some())
}

#[test]
#[should_panic(expected = "File .watch.yaml cannot be open. Cause: No such file or directory (os error 2)")]
fn it_raise_panic_when_file_not_found() {
   setup();
   let _ = remove_file(".watch.yaml");
   let mut args = funzzy::cli::Args::new();
   args.cmd_watch = true;
   assert!(funzzy::cli::command(&args).is_some())
}

fn setup() {
    let mut file: File = File::create(".watch.yaml").unwrap();
    file.write_all(funzzy::cli::init::DEFAULT_CONTENT.as_ref());
}
<commit_msg>tests: removing test file not found<commit_after>extern crate funzzy;
mod cli;

#[warn(unused_imports)]
use std::io::prelude::*;
use std::fs::{ File, remove_file };

#[test]
fn it_returns_some_command() {
   let mut args = funzzy::cli::Args::new();
   args.cmd_init = true;
   assert!(funzzy::cli::command(&args).is_some())
}

#[test]
fn it_returns_no_command() {
   let args = funzzy::cli::Args::new();
   assert!(funzzy::cli::command(&args).is_none())
}

#[test]
fn it_returns_watch_command() {
   let mut args = funzzy::cli::Args::new();
   args.cmd_watch = true;
   assert!(funzzy::cli::command(&args).is_some())
}

#[test]
fn it_returns_watch_command_with_arbitrary_command() {
   let mut args = funzzy::cli::Args::new();
   args.cmd_watch = true;
   args.flag_c = true;
   args.arg_command = vec![String::from("cargo build")];
   assert!(funzzy::cli::command(&args).is_some())
}
<|endoftext|>


<commit_before>type Node<T> = Option<Box<Link<T>>>;

struct Link<T> {
    value: T,
    next: Node<T>,
}

struct LinkedList<T> {
    head: Option<Link<T>>,
}

impl<T> LinkedList<T> {
    pub fn new() -> LinkedList<T> {
        LinkedList{head: None}
    }

    pub fn insert(&mut self, value: T) -> LinkedList<T> {
        let h = match self.head.take() {
            Some(n) => Some(Link{value: value, next: Some(Box::new(n))}),
            None => None,
        };

        LinkedList{head: h}
    }
}

#[test]
fn it_works() {
    let mut ll: LinkedList<i32> = LinkedList::new();
    ll.insert(1);
    ll.insert(2);
}
<commit_msg>LinkedList with insert working<commit_after>type Node<T> = Option<Box<Link<T>>>;

struct Link<T> {
    value: T,
    next: Node<T>,
}

struct LinkedList<T> {
    head: Option<Link<T>>,
}

impl<T> LinkedList<T> {
    pub fn new() -> LinkedList<T> {
        LinkedList{head: None}
    }

    pub fn insert(&mut self, value: T) {
        let h = match self.head.take() {
            Some(n) => Some(Box::new(n)),
            None => None,
        };

        self.head = Some(Link{value: value, next: h})
    }

    pub fn first(&self) -> Option<&T> {
        match self.head {
            Some(ref h) => Some(&h.value),
            None => None,
        }
    }
}

#[test]
fn it_works() {
    let mut ll: LinkedList<i32> = LinkedList::new();
    assert_eq!(ll.first(), None);

    ll.insert(1);
    assert_eq!(ll.first(), Some(&1));

    ll.insert(2);
    assert_eq!(ll.first(), Some(&2));
}
<|endoftext|>


<commit_before>//! Tests from RecorderTest.java

use hdrhistogram::{sync::SyncHistogram, Histogram};
use std::sync::Arc;
use std::{thread, time};

const TRACKABLE_MAX: u64 = 3600 * 1000 * 1000;
// Store up to 2 * 10^3 in single-unit precision. Can be 5 at most.
const SIGFIG: u8 = 3;
const TEST_VALUE_LEVEL: u64 = 4;

#[test]
fn record_through() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}

#[test]
fn recorder_drop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    let mut r = h.recorder();
    let jh = thread::spawn(move || {
        r += TEST_VALUE_LEVEL;
    });
    h.refresh();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
    jh.join().unwrap();
}

#[test]
fn record_nodrop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    let barrier = Arc::new(std::sync::Barrier::new(2));
    let mut r = h.recorder();
    let b = Arc::clone(&barrier);
    let jh = thread::spawn(move || {
        r += TEST_VALUE_LEVEL;
        b.wait();
    });
    h.refresh();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
    barrier.wait();
    jh.join().unwrap();
}

#[test]
fn phase_timeout() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    let mut r = h.recorder();
    r += TEST_VALUE_LEVEL;
    h.refresh_timeout(time::Duration::from_millis(100));

    // second TEST_VALUE_LEVEL should not be visible
    // since no record happened after phase()
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}

#[test]
fn recorder_synchronize() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let barrier = Arc::new(std::sync::Barrier::new(2));
    let mut r = h.recorder();
    let b = Arc::clone(&barrier);
    let jh = thread::spawn(move || {
        let n = 10_000;
        for _ in 0..n {
            r += TEST_VALUE_LEVEL;
        }
        // one of the writes above will unblock the reader's first phase
        // the 1st barrier below ensures that the reader's second phase isn't passed by a write too
        // the 2nd barrier below ensures that there is at least one write to synchronize,
        // and that that write doesn't wake up the 2nd phase
        b.wait();
        r += TEST_VALUE_LEVEL;
        b.wait();
        r.synchronize();
        n + 1
    });
    h.refresh(); // this should be unblocked by one of the writes
    barrier.wait();
    barrier.wait();
    h.refresh(); // this will be unblocked by, and will unblock, the synchronize
    let n = jh.join().unwrap();
    h.refresh(); // no recorders, so we should be fine

    assert_eq!(h.count_at(TEST_VALUE_LEVEL), n);
    assert_eq!(h.len(), n);
}

#[test]
fn phase_no_wait_after_drop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    {
        let _ = h.recorder();
    }
    h.refresh(); // this shouldn't block since the recorder went away

    assert_eq!(h.len(), 0);
}

#[test]
fn mt_record_static() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 100_000;
                for _ in 0..n {
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                r.synchronize();
                n
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn mt_record_dynamic() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 300_000;
                for _ in 0..n {
                    if n % 317 == 0 {
                        let r2 = r.clone();
                        let mut r2 = std::mem::replace(&mut r, r2);
                        thread::spawn(move || {
                            r2.synchronize();
                        });
                    }
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                r.synchronize();
                n as u64
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn mt_record_dynamic_nosync() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 300_000;
                for _ in 0..n {
                    if n % 317 == 0 {
                        r = r.clone();
                    }
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                n as u64
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn concurrent_writes() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    let mut r = h.recorder();
    r += TEST_VALUE_LEVEL;
    h.refresh_timeout(time::Duration::from_millis(100));

    // second TEST_VALUE_LEVEL should not be visible
    // since no record happened after phase()
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}
<commit_msg>Better test coverage<commit_after>//! Tests from RecorderTest.java

use hdrhistogram::{sync::SyncHistogram, Histogram};
use std::sync::Arc;
use std::{thread, time};

const TRACKABLE_MAX: u64 = 3600 * 1000 * 1000;
// Store up to 2 * 10^3 in single-unit precision. Can be 5 at most.
const SIGFIG: u8 = 3;
const TEST_VALUE_LEVEL: u64 = 4;

#[test]
fn record_through() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}

#[test]
fn recorder_drop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    let mut r = h.recorder();
    let jh = thread::spawn(move || {
        r += TEST_VALUE_LEVEL;
    });
    h.refresh();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
    jh.join().unwrap();
}

#[test]
fn record_nodrop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    let barrier = Arc::new(std::sync::Barrier::new(2));
    let mut r = h.recorder();
    let b = Arc::clone(&barrier);
    let jh = thread::spawn(move || {
        r += TEST_VALUE_LEVEL;
        b.wait();
    });
    h.refresh();
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
    barrier.wait();
    jh.join().unwrap();
}

#[test]
fn phase_timeout() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    let mut r = h.recorder();
    r += TEST_VALUE_LEVEL;
    h.refresh_timeout(time::Duration::from_millis(100));

    // second TEST_VALUE_LEVEL should not be visible
    // since no record happened after phase()
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}

#[test]
fn recorder_synchronize() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let barrier = Arc::new(std::sync::Barrier::new(2));
    let mut r = h.recorder();
    let b = Arc::clone(&barrier);
    let jh = thread::spawn(move || {
        let n = 10_000;
        for _ in 0..n {
            r += TEST_VALUE_LEVEL;
        }
        // one of the writes above will unblock the reader's first phase
        // the 1st barrier below ensures that the reader's second phase isn't passed by a write too
        // the 2nd barrier below ensures that there is at least one write to synchronize,
        // and that that write doesn't wake up the 2nd phase
        b.wait();
        r += TEST_VALUE_LEVEL;
        b.wait();
        r.synchronize();
        n + 1
    });
    h.refresh(); // this should be unblocked by one of the writes
    barrier.wait();
    barrier.wait();
    h.refresh(); // this will be unblocked by, and will unblock, the synchronize
    let n = jh.join().unwrap();
    h.refresh(); // no recorders, so we should be fine

    assert_eq!(h.count_at(TEST_VALUE_LEVEL), n);
    assert_eq!(h.len(), n);
}

#[test]
fn phase_no_wait_after_drop() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    {
        let _ = h.recorder();
    }
    h.refresh(); // this shouldn't block since the recorder went away

    assert_eq!(h.len(), 0);
}

#[test]
fn mt_record_static() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 100_000;
                for _ in 0..n {
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                r.synchronize();
                n
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn no_block_empty_sync() {
    let h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    h.recorder().synchronize();
}

#[test]
fn refresh_times_out() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let _r = h.recorder();
    h.refresh_timeout(time::Duration::from_millis(100));
}

#[test]
fn mt_record_dynamic() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 30_000;
                for i in 0..n {
                    if i % 1_000 == 0 {
                        let r2 = r.clone();
                        let mut r2 = std::mem::replace(&mut r, r2);
                        thread::spawn(move || {
                            r2.synchronize();
                        });
                    }
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                r.synchronize();
                n as u64
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn idle_recorder() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let barrier = Arc::new(std::sync::Barrier::new(2));
    let mut r = h.recorder();
    let i = r.idle();
    h.refresh(); // this should not block
    h.refresh(); // nor should this
    drop(i);
    let b = Arc::clone(&barrier);
    let jh = thread::spawn(move || {
        r += TEST_VALUE_LEVEL;
        b.wait();
        r.synchronize();
    });
    barrier.wait();
    h.refresh(); // this will block!

    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
    jh.join().unwrap();
}

#[test]
fn mt_record_dynamic_nosync() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();

    let n = 16;
    let barrier = Arc::new(std::sync::Barrier::new(n + 1));
    let jhs: Vec<_> = (0..n)
        .map(|_| {
            let mut r = h.recorder();
            let barrier = Arc::clone(&barrier);
            thread::spawn(move || {
                let n = 30_000;
                for i in 0..n {
                    if i % 1_000 == 0 {
                        r = r.clone();
                    }
                    r += TEST_VALUE_LEVEL;
                }
                barrier.wait();
                n as u64
            })
        })
        .collect();

    barrier.wait();
    h.refresh();

    assert_eq!(h.len(), jhs.into_iter().map(|r| r.join().unwrap()).sum());
}

#[test]
fn concurrent_writes() {
    let mut h: SyncHistogram<_> = Histogram::<u64>::new_with_max(TRACKABLE_MAX, SIGFIG)
        .unwrap()
        .into();
    h.record(TEST_VALUE_LEVEL).unwrap();
    let mut r = h.recorder();
    r += TEST_VALUE_LEVEL;
    h.refresh_timeout(time::Duration::from_millis(100));

    // second TEST_VALUE_LEVEL should not be visible
    // since no record happened after phase()
    assert_eq!(h.count_at(TEST_VALUE_LEVEL), 1);
    assert_eq!(h.len(), 1);
}
<|endoftext|>