<commit_before>//! Checks for continue statements in loops that are redundant.
//!
//! For example, the lint would catch
//!
//! ```rust
//! let mut a = 1;
//! let x = true;
//!
//! while a < 5 {
//!     a = 6;
//!     if x {
//!         // ...
//!     } else {
//!         continue;
//!     }
//!     println!("Hello, world");
//! }
//! ```
//!
//! And suggest something like this:
//!
//! ```rust
//! let mut a = 1;
//! let x = true;
//!
//! while a < 5 {
//!     a = 6;
//!     if x {
//!         // ...
//!         println!("Hello, world");
//!     }
//! }
//! ```
//!
//! This lint is **warn** by default.
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{original_sp, DUMMY_SP};
use rustc_span::Span;
use syntax::ast;

use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** The lint checks for `if`-statements appearing in loops
    /// that contain a `continue` statement in either their main blocks or their
    /// `else`-blocks, when omitting the `else`-block possibly with some
    /// rearrangement of code can make the code easier to understand.
    ///
    /// **Why is this bad?** Having explicit `else` blocks for `if` statements
    /// containing `continue` in their THEN branch adds unnecessary branching and
    /// nesting to the code. Having an else block containing just `continue` can
    /// also be better written by grouping the statements following the whole `if`
    /// statement within the THEN block and omitting the else block completely.
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust
    /// # fn condition() -> bool { false }
    /// # fn update_condition() {}
    /// # let x = false;
    /// while condition() {
    ///     update_condition();
    ///     if x {
    ///         // ...
    ///     } else {
    ///         continue;
    ///     }
    ///     println!("Hello, world");
    /// }
    /// ```
    ///
    /// Could be rewritten as
    ///
    /// ```rust
    /// # fn condition() -> bool { false }
    /// # fn update_condition() {}
    /// # let x = false;
    /// while condition() {
    ///     update_condition();
    ///     if x {
    ///         // ...
    ///         println!("Hello, world");
    ///     }
    /// }
    /// ```
    ///
    /// As another example, the following code
    ///
    /// ```rust
    /// # fn waiting() -> bool { false }
    /// loop {
    ///     if waiting() {
    ///         continue;
    ///     } else {
    ///         // Do something useful
    ///     }
    ///     # break;
    /// }
    /// ```
    /// Could be rewritten as
    ///
    /// ```rust
    /// # fn waiting() -> bool { false }
    /// loop {
    ///     if waiting() {
    ///         continue;
    ///     }
    ///     // Do something useful
    ///     # break;
    /// }
    /// ```
    pub NEEDLESS_CONTINUE,
    pedantic,
    "`continue` statements that can be replaced by a rearrangement of code"
}

declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);

impl EarlyLintPass for NeedlessContinue {
    fn check_expr(&mut self, ctx: &EarlyContext<'_>, expr: &ast::Expr) {
        if !expr.span.from_expansion() {
            check_and_warn(ctx, expr);
        }
    }
}

/* This lint has to mainly deal with two cases of needless continue
 * statements. */
// Case 1 [Continue inside else block]:
//
//     loop {
//         // region A
//         if cond {
//             // region B
//         } else {
//             continue;
//         }
//         // region C
//     }
//
// This code can better be written as follows:
//
//     loop {
//         // region A
//         if cond {
//             // region B
//             // region C
//         }
//     }
//
// Case 2 [Continue inside then block]:
//
//     loop {
//       // region A
//       if cond {
//           continue;
//           // potentially more code here.
//       } else {
//           // region B
//       }
//       // region C
//     }
//
//
// This snippet can be refactored to:
//
//     loop {
//       // region A
//       if !cond {
//           // region B
//           // region C
//       }
//     }
//

/// Given an expression, returns true if either of the following is true
///
/// - The expression is a `continue` node.
/// - The expression node is a block with the first statement being a
/// `continue`.
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
    match else_expr.kind {
        ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
        ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
        _ => false,
    }
}

fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
    block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
            if let ast::ExprKind::Continue(ref l) = e.kind {
                compare_labels(label, l.as_ref())
            } else {
                false
            }
        },
        _ => false,
    })
}

/// If the `continue` has a label, check it matches the label of the loop.
fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
    match (loop_label, continue_label) {
        // `loop { continue; }` or `'a loop { continue; }`
        (_, None) => true,
        // `loop { continue 'a; }`
        (None, _) => false,
        // `'a loop { continue 'a; }` or `'a loop { continue 'b; }`
        (Some(x), Some(y)) => x.ident == y.ident,
    }
}

/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
/// the AST object representing the loop block of `expr`.
fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
where
    F: FnMut(&ast::Block, Option<&ast::Label>),
{
    if let ast::ExprKind::While(_, loop_block, label)
    | ast::ExprKind::ForLoop(_, _, loop_block, label)
    | ast::ExprKind::Loop(loop_block, label) = &expr.kind
    {
        func(loop_block, label.as_ref());
    }
}

/// If `stmt` is an if expression node with an `else` branch, calls func with
/// the
/// following:
///
/// - The `if` expression itself,
/// - The `if` condition expression,
/// - The `then` block, and
/// - The `else` expression.
fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
where
    F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
{
    match stmt.kind {
        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
            if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
                func(e, cond, if_block, else_expr);
            }
        },
        _ => {},
    }
}

/// A type to distinguish between the two distinct cases this lint handles.
#[derive(Copy, Clone, Debug)]
enum LintType {
    ContinueInsideElseBlock,
    ContinueInsideThenBlock,
}

/// Data we pass around for construction of help messages.
struct LintData<'a> {
    /// The `if` expression encountered in the above loop.
    if_expr: &'a ast::Expr,
    /// The condition expression for the above `if`.
    if_cond: &'a ast::Expr,
    /// The `then` block of the `if` statement.
    if_block: &'a ast::Block,
    /// The `else` block of the `if` statement.
    /// Note that we only work with `if` exprs that have an `else` branch.
    else_expr: &'a ast::Expr,
    /// The 0-based index of the `if` statement in the containing loop block.
    stmt_idx: usize,
    /// The statements of the loop block.
    block_stmts: &'a [ast::Stmt],
}

const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";

const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
                                         expression";

const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
                                             follows (in the loop) with the `if` block";

const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";

fn emit_warning<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
    // snip    is the whole *help* message that appears after the warning.
    // message is the warning message.
    // expr    is the expression which the lint warning message refers to.
    let (snip, message, expr) = match typ {
        LintType::ContinueInsideElseBlock => (
            suggestion_snippet_for_continue_inside_else(ctx, data),
            MSG_REDUNDANT_ELSE_BLOCK,
            data.else_expr,
        ),
        LintType::ContinueInsideThenBlock => (
            suggestion_snippet_for_continue_inside_if(ctx, data),
            MSG_ELSE_BLOCK_NOT_NEEDED,
            data.if_expr,
        ),
    };
    span_lint_and_help(
        ctx,
        NEEDLESS_CONTINUE,
        expr.span,
        message,
        &format!("{}\n{}", header, snip),
    );
}

fn suggestion_snippet_for_continue_inside_if<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
    let cond_code = snippet(ctx, data.if_cond.span, "..");

    let continue_code = snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span));

    let else_code = snippet_block(ctx, data.else_expr.span, "..", Some(data.if_expr.span));

    let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
    format!(
        "{indent}if {} {}\n{indent}{}",
        cond_code,
        continue_code,
        else_code,
        indent = " ".repeat(indent_if),
    )
}

fn suggestion_snippet_for_continue_inside_else<'a>(ctx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
    let cond_code = snippet(ctx, data.if_cond.span, "..");

    // Region B
    let block_code = erode_from_back(&snippet_block(ctx, data.if_block.span, "..", Some(data.if_expr.span)));

    // Region C
    // These is the code in the loop block that follows the if/else construction
    // we are complaining about. We want to pull all of this code into the
    // `then` block of the `if` statement.
    let indent = span_of_first_expr_in_block(data.if_block)
        .and_then(|span| indent_of(ctx, span))
        .unwrap_or(0);
    let to_annex = data.block_stmts[data.stmt_idx + 1..]
        .iter()
        .map(|stmt| original_sp(stmt.span, DUMMY_SP))
        .map(|span| {
            let snip = snippet_block(ctx, span, "..", None).into_owned();
            snip.lines()
                .map(|line| format!("{}{}", " ".repeat(indent), line))
                .collect::<Vec<_>>()
                .join("\n")
        })
        .collect::<Vec<_>>()
        .join("\n");

    let indent_if = indent_of(ctx, data.if_expr.span).unwrap_or(0);
    format!(
        "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
        cond_code,
        block_code,
        to_annex,
        indent = " ".repeat(indent),
        indent_if = " ".repeat(indent_if),
    )
}

fn check_and_warn<'a>(ctx: &EarlyContext<'_>, expr: &'a ast::Expr) {
    with_loop_block(expr, |loop_block, label| {
        for (i, stmt) in loop_block.stmts.iter().enumerate() {
            with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
                let data = &LintData {
                    stmt_idx: i,
                    if_expr,
                    if_cond: cond,
                    if_block: then_block,
                    else_expr,
                    block_stmts: &loop_block.stmts,
                };
                if needless_continue_in_else(else_expr, label) {
                    emit_warning(
                        ctx,
                        data,
                        DROP_ELSE_BLOCK_AND_MERGE_MSG,
                        LintType::ContinueInsideElseBlock,
                    );
                } else if is_first_block_stmt_continue(then_block, label) {
                    emit_warning(ctx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
                }
            });
        }
    });
}

/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating
/// till a non-whitespace character is found.  e.g., the string. If no closing `}` is present, the
/// string will be preserved.
///
/// ```rust
/// {
///     let x = 5;
/// }
/// ```
///
/// is transformed to
///
/// ```ignore
///     {
///         let x = 5;
/// ```
#[must_use]
fn erode_from_back(s: &str) -> String {
    let mut ret = s.to_string();
    while ret.pop().map_or(false, |c| c != '}') {}
    while let Some(c) = ret.pop() {
        if !c.is_whitespace() {
            ret.push(c);
            break;
        }
    }
    if ret.is_empty() {
        s.to_string()
    } else {
        ret
    }
}

fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
    block.stmts.iter().next().map(|stmt| stmt.span)
}

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

    #[test]
    #[rustfmt::skip]
    fn test_erode_from_back() {
        let input = "\
{
    let x = 5;
    let y = format!(\"{}\", 42);
}";

        let expected = "\
{
    let x = 5;
    let y = format!(\"{}\", 42);";

        let got = erode_from_back(input);
        assert_eq!(expected, got);
    }

    #[test]
    #[rustfmt::skip]
    fn test_erode_from_back_no_brace() {
        let input = "\
let x = 5;
let y = something();
";
        let expected = input;
        let got = erode_from_back(input);
        assert_eq!(expected, got);
    }
}
<commit_msg>Rename ctx->cx in needless_continue<commit_after>//! Checks for continue statements in loops that are redundant.
//!
//! For example, the lint would catch
//!
//! ```rust
//! let mut a = 1;
//! let x = true;
//!
//! while a < 5 {
//!     a = 6;
//!     if x {
//!         // ...
//!     } else {
//!         continue;
//!     }
//!     println!("Hello, world");
//! }
//! ```
//!
//! And suggest something like this:
//!
//! ```rust
//! let mut a = 1;
//! let x = true;
//!
//! while a < 5 {
//!     a = 6;
//!     if x {
//!         // ...
//!         println!("Hello, world");
//!     }
//! }
//! ```
//!
//! This lint is **warn** by default.
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::{original_sp, DUMMY_SP};
use rustc_span::Span;
use syntax::ast;

use crate::utils::{indent_of, snippet, snippet_block, span_lint_and_help};

declare_clippy_lint! {
    /// **What it does:** The lint checks for `if`-statements appearing in loops
    /// that contain a `continue` statement in either their main blocks or their
    /// `else`-blocks, when omitting the `else`-block possibly with some
    /// rearrangement of code can make the code easier to understand.
    ///
    /// **Why is this bad?** Having explicit `else` blocks for `if` statements
    /// containing `continue` in their THEN branch adds unnecessary branching and
    /// nesting to the code. Having an else block containing just `continue` can
    /// also be better written by grouping the statements following the whole `if`
    /// statement within the THEN block and omitting the else block completely.
    ///
    /// **Known problems:** None
    ///
    /// **Example:**
    /// ```rust
    /// # fn condition() -> bool { false }
    /// # fn update_condition() {}
    /// # let x = false;
    /// while condition() {
    ///     update_condition();
    ///     if x {
    ///         // ...
    ///     } else {
    ///         continue;
    ///     }
    ///     println!("Hello, world");
    /// }
    /// ```
    ///
    /// Could be rewritten as
    ///
    /// ```rust
    /// # fn condition() -> bool { false }
    /// # fn update_condition() {}
    /// # let x = false;
    /// while condition() {
    ///     update_condition();
    ///     if x {
    ///         // ...
    ///         println!("Hello, world");
    ///     }
    /// }
    /// ```
    ///
    /// As another example, the following code
    ///
    /// ```rust
    /// # fn waiting() -> bool { false }
    /// loop {
    ///     if waiting() {
    ///         continue;
    ///     } else {
    ///         // Do something useful
    ///     }
    ///     # break;
    /// }
    /// ```
    /// Could be rewritten as
    ///
    /// ```rust
    /// # fn waiting() -> bool { false }
    /// loop {
    ///     if waiting() {
    ///         continue;
    ///     }
    ///     // Do something useful
    ///     # break;
    /// }
    /// ```
    pub NEEDLESS_CONTINUE,
    pedantic,
    "`continue` statements that can be replaced by a rearrangement of code"
}

declare_lint_pass!(NeedlessContinue => [NEEDLESS_CONTINUE]);

impl EarlyLintPass for NeedlessContinue {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &ast::Expr) {
        if !expr.span.from_expansion() {
            check_and_warn(cx, expr);
        }
    }
}

/* This lint has to mainly deal with two cases of needless continue
 * statements. */
// Case 1 [Continue inside else block]:
//
//     loop {
//         // region A
//         if cond {
//             // region B
//         } else {
//             continue;
//         }
//         // region C
//     }
//
// This code can better be written as follows:
//
//     loop {
//         // region A
//         if cond {
//             // region B
//             // region C
//         }
//     }
//
// Case 2 [Continue inside then block]:
//
//     loop {
//       // region A
//       if cond {
//           continue;
//           // potentially more code here.
//       } else {
//           // region B
//       }
//       // region C
//     }
//
//
// This snippet can be refactored to:
//
//     loop {
//       // region A
//       if !cond {
//           // region B
//           // region C
//       }
//     }
//

/// Given an expression, returns true if either of the following is true
///
/// - The expression is a `continue` node.
/// - The expression node is a block with the first statement being a
/// `continue`.
fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
    match else_expr.kind {
        ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
        ast::ExprKind::Continue(l) => compare_labels(label, l.as_ref()),
        _ => false,
    }
}

fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
    block.stmts.get(0).map_or(false, |stmt| match stmt.kind {
        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
            if let ast::ExprKind::Continue(ref l) = e.kind {
                compare_labels(label, l.as_ref())
            } else {
                false
            }
        },
        _ => false,
    })
}

/// If the `continue` has a label, check it matches the label of the loop.
fn compare_labels(loop_label: Option<&ast::Label>, continue_label: Option<&ast::Label>) -> bool {
    match (loop_label, continue_label) {
        // `loop { continue; }` or `'a loop { continue; }`
        (_, None) => true,
        // `loop { continue 'a; }`
        (None, _) => false,
        // `'a loop { continue 'a; }` or `'a loop { continue 'b; }`
        (Some(x), Some(y)) => x.ident == y.ident,
    }
}

/// If `expr` is a loop expression (while/while let/for/loop), calls `func` with
/// the AST object representing the loop block of `expr`.
fn with_loop_block<F>(expr: &ast::Expr, mut func: F)
where
    F: FnMut(&ast::Block, Option<&ast::Label>),
{
    if let ast::ExprKind::While(_, loop_block, label)
    | ast::ExprKind::ForLoop(_, _, loop_block, label)
    | ast::ExprKind::Loop(loop_block, label) = &expr.kind
    {
        func(loop_block, label.as_ref());
    }
}

/// If `stmt` is an if expression node with an `else` branch, calls func with
/// the
/// following:
///
/// - The `if` expression itself,
/// - The `if` condition expression,
/// - The `then` block, and
/// - The `else` expression.
fn with_if_expr<F>(stmt: &ast::Stmt, mut func: F)
where
    F: FnMut(&ast::Expr, &ast::Expr, &ast::Block, &ast::Expr),
{
    match stmt.kind {
        ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
            if let ast::ExprKind::If(ref cond, ref if_block, Some(ref else_expr)) = e.kind {
                func(e, cond, if_block, else_expr);
            }
        },
        _ => {},
    }
}

/// A type to distinguish between the two distinct cases this lint handles.
#[derive(Copy, Clone, Debug)]
enum LintType {
    ContinueInsideElseBlock,
    ContinueInsideThenBlock,
}

/// Data we pass around for construction of help messages.
struct LintData<'a> {
    /// The `if` expression encountered in the above loop.
    if_expr: &'a ast::Expr,
    /// The condition expression for the above `if`.
    if_cond: &'a ast::Expr,
    /// The `then` block of the `if` statement.
    if_block: &'a ast::Block,
    /// The `else` block of the `if` statement.
    /// Note that we only work with `if` exprs that have an `else` branch.
    else_expr: &'a ast::Expr,
    /// The 0-based index of the `if` statement in the containing loop block.
    stmt_idx: usize,
    /// The statements of the loop block.
    block_stmts: &'a [ast::Stmt],
}

const MSG_REDUNDANT_ELSE_BLOCK: &str = "this `else` block is redundant";

const MSG_ELSE_BLOCK_NOT_NEEDED: &str = "there is no need for an explicit `else` block for this `if` \
                                         expression";

const DROP_ELSE_BLOCK_AND_MERGE_MSG: &str = "consider dropping the `else` clause and merging the code that \
                                             follows (in the loop) with the `if` block";

const DROP_ELSE_BLOCK_MSG: &str = "consider dropping the `else` clause";

fn emit_warning<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>, header: &str, typ: LintType) {
    // snip    is the whole *help* message that appears after the warning.
    // message is the warning message.
    // expr    is the expression which the lint warning message refers to.
    let (snip, message, expr) = match typ {
        LintType::ContinueInsideElseBlock => (
            suggestion_snippet_for_continue_inside_else(cx, data),
            MSG_REDUNDANT_ELSE_BLOCK,
            data.else_expr,
        ),
        LintType::ContinueInsideThenBlock => (
            suggestion_snippet_for_continue_inside_if(cx, data),
            MSG_ELSE_BLOCK_NOT_NEEDED,
            data.if_expr,
        ),
    };
    span_lint_and_help(
        cx,
        NEEDLESS_CONTINUE,
        expr.span,
        message,
        &format!("{}\n{}", header, snip),
    );
}

fn suggestion_snippet_for_continue_inside_if<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
    let cond_code = snippet(cx, data.if_cond.span, "..");

    let continue_code = snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span));

    let else_code = snippet_block(cx, data.else_expr.span, "..", Some(data.if_expr.span));

    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
    format!(
        "{indent}if {} {}\n{indent}{}",
        cond_code,
        continue_code,
        else_code,
        indent = " ".repeat(indent_if),
    )
}

fn suggestion_snippet_for_continue_inside_else<'a>(cx: &EarlyContext<'_>, data: &'a LintData<'_>) -> String {
    let cond_code = snippet(cx, data.if_cond.span, "..");

    // Region B
    let block_code = erode_from_back(&snippet_block(cx, data.if_block.span, "..", Some(data.if_expr.span)));

    // Region C
    // These is the code in the loop block that follows the if/else construction
    // we are complaining about. We want to pull all of this code into the
    // `then` block of the `if` statement.
    let indent = span_of_first_expr_in_block(data.if_block)
        .and_then(|span| indent_of(cx, span))
        .unwrap_or(0);
    let to_annex = data.block_stmts[data.stmt_idx + 1..]
        .iter()
        .map(|stmt| original_sp(stmt.span, DUMMY_SP))
        .map(|span| {
            let snip = snippet_block(cx, span, "..", None).into_owned();
            snip.lines()
                .map(|line| format!("{}{}", " ".repeat(indent), line))
                .collect::<Vec<_>>()
                .join("\n")
        })
        .collect::<Vec<_>>()
        .join("\n");

    let indent_if = indent_of(cx, data.if_expr.span).unwrap_or(0);
    format!(
        "{indent_if}if {} {}\n{indent}// merged code follows:\n{}\n{indent_if}}}",
        cond_code,
        block_code,
        to_annex,
        indent = " ".repeat(indent),
        indent_if = " ".repeat(indent_if),
    )
}

fn check_and_warn<'a>(cx: &EarlyContext<'_>, expr: &'a ast::Expr) {
    with_loop_block(expr, |loop_block, label| {
        for (i, stmt) in loop_block.stmts.iter().enumerate() {
            with_if_expr(stmt, |if_expr, cond, then_block, else_expr| {
                let data = &LintData {
                    stmt_idx: i,
                    if_expr,
                    if_cond: cond,
                    if_block: then_block,
                    else_expr,
                    block_stmts: &loop_block.stmts,
                };
                if needless_continue_in_else(else_expr, label) {
                    emit_warning(
                        cx,
                        data,
                        DROP_ELSE_BLOCK_AND_MERGE_MSG,
                        LintType::ContinueInsideElseBlock,
                    );
                } else if is_first_block_stmt_continue(then_block, label) {
                    emit_warning(cx, data, DROP_ELSE_BLOCK_MSG, LintType::ContinueInsideThenBlock);
                }
            });
        }
    });
}

/// Eats at `s` from the end till a closing brace `}` is encountered, and then continues eating
/// till a non-whitespace character is found.  e.g., the string. If no closing `}` is present, the
/// string will be preserved.
///
/// ```rust
/// {
///     let x = 5;
/// }
/// ```
///
/// is transformed to
///
/// ```ignore
///     {
///         let x = 5;
/// ```
#[must_use]
fn erode_from_back(s: &str) -> String {
    let mut ret = s.to_string();
    while ret.pop().map_or(false, |c| c != '}') {}
    while let Some(c) = ret.pop() {
        if !c.is_whitespace() {
            ret.push(c);
            break;
        }
    }
    if ret.is_empty() {
        s.to_string()
    } else {
        ret
    }
}

fn span_of_first_expr_in_block(block: &ast::Block) -> Option<Span> {
    block.stmts.iter().next().map(|stmt| stmt.span)
}

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

    #[test]
    #[rustfmt::skip]
    fn test_erode_from_back() {
        let input = "\
{
    let x = 5;
    let y = format!(\"{}\", 42);
}";

        let expected = "\
{
    let x = 5;
    let y = format!(\"{}\", 42);";

        let got = erode_from_back(input);
        assert_eq!(expected, got);
    }

    #[test]
    #[rustfmt::skip]
    fn test_erode_from_back_no_brace() {
        let input = "\
let x = 5;
let y = something();
";
        let expected = input;
        let got = erode_from_back(input);
        assert_eq!(expected, got);
    }
}
<|endoftext|>


<commit_before>use decoders::*;
use decoders::tiff::*;
use decoders::basics::*;
use decoders::ljpeg::*;
use std::f32::NAN;
use itertools::Itertools;

#[derive(Debug, Clone)]
pub struct Cr2Decoder<'a> {
  buffer: &'a [u8],
  rawloader: &'a RawLoader,
  tiff: TiffIFD<'a>,
}

impl<'a> Cr2Decoder<'a> {
  pub fn new(buf: &'a [u8], tiff: TiffIFD<'a>, rawloader: &'a RawLoader) -> Cr2Decoder<'a> {
    Cr2Decoder {
      buffer: buf,
      tiff: tiff,
      rawloader: rawloader,
    }
  }
}

impl<'a> Decoder for Cr2Decoder<'a> {
  fn image(&self) -> Result<Image,String> {
    let camera = try!(self.rawloader.check_supported(&self.tiff));
    let (raw, offset) = {
      if let Some(raw) = self.tiff.find_first_ifd(Tag::Cr2Id) {
        (raw, fetch_tag!(raw, Tag::StripOffsets).get_usize(0))
      } else if let Some(raw) = self.tiff.find_first_ifd(Tag::CFAPattern) {
        (raw, fetch_tag!(raw, Tag::StripOffsets).get_usize(0))
      } else if let Some(off) = self.tiff.find_entry(Tag::Cr2OldOffset) {
        (&self.tiff, off.get_usize(0))
      } else {
        return Err("CR2: Couldn't find raw info".to_string())
      }
    };
    let src = &self.buffer[offset..];

    let (width, height, cpp, image) = {
      let decompressor = try!(LjpegDecompressor::new(src));
      let ljpegwidth = decompressor.width();
      let mut width = ljpegwidth;
      let mut height = decompressor.height();
      let cpp = if decompressor.super_h() == 2 {3} else {1};
      let mut ljpegout = vec![0 as u16; width*height];
      try!(decompressor.decode(&mut ljpegout, 0, width, width, height));

      // Linearize the output (applies only to D2000 as far as I can tell)
      if camera.find_hint("linearization") {
        let table = {
          let linearization = fetch_tag!(self.tiff, Tag::GrayResponse);
          let mut t = [0 as u16;4096];
          for i in 0..t.len() {
            t[i] = linearization.get_u32(i) as u16;
          }
          LookupTable::new(&t)
        };

        let mut random = ljpegout[0] as u32;
        for o in ljpegout.chunks_mut(1) {
          o[0] = table.dither(o[0], &mut random);
        }
      }

      // Convert the YUV in sRAWs to RGB
      if cpp == 3 {
        try!(self.convert_to_rgb(camera, &mut ljpegout));
        if raw.has_entry(Tag::ImageWidth) {
          width = fetch_tag!(raw, Tag::ImageWidth).get_usize(0) * cpp;
          height = fetch_tag!(raw, Tag::ImageLength).get_usize(0) ;
        } else if width/cpp < height {
          let temp = width/cpp;
          width = height*cpp;
          height = temp;
        }
      }
      if camera.find_hint("double_line") {
        width /= 2;
        height *= 2;
      }

      // Take each of the vertical fields and put them into the right location
      // FIXME: Doing this at the decode would reduce about 5% in runtime but I haven't
      //        been able to do it without hairy code
      if let Some(canoncol) = raw.find_entry(Tag::Cr2StripeWidths) {
        if canoncol.get_usize(0) == 0 {
          (width, height, cpp, ljpegout)
        } else {
          let mut out = vec![0 as u16; width*height];
          let mut fieldwidths = Vec::new();
          for _ in 0..canoncol.get_usize(0) {
            fieldwidths.push(canoncol.get_usize(1));
          }
          fieldwidths.push(canoncol.get_usize(2));

          if decompressor.super_v() == 2 {
            // We've decoded 2 lines at a time so we also need to copy two strips at a time
            let nfields = fieldwidths.len();
            let fieldwidth = fieldwidths[0];
            let mut fieldstart = 0;
            let mut inpos = 0;
            for _ in 0..nfields {
              for row in (0..height).step(2) {
                for col in (0..fieldwidth).step(3) {
                  let outpos = row*width+fieldstart+col;
                  out[outpos..outpos+3].copy_from_slice(&ljpegout[inpos..inpos+3]);
                  let outpos = (row+1)*width+fieldstart+col;
                  let inpos2 = inpos+ljpegwidth;
                  out[outpos..outpos+3].copy_from_slice(&ljpegout[inpos2..inpos2+3]);
                  inpos += 3;
                  if inpos % ljpegwidth == 0 {
                    // we've used a full input line and we're reading 2 by 2 so skip one
                    inpos += ljpegwidth;
                  }
                }
              }
              fieldstart += fieldwidth;
            }
          } else {
            let sh = decompressor.super_h();
            let mut fieldstart = 0;
            let mut fieldpos = 0;
            for fieldwidth in fieldwidths {
              let fieldwidth = fieldwidth/sh*cpp;
              for row in 0..height {
                let outpos = row*width+fieldstart;
                let inpos = fieldpos+row*fieldwidth;
                let outb = &mut out[outpos..outpos+fieldwidth];
                let inb = &ljpegout[inpos..inpos+fieldwidth];
                outb.copy_from_slice(inb);
              }
              fieldstart += fieldwidth;
              fieldpos += fieldwidth*height;
            }
          }

          (width, height, cpp, out)
        }
      } else {
        (width, height, cpp, ljpegout)
      }
    };

    let mut img = Image::new(camera, width, height, try!(self.get_wb(camera)), image);
    if cpp == 3 {
      img.cpp = 3;
      img.width /= 3;
      img.crops = [0,0,0,0];
      img.blacklevels = [0,0,0,0];
      img.whitelevels = [65535,65535,65535,65535];
    }
    Ok(img)
  }
}

impl<'a> Cr2Decoder<'a> {
  fn get_wb(&self, cam: &Camera) -> Result<[f32;4], String> {
    if let Some(levels) = self.tiff.find_entry(Tag::Cr2ColorData) {
      let offset = if cam.wb_offset != 0 {cam.wb_offset} else {63};
      Ok([levels.get_force_u16(offset) as f32, levels.get_force_u16(offset+1) as f32,
          levels.get_force_u16(offset+3) as f32, NAN])
    } else if let Some(levels) = self.tiff.find_entry(Tag::Cr2PowerShotWB) {
      Ok([levels.get_force_u32(3) as f32, levels.get_force_u32(2) as f32,
          levels.get_force_u32(4) as f32, NAN])
    } else if let Some(levels) = self.tiff.find_entry(Tag::Cr2OldWB) {
      Ok([levels.get_f32(0), levels.get_f32(1), levels.get_f32(2), NAN])
    } else {
      // At least the D2000 has no WB
      Ok([NAN,NAN,NAN,NAN])
    }
  }

  fn convert_to_rgb(&self, cam: &Camera, image: &mut [u16]) -> Result<(),String>{
    let coeffs = try!(self.get_wb(cam));
    let c1 = (1024.0*1024.0/coeffs[0]) as i32;
    let c2 = coeffs[1] as i32;
    let c3 = (1024.0*1024.0/coeffs[2]) as i32;

    for pix in image.chunks_mut(3) {
      let y = pix[0] as i32;
      let cb = pix[1] as i32 - 16383;
      let cr = pix[2] as i32 - 16383;

      let r = c1 * (y + cr);
      let g = c2 * (y + ((-778*cb - (cr<<11)) >> 12));
      let b = c3 * (y + cb);

      pix[0] = clampbits(r >> 8, 16) as u16;
      pix[1] = clampbits(g >> 8, 16) as u16;
      pix[2] = clampbits(b >> 8, 16) as u16;
    }
    Ok(())
  }
}
<commit_msg>CR2: hopefully the last dimensions fix<commit_after>use decoders::*;
use decoders::tiff::*;
use decoders::basics::*;
use decoders::ljpeg::*;
use std::f32::NAN;
use itertools::Itertools;

#[derive(Debug, Clone)]
pub struct Cr2Decoder<'a> {
  buffer: &'a [u8],
  rawloader: &'a RawLoader,
  tiff: TiffIFD<'a>,
}

impl<'a> Cr2Decoder<'a> {
  pub fn new(buf: &'a [u8], tiff: TiffIFD<'a>, rawloader: &'a RawLoader) -> Cr2Decoder<'a> {
    Cr2Decoder {
      buffer: buf,
      tiff: tiff,
      rawloader: rawloader,
    }
  }
}

impl<'a> Decoder for Cr2Decoder<'a> {
  fn image(&self) -> Result<Image,String> {
    let camera = try!(self.rawloader.check_supported(&self.tiff));
    let (raw, offset) = {
      if let Some(raw) = self.tiff.find_first_ifd(Tag::Cr2Id) {
        (raw, fetch_tag!(raw, Tag::StripOffsets).get_usize(0))
      } else if let Some(raw) = self.tiff.find_first_ifd(Tag::CFAPattern) {
        (raw, fetch_tag!(raw, Tag::StripOffsets).get_usize(0))
      } else if let Some(off) = self.tiff.find_entry(Tag::Cr2OldOffset) {
        (&self.tiff, off.get_usize(0))
      } else {
        return Err("CR2: Couldn't find raw info".to_string())
      }
    };
    let src = &self.buffer[offset..];

    let (width, height, cpp, image) = {
      let decompressor = try!(LjpegDecompressor::new(src));
      let ljpegwidth = decompressor.width();
      let mut width = ljpegwidth;
      let mut height = decompressor.height();
      let cpp = if decompressor.super_h() == 2 {3} else {1};
      let mut ljpegout = vec![0 as u16; width*height];
      try!(decompressor.decode(&mut ljpegout, 0, width, width, height));

      // Linearize the output (applies only to D2000 as far as I can tell)
      if camera.find_hint("linearization") {
        let table = {
          let linearization = fetch_tag!(self.tiff, Tag::GrayResponse);
          let mut t = [0 as u16;4096];
          for i in 0..t.len() {
            t[i] = linearization.get_u32(i) as u16;
          }
          LookupTable::new(&t)
        };

        let mut random = ljpegout[0] as u32;
        for o in ljpegout.chunks_mut(1) {
          o[0] = table.dither(o[0], &mut random);
        }
      }

      // Convert the YUV in sRAWs to RGB
      if cpp == 3 {
        try!(self.convert_to_rgb(camera, &mut ljpegout));
        if raw.has_entry(Tag::ImageWidth) {
          width = fetch_tag!(raw, Tag::ImageWidth).get_usize(0) * cpp;
          height = fetch_tag!(raw, Tag::ImageLength).get_usize(0) ;
        } else if width/cpp < height {
          let temp = width/cpp;
          width = height*cpp;
          height = temp;
        }
      } else if camera.find_hint("double_line") {
        width /= 2;
        height *= 2;
      }

      // Take each of the vertical fields and put them into the right location
      // FIXME: Doing this at the decode would reduce about 5% in runtime but I haven't
      //        been able to do it without hairy code
      if let Some(canoncol) = raw.find_entry(Tag::Cr2StripeWidths) {
        if canoncol.get_usize(0) == 0 {
          (width, height, cpp, ljpegout)
        } else {
          let mut out = vec![0 as u16; width*height];
          let mut fieldwidths = Vec::new();
          for _ in 0..canoncol.get_usize(0) {
            fieldwidths.push(canoncol.get_usize(1));
          }
          fieldwidths.push(canoncol.get_usize(2));

          if decompressor.super_v() == 2 {
            // We've decoded 2 lines at a time so we also need to copy two strips at a time
            let nfields = fieldwidths.len();
            let fieldwidth = fieldwidths[0];
            let mut fieldstart = 0;
            let mut inpos = 0;
            for _ in 0..nfields {
              for row in (0..height).step(2) {
                for col in (0..fieldwidth).step(3) {
                  let outpos = row*width+fieldstart+col;
                  out[outpos..outpos+3].copy_from_slice(&ljpegout[inpos..inpos+3]);
                  let outpos = (row+1)*width+fieldstart+col;
                  let inpos2 = inpos+ljpegwidth;
                  out[outpos..outpos+3].copy_from_slice(&ljpegout[inpos2..inpos2+3]);
                  inpos += 3;
                  if inpos % ljpegwidth == 0 {
                    // we've used a full input line and we're reading 2 by 2 so skip one
                    inpos += ljpegwidth;
                  }
                }
              }
              fieldstart += fieldwidth;
            }
          } else {
            let sh = decompressor.super_h();
            let mut fieldstart = 0;
            let mut fieldpos = 0;
            for fieldwidth in fieldwidths {
              let fieldwidth = fieldwidth/sh*cpp;
              for row in 0..height {
                let outpos = row*width+fieldstart;
                let inpos = fieldpos+row*fieldwidth;
                let outb = &mut out[outpos..outpos+fieldwidth];
                let inb = &ljpegout[inpos..inpos+fieldwidth];
                outb.copy_from_slice(inb);
              }
              fieldstart += fieldwidth;
              fieldpos += fieldwidth*height;
            }
          }

          (width, height, cpp, out)
        }
      } else {
        (width, height, cpp, ljpegout)
      }
    };

    let mut img = Image::new(camera, width, height, try!(self.get_wb(camera)), image);
    if cpp == 3 {
      img.cpp = 3;
      img.width /= 3;
      img.crops = [0,0,0,0];
      img.blacklevels = [0,0,0,0];
      img.whitelevels = [65535,65535,65535,65535];
    }
    Ok(img)
  }
}

impl<'a> Cr2Decoder<'a> {
  fn get_wb(&self, cam: &Camera) -> Result<[f32;4], String> {
    if let Some(levels) = self.tiff.find_entry(Tag::Cr2ColorData) {
      let offset = if cam.wb_offset != 0 {cam.wb_offset} else {63};
      Ok([levels.get_force_u16(offset) as f32, levels.get_force_u16(offset+1) as f32,
          levels.get_force_u16(offset+3) as f32, NAN])
    } else if let Some(levels) = self.tiff.find_entry(Tag::Cr2PowerShotWB) {
      Ok([levels.get_force_u32(3) as f32, levels.get_force_u32(2) as f32,
          levels.get_force_u32(4) as f32, NAN])
    } else if let Some(levels) = self.tiff.find_entry(Tag::Cr2OldWB) {
      Ok([levels.get_f32(0), levels.get_f32(1), levels.get_f32(2), NAN])
    } else {
      // At least the D2000 has no WB
      Ok([NAN,NAN,NAN,NAN])
    }
  }

  fn convert_to_rgb(&self, cam: &Camera, image: &mut [u16]) -> Result<(),String>{
    let coeffs = try!(self.get_wb(cam));
    let c1 = (1024.0*1024.0/coeffs[0]) as i32;
    let c2 = coeffs[1] as i32;
    let c3 = (1024.0*1024.0/coeffs[2]) as i32;

    for pix in image.chunks_mut(3) {
      let y = pix[0] as i32;
      let cb = pix[1] as i32 - 16383;
      let cr = pix[2] as i32 - 16383;

      let r = c1 * (y + cr);
      let g = c2 * (y + ((-778*cb - (cr<<11)) >> 12));
      let b = c3 * (y + cb);

      pix[0] = clampbits(r >> 8, 16) as u16;
      pix[1] = clampbits(g >> 8, 16) as u16;
      pix[2] = clampbits(b >> 8, 16) as u16;
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>use std::fmt::{Show, Formatter, Error};
use std::io::fs::PathExtensions;
use std::io;
use toml;

use app_result::{AppResult, app_err};

pub enum TagsRoot
{
   /// the source directory and the dependencies
   /// of the cargo project
   Src {
      src_dir: Path,
      dependencies: Vec<SourceKind>
   },

   /// a library and its depedencies
   Lib {
      src_kind: SourceKind,
      dependencies: Vec<SourceKind>
   }
}

#[deriving(Show)]
pub enum SourceKind
{
   /// the source is from a git repository
   Git {
      lib_name: String,
      commit_hash: String
   },

   /// the source is from crates.io
   CratesIo {
      lib_name: String,
      version: String
   }
}

pub type TagsRoots = Vec<TagsRoot>;

/// Reads the dependecies from the `Cargo.toml` located in `cargo_toml_dir`
pub fn read_dependencies(cargo_toml_dir: &Path) -> AppResult<TagsRoots>
{
   let mut cargo_toml = cargo_toml_dir.clone();
   cargo_toml.push("Cargo.toml");

   let toml_string = try!(io::File::open(&cargo_toml).read_to_string());
   let mut toml_parser = toml::Parser::new(&*toml_string);

   let toml_table = try!(
      toml_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_toml.display(), toml_parser.errors)))
   );

   let mut tags_roots: TagsRoots = Vec::new();
   let deps_string = "dependencies".to_string();
   if ! toml_table.contains_key(&deps_string) {
      return Ok(tags_roots);
   }

   let deps_table = try!(
      toml_table.get(&deps_string)
         .and_then(|value| value.as_table())
         .ok_or(app_err(format!("Couldn't get toml::Table entry for 'dependency'!")))
   );

   let mut cargo_lock = cargo_toml_dir.clone();
   cargo_lock.push("Cargo.lock");

   let lock_string = try!(io::File::open(&cargo_lock).read_to_string());
   let mut lock_parser = toml::Parser::new(&*lock_string);

   let lock_table = try!(
      lock_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_lock.display(), lock_parser.errors)))
   );

   let packages = try!(
      lock_table.get(&"package".to_string())
         .and_then(|value| value.as_slice())
         .and_then(|slice| {
            let tables = slice.iter()
               .filter_map(|value| value.as_table())
               .collect::<Vec<&toml::TomlTable>>();

            if ! tables.is_empty() { Some(tables) } else { None }
         })
         .ok_or(app_err(format!("Couldn't get Array of Tables entry for 'package'!")))
   );

   for package in packages.iter() {
      let lib_name = try!(
         package.get(&"name".to_string())
            .and_then(|n| n.as_str().map(|n| n.to_string()))
            .ok_or_else(|| app_err(format!("Couldn't find name string in package: '{}'!", package)))
      );

      let dep_names = try!(get_dependencies(*package, &lib_name));
      let mut dep_src_kinds: Vec<SourceKind> = Vec::new();
      for dep_name in dep_names.iter() {
         let dep_package = try!(find_package(&packages, dep_name));
         dep_src_kinds.push(try!(get_source_kind(dep_package, dep_name)));
      }

      let src_kind = try!(get_source_kind(*package, &lib_name));
      tags_roots.push(TagsRoot::Lib { src_kind: src_kind, dependencies: dep_src_kinds });
   }

   let mut lib_src_kinds: Vec<SourceKind> = Vec::new();
   for (lib_name, value) in deps_table.iter() {
      match *value {
         toml::String(_) | toml::Table(_) => {
            let lib_package = try!(find_package(&packages, lib_name));
            lib_src_kinds.push(try!(get_source_kind(lib_package, lib_name)));
         }

         _ => {
            return Err(app_err(format!(
               "Expected a String or a Table for the dependency with the name '{}', but got: '{}'", lib_name, value
            )));
         }
      }
   }

   tags_roots.push(TagsRoot::Src { src_dir: cargo_toml_dir.clone(), dependencies: lib_src_kinds });

   Ok(tags_roots)
}

fn get_source_kind(lib_package: &toml::TomlTable, lib_name: &String) -> AppResult<SourceKind>
{
   let version_str = &String::from_str("version");
   let source_str = &String::from_str("source");

   let source = try!(
      lib_package.get(source_str)
         .and_then(|src| src.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find source string in package: '{}'!", lib_package)))
   );

   let src_type = try!(
      source.split('+')
         .nth(0)
         .ok_or_else(|| app_err(format!("Couldn't find source type in: '{}'!", source)))
   );

   let version = try!(
      lib_package.get(version_str)
         .and_then(|vers| vers.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find version string in package: '{}'!", lib_package)))
   );

   match src_type {
      "git" => {
         let commit_hash = try!(
            source.split('#')
               .last()
               .ok_or_else(|| app_err(format!("Couldn't find commit hash in source entry: '{}'!", source)))
         );

         Ok(SourceKind::Git { lib_name: lib_name.clone(), commit_hash: commit_hash.to_string() })
      },

      "registry" => {
         Ok(SourceKind::CratesIo { lib_name: lib_name.clone(), version: version.to_string() })
      }

      _ => {
         Err(app_err(format!("Unexpected source type '{}' in package: '{}'!", src_type, lib_package)))
      }
   }
}

fn get_dependencies(lib_package: &toml::TomlTable, lib_name: &String) -> AppResult<Vec<String>>
{
   let deps_str = &String::from_str("dependencies");
   let dep_strs = match lib_package.get(deps_str) {
      None => {
         Vec::<&str>::new()
      },

      Some(value) => {
         try!(
            value.as_slice()
               .and_then(|slice| {
                  let ds = slice.iter()
                     .filter_map(|v| v.as_str())
                     .collect::<Vec<&str>>();

                  if ! ds.is_empty() { Some(ds) } else { None }
               })
               .ok_or_else(|| app_err(format!("Couldn't get Array of Strings for 'dependencies' entry: '{}'!", lib_package)))
         )
      }
   };

   let mut dep_names: Vec<String> = Vec::new();
   for dep_str in dep_strs.iter() {
      let dep_name = try!(
         dep_str.split(' ')
            .nth(0)
            .map(|d| d.to_string())
            .ok_or_else(|| app_err(format!("Couldn't get name from dependency: '{}'!", dep_str)))
      );

      dep_names.push(dep_name);
   }

   Ok(dep_names)
}

fn find_package<'a>(packages: &'a Vec<&toml::TomlTable>, lib_name: &String) -> AppResult<&'a toml::TomlTable>
{
   let name_str = &String::from_str("name");

   let package = try!(
      packages.iter()
         .find(|t| t.get(name_str).and_then(|n| n.as_str()) == Some(lib_name[]))
         .ok_or_else(|| app_err(format!("Couldn't find package with name = '{}'!", lib_name)))
   );

   Ok(*package)
}

impl Show for TagsRoot
{
   fn fmt(&self, f: &mut Formatter) -> Result<(), Error>
   {
      match *self {
         TagsRoot::Src { ref src_dir, ref dependencies } => {
            write!(f, "Src ( src_dir: {}, dependencies: {} )", src_dir.display(), dependencies)
         },

         TagsRoot::Lib { ref src_kind, ref dependencies } => {
            write!(f, "Lib ( src_kind: {}, dependencies: {} )", src_kind, dependencies)
         }
      }
   }
}
<commit_msg>Implement more compact Show instance for SourceKind<commit_after>use std::fmt::{Show, Formatter, Error};
use std::io::fs::PathExtensions;
use std::io;
use toml;

use app_result::{AppResult, app_err};

pub enum TagsRoot
{
   /// the source directory and the dependencies
   /// of the cargo project
   Src {
      src_dir: Path,
      dependencies: Vec<SourceKind>
   },

   /// a library and its depedencies
   Lib {
      src_kind: SourceKind,
      dependencies: Vec<SourceKind>
   }
}

pub enum SourceKind
{
   /// the source is from a git repository
   Git {
      lib_name: String,
      commit_hash: String
   },

   /// the source is from crates.io
   CratesIo {
      lib_name: String,
      version: String
   }
}

pub type TagsRoots = Vec<TagsRoot>;

/// Reads the dependecies from the `Cargo.toml` located in `cargo_toml_dir`
pub fn read_dependencies(cargo_toml_dir: &Path) -> AppResult<TagsRoots>
{
   let mut cargo_toml = cargo_toml_dir.clone();
   cargo_toml.push("Cargo.toml");

   let toml_string = try!(io::File::open(&cargo_toml).read_to_string());
   let mut toml_parser = toml::Parser::new(&*toml_string);

   let toml_table = try!(
      toml_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_toml.display(), toml_parser.errors)))
   );

   let mut tags_roots: TagsRoots = Vec::new();
   let deps_string = "dependencies".to_string();
   if ! toml_table.contains_key(&deps_string) {
      return Ok(tags_roots);
   }

   let deps_table = try!(
      toml_table.get(&deps_string)
         .and_then(|value| value.as_table())
         .ok_or(app_err(format!("Couldn't get toml::Table entry for 'dependency'!")))
   );

   let mut cargo_lock = cargo_toml_dir.clone();
   cargo_lock.push("Cargo.lock");

   let lock_string = try!(io::File::open(&cargo_lock).read_to_string());
   let mut lock_parser = toml::Parser::new(&*lock_string);

   let lock_table = try!(
      lock_parser.parse()
         .ok_or_else(|| app_err(format!("Couldn't parse '{}': {}", cargo_lock.display(), lock_parser.errors)))
   );

   let packages = try!(
      lock_table.get(&"package".to_string())
         .and_then(|value| value.as_slice())
         .and_then(|slice| {
            let tables = slice.iter()
               .filter_map(|value| value.as_table())
               .collect::<Vec<&toml::TomlTable>>();

            if ! tables.is_empty() { Some(tables) } else { None }
         })
         .ok_or(app_err(format!("Couldn't get Array of Tables entry for 'package'!")))
   );

   for package in packages.iter() {
      let lib_name = try!(
         package.get(&"name".to_string())
            .and_then(|n| n.as_str().map(|n| n.to_string()))
            .ok_or_else(|| app_err(format!("Couldn't find name string in package: '{}'!", package)))
      );

      let dep_names = try!(get_dependencies(*package, &lib_name));
      let mut dep_src_kinds: Vec<SourceKind> = Vec::new();
      for dep_name in dep_names.iter() {
         let dep_package = try!(find_package(&packages, dep_name));
         dep_src_kinds.push(try!(get_source_kind(dep_package, dep_name)));
      }

      let src_kind = try!(get_source_kind(*package, &lib_name));
      tags_roots.push(TagsRoot::Lib { src_kind: src_kind, dependencies: dep_src_kinds });
   }

   let mut lib_src_kinds: Vec<SourceKind> = Vec::new();
   for (lib_name, value) in deps_table.iter() {
      match *value {
         toml::String(_) | toml::Table(_) => {
            let lib_package = try!(find_package(&packages, lib_name));
            lib_src_kinds.push(try!(get_source_kind(lib_package, lib_name)));
         }

         _ => {
            return Err(app_err(format!(
               "Expected a String or a Table for the dependency with the name '{}', but got: '{}'", lib_name, value
            )));
         }
      }
   }

   tags_roots.push(TagsRoot::Src { src_dir: cargo_toml_dir.clone(), dependencies: lib_src_kinds });

   Ok(tags_roots)
}

fn get_source_kind(lib_package: &toml::TomlTable, lib_name: &String) -> AppResult<SourceKind>
{
   let version_str = &String::from_str("version");
   let source_str = &String::from_str("source");

   let source = try!(
      lib_package.get(source_str)
         .and_then(|src| src.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find source string in package: '{}'!", lib_package)))
   );

   let src_type = try!(
      source.split('+')
         .nth(0)
         .ok_or_else(|| app_err(format!("Couldn't find source type in: '{}'!", source)))
   );

   let version = try!(
      lib_package.get(version_str)
         .and_then(|vers| vers.as_str())
         .ok_or_else(|| app_err(format!("Couldn't find version string in package: '{}'!", lib_package)))
   );

   match src_type {
      "git" => {
         let commit_hash = try!(
            source.split('#')
               .last()
               .ok_or_else(|| app_err(format!("Couldn't find commit hash in source entry: '{}'!", source)))
         );

         Ok(SourceKind::Git { lib_name: lib_name.clone(), commit_hash: commit_hash.to_string() })
      },

      "registry" => {
         Ok(SourceKind::CratesIo { lib_name: lib_name.clone(), version: version.to_string() })
      }

      _ => {
         Err(app_err(format!("Unexpected source type '{}' in package: '{}'!", src_type, lib_package)))
      }
   }
}

fn get_dependencies(lib_package: &toml::TomlTable, lib_name: &String) -> AppResult<Vec<String>>
{
   let deps_str = &String::from_str("dependencies");
   let dep_strs = match lib_package.get(deps_str) {
      None => {
         Vec::<&str>::new()
      },

      Some(value) => {
         try!(
            value.as_slice()
               .and_then(|slice| {
                  let ds = slice.iter()
                     .filter_map(|v| v.as_str())
                     .collect::<Vec<&str>>();

                  if ! ds.is_empty() { Some(ds) } else { None }
               })
               .ok_or_else(|| app_err(format!("Couldn't get Array of Strings for 'dependencies' entry: '{}'!", lib_package)))
         )
      }
   };

   let mut dep_names: Vec<String> = Vec::new();
   for dep_str in dep_strs.iter() {
      let dep_name = try!(
         dep_str.split(' ')
            .nth(0)
            .map(|d| d.to_string())
            .ok_or_else(|| app_err(format!("Couldn't get name from dependency: '{}'!", dep_str)))
      );

      dep_names.push(dep_name);
   }

   Ok(dep_names)
}

fn find_package<'a>(packages: &'a Vec<&toml::TomlTable>, lib_name: &String) -> AppResult<&'a toml::TomlTable>
{
   let name_str = &String::from_str("name");

   let package = try!(
      packages.iter()
         .find(|t| t.get(name_str).and_then(|n| n.as_str()) == Some(lib_name[]))
         .ok_or_else(|| app_err(format!("Couldn't find package with name = '{}'!", lib_name)))
   );

   Ok(*package)
}

impl Show for TagsRoot
{
   fn fmt(&self, f: &mut Formatter) -> Result<(), Error>
   {
      match *self {
         TagsRoot::Src { ref src_dir, ref dependencies } => {
            write!(f, "Src ( src_dir: {}, dependencies: {} )", src_dir.display(), dependencies)
         },

         TagsRoot::Lib { ref src_kind, ref dependencies } => {
            write!(f, "Lib ( src_kind: {}, dependencies: {} )", src_kind, dependencies)
         }
      }
   }
}

impl Show for SourceKind
{
   fn fmt(&self, f: &mut Formatter) -> Result<(), Error>
   {
      match *self {
         SourceKind::Git { ref lib_name, ref commit_hash } => {
            write!(f, "{}#{}", lib_name, commit_hash)
         },

         SourceKind::CratesIo { ref lib_name, ref version } => {
            write!(f, "{}-{}", lib_name, version)
         }
      }
   }
}
<|endoftext|>


<commit_before>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable>;
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Box<Containable>,
    children: Vec<Rc<Box<Containable>>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        self.children.iter().map(|child| Rc::downgrade(&child)).collect()
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        for child in self.get_children() {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

struct View {
    handle: Option<WlcView>,
    parent: Box<Containable>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        Some(&*self.parent)
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        unimplemented!();
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        unimplemented!();
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        unimplemented!();
    }
}
<commit_msg>Made get_parent_by_type a default trait method<commit_after>//! Layout handling

use rustwlc::handle::{WlcView, WlcOutput};
use std::rc::{Rc, Weak};

#[derive(PartialEq, Clone, Copy)]
pub enum ContainerType {
    Root,        /* Root container, only one exists */
    Output,      /* Output, like a monitor or head */
    Workspace,   /* A workspace */
    View         /* A view (aka a window) */
}

/// Types of container
enum ContainerTypes {
    /// Root container, only one exists
    Root,
    /// WlcOutput/monitor
    Output,
    /// A workspace
    Workspace,
    /// A view (window)
    View
}

/// Layout mode for a container
enum Layout {
    None,
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Like i3, everything (workspaces, containers, views) are containable.
pub trait Containable {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable>;

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>>;

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType;

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool;

    /// Removes this container and all of its children
    fn remove_container(&self);

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool);

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64);

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64);

    /// Finds a parent container with the given type, if there is any
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        let mut container = self.get_parent();
        while container.is_some() && container.unwrap().get_type() != type_ {
            container = container.unwrap().get_parent();
        }
        container
    }
}

/// View specific functions
pub trait Viewable {
    /// Determines if the view is full screen
    fn is_fullscreen(&self) -> bool;

    /// Figures out if the view is focused
    fn is_active(&self) -> bool;

    /// Returns true if this view is a parent is an ancestor of the child
    fn is_parent_of<T: Containable>(&self, child: T) -> bool;

    /// Returns true if this view is a child is an decedent of the parent
    fn is_child_of<T: Containable>(&self, parent: T) -> bool;

    /// Gets the active workspace of the view
    fn active_workspace<T: Containable>(&self) -> T;
}

struct Container {
    handle: Option<WlcOutput>,

    parent: Box<Containable>,
    children: Vec<Rc<Box<Containable>>>,
    type_: ContainerType,
    layout: Layout,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for Container {

    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        match self.type_ {
            ContainerType::Root => None,
            _ => Some(&*self.parent),
        }
    }
    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        self.children.iter().map(|child| Rc::downgrade(&child)).collect()
    }

    fn get_type(&self) -> ContainerType {
        self.type_
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        for child in self.get_children() {
            if let Some(child) = child.upgrade() {
                if let Ok(child) = Rc::try_unwrap(child) {
                    child.remove_container();
                    drop(child);
                }
            }
        }
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X (width) and Y (height) dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

}

struct View {
    handle: Option<WlcView>,
    parent: Box<Containable>,

    width: u64,
    height: u64,

    x: i64,
    y: i64,

    visible: bool,
    is_focused: bool,
    is_floating: bool,
}

impl Containable for View {
    /// Gets the parent that this container sits in.
    ///
    /// If the container is the root, it returns None
    fn get_parent(&self) -> Option<&Containable> {
        Some(&*self.parent)
    }

    /// Gets the children of this container.
    ///
    /// Views never have children
    fn get_children(&self) -> Vec<Weak<Box<Containable>>> {
        unimplemented!();
    }

    /// Gets the type of the container
    fn get_type(&self) -> ContainerType {
        ContainerType::View
    }

    /// Returns true if this container is focused.
    fn is_focused(&self) -> bool {
        self.is_focused
    }

    /// Removes this container and all of its children
    fn remove_container(&self) {
        unimplemented!();
    }

    /// Sets this container (and everything in it) to given visibility
    fn set_visibility(&mut self, visibility: bool) {
        self.visible = visibility
    }

    /// Gets the X and Y dimensions of the container
    fn get_dimensions(&self) -> (u64, u64) {
        (self.width, self.height)
    }

    /// Gets the position of this container on the screen
    fn get_position(&self) -> (i64, i64) {
        (self.x, self.y)
    }

    /// Finds a parent container with the given type
    fn get_parent_by_type(&self, type_: ContainerType) -> Option<&Containable> {
        unimplemented!();
    }
}
<|endoftext|>


<commit_before>//! It's a tree!

use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::ptr;

use rustwlc::handle::{WlcView, WlcOutput};

/// A handle to either a view or output
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Handle {
    View(WlcView),
    Output(WlcOutput)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A Container, houses views and other containers
    Container,
    /// A view (window)
    View
}

/// Layout mode for a container
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Represents an item in the container tree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container {
    Root,
    Output {
        handle: WlcOutput
    },
    Workspace {
        name: String,
        focused: bool
    },
    Container {
        layout: Layout,
        visible: bool,
        focused: bool,
        floating: bool,
    },
    View {
        handle: WlcView
    }
}

#[derive(Debug, PartialEq, Eq)]
struct Node<T> {
    // We need a mut pointer so we can modify the parent
    parent: *mut Node<T>,
    val: T,
    children: Vec<Node<T>>
}

impl<T> Node<T> {
    /// Create a new node with the existing value.
    /// For root-style constructors.
    pub fn new(val: T) -> Node<T> {
        Node {
            parent: ptr::null_mut(),
            val: val,
            children: Vec::new()
        }
    }

    /// Add a new child node to this node, using a value
    pub fn new_child(&mut self, val: T) -> &mut Node<T> {
        let self_mut = self as *mut Node<T>;
        self.children.push(Node {
            parent: self_mut,
            val: val,
            children: Vec::new()
        });
        let last_ix = self.children.len() -1;
        &mut self.children[last_ix]
    }

    /// Whether this node has a (currently-reachable) parent
    pub fn has_parent(&self) -> bool {
        self.parent.is_null()
    }

    /// Gets the parent of this node (if it exists)
    pub fn get_parent(&self) -> Option<&mut Node<T>> {
        if self.parent.is_null() {
            return None;
        }
        unsafe {
            return Some(&mut *self.parent);
        }
    }

    /// Borrow the children of this node.
    pub fn get_children(&self) -> &[Node<T>] {
        &self.children
    }

    /// Mutably borrow the children of this mutable node
    pub fn get_mut_children(&mut self) -> &mut[Node<T>] {
        &mut self.children
    }

    /// Remove a child at the given index
    pub fn remove_child_at(&mut self, index: usize) -> Node<T> {
        let mut child = self.children.remove(index);
        child.parent = ptr::null_mut();
        child
    }

    /// Whether this node is a parent of another node
    pub fn is_parent_of(&self, other: &Node<T>) -> bool {
        self.parent == other.parent as *mut Node<T>
    }
}

impl <T: PartialEq> Node<T> {

    /// Remove a node from its parent.
    /// This method will mutate the parent if it exists.
    pub fn remove_from_parent(&mut self) -> Option<Node<T>> {
        if self.get_parent().is_none() {
            return None;
        }
        if let Some(index) = self.children.iter().position(|c| c == self) {
            self.parent = ptr::null_mut();
            self.children.remove(index);
        }
        return None;
    }

    /// Removes a child from self
    pub fn remove_child(&mut self, other: &Node<T>) -> Option<Node<T>> {
        if let Some(index) = self.children.iter().position(|c| c == other) {
            let mut child = self.children.remove(index);
            child.parent = ptr::null_mut();
            Some(child)
        }
        else {
            None
        }
    }

    pub fn move_to(mut self, new_parent: &mut Node<T>) {
        self.remove_from_parent();
        self.parent = new_parent as *mut Node<T>;
        new_parent.children.push(self);
    }
}

impl<T> Drop for Node<T> {
    fn drop(&mut self) {
        println!("Dropping a node.");
        let children: &mut Vec<Node<T>> = &mut self.children;
        for mut child in children {
            child.parent = ptr::null_mut();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Node;
    use log::LogLevelFilter;
    use env_logger::LogBuilder;

    /// Nodes can have children added to them
    #[test]
    fn test_add_child() {
        let mut root = Node::new(0);
        root.new_child(1);
        root.new_child(2); // This is okay
        {
            let mut third_child = root.new_child(3);
            //root.new_child(4); // Have to wait for 3rd child to drop
        }
        root.new_child(4); // Now this works yay standard borrowing
        assert_eq!(root.children.len(), 4);
        println!("Done with the test now.");
    }

    /// These operations will for example operate on the parent
    /// under an if let. `remove_from_parent` will not panic if the node
    /// already is parentless, for example.
    #[test]
    fn optional_operations() {

    }
}
<commit_msg>Add docstrings<commit_after>//! It's a tree!

use std::fmt::Debug;
use std::ops::{Deref, DerefMut};
use std::ptr;

use rustwlc::handle::{WlcView, WlcOutput};

/// A handle to either a view or output
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Handle {
    View(WlcView),
    Output(WlcOutput)
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ContainerType {
    /// Root container, only one exists
    Root,
    /// WlcOutput/Monitor
    Output,
    /// A workspace
    Workspace,
    /// A Container, houses views and other containers
    Container,
    /// A view (window)
    View
}

/// Layout mode for a container
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Layout {
    Horizontal,
    Vertical,
    Stacked,
    Tabbed,
    Floating
}

/// Represents an item in the container tree.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Container {
    /// Root node of the container
    Root,
    /// Output
    Output {
        /// Handle to the wlc
        handle: WlcOutput
    },
    /// Workspace
    Workspace {
        /// Name of the workspace
        name: String,
        /// Whether the workspace is focused
        ///
        /// Multiple workspaces can be focused
        focused: bool
    },
    /// Container
    Container {
        /// How the container is layed out
        layout: Layout,
        /// Whether the container is visible
        visible: bool,
        /// If the container is focused
        focused: bool,
        /// If the container is floating
        floating: bool,
    },
    /// View or window
    View {
        /// The wlc handle to the view
        handle: WlcView,
        /// Whether this view is visible
        visible: bool,
        /// Whether this view is focused
        focused: bool,
        /// Whether this view is floating
        floating: bool,
    }
}

#[derive(Debug, PartialEq, Eq)]
struct Node<T> {
    // We need a mut pointer so we can modify the parent
    parent: *mut Node<T>,
    val: T,
    children: Vec<Node<T>>
}

impl<T> Node<T> {
    /// Create a new node with the existing value.
    /// For root-style constructors.
    pub fn new(val: T) -> Node<T> {
        Node {
            parent: ptr::null_mut(),
            val: val,
            children: Vec::new()
        }
    }

    /// Add a new child node to this node, using a value
    pub fn new_child(&mut self, val: T) -> &mut Node<T> {
        let self_mut = self as *mut Node<T>;
        self.children.push(Node {
            parent: self_mut,
            val: val,
            children: Vec::new()
        });
        let last_ix = self.children.len() -1;
        &mut self.children[last_ix]
    }

    /// Whether this node has a (currently-reachable) parent
    pub fn has_parent(&self) -> bool {
        self.parent.is_null()
    }

    /// Gets the parent of this node (if it exists)
    pub fn get_parent(&self) -> Option<&mut Node<T>> {
        if self.parent.is_null() {
            return None;
        }
        unsafe {
            return Some(&mut *self.parent);
        }
    }

    /// Borrow the children of this node.
    pub fn get_children(&self) -> &[Node<T>] {
        &self.children
    }

    /// Mutably borrow the children of this mutable node
    pub fn get_mut_children(&mut self) -> &mut[Node<T>] {
        &mut self.children
    }

    /// Remove a child at the given index
    pub fn remove_child_at(&mut self, index: usize) -> Node<T> {
        let mut child = self.children.remove(index);
        child.parent = ptr::null_mut();
        child
    }

    /// Whether this node is a parent of another node
    pub fn is_parent_of(&self, other: &Node<T>) -> bool {
        self.parent == other.parent as *mut Node<T>
    }
}

impl <T: PartialEq> Node<T> {

    /// Remove a node from its parent.
    /// This method will mutate the parent if it exists.
    pub fn remove_from_parent(&mut self) -> Option<Node<T>> {
        if self.get_parent().is_none() {
            return None;
        }
        if let Some(index) = self.children.iter().position(|c| c == self) {
            self.parent = ptr::null_mut();
            self.children.remove(index);
        }
        return None;
    }

    /// Removes a child from self
    pub fn remove_child(&mut self, other: &Node<T>) -> Option<Node<T>> {
        if let Some(index) = self.children.iter().position(|c| c == other) {
            let mut child = self.children.remove(index);
            child.parent = ptr::null_mut();
            Some(child)
        }
        else {
            None
        }
    }

    pub fn move_to(mut self, new_parent: &mut Node<T>) {
        self.remove_from_parent();
        self.parent = new_parent as *mut Node<T>;
        new_parent.children.push(self);
    }
}

impl<T> Drop for Node<T> {
    fn drop(&mut self) {
        println!("Dropping a node.");
        let children: &mut Vec<Node<T>> = &mut self.children;
        for mut child in children {
            child.parent = ptr::null_mut();
        }
    }
}

#[cfg(test)]
mod tests {
    use super::Node;
    use log::LogLevelFilter;
    use env_logger::LogBuilder;

    /// Nodes can have children added to them
    #[test]
    fn test_add_child() {
        let mut root = Node::new(0);
        root.new_child(1);
        root.new_child(2); // This is okay
        {
            let mut third_child = root.new_child(3);
            //root.new_child(4); // Have to wait for 3rd child to drop
        }
        root.new_child(4); // Now this works yay standard borrowing
        assert_eq!(root.children.len(), 4);
        println!("Done with the test now.");
    }

    /// These operations will for example operate on the parent
    /// under an if let. `remove_from_parent` will not panic if the node
    /// already is parentless, for example.
    #[test]
    fn optional_operations() {

    }
}
<|endoftext|>


<commit_before>use nom::{Consumer, FileProducer};
use std::io::{Error, ErrorKind, Result};
use std::u32;

use metadata::{
  Block, BlockData,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
};
use metadata::types::MetaDataConsumer;

// Will return true when the unwrapped value of `$option` and `$compare`
// match or `$option` is `Option::None`, otherwise false.
#[inline]
pub fn optional_eq<T: Eq>(option: Option<T>, other: T) -> bool {
  option.map_or(true, |value| value == other)
}

// With the given filename, return all metadata blocks available.
//
// This function expects a flac file, but will return a proper `Result::Err`
// when things go wrong.
//
// # Failures
//
// * `ErrorKind::NotFound` is returned when the given filename isn't found.
// * `ErrorKind::InvalidData` is returned when the data within the file
//   isn't valid FLAC data.
pub fn get_metadata(filename: &str) -> Result<Vec<Block>> {
  FileProducer::new(filename, 1024).and_then(|mut producer| {
    let mut consumer = MetaDataConsumer::new();

    consumer.run(&mut producer);

    if !consumer.data.is_empty() {
      Ok(consumer.data)
    } else {
      let error_str = "parser: couldn't find any metadata";

      Err(Error::new(ErrorKind::InvalidData, error_str))
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `StreamInfo` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_stream_info("path/to/file.flac") {
///   Ok(stream_info) => {
///     // Use the stream_info variable...
///   }
///   Err(error)      => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let stream_info = metadata::get_stream_info("path/to/file.flac").unwrap();
/// ```
pub fn get_stream_info(filename: &str) -> Result<StreamInfo> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find StreamInfo";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `VorbisComment` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_vorbis_comment("path/to/file.flac") {
///   Ok(vorbis_comment) => {
///     // Use the vorbis_comment variable...
///   }
///   Err(error)         => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let vorbis_comment =
///   metadata::get_vorbis_comment("path/to/file.flac").unwrap();
/// ```
pub fn get_vorbis_comment(filename: &str) -> Result<VorbisComment> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find VorbisComment";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `CueSheet` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_cue_sheet("path/to/file.flac") {
///   Ok(cue_sheet) => {
///     // Use the cue_sheet variable...
///   }
///   Err(error)    => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors
///
/// ```no_run
/// use flac::metadata;
///
/// let cue_sheet = metadata::get_cue_sheet("path/to/file.flac").unwrap();
/// ```
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find CueSheet";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file and this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// Putting `None` into any of the optional constraints conveys that you
/// want any of that parameter. Otherwise it will try to look for the image
/// that matches within the given constraints.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found,
///   there is no `Picture` within the file, or no `Picture` that fits the
///   given constraints.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let result = metadata::get_picture("path/to/file.flac",
///                                    Some(PictureType::FileIcon),
///                                    Some("image/gif"),
///                                    None, None, None,
///                                    None, None);
///
/// match result {
///   Ok(picture) => {
///     // Use the picture variable...
///   }
///   Err(error)  => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let picture = metadata::get_picture("path/to/file.flac",
///                                     Some(PictureType::FileIcon),
///                                     Some("image/gif"),
///                                     None, None, None,
///                                     None, None).unwrap();
/// ```
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find any Picture";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let BlockData::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq(picture_type, picture.picture_type) &&
           optional_eq(mime_type, &picture.mime_type) &&
           optional_eq(description, &picture.description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

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

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq!(0, Some(1)));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq!(0, None), "Should always return true when None");
    assert!(optional_eq!(0, Some(0)), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err().kind(), ErrorKind::NotFound);
    assert_eq!(invalid_data.unwrap_err().kind(), ErrorKind::InvalidData);
    assert!(result.is_ok());
  }
}
<commit_msg>Change use of `optional_eq` in tests<commit_after>use nom::{Consumer, FileProducer};
use std::io::{Error, ErrorKind, Result};
use std::u32;

use metadata::{
  Block, BlockData,
  StreamInfo, CueSheet, VorbisComment, Picture,
  PictureType,
};
use metadata::types::MetaDataConsumer;

// Will return true when the unwrapped value of `$option` and `$compare`
// match or `$option` is `Option::None`, otherwise false.
#[inline]
pub fn optional_eq<T: Eq>(option: Option<T>, other: T) -> bool {
  option.map_or(true, |value| value == other)
}

// With the given filename, return all metadata blocks available.
//
// This function expects a flac file, but will return a proper `Result::Err`
// when things go wrong.
//
// # Failures
//
// * `ErrorKind::NotFound` is returned when the given filename isn't found.
// * `ErrorKind::InvalidData` is returned when the data within the file
//   isn't valid FLAC data.
pub fn get_metadata(filename: &str) -> Result<Vec<Block>> {
  FileProducer::new(filename, 1024).and_then(|mut producer| {
    let mut consumer = MetaDataConsumer::new();

    consumer.run(&mut producer);

    if !consumer.data.is_empty() {
      Ok(consumer.data)
    } else {
      let error_str = "parser: couldn't find any metadata";

      Err(Error::new(ErrorKind::InvalidData, error_str))
    }
  })
}

/// Reads and returns the `StreamInfo` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `StreamInfo` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_stream_info("path/to/file.flac") {
///   Ok(stream_info) => {
///     // Use the stream_info variable...
///   }
///   Err(error)      => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let stream_info = metadata::get_stream_info("path/to/file.flac").unwrap();
/// ```
pub fn get_stream_info(filename: &str) -> Result<StreamInfo> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find StreamInfo";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::StreamInfo(stream_info) = block.data {
        result = Ok(stream_info);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `VorbisComment` metadata block of the given FLAC
/// file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `VorbisComment` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_vorbis_comment("path/to/file.flac") {
///   Ok(vorbis_comment) => {
///     // Use the vorbis_comment variable...
///   }
///   Err(error)         => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
///
/// let vorbis_comment =
///   metadata::get_vorbis_comment("path/to/file.flac").unwrap();
/// ```
pub fn get_vorbis_comment(filename: &str) -> Result<VorbisComment> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find VorbisComment";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::VorbisComment(vorbis_comment) = block.data {
        result = Ok(vorbis_comment);
        break;
      }
    }

    result
  })
}

/// Reads and returns the `CueSheet` metadata block of the given FLAC file.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found
///   or there is no `CueSheet` within the file.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
///
/// match metadata::get_cue_sheet("path/to/file.flac") {
///   Ok(cue_sheet) => {
///     // Use the cue_sheet variable...
///   }
///   Err(error)    => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors
///
/// ```no_run
/// use flac::metadata;
///
/// let cue_sheet = metadata::get_cue_sheet("path/to/file.flac").unwrap();
/// ```
pub fn get_cue_sheet(filename: &str) -> Result<CueSheet> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find CueSheet";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    for block in blocks {
      if let BlockData::CueSheet(cue_sheet) = block.data {
        result = Ok(cue_sheet);
        break;
      }
    }

    result
  })
}

/// Reads and returns a `Picture` metadata block of the given FLAC file.
///
/// There can be more than one `Picture` block in a file and this function
/// takes optional, that being `Option<T>`, parameters that act as
/// constraints to search within. The `Picture` with the largest area
/// matching all constraints will be returned.
///
/// Putting `None` into any of the optional constraints conveys that you
/// want any of that parameter. Otherwise it will try to look for the image
/// that matches within the given constraints.
///
/// # Failures
///
/// * `ErrorKind::NotFound` is returned when the given filename isn't found,
///   there is no `Picture` within the file, or no `Picture` that fits the
///   given constraints.
/// * `ErrorKind::InvalidData` is returned when the data within the file
///   isn't valid FLAC data.
///
/// # Examples
///
/// Handling errors might look something like this:
///
/// ```
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let result = metadata::get_picture("path/to/file.flac",
///                                    Some(PictureType::FileIcon),
///                                    Some("image/gif"),
///                                    None, None, None,
///                                    None, None);
///
/// match result {
///   Ok(picture) => {
///     // Use the picture variable...
///   }
///   Err(error)  => println!("{}", error),
/// }
/// ```
///
/// Or just ignore the errors:
///
/// ```no_run
/// use flac::metadata;
/// use flac::metadata::PictureType;
///
/// let picture = metadata::get_picture("path/to/file.flac",
///                                     Some(PictureType::FileIcon),
///                                     Some("image/gif"),
///                                     None, None, None,
///                                     None, None).unwrap();
/// ```
pub fn get_picture(filename: &str,
                   picture_type: Option<PictureType>,
                   mime_type: Option<&str>,
                   description: Option<&str>,
                   max_width: Option<u32>,
                   max_height: Option<u32>,
                   max_depth: Option<u32>,
                   max_colors: Option<u32>)
                   -> Result<Picture> {
  get_metadata(filename).and_then(|blocks| {
    let error_str  = "metadata: couldn't find any Picture";
    let mut result = Err(Error::new(ErrorKind::NotFound, error_str));

    let mut max_area_seen  = 0;
    let mut max_depth_seen = 0;

    let max_value      = u32::max_value();
    let max_width_num  = max_width.unwrap_or(max_value);
    let max_height_num = max_height.unwrap_or(max_value);
    let max_depth_num  = max_depth.unwrap_or(max_value);
    let max_colors_num = max_colors.unwrap_or(max_value);

    for block in blocks {
      if let BlockData::Picture(picture) = block.data {
        let area = (picture.width as u64) * (picture.height as u64);

        if optional_eq(picture_type, picture.picture_type) &&
           optional_eq(mime_type, &picture.mime_type) &&
           optional_eq(description, &picture.description) &&
           picture.width <= max_width_num &&
           picture.height <= max_height_num &&
           picture.depth <= max_depth_num &&
           picture.colors <= max_colors_num &&
           (area > max_area_seen || (area == max_area_seen &&
                                     picture.depth > max_depth_seen)) {
          max_area_seen  = area;
          max_depth_seen = picture.depth;
          result         = Ok(picture);
        }
      }
    }

    result
  })
}

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

  #[test]
  #[should_panic]
  fn test_panic_optional_eq() {
    assert!(optional_eq(Some(1), 0));
  }

  #[test]
  fn test_optional_eq() {
    assert!(optional_eq(None, 0), "Should always return true when None");
    assert!(optional_eq(Some(0), 0), "Should return true (Some(0) == 0)");
  }

  #[test]
  fn test_get_metadata() {
    let not_found    = get_metadata("non-existent/file.txt");
    let invalid_data = get_metadata("README.md");
    let result       = get_metadata("tests/assets/input-SVAUP.flac");

    assert_eq!(not_found.unwrap_err().kind(), ErrorKind::NotFound);
    assert_eq!(invalid_data.unwrap_err().kind(), ErrorKind::InvalidData);
    assert!(result.is_ok());
  }
}
<|endoftext|>