<commit_before>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::parser;
use syntax::token;
use syntax::tokenstream::TokenStream;
use syntax_pos::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) {
                if fmt_str.contents.is_empty() {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.path.span, String::from("println")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.path.span, String::from("writeln")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.tts, true) {
                if fmt_str.contents.is_empty() {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// The arguments of a `print[ln]!` or `write[ln]!` invocation.
struct FmtStr {
    /// The contents of the format string (inside the quotes).
    contents: String,
    style: StrStyle,
    /// The span of the format string, including quotes, the raw marker, and any raw hashes.
    span: Span,
}

impl FmtStr {
    /// Given a format string that ends in a newline and its span, calculates the span of the
    /// newline.
    fn newline_span(&self) -> Span {
        let sp = self.span;

        let newline_sp_hi = sp.hi()
            - match self.style {
                StrStyle::Cooked => BytePos(1),
                StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
            };

        let newline_sp_len = if self.contents.ends_with('\n') {
            BytePos(1)
        } else if self.contents.ends_with(r"\n") {
            BytePos(2)
        } else {
            panic!("expected format string to contain a newline");
        };

        sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<FmtStr>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let (fmtstr, fmtstyle) = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok((fmtstr, fmtstyle)) => (fmtstr.to_string(), fmtstyle),
        Err(_) => return (None, expr),
    };
    let fmtspan = parser.prev_span;
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
        };
        if !parser.eat(&token::Comma) {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                expr,
            );
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                None,
            );
        };
        match &token_expr.kind {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.kind {
                    if let ExprKind::Path(_, p) = &lhs.kind {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string constains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmt_str: &FmtStr) -> bool {
    let s = &fmt_str.contents;

    if s.ends_with('\n') {
        return true;
    } else if let StrStyle::Raw(_) = fmt_str.style {
        return false;
    }

    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<commit_msg>Rustup to rustc 1.40.0-nightly (ac162c6ab 2019-11-09)<commit_after>use crate::utils::{snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then};
use rustc::lint::{EarlyContext, EarlyLintPass, LintArray, LintPass};
use rustc::{declare_lint_pass, declare_tool_lint};
use rustc_errors::Applicability;
use std::borrow::Cow;
use syntax::ast::*;
use syntax::parse::parser;
use syntax::token;
use syntax::tokenstream::TokenStream;
use syntax_pos::{BytePos, Span};

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `println!("")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `println!()`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// println!("");
    /// ```
    pub PRINTLN_EMPTY_STRING,
    style,
    "using `println!(\"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `print!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `println!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # let name = "World";
    /// print!("Hello {}!\n", name);
    /// ```
    /// use println!() instead
    /// ```rust
    /// # let name = "World";
    /// println!("Hello {}!", name);
    /// ```
    pub PRINT_WITH_NEWLINE,
    style,
    "using `print!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** Checks for printing on *stdout*. The purpose of this lint
    /// is to catch debugging remnants.
    ///
    /// **Why is this bad?** People often print on *stdout* while debugging an
    /// application and might forget to remove those prints afterward.
    ///
    /// **Known problems:** Only catches `print!` and `println!` calls.
    ///
    /// **Example:**
    /// ```rust
    /// println!("Hello world!");
    /// ```
    pub PRINT_STDOUT,
    restriction,
    "printing on stdout"
}

declare_clippy_lint! {
    /// **What it does:** Checks for use of `Debug` formatting. The purpose of this
    /// lint is to catch debugging remnants.
    ///
    /// **Why is this bad?** The purpose of the `Debug` trait is to facilitate
    /// debugging Rust code. It should not be used in user-facing output.
    ///
    /// **Example:**
    /// ```rust
    /// # let foo = "bar";
    /// println!("{:?}", foo);
    /// ```
    pub USE_DEBUG,
    restriction,
    "use of `Debug`-based formatting"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `print!`/`println!` args.
    ///
    /// **Why is this bad?** Using literals as `println!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `println!("{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// println!("{}", "foo");
    /// ```
    /// use the literal without formatting:
    /// ```rust
    /// println!("foo");
    /// ```
    pub PRINT_LITERAL,
    style,
    "printing a literal with a format string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `writeln!(buf, "")` to
    /// print a newline.
    ///
    /// **Why is this bad?** You should use `writeln!(buf)`, which is simpler.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "");
    /// ```
    pub WRITELN_EMPTY_STRING,
    style,
    "using `writeln!(buf, \"\")` with an empty string"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns when you use `write!()` with a format
    /// string that
    /// ends in a newline.
    ///
    /// **Why is this bad?** You should use `writeln!()` instead, which appends the
    /// newline.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// # let name = "World";
    /// write!(buf, "Hello {}!\n", name);
    /// ```
    pub WRITE_WITH_NEWLINE,
    style,
    "using `write!()` with a format string that ends in a single newline"
}

declare_clippy_lint! {
    /// **What it does:** This lint warns about the use of literals as `write!`/`writeln!` args.
    ///
    /// **Why is this bad?** Using literals as `writeln!` args is inefficient
    /// (c.f., https://github.com/matthiaskrgr/rust-str-bench) and unnecessary
    /// (i.e., just put the literal in the format string)
    ///
    /// **Known problems:** Will also warn with macro calls as arguments that expand to literals
    /// -- e.g., `writeln!(buf, "{}", env!("FOO"))`.
    ///
    /// **Example:**
    /// ```rust
    /// # use std::fmt::Write;
    /// # let mut buf = String::new();
    /// writeln!(buf, "{}", "foo");
    /// ```
    pub WRITE_LITERAL,
    style,
    "writing a literal with a format string"
}

declare_lint_pass!(Write => [
    PRINT_WITH_NEWLINE,
    PRINTLN_EMPTY_STRING,
    PRINT_STDOUT,
    USE_DEBUG,
    PRINT_LITERAL,
    WRITE_WITH_NEWLINE,
    WRITELN_EMPTY_STRING,
    WRITE_LITERAL
]);

impl EarlyLintPass for Write {
    fn check_mac(&mut self, cx: &EarlyContext<'_>, mac: &Mac) {
        if mac.path == sym!(println) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `println!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) {
                if fmt_str.contents.is_empty() {
                    span_lint_and_sugg(
                        cx,
                        PRINTLN_EMPTY_STRING,
                        mac.span,
                        "using `println!(\"\")`",
                        "replace it with",
                        "println!()".to_string(),
                        Applicability::MachineApplicable,
                    );
                }
            }
        } else if mac.path == sym!(print) {
            span_lint(cx, PRINT_STDOUT, mac.span, "use of `print!`");
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, false) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        PRINT_WITH_NEWLINE,
                        mac.span,
                        "using `print!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `println!` instead",
                                vec![
                                    (mac.path.span, String::from("println")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    );
                }
            }
        } else if mac.path == sym!(write) {
            if let (Some(fmt_str), _) = check_tts(cx, &mac.tts, true) {
                if check_newlines(&fmt_str) {
                    span_lint_and_then(
                        cx,
                        WRITE_WITH_NEWLINE,
                        mac.span,
                        "using `write!()` with a format string that ends in a single newline",
                        |err| {
                            err.multipart_suggestion(
                                "use `writeln!()` instead",
                                vec![
                                    (mac.path.span, String::from("writeln")),
                                    (fmt_str.newline_span(), String::new()),
                                ],
                                Applicability::MachineApplicable,
                            );
                        },
                    )
                }
            }
        } else if mac.path == sym!(writeln) {
            if let (Some(fmt_str), expr) = check_tts(cx, &mac.tts, true) {
                if fmt_str.contents.is_empty() {
                    let mut applicability = Applicability::MachineApplicable;
                    let suggestion = expr.map_or_else(
                        move || {
                            applicability = Applicability::HasPlaceholders;
                            Cow::Borrowed("v")
                        },
                        move |expr| snippet_with_applicability(cx, expr.span, "v", &mut applicability),
                    );

                    span_lint_and_sugg(
                        cx,
                        WRITELN_EMPTY_STRING,
                        mac.span,
                        format!("using `writeln!({}, \"\")`", suggestion).as_str(),
                        "replace it with",
                        format!("writeln!({})", suggestion),
                        applicability,
                    );
                }
            }
        }
    }
}

/// The arguments of a `print[ln]!` or `write[ln]!` invocation.
struct FmtStr {
    /// The contents of the format string (inside the quotes).
    contents: String,
    style: StrStyle,
    /// The span of the format string, including quotes, the raw marker, and any raw hashes.
    span: Span,
}

impl FmtStr {
    /// Given a format string that ends in a newline and its span, calculates the span of the
    /// newline.
    fn newline_span(&self) -> Span {
        let sp = self.span;

        let newline_sp_hi = sp.hi()
            - match self.style {
                StrStyle::Cooked => BytePos(1),
                StrStyle::Raw(hashes) => BytePos((1 + hashes).into()),
            };

        let newline_sp_len = if self.contents.ends_with('\n') {
            BytePos(1)
        } else if self.contents.ends_with(r"\n") {
            BytePos(2)
        } else {
            panic!("expected format string to contain a newline");
        };

        sp.with_lo(newline_sp_hi - newline_sp_len).with_hi(newline_sp_hi)
    }
}

/// Checks the arguments of `print[ln]!` and `write[ln]!` calls. It will return a tuple of two
/// `Option`s. The first `Option` of the tuple is the macro's format string. It includes
/// the contents of the string, whether it's a raw string, and the span of the literal in the
/// source. The second `Option` in the tuple is, in the `write[ln]!` case, the expression the
/// `format_str` should be written to.
///
/// Example:
///
/// Calling this function on
/// ```rust
/// # use std::fmt::Write;
/// # let mut buf = String::new();
/// # let something = "something";
/// writeln!(buf, "string to write: {}", something);
/// ```
/// will return
/// ```rust,ignore
/// (Some("string to write: {}"), Some(buf))
/// ```
#[allow(clippy::too_many_lines)]
fn check_tts<'a>(cx: &EarlyContext<'a>, tts: &TokenStream, is_write: bool) -> (Option<FmtStr>, Option<Expr>) {
    use fmt_macros::*;
    let tts = tts.clone();

    let mut parser = parser::Parser::new(&cx.sess.parse_sess, tts, None, false, false, None);
    let mut expr: Option<Expr> = None;
    if is_write {
        expr = match parser.parse_expr().map_err(|mut err| err.cancel()) {
            Ok(p) => Some(p.into_inner()),
            Err(_) => return (None, None),
        };
        // might be `writeln!(foo)`
        if parser.expect(&token::Comma).map_err(|mut err| err.cancel()).is_err() {
            return (None, expr);
        }
    }

    let (fmtstr, fmtstyle) = match parser.parse_str().map_err(|mut err| err.cancel()) {
        Ok((fmtstr, fmtstyle)) => (fmtstr.to_string(), fmtstyle),
        Err(_) => return (None, expr),
    };
    let fmtspan = parser.prev_span;
    let tmp = fmtstr.clone();
    let mut args = vec![];
    let mut fmt_parser = Parser::new(&tmp, None, Vec::new(), false);
    while let Some(piece) = fmt_parser.next() {
        if !fmt_parser.errors.is_empty() {
            return (None, expr);
        }
        if let Piece::NextArgument(arg) = piece {
            if arg.format.ty == "?" {
                // FIXME: modify rustc's fmt string parser to give us the current span
                span_lint(cx, USE_DEBUG, parser.prev_span, "use of `Debug`-based formatting");
            }
            args.push(arg);
        }
    }
    let lint = if is_write { WRITE_LITERAL } else { PRINT_LITERAL };
    let mut idx = 0;
    loop {
        const SIMPLE: FormatSpec<'_> = FormatSpec {
            fill: None,
            align: AlignUnknown,
            flags: 0,
            precision: CountImplied,
            precision_span: None,
            width: CountImplied,
            width_span: None,
            ty: "",
            ty_span: None,
        };
        if !parser.eat(&token::Comma) {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                expr,
            );
        }
        let token_expr = if let Ok(expr) = parser.parse_expr().map_err(|mut err| err.cancel()) {
            expr
        } else {
            return (
                Some(FmtStr {
                    contents: fmtstr,
                    style: fmtstyle,
                    span: fmtspan,
                }),
                None,
            );
        };
        match &token_expr.kind {
            ExprKind::Lit(_) => {
                let mut all_simple = true;
                let mut seen = false;
                for arg in &args {
                    match arg.position {
                        ArgumentImplicitlyIs(n) | ArgumentIs(n) => {
                            if n == idx {
                                all_simple &= arg.format == SIMPLE;
                                seen = true;
                            }
                        },
                        ArgumentNamed(_) => {},
                    }
                }
                if all_simple && seen {
                    span_lint(cx, lint, token_expr.span, "literal with an empty format string");
                }
                idx += 1;
            },
            ExprKind::Assign(lhs, rhs) => {
                if let ExprKind::Lit(_) = rhs.kind {
                    if let ExprKind::Path(_, p) = &lhs.kind {
                        let mut all_simple = true;
                        let mut seen = false;
                        for arg in &args {
                            match arg.position {
                                ArgumentImplicitlyIs(_) | ArgumentIs(_) => {},
                                ArgumentNamed(name) => {
                                    if *p == name {
                                        seen = true;
                                        all_simple &= arg.format == SIMPLE;
                                    }
                                },
                            }
                        }
                        if all_simple && seen {
                            span_lint(cx, lint, rhs.span, "literal with an empty format string");
                        }
                    }
                }
            },
            _ => idx += 1,
        }
    }
}

/// Checks if the format string constains a single newline that terminates it.
///
/// Literal and escaped newlines are both checked (only literal for raw strings).
fn check_newlines(fmt_str: &FmtStr) -> bool {
    let s = &fmt_str.contents;

    if s.ends_with('\n') {
        return true;
    } else if let StrStyle::Raw(_) = fmt_str.style {
        return false;
    }

    if s.len() < 2 {
        return false;
    }

    let bytes = s.as_bytes();
    if bytes[bytes.len() - 2] != b'\\' || bytes[bytes.len() - 1] != b'n' {
        return false;
    }

    let mut escaping = false;
    for (index, &byte) in bytes.iter().enumerate() {
        if escaping {
            if byte == b'n' {
                return index == bytes.len() - 1;
            }
            escaping = false;
        } else if byte == b'\\' {
            escaping = true;
        }
    }

    false
}
<|endoftext|>


<commit_before>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#[doc(hidden)]
#[macro_export]
macro_rules! consumes_to {
    ( $_rules:ident, $tokens:expr, [] ) => ();
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ) ] ) => {
        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ),
                                    $( $names:ident $calls:tt ),* ] ) => {

        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
                                                  [ $( $names:ident $calls:tt ),* ] ) ] ) => {
        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
                                                  [ $( $nested_names:ident $nested_calls:tt ),* ] ),
                                    $( $names:ident $calls:tt ),* ] ) => {

        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $nested_names $nested_calls ),* ]);

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
    };
}

/// A `macro` which facilitates grammar testing and debugging by comparing produced tokens.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate pest;
/// # use std::rc::Rc;
/// # use pest::{Error, Parser};
/// # use pest::inputs::Input;
/// # use pest::iterators::Pairs;
/// # fn main() {
/// # #[allow(non_camel_case_types)]
/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// # enum Rule {
/// #     a,
/// #     b,
/// #     c
/// # }
/// #
/// # struct AbcParser;
/// #
/// # impl Parser<Rule> for AbcParser {
/// #     fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
/// #         pest::state(input, |state, pos| {
/// #             state.rule(Rule::a, pos, |state, pos| {
/// #                 state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 }).unwrap().skip(1)
/// #             }).and_then(|p| {
/// #                 state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 })
/// #             })
/// #         })
/// #     }
/// # }
/// parses_to! {
///     parser: AbcParser,
///     input:  "abcde",
///     rule:   Rule::a,
///     tokens: [
///         a(0, 3, [
///             b(1, 2)
///         ]),
///         c(4, 5)
///     ]
/// };
/// # }
/// ```
#[macro_export]
macro_rules! parses_to {
    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
      tokens: [ $( $names:ident $calls:tt ),* ] ) => {

        #[allow(unused_mut)]
        {
            use $crate::Parser;

            let mut tokens = $parser::parse_str($rules::$rule, $string).unwrap().tokens();

            consumes_to!($rules, &mut tokens, [ $( $names $calls ),* ]);

            let rest: Vec<_> = tokens.collect();

            assert!(rest.is_empty(), format!("expected end of stream, but found {:?}", rest));
        }
    };
}

/// A `macro` which facilitates grammar testing and debugging by comparing produced errors.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate pest;
/// # use std::rc::Rc;
/// # use pest::{Error, Parser};
/// # use pest::inputs::Input;
/// # use pest::iterators::Pairs;
/// # fn main() {
/// # #[allow(non_camel_case_types)]
/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// # enum Rule {
/// #     a,
/// #     b,
/// #     c
/// # }
/// #
/// # struct AbcParser;
/// #
/// # impl Parser<Rule> for AbcParser {
/// #     fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
/// #         pest::state(input, |state, pos| {
/// #             state.rule(Rule::a, pos, |state, pos| {
/// #                 state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 }).unwrap().skip(1)
/// #             }).and_then(|p| {
/// #                 state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
/// #                     pos.match_string("e")
/// #                 })
/// #             })
/// #         })
/// #     }
/// # }
/// fails_with! {
///     parser: AbcParser,
///     input: "abcdf",
///     rule: Rule::a,
///     positives: vec![Rule::c],
///     negatives: vec![],
///     pos: 4
/// };
/// # }
/// ```
#[macro_export]
macro_rules! fails_with {
    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
      positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => {

        #[allow(unused_mut)]
        {
            use $crate::Parser;

            let error = $parser::parse_str($rules::$rule, $string).unwrap_err();

            match error {
                Error::ParsingError { positives, negatives, pos } => {
                    assert_eq!(positives, $positives);
                    assert_eq!(negatives, $negatives);
                    assert_eq!(pos.pos(), $pos);
                }
                _ => unreachable!()
            };
        }
    };
}

#[cfg(test)]
mod tests {
    use std::rc::Rc;

    use super::super::error::Error;
    use super::super::inputs::Input;
    use super::super::{Parser, state};
    use super::super::iterators::Pairs;

    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    enum Rule {
        a,
        b,
        c
    }

    struct AbcParser;

    impl Parser<Rule> for AbcParser {
        fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
            state(input, |state, pos| {
                state.rule(Rule::a, pos, |state, pos| {
                    state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
                        pos.skip(1)
                    }).unwrap().skip(1)
                }).and_then(|p| {
                    state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
                        pos.match_string("e")
                    })
                })
            })
        }
    }

    #[test]
    fn parses_to() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: [
                a(0, 3, [
                    b(1, 2)
                ]),
                c(4, 5)
            ]
        };
    }

    #[test]
    #[should_panic]
    fn missing_end() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: [
                a(0, 3, [
                    b(1, 2)
                ])
            ]
        };
    }

    #[test]
    #[should_panic]
    fn empty() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: []
        };
    }

    #[test]
    fn fails_with() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_positives() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::a],
            negatives: vec![],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_negatives() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![Rule::c],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_pos() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![],
            pos: 3
        };
    }
}
<commit_msg>Fixed macro path.<commit_after>// pest. The Elegant Parser
// Copyright (C) 2017  Dragoș Tiselice
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#[doc(hidden)]
#[macro_export]
macro_rules! consumes_to {
    ( $_rules:ident, $tokens:expr, [] ) => ();
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ) ] ) => {
        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr ),
                                    $( $names:ident $calls:tt ),* ] ) => {

        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
                                                  [ $( $names:ident $calls:tt ),* ] ) ] ) => {
        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };
    };
    ( $rules:ident, $tokens:expr, [ $name:ident ( $start:expr, $end:expr,
                                                  [ $( $nested_names:ident $nested_calls:tt ),* ] ),
                                    $( $names:ident $calls:tt ),* ] ) => {

        let expected = format!("expected Start {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $start);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::Start { rule, pos } => {
                let message = format!("{} but found Start {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $start {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $nested_names $nested_calls ),* ]);

        let expected = format!("expected End {{ rule: {:?}, pos: Position {{ pos: {} }} }}",
                               $rules::$name, $end);
        match $tokens.next().expect(&format!("{} but found nothing", expected)) {
            $crate::Token::End { rule, pos } => {
                let message = format!("{} but found End {{ rule: {:?}, pos: Position {{ {} }} }}",
                                      expected, rule, pos.pos());

                if rule != $rules::$name || pos.pos() != $end {
                    panic!("{}", message);
                }
            },
            token => panic!("{}", format!("{} but found {:?}", expected, token))
        };

        consumes_to!($rules, $tokens, [ $( $names $calls ),* ]);
    };
}

/// A `macro` which facilitates grammar testing and debugging by comparing produced tokens.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate pest;
/// # use std::rc::Rc;
/// # use pest::{Error, Parser};
/// # use pest::inputs::Input;
/// # use pest::iterators::Pairs;
/// # fn main() {
/// # #[allow(non_camel_case_types)]
/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// # enum Rule {
/// #     a,
/// #     b,
/// #     c
/// # }
/// #
/// # struct AbcParser;
/// #
/// # impl Parser<Rule> for AbcParser {
/// #     fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
/// #         pest::state(input, |state, pos| {
/// #             state.rule(Rule::a, pos, |state, pos| {
/// #                 state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 }).unwrap().skip(1)
/// #             }).and_then(|p| {
/// #                 state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 })
/// #             })
/// #         })
/// #     }
/// # }
/// parses_to! {
///     parser: AbcParser,
///     input:  "abcde",
///     rule:   Rule::a,
///     tokens: [
///         a(0, 3, [
///             b(1, 2)
///         ]),
///         c(4, 5)
///     ]
/// };
/// # }
/// ```
#[macro_export]
macro_rules! parses_to {
    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
      tokens: [ $( $names:ident $calls:tt ),* ] ) => {

        #[allow(unused_mut)]
        {
            use $crate::Parser;

            let mut tokens = $parser::parse_str($rules::$rule, $string).unwrap().tokens();

            consumes_to!($rules, &mut tokens, [ $( $names $calls ),* ]);

            let rest: Vec<_> = tokens.collect();

            assert!(rest.is_empty(), format!("expected end of stream, but found {:?}", rest));
        }
    };
}

/// A `macro` which facilitates grammar testing and debugging by comparing produced errors.
///
/// # Examples
///
/// ```
/// # #[macro_use]
/// # extern crate pest;
/// # use std::rc::Rc;
/// # use pest::{Error, Parser};
/// # use pest::inputs::Input;
/// # use pest::iterators::Pairs;
/// # fn main() {
/// # #[allow(non_camel_case_types)]
/// # #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
/// # enum Rule {
/// #     a,
/// #     b,
/// #     c
/// # }
/// #
/// # struct AbcParser;
/// #
/// # impl Parser<Rule> for AbcParser {
/// #     fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
/// #         pest::state(input, |state, pos| {
/// #             state.rule(Rule::a, pos, |state, pos| {
/// #                 state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
/// #                     pos.skip(1)
/// #                 }).unwrap().skip(1)
/// #             }).and_then(|p| {
/// #                 state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
/// #                     pos.match_string("e")
/// #                 })
/// #             })
/// #         })
/// #     }
/// # }
/// fails_with! {
///     parser: AbcParser,
///     input: "abcdf",
///     rule: Rule::a,
///     positives: vec![Rule::c],
///     negatives: vec![],
///     pos: 4
/// };
/// # }
/// ```
#[macro_export]
macro_rules! fails_with {
    ( parser: $parser:ident, input: $string:expr, rule: $rules:tt :: $rule:tt,
      positives: $positives:expr, negatives: $negatives:expr, pos: $pos:expr ) => {

        #[allow(unused_mut)]
        {
            use $crate::Parser;

            let error = $parser::parse_str($rules::$rule, $string).unwrap_err();

            match error {
                $crate::Error::ParsingError { positives, negatives, pos } => {
                    assert_eq!(positives, $positives);
                    assert_eq!(negatives, $negatives);
                    assert_eq!(pos.pos(), $pos);
                }
                _ => unreachable!()
            };
        }
    };
}

#[cfg(test)]
mod tests {
    use std::rc::Rc;

    use super::super::error::Error;
    use super::super::inputs::Input;
    use super::super::{Parser, state};
    use super::super::iterators::Pairs;

    #[allow(non_camel_case_types)]
    #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
    enum Rule {
        a,
        b,
        c
    }

    struct AbcParser;

    impl Parser<Rule> for AbcParser {
        fn parse<I: Input>(_: Rule, input: Rc<I>) -> Result<Pairs<Rule, I>, Error<Rule, I>> {
            state(input, |state, pos| {
                state.rule(Rule::a, pos, |state, pos| {
                    state.rule(Rule::b, pos.skip(1).unwrap(), |_, pos| {
                        pos.skip(1)
                    }).unwrap().skip(1)
                }).and_then(|p| {
                    state.rule(Rule::c, p.skip(1).unwrap(), |_, pos| {
                        pos.match_string("e")
                    })
                })
            })
        }
    }

    #[test]
    fn parses_to() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: [
                a(0, 3, [
                    b(1, 2)
                ]),
                c(4, 5)
            ]
        };
    }

    #[test]
    #[should_panic]
    fn missing_end() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: [
                a(0, 3, [
                    b(1, 2)
                ])
            ]
        };
    }

    #[test]
    #[should_panic]
    fn empty() {
        parses_to! {
            parser: AbcParser,
            input: "abcde",
            rule: Rule::a,
            tokens: []
        };
    }

    #[test]
    fn fails_with() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_positives() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::a],
            negatives: vec![],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_negatives() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![Rule::c],
            pos: 4
        };
    }

    #[test]
    #[should_panic]
    fn wrong_pos() {
        fails_with! {
            parser: AbcParser,
            input: "abcdf",
            rule: Rule::a,
            positives: vec![Rule::c],
            negatives: vec![],
            pos: 3
        };
    }
}
<|endoftext|>


<commit_before>//! The project module takes care of muxed related initialization. Locating the
/// users home directory. Finding the desired config files, and reading the
/// configs in.

use std::path::Path;
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::{YamlLoader, Yaml};
#[cfg(not(test))] use std::env::home_dir;

pub mod parser;
pub mod processor;

/// The muxed project folder name. Should be located in the users home dir as a
/// hidden directory.
static MUXED_FOLDER: &'static str = "muxed";

/// Using the provided project name, locate the path to that project file. It
/// should be something similar to: `~/.muxed/my_project.yml`
/// Read in the contents of the config (which should be Yaml), and parse the
/// contents as yaml.
///
/// # Examples
///
/// Given the project name "compiler" and a project file found at:
/// `~/.muxed/compiler.yml`.
///
/// ```
/// let yaml: Vec<Yaml> = read("compiler".to_string());
/// ```
///
/// project_name: The name of the project, corresponding to the project config
/// file.
pub fn read(project_name: &String) -> Vec<Yaml> {
    let config = format!("{}/.{}/{}.yml", homedir_string(), &MUXED_FOLDER, project_name);
    let path = Path::new(&config);
    let mut contents = String::new();
    let _ = File::open(path).expect("Config Read error").read_to_string(&mut contents);
    YamlLoader::load_from_str(&contents).expect("Yaml was not parsed")
}

/// Return the users homedir as a string.
#[cfg(not(test))] fn homedir_string() -> String {
    match home_dir() {
        Some(dir) => format!("{}", dir.display()),
        None      => panic!("Impossible to get your home dir!")
    }
}

/// Return the temp dir as the users home dir during testing.
#[cfg(test)] fn homedir_string() -> String {
    String::from("/tmp")
}
<commit_msg>Return a proper result when fetching homedir<commit_after>//! The project module takes care of muxed related initialization. Locating the
/// users home directory. Finding the desired config files, and reading the
/// configs in.

use std::path::{Path, PathBuf};
use std::io::prelude::*;
use std::fs::File;
use yaml_rust::{YamlLoader, Yaml};
#[cfg(not(test))] use std::env::home_dir;

pub mod parser;
pub mod processor;

/// The muxed project folder name. Should be located in the users home dir as a
/// hidden directory.
static MUXED_FOLDER: &'static str = "muxed";

/// Using the provided project name, locate the path to that project file. It
/// should be something similar to: `~/.muxed/my_project.yml`
/// Read in the contents of the config (which should be Yaml), and parse the
/// contents as yaml.
///
/// # Examples
///
/// Given the project name "compiler" and a project file found at:
/// `~/.muxed/compiler.yml`.
///
/// ```
/// let yaml: Vec<Yaml> = read("compiler".to_string());
/// ```
///
/// project_name: The name of the project, corresponding to the project config
/// file.
pub fn read(project_name: &String) -> Vec<Yaml> {
    let home = try!(homedir().map_err(|e| e));
    let config = format!("{}/.{}/{}.yml", home.display(), &MUXED_FOLDER, project_name);
    let path = Path::new(&config);
    let mut contents = String::new();
    let _ = File::open(path).expect("Config Read error").read_to_string(&mut contents);
    YamlLoader::load_from_str(&contents).expect("Yaml was not parsed")
}

/// Return the users homedir as a string.
#[cfg(not(test))] fn homedir() -> Result<PathBuf, String>{
    match home_dir() {
        Some(dir) => Ok(dir),
        None      => Err(String::from("We couldn't find your home directory."))
    }
}

/// Return the temp dir as the users home dir during testing.
#[cfg(test)] fn homedir() -> Result<PathBuf, String> {
    Ok(PathBuf::from("/tmp"))
}
<|endoftext|>


<commit_before>use std::path::Path;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    mpsc::{self, Receiver, Sender},
    Arc,
};
use std::thread::{self, JoinHandle};

use glium;
use glium::backend::Facade;
use glium::texture::{MipmapsOption, RawImage2d, Texture2d, UncompressedFloatFormat};
use glium::{uniform, DrawParameters, IndexBuffer, Rect, Surface, VertexBuffer};

use crate::camera::Camera;
use crate::scene::Scene;
use crate::stats;
use crate::vertex::RawVertex;

mod config;
mod render_worker;
mod traced_image;

pub use self::config::*;
use self::render_worker::RenderWorker;
use self::traced_image::TracedImage;

pub struct RenderCoordinator {
    width: u32,
    height: u32,
    max_blocks: Option<usize>,
    current_block: AtomicUsize,
    block_width: u32,
    block_height: u32,
    x_blocks: usize,
    y_blocks: usize,
}

impl RenderCoordinator {
    fn new(width: u32, height: u32, config: &RenderConfig) -> RenderCoordinator {
        let block_height = 50;
        let block_width = 50;
        let x_blocks = (f64::from(width) / f64::from(block_width)).ceil() as usize;
        let y_blocks = (f64::from(height) / f64::from(block_height)).ceil() as usize;
        let blocks_per_iter = x_blocks * y_blocks;
        let max_blocks = config.max_iterations.map(|iters| iters * blocks_per_iter);
        RenderCoordinator {
            width,
            height,
            max_blocks,
            current_block: AtomicUsize::new(0),
            block_width,
            block_height,
            x_blocks,
            y_blocks,
        }
    }

    fn next_block(&self) -> Option<Rect> {
        let block_i = self.current_block.fetch_add(1, Ordering::Relaxed);
        if let Some(max) = self.max_blocks {
            if block_i >= max {
                return None;
            }
        };
        let iter_i = block_i % (self.x_blocks * self.y_blocks);
        let x_i = (iter_i % self.x_blocks) as u32;
        let y_i = (iter_i / self.x_blocks) as u32;
        let start_x = self.block_width * x_i;
        let end_x = (self.block_width * (x_i + 1)).min(self.width);
        let start_y = self.block_height * y_i;
        let end_y = (self.block_height * (y_i + 1)).min(self.height);
        Some(Rect {
            left: start_x,
            bottom: start_y,
            width: end_x - start_x,
            height: end_y - start_y,
        })
    }
}

struct PTVisualizer {
    shader: glium::Program,
    vertex_buffer: VertexBuffer<RawVertex>,
    index_buffer: IndexBuffer<u32>,
    texture: Texture2d,
    tone_map: bool,
}

fn create_texture<F: Facade>(facade: &F, texture_source: RawImage2d<f32>) -> Texture2d {
    Texture2d::with_format(
        facade,
        texture_source,
        UncompressedFloatFormat::F32F32F32,
        MipmapsOption::NoMipmap,
    )
    .unwrap()
}

impl PTVisualizer {
    fn new<F: Facade>(
        facade: &F,
        texture_source: RawImage2d<f32>,
        config: &RenderConfig,
    ) -> PTVisualizer {
        let vertices = vec![
            RawVertex {
                pos: [-1.0, -1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [0.0, 0.0],
            },
            RawVertex {
                pos: [1.0, -1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [1.0, 0.0],
            },
            RawVertex {
                pos: [1.0, 1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [1.0, 1.0],
            },
            RawVertex {
                pos: [-1.0, 1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [0.0, 1.0],
            },
        ];
        let vertex_buffer =
            VertexBuffer::new(facade, &vertices).expect("Failed to create vertex buffer!");
        let indices = vec![0, 1, 2, 0, 2, 3];
        let index_buffer =
            IndexBuffer::new(facade, glium::index::PrimitiveType::TrianglesList, &indices)
                .expect("Failed to create index buffer!");

        // Image shader
        let vertex_shader_src = include_str!("shaders/image.vert");
        let fragment_shader_src = include_str!("shaders/image.frag");
        let shader =
            glium::Program::from_source(facade, vertex_shader_src, fragment_shader_src, None)
                .expect("Failed to create program!");

        let texture = create_texture(facade, texture_source);

        PTVisualizer {
            shader,
            vertex_buffer,
            index_buffer,
            texture,
            tone_map: config.tone_map,
        }
    }

    fn render<S: Surface>(&self, target: &mut S) {
        let uniforms = uniform! {
            image: &self.texture,
            tone_map: self.tone_map,
        };
        let draw_parameters = DrawParameters {
            ..Default::default()
        };
        target
            .draw(
                &self.vertex_buffer,
                &self.index_buffer,
                &self.shader,
                &uniforms,
                &draw_parameters,
            )
            .unwrap();
    }

    fn new_image<F: Facade>(
        &mut self,
        facade: &F,
        texture_source: RawImage2d<f32>,
        config: &RenderConfig,
    ) {
        self.texture = create_texture(facade, texture_source);
        self.tone_map = config.tone_map;
    }

    fn update_texture(&mut self, rect: Rect, texture_block: RawImage2d<f32>) {
        self.texture.write(rect, texture_block);
    }
}

pub struct PTRenderer {
    visualizer: Option<PTVisualizer>,
    image: TracedImage,
    coordinator: Arc<RenderCoordinator>,
    result_rx: Option<Receiver<(Rect, Vec<f32>)>>,
    message_txs: Vec<Sender<()>>,
    thread_handles: Vec<JoinHandle<()>>,
}

impl PTRenderer {
    pub fn new() -> PTRenderer {
        let image = TracedImage::empty(0, 0);
        PTRenderer {
            visualizer: None,
            image,
            coordinator: Arc::new(RenderCoordinator::new(0, 0, &RenderConfig::default())),
            result_rx: None,
            message_txs: Vec::new(),
            thread_handles: Vec::new(),
        }
    }

    fn start_render(&mut self, scene: &Arc<Scene>, camera: &Camera, config: &RenderConfig) {
        stats::start_render();
        let width = camera.width;
        let height = camera.height;
        self.image = TracedImage::empty(width, height);
        self.coordinator = Arc::new(RenderCoordinator::new(width, height, config));

        let (result_tx, result_rx) = mpsc::channel();
        self.result_rx = Some(result_rx);
        for _ in 0..num_cpus::get_physical().max(1) {
            let result_tx = result_tx.clone();
            let (message_tx, message_rx) = mpsc::channel();
            self.message_txs.push(message_tx);
            let coordinator = self.coordinator.clone();
            let camera = camera.clone();
            let config = config.clone();
            let scene = scene.clone();
            let handle = thread::spawn(move || {
                let worker =
                    RenderWorker::new(scene, camera, config, coordinator, message_rx, result_tx);
                worker.run();
            });
            self.thread_handles.push(handle);
        }
    }

    pub fn online_render<F: Facade>(
        &mut self,
        facade: &F,
        scene: &Arc<Scene>,
        camera: &Camera,
        config: &RenderConfig,
    ) {
        // TODO: Add support for max_iterations
        self.start_render(scene, camera, config);
        if let Some(visualizer) = &mut self.visualizer {
            visualizer.new_image(facade, self.image.get_texture_source(), config);
        } else {
            self.visualizer = Some(PTVisualizer::new(
                facade,
                self.image.get_texture_source(),
                config,
            ));
        }
    }

    pub fn offline_render(&mut self, scene: &Arc<Scene>, camera: &Camera, config: &RenderConfig) {
        self.start_render(scene, camera, config);

        // Wait for all the threads to finish
        for handle in self.thread_handles.drain(..) {
            handle.join().unwrap();
        }

        // TODO: update image during render
        for (rect, block) in self.result_rx.as_ref().unwrap().try_iter() {
            self.image.update_block(rect, &block);
        }
        stats::stop_render();
    }

    pub fn render<S: Surface>(&mut self, target: &mut S) {
        if let Some(ref rx) = self.result_rx {
            let visualizer = self.visualizer.as_mut().unwrap();
            for (rect, block) in rx.try_iter().take(10) {
                let (rect, texture_block) = self.image.update_block(rect, &block);
                visualizer.update_texture(rect, texture_block);
            }
            visualizer.render(target);
        }
    }

    pub fn stop_threads(&mut self) {
        for sender in &self.message_txs {
            sender.send(()).unwrap();
        }
        for handle in self.thread_handles.drain(..) {
            handle.join().unwrap();
        }
        // Drop channels only after join to make sure
        // that stop messages are properly received
        self.message_txs.clear();
        stats::stop_render();
    }

    pub fn save_image(&self, path: &Path) {
        self.image.save_image(path);
    }
}
<commit_msg>Remove useless max<commit_after>use std::path::Path;
use std::sync::{
    atomic::{AtomicUsize, Ordering},
    mpsc::{self, Receiver, Sender},
    Arc,
};
use std::thread::{self, JoinHandle};

use glium;
use glium::backend::Facade;
use glium::texture::{MipmapsOption, RawImage2d, Texture2d, UncompressedFloatFormat};
use glium::{uniform, DrawParameters, IndexBuffer, Rect, Surface, VertexBuffer};

use crate::camera::Camera;
use crate::scene::Scene;
use crate::stats;
use crate::vertex::RawVertex;

mod config;
mod render_worker;
mod traced_image;

pub use self::config::*;
use self::render_worker::RenderWorker;
use self::traced_image::TracedImage;

pub struct RenderCoordinator {
    width: u32,
    height: u32,
    max_blocks: Option<usize>,
    current_block: AtomicUsize,
    block_width: u32,
    block_height: u32,
    x_blocks: usize,
    y_blocks: usize,
}

impl RenderCoordinator {
    fn new(width: u32, height: u32, config: &RenderConfig) -> RenderCoordinator {
        let block_height = 50;
        let block_width = 50;
        let x_blocks = (f64::from(width) / f64::from(block_width)).ceil() as usize;
        let y_blocks = (f64::from(height) / f64::from(block_height)).ceil() as usize;
        let blocks_per_iter = x_blocks * y_blocks;
        let max_blocks = config.max_iterations.map(|iters| iters * blocks_per_iter);
        RenderCoordinator {
            width,
            height,
            max_blocks,
            current_block: AtomicUsize::new(0),
            block_width,
            block_height,
            x_blocks,
            y_blocks,
        }
    }

    fn next_block(&self) -> Option<Rect> {
        let block_i = self.current_block.fetch_add(1, Ordering::Relaxed);
        if let Some(max) = self.max_blocks {
            if block_i >= max {
                return None;
            }
        };
        let iter_i = block_i % (self.x_blocks * self.y_blocks);
        let x_i = (iter_i % self.x_blocks) as u32;
        let y_i = (iter_i / self.x_blocks) as u32;
        let start_x = self.block_width * x_i;
        let end_x = (self.block_width * (x_i + 1)).min(self.width);
        let start_y = self.block_height * y_i;
        let end_y = (self.block_height * (y_i + 1)).min(self.height);
        Some(Rect {
            left: start_x,
            bottom: start_y,
            width: end_x - start_x,
            height: end_y - start_y,
        })
    }
}

struct PTVisualizer {
    shader: glium::Program,
    vertex_buffer: VertexBuffer<RawVertex>,
    index_buffer: IndexBuffer<u32>,
    texture: Texture2d,
    tone_map: bool,
}

fn create_texture<F: Facade>(facade: &F, texture_source: RawImage2d<f32>) -> Texture2d {
    Texture2d::with_format(
        facade,
        texture_source,
        UncompressedFloatFormat::F32F32F32,
        MipmapsOption::NoMipmap,
    )
    .unwrap()
}

impl PTVisualizer {
    fn new<F: Facade>(
        facade: &F,
        texture_source: RawImage2d<f32>,
        config: &RenderConfig,
    ) -> PTVisualizer {
        let vertices = vec![
            RawVertex {
                pos: [-1.0, -1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [0.0, 0.0],
            },
            RawVertex {
                pos: [1.0, -1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [1.0, 0.0],
            },
            RawVertex {
                pos: [1.0, 1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [1.0, 1.0],
            },
            RawVertex {
                pos: [-1.0, 1.0, 0.0],
                normal: [0.0, 0.0, 0.0],
                tex_coords: [0.0, 1.0],
            },
        ];
        let vertex_buffer =
            VertexBuffer::new(facade, &vertices).expect("Failed to create vertex buffer!");
        let indices = vec![0, 1, 2, 0, 2, 3];
        let index_buffer =
            IndexBuffer::new(facade, glium::index::PrimitiveType::TrianglesList, &indices)
                .expect("Failed to create index buffer!");

        // Image shader
        let vertex_shader_src = include_str!("shaders/image.vert");
        let fragment_shader_src = include_str!("shaders/image.frag");
        let shader =
            glium::Program::from_source(facade, vertex_shader_src, fragment_shader_src, None)
                .expect("Failed to create program!");

        let texture = create_texture(facade, texture_source);

        PTVisualizer {
            shader,
            vertex_buffer,
            index_buffer,
            texture,
            tone_map: config.tone_map,
        }
    }

    fn render<S: Surface>(&self, target: &mut S) {
        let uniforms = uniform! {
            image: &self.texture,
            tone_map: self.tone_map,
        };
        let draw_parameters = DrawParameters {
            ..Default::default()
        };
        target
            .draw(
                &self.vertex_buffer,
                &self.index_buffer,
                &self.shader,
                &uniforms,
                &draw_parameters,
            )
            .unwrap();
    }

    fn new_image<F: Facade>(
        &mut self,
        facade: &F,
        texture_source: RawImage2d<f32>,
        config: &RenderConfig,
    ) {
        self.texture = create_texture(facade, texture_source);
        self.tone_map = config.tone_map;
    }

    fn update_texture(&mut self, rect: Rect, texture_block: RawImage2d<f32>) {
        self.texture.write(rect, texture_block);
    }
}

pub struct PTRenderer {
    visualizer: Option<PTVisualizer>,
    image: TracedImage,
    coordinator: Arc<RenderCoordinator>,
    result_rx: Option<Receiver<(Rect, Vec<f32>)>>,
    message_txs: Vec<Sender<()>>,
    thread_handles: Vec<JoinHandle<()>>,
}

impl PTRenderer {
    pub fn new() -> PTRenderer {
        let image = TracedImage::empty(0, 0);
        PTRenderer {
            visualizer: None,
            image,
            coordinator: Arc::new(RenderCoordinator::new(0, 0, &RenderConfig::default())),
            result_rx: None,
            message_txs: Vec::new(),
            thread_handles: Vec::new(),
        }
    }

    fn start_render(&mut self, scene: &Arc<Scene>, camera: &Camera, config: &RenderConfig) {
        stats::start_render();
        let width = camera.width;
        let height = camera.height;
        self.image = TracedImage::empty(width, height);
        self.coordinator = Arc::new(RenderCoordinator::new(width, height, config));

        let (result_tx, result_rx) = mpsc::channel();
        self.result_rx = Some(result_rx);
        for _ in 0..num_cpus::get_physical() {
            let result_tx = result_tx.clone();
            let (message_tx, message_rx) = mpsc::channel();
            self.message_txs.push(message_tx);
            let coordinator = self.coordinator.clone();
            let camera = camera.clone();
            let config = config.clone();
            let scene = scene.clone();
            let handle = thread::spawn(move || {
                let worker =
                    RenderWorker::new(scene, camera, config, coordinator, message_rx, result_tx);
                worker.run();
            });
            self.thread_handles.push(handle);
        }
    }

    pub fn online_render<F: Facade>(
        &mut self,
        facade: &F,
        scene: &Arc<Scene>,
        camera: &Camera,
        config: &RenderConfig,
    ) {
        // TODO: Add support for max_iterations
        self.start_render(scene, camera, config);
        if let Some(visualizer) = &mut self.visualizer {
            visualizer.new_image(facade, self.image.get_texture_source(), config);
        } else {
            self.visualizer = Some(PTVisualizer::new(
                facade,
                self.image.get_texture_source(),
                config,
            ));
        }
    }

    pub fn offline_render(&mut self, scene: &Arc<Scene>, camera: &Camera, config: &RenderConfig) {
        self.start_render(scene, camera, config);

        // Wait for all the threads to finish
        for handle in self.thread_handles.drain(..) {
            handle.join().unwrap();
        }

        // TODO: update image during render
        for (rect, block) in self.result_rx.as_ref().unwrap().try_iter() {
            self.image.update_block(rect, &block);
        }
        stats::stop_render();
    }

    pub fn render<S: Surface>(&mut self, target: &mut S) {
        if let Some(ref rx) = self.result_rx {
            let visualizer = self.visualizer.as_mut().unwrap();
            for (rect, block) in rx.try_iter().take(10) {
                let (rect, texture_block) = self.image.update_block(rect, &block);
                visualizer.update_texture(rect, texture_block);
            }
            visualizer.render(target);
        }
    }

    pub fn stop_threads(&mut self) {
        for sender in &self.message_txs {
            sender.send(()).unwrap();
        }
        for handle in self.thread_handles.drain(..) {
            handle.join().unwrap();
        }
        // Drop channels only after join to make sure
        // that stop messages are properly received
        self.message_txs.clear();
        stats::stop_render();
    }

    pub fn save_image(&self, path: &Path) {
        self.image.save_image(path);
    }
}
<|endoftext|>