<commit_before>enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    fn next(&self) -> Self {
        match self {
            Direction::Up => Direction::Right,
            Direction::Down => Direction::Left,
            Direction::Left => Direction::Up,
            Direction::Right => Direction::Down,
        }
    }
}

struct SpiralMatrix {
    size: usize,
    matrix: Vec<Vec<usize>>,
}

impl SpiralMatrix {
    fn new(size: usize) -> Self {
        SpiralMatrix {
            size,
            matrix: (0..size).map(|_| (0..size).map(|_| 0).collect()).collect(),
        }
    }

    fn set(&mut self, x: usize, y: usize, value: usize) {
        self.matrix[y][x] = value;
    }

    fn is_empty(&self, x: usize, y: usize) -> bool {
        self.matrix[y][x] == 0
    }
}

impl Into<Vec<Vec<usize>>> for SpiralMatrix {
    fn into(self) -> Vec<Vec<usize>> {
        self.matrix
    }
}

struct MatrixPosition {
    x: usize,
    y: usize,
}

impl MatrixPosition {
    fn new() -> Self {
        MatrixPosition { x: 0, y: 0 }
    }

    fn step(&mut self, matrix: &SpiralMatrix, direction: &Direction) -> bool {
        match direction {
            Direction::Up => if self.y > 0 && matrix.is_empty(self.x, self.y - 1) {
                self.y -= 1;
                true
            } else {
                false
            },
            Direction::Down => if self.y < matrix.size - 1 && matrix.is_empty(self.x, self.y + 1) {
                self.y += 1;
                true
            } else {
                false
            },
            Direction::Left => if self.x > 0 && matrix.is_empty(self.x - 1, self.y) {
                self.x -= 1;
                true
            } else {
                false
            },
            Direction::Right => {
                if self.x < matrix.size - 1 && matrix.is_empty(self.x + 1, self.y) {
                    self.x += 1;
                    true
                } else {
                    false
                }
            }
        }
    }
}

pub fn spiral_matrix(size: usize) -> Vec<Vec<usize>> {
    let mut matrix = SpiralMatrix::new(size);

    let mut position = MatrixPosition::new();
    let mut direction = Direction::Right;

    for i in 1..(size.pow(2) + 1) {
        matrix.set(position.x, position.y, i);

        if !position.step(&matrix, &direction) {
            direction = direction.next();
            position.step(&matrix, &direction);
        }
    }

    matrix.into()
}
<commit_msg>Improve solution for Spiral Matrix<commit_after>enum Direction {
    Up,
    Down,
    Left,
    Right,
}

impl Direction {
    fn next(&self) -> Self {
        match self {
            Direction::Up => Direction::Right,
            Direction::Down => Direction::Left,
            Direction::Left => Direction::Up,
            Direction::Right => Direction::Down,
        }
    }
}

struct SpiralMatrix {
    size: usize,
    matrix: Vec<Vec<usize>>,
}

impl SpiralMatrix {
    fn new(size: usize) -> Self {
        SpiralMatrix {
            size,
            matrix: (0..size).map(|_| (0..size).map(|_| 0).collect()).collect(),
        }
    }

    fn set(&mut self, coords: (usize, usize), value: usize) {
        self.matrix[coords.1][coords.0] = value;
    }

    fn is_empty(&self, coords: (usize, usize)) -> bool {
        self.matrix[coords.1][coords.0] == 0
    }
}

impl Into<Vec<Vec<usize>>> for SpiralMatrix {
    fn into(self) -> Vec<Vec<usize>> {
        self.matrix
    }
}

struct MatrixPosition {
    coords: (usize, usize),
}

impl MatrixPosition {
    fn new() -> Self {
        MatrixPosition { coords: (0, 0) }
    }

    fn step(&self, matrix: &SpiralMatrix, direction: &Direction) -> Option<Self> {
        let coords = match direction {
            Direction::Up => if self.coords.1 > 0 {
                (self.coords.0, self.coords.1 - 1)
            } else {
                return None;
            },
            Direction::Down => if self.coords.1 < matrix.size - 1 {
                (self.coords.0, self.coords.1 + 1)
            } else {
                return None;
            },
            Direction::Left => if self.coords.0 > 0 {
                (self.coords.0 - 1, self.coords.1)
            } else {
                return None;
            },
            Direction::Right => if self.coords.0 < matrix.size - 1 {
                (self.coords.0 + 1, self.coords.1)
            } else {
                return None;
            },
        };

        if matrix.is_empty(coords) {
            Some(Self { coords })
        } else {
            None
        }
    }
}

pub fn spiral_matrix(size: usize) -> Vec<Vec<usize>> {
    let mut matrix = SpiralMatrix::new(size);

    let mut position = MatrixPosition::new();
    let mut direction = Direction::Right;

    for i in 1..(size.pow(2) + 1) {
        matrix.set(position.coords, i);

        position = if let Some(next_position) = position.step(&matrix, &direction) {
            next_position
        } else {
            direction = direction.next();
            position.step(&matrix, &direction).unwrap_or(position)
        }
    }

    matrix.into()
}
<|endoftext|>


<commit_before>extern crate nannou;

use nannou::prelude::*;

fn main() {
    nannou::view(view);
}

fn view(app: &App, frame: Frame) -> Frame {
    // Prepare to draw.
    let draw = app.draw();

    // Clear the background to pink.
    draw.background().color(LIGHT_PURPLE);

    // Draw a red ellipse with default size and position.
    draw.ellipse().color(DARK_BLUE);

    // Write to the window frame.
    draw.to_frame(app, &frame).unwrap();

    // Return the drawn frame.
    frame
}
<commit_msg>template_sketch.rs: Fix color name in comment<commit_after>extern crate nannou;

use nannou::prelude::*;

fn main() {
    nannou::view(view);
}

fn view(app: &App, frame: Frame) -> Frame {
    // Prepare to draw.
    let draw = app.draw();

    // Clear the background to purple.
    draw.background().color(LIGHT_PURPLE);

    // Draw a blue ellipse with default size and position.
    draw.ellipse().color(DARK_BLUE);

    // Write to the window frame.
    draw.to_frame(app, &frame).unwrap();

    // Return the drawn frame.
    frame
}
<|endoftext|>


<commit_before>//! Clippy wrappers around rustc's diagnostic functions.

use crate::reexport::*;
use rustc::lint::{LateContext, Lint, LintContext};
use rustc_errors::{Applicability, CodeSuggestion, Substitution, SubstitutionPart, SuggestionStyle};
use std::env;
use syntax::errors::DiagnosticBuilder;
use syntax::source_map::Span;

/// Wrapper around `DiagnosticBuilder` that adds a link to Clippy documentation for the emitted lint
pub struct DiagnosticWrapper<'a>(pub DiagnosticBuilder<'a>);

impl<'a> Drop for DiagnosticWrapper<'a> {
    fn drop(&mut self) {
        self.0.emit();
    }
}

impl<'a> DiagnosticWrapper<'a> {
    fn docs_link(&mut self, lint: &'static Lint) {
        if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
            self.0.help(&format!(
                "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
                &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
                    // extract just major + minor version and ignore patch versions
                    format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
                }),
                lint.name_lower().replacen("clippy::", "", 1)
            ));
        }
    }
}

/// Emit a basic lint message with a `msg` and a `span`.
///
/// This is the most primitive of our lint emission methods and can
/// be a good way to get a new lint started.
///
/// Usually it's nicer to provide more context for lint messages.
/// Be sure the output is understandable when you use this method.
///
/// # Example
///
/// ```ignore
/// error: usage of mem::forget on Drop type
///   --> $DIR/mem_forget.rs:17:5
///    |
/// 17 |     std::mem::forget(seven);
///    |     ^^^^^^^^^^^^^^^^^^^^^^^
/// ```
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
    DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).docs_link(lint);
}

/// Same as `span_lint` but with an extra `help` message.
///
/// Use this if you want to provide some general help but
/// can't provide a specific machine applicable suggestion.
///
/// The `help` message is not attached to any `Span`.
///
/// # Example
///
/// ```ignore
/// error: constant division of 0.0 with 0.0 will always result in NaN
///   --> $DIR/zero_div_zero.rs:6:25
///    |
/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
///    |                         ^^^^^^^^^^^^
///    |
///    = help: Consider using `std::f64::NAN` if you would like a constant representing NaN
/// ```
pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    span: Span,
    msg: &str,
    help: &str,
) {
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
    db.0.help(help);
    db.docs_link(lint);
}

/// Like `span_lint` but with a `note` section instead of a `help` message.
///
/// The `note` message is presented separately from the main lint message
/// and is attached to a specific span:
///
/// # Example
///
/// ```ignore
/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
///   --> $DIR/drop_forget_ref.rs:10:5
///    |
/// 10 |     forget(&SomeStruct);
///    |     ^^^^^^^^^^^^^^^^^^^
///    |
///    = note: `-D clippy::forget-ref` implied by `-D warnings`
/// note: argument has type &SomeStruct
///   --> $DIR/drop_forget_ref.rs:10:12
///    |
/// 10 |     forget(&SomeStruct);
///    |            ^^^^^^^^^^^
/// ```
pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    span: Span,
    msg: &str,
    note_span: Span,
    note: &str,
) {
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
    if note_span == span {
        db.0.note(note);
    } else {
        db.0.span_note(note_span, note);
    }
    db.docs_link(lint);
}

pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
    cx: &'a T,
    lint: &'static Lint,
    sp: Span,
    msg: &str,
    f: F,
) where
    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
{
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg));
    f(&mut db.0);
    db.docs_link(lint);
}

pub fn span_lint_node(cx: &LateContext<'_, '_>, lint: &'static Lint, node: NodeId, sp: Span, msg: &str) {
    DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg)).docs_link(lint);
}

pub fn span_lint_node_and_then(
    cx: &LateContext<'_, '_>,
    lint: &'static Lint,
    node: NodeId,
    sp: Span,
    msg: &str,
    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
) {
    let mut db = DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg));
    f(&mut db.0);
    db.docs_link(lint);
}

/// Add a span lint with a suggestion on how to fix it.
///
/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
/// 2)"`.
///
/// ```ignore
/// error: This `.fold` can be more succinctly expressed as `.any`
/// --> $DIR/methods.rs:390:13
///     |
/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
///     |
///     = note: `-D fold-any` implied by `-D warnings`
/// ```
pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    sp: Span,
    msg: &str,
    help: &str,
    sugg: String,
    applicability: Applicability,
) {
    span_lint_and_then(cx, lint, sp, msg, |db| {
        db.span_suggestion(sp, help, sugg, applicability);
    });
}

/// Create a suggestion made from several `span → replacement`.
///
/// Note: in the JSON format (used by `compiletest_rs`), the help message will
/// appear once per
/// replacement. In human-readable format though, it only appears once before
/// the whole suggestion.
pub fn multispan_sugg<I>(db: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I)
where
    I: IntoIterator<Item = (Span, String)>,
{
    let sugg = CodeSuggestion {
        substitutions: vec![Substitution {
            parts: sugg
                .into_iter()
                .map(|(span, snippet)| SubstitutionPart { snippet, span })
                .collect(),
        }],
        msg: help_msg,
        style: SuggestionStyle::ShowCode,
        applicability: Applicability::Unspecified,
    };
    db.suggestions.push(sugg);
}
<commit_msg>Make DiagnosticWrapper private<commit_after>//! Clippy wrappers around rustc's diagnostic functions.

use crate::reexport::*;
use rustc::lint::{LateContext, Lint, LintContext};
use rustc_errors::{Applicability, CodeSuggestion, Substitution, SubstitutionPart, SuggestionStyle};
use std::env;
use syntax::errors::DiagnosticBuilder;
use syntax::source_map::Span;

/// Wrapper around `DiagnosticBuilder` that adds a link to Clippy documentation for the emitted lint
struct DiagnosticWrapper<'a>(DiagnosticBuilder<'a>);

impl<'a> Drop for DiagnosticWrapper<'a> {
    fn drop(&mut self) {
        self.0.emit();
    }
}

impl<'a> DiagnosticWrapper<'a> {
    fn docs_link(&mut self, lint: &'static Lint) {
        if env::var("CLIPPY_DISABLE_DOCS_LINKS").is_err() {
            self.0.help(&format!(
                "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{}",
                &option_env!("RUST_RELEASE_NUM").map_or("master".to_string(), |n| {
                    // extract just major + minor version and ignore patch versions
                    format!("rust-{}", n.rsplitn(2, '.').nth(1).unwrap())
                }),
                lint.name_lower().replacen("clippy::", "", 1)
            ));
        }
    }
}

/// Emit a basic lint message with a `msg` and a `span`.
///
/// This is the most primitive of our lint emission methods and can
/// be a good way to get a new lint started.
///
/// Usually it's nicer to provide more context for lint messages.
/// Be sure the output is understandable when you use this method.
///
/// # Example
///
/// ```ignore
/// error: usage of mem::forget on Drop type
///   --> $DIR/mem_forget.rs:17:5
///    |
/// 17 |     std::mem::forget(seven);
///    |     ^^^^^^^^^^^^^^^^^^^^^^^
/// ```
pub fn span_lint<'a, T: LintContext<'a>>(cx: &T, lint: &'static Lint, sp: Span, msg: &str) {
    DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg)).docs_link(lint);
}

/// Same as `span_lint` but with an extra `help` message.
///
/// Use this if you want to provide some general help but
/// can't provide a specific machine applicable suggestion.
///
/// The `help` message is not attached to any `Span`.
///
/// # Example
///
/// ```ignore
/// error: constant division of 0.0 with 0.0 will always result in NaN
///   --> $DIR/zero_div_zero.rs:6:25
///    |
/// 6  |     let other_f64_nan = 0.0f64 / 0.0;
///    |                         ^^^^^^^^^^^^
///    |
///    = help: Consider using `std::f64::NAN` if you would like a constant representing NaN
/// ```
pub fn span_help_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    span: Span,
    msg: &str,
    help: &str,
) {
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
    db.0.help(help);
    db.docs_link(lint);
}

/// Like `span_lint` but with a `note` section instead of a `help` message.
///
/// The `note` message is presented separately from the main lint message
/// and is attached to a specific span:
///
/// # Example
///
/// ```ignore
/// error: calls to `std::mem::forget` with a reference instead of an owned value. Forgetting a reference does nothing.
///   --> $DIR/drop_forget_ref.rs:10:5
///    |
/// 10 |     forget(&SomeStruct);
///    |     ^^^^^^^^^^^^^^^^^^^
///    |
///    = note: `-D clippy::forget-ref` implied by `-D warnings`
/// note: argument has type &SomeStruct
///   --> $DIR/drop_forget_ref.rs:10:12
///    |
/// 10 |     forget(&SomeStruct);
///    |            ^^^^^^^^^^^
/// ```
pub fn span_note_and_lint<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    span: Span,
    msg: &str,
    note_span: Span,
    note: &str,
) {
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, span, msg));
    if note_span == span {
        db.0.note(note);
    } else {
        db.0.span_note(note_span, note);
    }
    db.docs_link(lint);
}

pub fn span_lint_and_then<'a, 'tcx: 'a, T: LintContext<'tcx>, F>(
    cx: &'a T,
    lint: &'static Lint,
    sp: Span,
    msg: &str,
    f: F,
) where
    F: for<'b> FnOnce(&mut DiagnosticBuilder<'b>),
{
    let mut db = DiagnosticWrapper(cx.struct_span_lint(lint, sp, msg));
    f(&mut db.0);
    db.docs_link(lint);
}

pub fn span_lint_node(cx: &LateContext<'_, '_>, lint: &'static Lint, node: NodeId, sp: Span, msg: &str) {
    DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg)).docs_link(lint);
}

pub fn span_lint_node_and_then(
    cx: &LateContext<'_, '_>,
    lint: &'static Lint,
    node: NodeId,
    sp: Span,
    msg: &str,
    f: impl FnOnce(&mut DiagnosticBuilder<'_>),
) {
    let mut db = DiagnosticWrapper(cx.tcx.struct_span_lint_node(lint, node, sp, msg));
    f(&mut db.0);
    db.docs_link(lint);
}

/// Add a span lint with a suggestion on how to fix it.
///
/// These suggestions can be parsed by rustfix to allow it to automatically fix your code.
/// In the example below, `help` is `"try"` and `sugg` is the suggested replacement `".any(|x| x >
/// 2)"`.
///
/// ```ignore
/// error: This `.fold` can be more succinctly expressed as `.any`
/// --> $DIR/methods.rs:390:13
///     |
/// 390 |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
///     |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `.any(|x| x > 2)`
///     |
///     = note: `-D fold-any` implied by `-D warnings`
/// ```
pub fn span_lint_and_sugg<'a, 'tcx: 'a, T: LintContext<'tcx>>(
    cx: &'a T,
    lint: &'static Lint,
    sp: Span,
    msg: &str,
    help: &str,
    sugg: String,
    applicability: Applicability,
) {
    span_lint_and_then(cx, lint, sp, msg, |db| {
        db.span_suggestion(sp, help, sugg, applicability);
    });
}

/// Create a suggestion made from several `span → replacement`.
///
/// Note: in the JSON format (used by `compiletest_rs`), the help message will
/// appear once per
/// replacement. In human-readable format though, it only appears once before
/// the whole suggestion.
pub fn multispan_sugg<I>(db: &mut DiagnosticBuilder<'_>, help_msg: String, sugg: I)
where
    I: IntoIterator<Item = (Span, String)>,
{
    let sugg = CodeSuggestion {
        substitutions: vec![Substitution {
            parts: sugg
                .into_iter()
                .map(|(span, snippet)| SubstitutionPart { snippet, span })
                .collect(),
        }],
        msg: help_msg,
        style: SuggestionStyle::ShowCode,
        applicability: Applicability::Unspecified,
    };
    db.suggestions.push(sugg);
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::io::{self, Write};
use std::{ops, str};

use rustc::hir::def_id::DefId;
use rustc::mir::{self, BasicBlock, Body, Location};
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_index::vec::Idx;

use crate::util::graphviz_safe_def_name;
use super::{Analysis, Results, ResultsRefCursor};

pub struct Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    body: &'a Body<'tcx>,
    def_id: DefId,

    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
}

impl Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub fn new(
        body: &'a Body<'tcx>,
        def_id: DefId,
        results: &'a Results<'tcx, A>,
    ) -> Self {
        let block_formatter = BlockFormatter {
            bg: Background::Light,
            prev_state: BitSet::new_empty(results.analysis.bits_per_block(body)),
            results: ResultsRefCursor::new(body, results),
        };

        Formatter {
            body,
            def_id,
            block_formatter: RefCell::new(block_formatter),
        }
    }
}

/// A pair of a basic block and an index into that basic blocks `successors`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct CfgEdge {
    source: BasicBlock,
    index: usize,
}

fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
    body[bb]
        .terminator()
        .successors()
        .enumerate()
        .map(|(index, _)| CfgEdge { source: bb, index })
        .collect()
}

impl dot::Labeller<'_> for Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    type Node = BasicBlock;
    type Edge = CfgEdge;

    fn graph_id(&self) -> dot::Id<'_> {
        let name = graphviz_safe_def_name(self.def_id);
        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
    }

    fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
        dot::Id::new(format!("bb_{}", n.index())).unwrap()
    }

    fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
        let mut label = Vec::new();
        self.block_formatter
            .borrow_mut()
            .write_node_label(&mut label, self.body, *block)
            .unwrap();
        dot::LabelText::html(String::from_utf8(label).unwrap())
    }

    fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
        Some(dot::LabelText::label("none"))
    }

    fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
        let label = &self.body
            [e.source]
            .terminator()
            .kind
            .fmt_successor_labels()
            [e.index];
        dot::LabelText::label(label.clone())
    }
}

impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    type Node = BasicBlock;
    type Edge = CfgEdge;

    fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
        self.body
            .basic_blocks()
            .indices()
            .collect::<Vec<_>>()
            .into()
    }

    fn edges(&self) -> dot::Edges<'_, Self::Edge> {
        self.body
            .basic_blocks()
            .indices()
            .flat_map(|bb| outgoing_edges(self.body, bb))
            .collect::<Vec<_>>()
            .into()
    }

    fn source(&self, edge: &Self::Edge) -> Self::Node {
        edge.source
    }

    fn target(&self, edge: &Self::Edge) -> Self::Node {
        self.body
            [edge.source]
            .terminator()
            .successors()
            .nth(edge.index)
            .copied()
            .unwrap()
    }
}

struct BlockFormatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    prev_state: BitSet,
    results: ResultsRefCursor<'a, 'a, 'tcx, A>,
    bg: Background,
}

impl BlockFormatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    fn toggle_background(&mut self) -> Background {
        let bg = self.bg;
        self.bg = !bg;
        bg
    }

    fn write_node_label(
        &mut self,
        w: &mut impl io::Write,
        body: &'a Body<'tcx>,
        block: BasicBlock,
    ) -> io::Result<()> {
        //   Sample output:
        //   +-+--------------------------------------------------+
        // A |                         bb4                        |
        //   +-+----------------------------------+---------------+
        // B |               MIR                  |     STATE     |
        //   +-+----------------------------------+---------------+
        // C | | (on entry)                       | {_0,_2,_3}    |
        //   +-+----------------------------------+---------------+
        // D |0|  0: StorageLive(_7)              |               |
        //   +-+----------------------------------+---------------+
        //   |1|  1: StorageLive(_8)              |               |
        //   +-+----------------------------------+---------------+
        //   |2|  2: _8 = &mut _1                 | +_8           |
        //   +-+----------------------------------+---------------+
        // E |T| _7 = const Foo::twiddle(move _8) | -_8           |
        //   +-+----------------------------------+---------------+
        // F | | (on unwind)                      | {_0,_2,_3,_7} |
        //   +-+----------------------------------+---------------+
        //   | | (on successful return)           | +_7           |
        //   +-+----------------------------------+---------------+

        write!(
            w,
            r#"
"#,
        )?;

        // A: Block info
        write!(
            w,
            r#"

                 bb{block_id}

"#,
            num_headers = 3,
            block_id = block.index(),
        )?;

        // B: Column headings
        write!(
            w,
            r#"

                 MIR
                 STATE

"#,
            fmt = r##"bgcolor="#a0a0a0" sides="tl""##,
        )?;

        // C: Entry state
        self.results.seek_to_block_start(block);
        self.write_row_with_curr_state(w, "", "(on entry)")?;
        self.prev_state.overwrite(self.results.get());

        // D: Statement transfer functions
        for (i, statement) in body[block].statements.iter().enumerate() {
            let location = Location { block, statement_index: i };

            let mir_col = format!("{:?}", statement);
            let i_col = i.to_string();

            self.results.seek_after(location);
            self.write_row_with_curr_diff(w, &i_col, &mir_col)?;
            self.prev_state.overwrite(self.results.get());
        }

        // E: Terminator transfer function
        let terminator = body[block].terminator();
        let location = body.terminator_loc(block);

        let mut mir_col = String::new();
        terminator.kind.fmt_head(&mut mir_col).unwrap();

        self.results.seek_after(location);
        self.write_row_with_curr_diff(w, "T", &mir_col)?;
        self.prev_state.overwrite(self.results.get());

        // F: Exit state
        if let mir::TerminatorKind::Call { destination: Some(_), ..  } = &terminator.kind {
            self.write_row_with_curr_state(w, "", "(on unwind)")?;

            self.results.seek_after_assume_call_returns(location);
            self.write_row_with_curr_diff(w, "", "(on successful return)")?;
        } else {
            self.write_row_with_curr_state(w, "", "(on exit)")?;
        }

        write!(w, "
")
    }

    fn write_row_with_curr_state(
        &mut self,
        w: &mut impl io::Write,
        i: &str,
        mir: &str,
    ) -> io::Result<()> {
        let bg = self.toggle_background();

        let mut out = Vec::new();
        write!(&mut out, "{{")?;
        pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
        write!(&mut out, "}}")?;

        write!(
            w,
            r#"

                 {i}
                 {mir}
                 {state}

"#,
            fmt = &["sides=\"tl\"", bg.attr()].join(" "),
            i = i,
            mir = dot::escape_html(mir),
            state = dot::escape_html(str::from_utf8(&out).unwrap()),
        )
    }

    fn write_row_with_curr_diff(
        &mut self,
        w: &mut impl io::Write,
        i: &str,
        mir: &str,
    ) -> io::Result<()> {
        let bg = self.toggle_background();
        let analysis = self.results.analysis();

        let diff = BitSetDiff::compute(&self.prev_state, self.results.get());

        let mut set = Vec::new();
        pretty_print_state_elems(&mut set, analysis, diff.set.iter())?;

        let mut clear = Vec::new();
        pretty_print_state_elems(&mut clear, analysis, diff.clear.iter())?;

        write!(
            w,
            r#"

                 {i}
                 {mir}
                 "#,
            i = i,
            fmt = &["sides=\"tl\"", bg.attr()].join(" "),
            mir = dot::escape_html(mir),
        )?;

        if !set.is_empty() {
            write!(
                w,
                r#"+{}"#,
                dot::escape_html(str::from_utf8(&set).unwrap()),
            )?;
        }

        if !set.is_empty() && !clear.is_empty() {
            write!(w, "  ")?;
        }

        if !clear.is_empty() {
            write!(
                w,
                r#"-{}"#,
                dot::escape_html(str::from_utf8(&clear).unwrap()),
            )?;
        }

        write!(w, "
")
    }
}

/// The operations required to transform one `BitSet` into another.
struct BitSetDiff<T: Idx> {
    set: HybridBitSet<T>,
    clear: HybridBitSet<T>,
}

impl<T: Idx> BitSetDiff<T> {
    fn compute(from: &BitSet<T>, to: &BitSet<T>) -> Self {
        assert_eq!(from.domain_size(), to.domain_size());
        let len = from.domain_size();

        let mut set = HybridBitSet::new_empty(len);
        let mut clear = HybridBitSet::new_empty(len);

        // FIXME: This could be made faster if `BitSet::xor` were implemented.
        for i in (0..len).map(|i| T::new(i)) {
            match (from.contains(i), to.contains(i)) {
                (false, true) => set.insert(i),
                (true, false) => clear.insert(i),
                _ => continue,
            };
        }

        BitSetDiff {
            set,
            clear,
        }
    }
}

/// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
/// list.
fn pretty_print_state_elems(
    w: &mut impl io::Write,
    analysis: &A,
    elems: impl Iterator<Item = A::Idx>,
) -> io::Result<()>
where
    A: Analysis<'tcx>,
{
    let mut first = true;
    for idx in elems {
        if first {
            first = false;
        } else {
            write!(w, ",")?;
        }

        analysis.pretty_print_idx(w, idx)?;
    }

    Ok(())
}

/// The background color used for zebra-striping the table.
#[derive(Clone, Copy)]
enum Background {
    Light,
    Dark,
}

impl Background {
    fn attr(self) -> &'static str {
        match self {
            Self::Dark => "bgcolor=\"#f0f0f0\"",
            Self::Light => "",
        }
    }
}

impl ops::Not for Background {
    type Output = Self;

    fn not(self) -> Self {
        match self {
            Self::Light => Self::Dark,
            Self::Dark => Self::Light,
        }
    }
}
<commit_msg>Reset row background for each block<commit_after>use std::cell::RefCell;
use std::io::{self, Write};
use std::{ops, str};

use rustc::hir::def_id::DefId;
use rustc::mir::{self, BasicBlock, Body, Location};
use rustc_index::bit_set::{BitSet, HybridBitSet};
use rustc_index::vec::Idx;

use crate::util::graphviz_safe_def_name;
use super::{Analysis, Results, ResultsRefCursor};

pub struct Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    body: &'a Body<'tcx>,
    def_id: DefId,

    // This must be behind a `RefCell` because `dot::Labeller` takes `&self`.
    block_formatter: RefCell<BlockFormatter<'a, 'tcx, A>>,
}

impl Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    pub fn new(
        body: &'a Body<'tcx>,
        def_id: DefId,
        results: &'a Results<'tcx, A>,
    ) -> Self {
        let block_formatter = BlockFormatter {
            bg: Background::Light,
            prev_state: BitSet::new_empty(results.analysis.bits_per_block(body)),
            results: ResultsRefCursor::new(body, results),
        };

        Formatter {
            body,
            def_id,
            block_formatter: RefCell::new(block_formatter),
        }
    }
}

/// A pair of a basic block and an index into that basic blocks `successors`.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct CfgEdge {
    source: BasicBlock,
    index: usize,
}

fn outgoing_edges(body: &Body<'_>, bb: BasicBlock) -> Vec<CfgEdge> {
    body[bb]
        .terminator()
        .successors()
        .enumerate()
        .map(|(index, _)| CfgEdge { source: bb, index })
        .collect()
}

impl dot::Labeller<'_> for Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    type Node = BasicBlock;
    type Edge = CfgEdge;

    fn graph_id(&self) -> dot::Id<'_> {
        let name = graphviz_safe_def_name(self.def_id);
        dot::Id::new(format!("graph_for_def_id_{}", name)).unwrap()
    }

    fn node_id(&self, n: &Self::Node) -> dot::Id<'_> {
        dot::Id::new(format!("bb_{}", n.index())).unwrap()
    }

    fn node_label(&self, block: &Self::Node) -> dot::LabelText<'_> {
        let mut label = Vec::new();
        self.block_formatter
            .borrow_mut()
            .write_node_label(&mut label, self.body, *block)
            .unwrap();
        dot::LabelText::html(String::from_utf8(label).unwrap())
    }

    fn node_shape(&self, _n: &Self::Node) -> Option<dot::LabelText<'_>> {
        Some(dot::LabelText::label("none"))
    }

    fn edge_label(&self, e: &Self::Edge) -> dot::LabelText<'_> {
        let label = &self.body
            [e.source]
            .terminator()
            .kind
            .fmt_successor_labels()
            [e.index];
        dot::LabelText::label(label.clone())
    }
}

impl dot::GraphWalk<'a> for Formatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    type Node = BasicBlock;
    type Edge = CfgEdge;

    fn nodes(&self) -> dot::Nodes<'_, Self::Node> {
        self.body
            .basic_blocks()
            .indices()
            .collect::<Vec<_>>()
            .into()
    }

    fn edges(&self) -> dot::Edges<'_, Self::Edge> {
        self.body
            .basic_blocks()
            .indices()
            .flat_map(|bb| outgoing_edges(self.body, bb))
            .collect::<Vec<_>>()
            .into()
    }

    fn source(&self, edge: &Self::Edge) -> Self::Node {
        edge.source
    }

    fn target(&self, edge: &Self::Edge) -> Self::Node {
        self.body
            [edge.source]
            .terminator()
            .successors()
            .nth(edge.index)
            .copied()
            .unwrap()
    }
}

struct BlockFormatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    prev_state: BitSet,
    results: ResultsRefCursor<'a, 'a, 'tcx, A>,
    bg: Background,
}

impl BlockFormatter<'a, 'tcx, A>
where
    A: Analysis<'tcx>,
{
    fn toggle_background(&mut self) -> Background {
        let bg = self.bg;
        self.bg = !bg;
        bg
    }

    fn write_node_label(
        &mut self,
        w: &mut impl io::Write,
        body: &'a Body<'tcx>,
        block: BasicBlock,
    ) -> io::Result<()> {
        //   Sample output:
        //   +-+--------------------------------------------------+
        // A |                         bb4                        |
        //   +-+----------------------------------+---------------+
        // B |               MIR                  |     STATE     |
        //   +-+----------------------------------+---------------+
        // C | | (on entry)                       | {_0,_2,_3}    |
        //   +-+----------------------------------+---------------+
        // D |0|  0: StorageLive(_7)              |               |
        //   +-+----------------------------------+---------------+
        //   |1|  1: StorageLive(_8)              |               |
        //   +-+----------------------------------+---------------+
        //   |2|  2: _8 = &mut _1                 | +_8           |
        //   +-+----------------------------------+---------------+
        // E |T| _7 = const Foo::twiddle(move _8) | -_8           |
        //   +-+----------------------------------+---------------+
        // F | | (on unwind)                      | {_0,_2,_3,_7} |
        //   +-+----------------------------------+---------------+
        //   | | (on successful return)           | +_7           |
        //   +-+----------------------------------+---------------+

        write!(
            w,
            r#"
"#,
        )?;

        // A: Block info
        write!(
            w,
            r#"

                 bb{block_id}

"#,
            num_headers = 3,
            block_id = block.index(),
        )?;

        // B: Column headings
        write!(
            w,
            r#"

                 MIR
                 STATE

"#,
            fmt = r##"bgcolor="#a0a0a0" sides="tl""##,
        )?;

        // C: Entry state
        self.bg = Background::Light;
        self.results.seek_to_block_start(block);
        self.write_row_with_curr_state(w, "", "(on entry)")?;
        self.prev_state.overwrite(self.results.get());

        // D: Statement transfer functions
        for (i, statement) in body[block].statements.iter().enumerate() {
            let location = Location { block, statement_index: i };

            let mir_col = format!("{:?}", statement);
            let i_col = i.to_string();

            self.results.seek_after(location);
            self.write_row_with_curr_diff(w, &i_col, &mir_col)?;
            self.prev_state.overwrite(self.results.get());
        }

        // E: Terminator transfer function
        let terminator = body[block].terminator();
        let location = body.terminator_loc(block);

        let mut mir_col = String::new();
        terminator.kind.fmt_head(&mut mir_col).unwrap();

        self.results.seek_after(location);
        self.write_row_with_curr_diff(w, "T", &mir_col)?;
        self.prev_state.overwrite(self.results.get());

        // F: Exit state
        if let mir::TerminatorKind::Call { destination: Some(_), ..  } = &terminator.kind {
            self.write_row_with_curr_state(w, "", "(on unwind)")?;

            self.results.seek_after_assume_call_returns(location);
            self.write_row_with_curr_diff(w, "", "(on successful return)")?;
        } else {
            self.write_row_with_curr_state(w, "", "(on exit)")?;
        }

        write!(w, "
")
    }

    fn write_row_with_curr_state(
        &mut self,
        w: &mut impl io::Write,
        i: &str,
        mir: &str,
    ) -> io::Result<()> {
        let bg = self.toggle_background();

        let mut out = Vec::new();
        write!(&mut out, "{{")?;
        pretty_print_state_elems(&mut out, self.results.analysis(), self.results.get().iter())?;
        write!(&mut out, "}}")?;

        write!(
            w,
            r#"

                 {i}
                 {mir}
                 {state}

"#,
            fmt = &["sides=\"tl\"", bg.attr()].join(" "),
            i = i,
            mir = dot::escape_html(mir),
            state = dot::escape_html(str::from_utf8(&out).unwrap()),
        )
    }

    fn write_row_with_curr_diff(
        &mut self,
        w: &mut impl io::Write,
        i: &str,
        mir: &str,
    ) -> io::Result<()> {
        let bg = self.toggle_background();
        let analysis = self.results.analysis();

        let diff = BitSetDiff::compute(&self.prev_state, self.results.get());

        let mut set = Vec::new();
        pretty_print_state_elems(&mut set, analysis, diff.set.iter())?;

        let mut clear = Vec::new();
        pretty_print_state_elems(&mut clear, analysis, diff.clear.iter())?;

        write!(
            w,
            r#"

                 {i}
                 {mir}
                 "#,
            i = i,
            fmt = &["sides=\"tl\"", bg.attr()].join(" "),
            mir = dot::escape_html(mir),
        )?;

        if !set.is_empty() {
            write!(
                w,
                r#"+{}"#,
                dot::escape_html(str::from_utf8(&set).unwrap()),
            )?;
        }

        if !set.is_empty() && !clear.is_empty() {
            write!(w, "  ")?;
        }

        if !clear.is_empty() {
            write!(
                w,
                r#"-{}"#,
                dot::escape_html(str::from_utf8(&clear).unwrap()),
            )?;
        }

        write!(w, "
")
    }
}

/// The operations required to transform one `BitSet` into another.
struct BitSetDiff<T: Idx> {
    set: HybridBitSet<T>,
    clear: HybridBitSet<T>,
}

impl<T: Idx> BitSetDiff<T> {
    fn compute(from: &BitSet<T>, to: &BitSet<T>) -> Self {
        assert_eq!(from.domain_size(), to.domain_size());
        let len = from.domain_size();

        let mut set = HybridBitSet::new_empty(len);
        let mut clear = HybridBitSet::new_empty(len);

        // FIXME: This could be made faster if `BitSet::xor` were implemented.
        for i in (0..len).map(|i| T::new(i)) {
            match (from.contains(i), to.contains(i)) {
                (false, true) => set.insert(i),
                (true, false) => clear.insert(i),
                _ => continue,
            };
        }

        BitSetDiff {
            set,
            clear,
        }
    }
}

/// Formats each `elem` using the pretty printer provided by `analysis` into a comma-separated
/// list.
fn pretty_print_state_elems(
    w: &mut impl io::Write,
    analysis: &A,
    elems: impl Iterator<Item = A::Idx>,
) -> io::Result<()>
where
    A: Analysis<'tcx>,
{
    let mut first = true;
    for idx in elems {
        if first {
            first = false;
        } else {
            write!(w, ",")?;
        }

        analysis.pretty_print_idx(w, idx)?;
    }

    Ok(())
}

/// The background color used for zebra-striping the table.
#[derive(Clone, Copy)]
enum Background {
    Light,
    Dark,
}

impl Background {
    fn attr(self) -> &'static str {
        match self {
            Self::Dark => "bgcolor=\"#f0f0f0\"",
            Self::Light => "",
        }
    }
}

impl ops::Not for Background {
    type Output = Self;

    fn not(self) -> Self {
        match self {
            Self::Light => Self::Dark,
            Self::Dark => Self::Light,
        }
    }
}
<|endoftext|>


<commit_before>#![allow(
    clippy::diverging_sub_expression,
    clippy::let_and_return,
    clippy::let_underscore_drop,
    clippy::never_loop,
    clippy::unit_arg
)]

use anyhow::{anyhow, ensure, Chain, Error, Result};
use std::fmt::Debug;
use std::iter;
use std::marker::PhantomData;
use std::ops::Add;

struct S;

impl<T> Add<T> for S {
    type Output = bool;
    fn add(self, rhs: T) -> Self::Output {
        let _ = rhs;
        false
    }
}

trait Trait: Sized {
    fn t(self, i: i32) -> i32 {
        i
    }
}

impl<T> Trait for T {}

#[track_caller]
fn assert_err<T: Debug>(result: impl FnOnce() -> Result<T>, expected: &'static str) {
    let actual = result().unwrap_err().to_string();

    let mut accepted_alternatives = expected.split('\n');
    let expected = accepted_alternatives.next_back().unwrap();
    if accepted_alternatives.any(|alternative| actual == alternative) {
        return;
    }

    assert_eq!(actual, expected);
}

#[test]
fn test_low_precedence_control_flow() {
    #[allow(unreachable_code)]
    let test = || {
        let val = loop {
            // Break has lower precedence than the comparison operators so the
            // expression here is `S + (break (1 == 1))`. It would be bad if the
            // ensure macro partitioned this input into `(S + break 1) == (1)`
            // because that means a different thing than what was written.
            ensure!(S + break 1 == 1);
        };
        Ok(val)
    };

    assert!(test().unwrap());
}

#[test]
fn test_low_precedence_binary_operator() {
    // Must not partition as `false == (true && false)`.
    let test = || Ok(ensure!(false == true && false));
    assert_err(test, "Condition failed: `false == true && false`");

    // But outside the root level, it is fine.
    let test = || Ok(ensure!(while false == true && false {} < ()));
    assert_err(
        test,
        "Condition failed: `while false == true && false { } < ()` (() vs ())",
    );
}

#[test]
fn test_closure() {
    // Must not partition as `(S + move) || (1 == 1)` by treating move as an
    // identifier, nor as `(S + move || 1) == (1)` by misinterpreting the
    // closure precedence.
    let test = || Ok(ensure!(S + move || 1 == 1));
    assert_err(test, "Condition failed: `S + (move || 1 == 1)`");

    let test = || Ok(ensure!(S + || 1 == 1));
    assert_err(test, "Condition failed: `S + (|| 1 == 1)`");

    // Must not partition as `S + ((move | ()) | 1) == 1` by treating those
    // pipes as bitwise-or.
    let test = || Ok(ensure!(S + move |()| 1 == 1));
    assert_err(test, "Condition failed: `S + (move |()| 1 == 1)`");

    let test = || Ok(ensure!(S + |()| 1 == 1));
    assert_err(test, "Condition failed: `S + (|()| 1 == 1)`");
}

#[test]
fn test_unary() {
    let mut x = &1;
    let test = || Ok(ensure!(*x == 2));
    assert_err(test, "Condition failed: `*x == 2` (1 vs 2)");

    let test = || Ok(ensure!(!x == 1));
    assert_err(test, "Condition failed: `!x == 1` (-2 vs 1)");

    let test = || Ok(ensure!(-x == 1));
    assert_err(test, "Condition failed: `-x == 1` (-1 vs 1)");

    let test = || Ok(ensure!(&x == &&2));
    assert_err(test, "Condition failed: `&x == &&2` (1 vs 2)");

    let test = || Ok(ensure!(&mut x == *&&mut &2));
    assert_err(test, "Condition failed: `&mut x == *&&mut &2` (1 vs 2)");
}

#[test]
fn test_if() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {}.t(1) == 2));
    assert_err(test, "Condition failed: `if false { }.t(1) == 2` (1 vs 2)");

    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {} else {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if false { } else { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {} else if false {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if false { } else if false { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let 1 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 = 2 { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let 1 | 2 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 | 2 = 2 { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let | 1 | 2 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 | 2 = 2 { }.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_loop() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(1 + loop { break 1 } == 1));
    assert_err(
        test,
        // 1.54 puts a double space after loop
        "Condition failed: `1 + loop  { break 1  } == 1` (2 vs 1)\n\
         Condition failed: `1 + loop { break 1  } == 1` (2 vs 1)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(1 + 'a: loop { break 'a 1 } == 1));
    assert_err(
        test,
        // 1.54 puts a double space after loop
        "Condition failed: `1 + 'a: loop  { break 'a 1  } == 1` (2 vs 1)\n\
         Condition failed: `1 + 'a: loop { break 'a 1  } == 1` (2 vs 1)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(while false {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `while false { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(while let None = Some(1) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `while let None = Some(1) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for _x in iter::once(0) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for _x in iter::once(0) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for | _x in iter::once(0) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for _x in iter::once(0) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for true | false in iter::empty() {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for true | false in iter::empty() { }.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_match() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(match 1 == 1 { true => 1, false => 0 } == 2));
    assert_err(
        test,
        "Condition failed: `match 1 == 1 { true => 1, false => 0, } == 2` (1 vs 2)",
    );
}

#[test]
fn test_atom() {
    let test = || Ok(ensure!([false, false].len() > 3));
    assert_err(
        test,
        "Condition failed: `[false, false].len() > 3` (2 vs 3)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!({ let x = 1; x } >= 3));
    assert_err(test, "Condition failed: `{ let x = 1; x } >= 3` (1 vs 3)");

    let test = || Ok(ensure!(S + async { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + async  { 1 } == true` (false vs true)",
    );

    let test = || Ok(ensure!(S + async move { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + async move  { 1 } == true` (false vs true)",
    );

    let test = || Ok(ensure!(S + unsafe { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + unsafe { 1 } == true` (false vs true)",
    );
}

#[test]
fn test_path() {
    let test = || Ok(ensure!(crate::S.t(1) == 2));
    assert_err(test, "Condition failed: `crate::S.t(1) == 2` (1 vs 2)");

    let test = || Ok(ensure!(::anyhow::Error::root_cause.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `::anyhow::Error::root_cause.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Error::msg::<&str>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(Error::msg::<&str,>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Error::msg::<<str as ToOwned>::Owned>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<<str as ToOwned>::Owned>.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Chain::<'static>::new.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(Chain::<'static,>::new.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_macro() {
    let test = || Ok(ensure!(anyhow!("...").to_string().len() <= 1));
    assert_err(
        test,
        "Condition failed: `anyhow!(\"...\").to_string().len() <= 1` (3 vs 1)",
    );

    let test = || Ok(ensure!(vec![1].len() < 1));
    assert_err(test, "Condition failed: `vec![1].len() < 1` (1 vs 1)");

    let test = || Ok(ensure!(stringify! {} != ""));
    assert_err(
        test,
        "Condition failed: `stringify! { } != \"\"` (\"\" vs \"\")",
    );
}

#[test]
fn test_trailer() {
    let test = || Ok(ensure!((|| 1)() == 2));
    assert_err(test, "Condition failed: `(|| 1)() == 2` (1 vs 2)");

    let test = || Ok(ensure!(b"hmm"[1] == b'c'));
    assert_err(test, "Condition failed: `b\"hmm\"[1] == b'c'` (109 vs 99)");

    let test = || Ok(ensure!(PhantomData::<u8> {} != PhantomData));
    assert_err(
        test,
        "Condition failed: `PhantomData::<u8>{} != PhantomData` (PhantomData vs PhantomData)",
    );

    let result = Ok::<_, Error>(1);
    let test = || Ok(ensure!(result? == 2));
    assert_err(test, "Condition failed: `result? == 2` (1 vs 2)");

    let test = || Ok(ensure!((2, 3).1 == 2));
    assert_err(test, "Condition failed: `(2, 3).1 == 2` (3 vs 2)");

    let err = anyhow!("");
    let test = || Ok(ensure!(err.is::<&str>() == false));
    assert_err(
        test,
        "Condition failed: `err.is::<&str>() == false` (true vs false)",
    );

    let test = || Ok(ensure!(err.is::<<str as ToOwned>::Owned>() == true));
    assert_err(
        test,
        "Condition failed: `err.is::<<str as ToOwned>::Owned>() == true` (false vs true)",
    );
}
<commit_msg>Add test of recursion limit<commit_after>#![allow(
    clippy::diverging_sub_expression,
    clippy::let_and_return,
    clippy::let_underscore_drop,
    clippy::never_loop,
    clippy::unit_arg
)]

use anyhow::{anyhow, ensure, Chain, Error, Result};
use std::fmt::Debug;
use std::iter;
use std::marker::PhantomData;
use std::ops::Add;

struct S;

impl<T> Add<T> for S {
    type Output = bool;
    fn add(self, rhs: T) -> Self::Output {
        let _ = rhs;
        false
    }
}

trait Trait: Sized {
    fn t(self, i: i32) -> i32 {
        i
    }
}

impl<T> Trait for T {}

#[track_caller]
fn assert_err<T: Debug>(result: impl FnOnce() -> Result<T>, expected: &'static str) {
    let actual = result().unwrap_err().to_string();

    let mut accepted_alternatives = expected.split('\n');
    let expected = accepted_alternatives.next_back().unwrap();
    if accepted_alternatives.any(|alternative| actual == alternative) {
        return;
    }

    assert_eq!(actual, expected);
}

#[test]
fn test_recursion() {
    // Must not blow the default #[recursion_limit], which is 128.
    #[rustfmt::skip]
    let test = || Ok(ensure!(
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false |
        false | false | false | false | false | false | false | false | false
    ));

    test().unwrap_err();
}

#[test]
fn test_low_precedence_control_flow() {
    #[allow(unreachable_code)]
    let test = || {
        let val = loop {
            // Break has lower precedence than the comparison operators so the
            // expression here is `S + (break (1 == 1))`. It would be bad if the
            // ensure macro partitioned this input into `(S + break 1) == (1)`
            // because that means a different thing than what was written.
            ensure!(S + break 1 == 1);
        };
        Ok(val)
    };

    assert!(test().unwrap());
}

#[test]
fn test_low_precedence_binary_operator() {
    // Must not partition as `false == (true && false)`.
    let test = || Ok(ensure!(false == true && false));
    assert_err(test, "Condition failed: `false == true && false`");

    // But outside the root level, it is fine.
    let test = || Ok(ensure!(while false == true && false {} < ()));
    assert_err(
        test,
        "Condition failed: `while false == true && false { } < ()` (() vs ())",
    );
}

#[test]
fn test_closure() {
    // Must not partition as `(S + move) || (1 == 1)` by treating move as an
    // identifier, nor as `(S + move || 1) == (1)` by misinterpreting the
    // closure precedence.
    let test = || Ok(ensure!(S + move || 1 == 1));
    assert_err(test, "Condition failed: `S + (move || 1 == 1)`");

    let test = || Ok(ensure!(S + || 1 == 1));
    assert_err(test, "Condition failed: `S + (|| 1 == 1)`");

    // Must not partition as `S + ((move | ()) | 1) == 1` by treating those
    // pipes as bitwise-or.
    let test = || Ok(ensure!(S + move |()| 1 == 1));
    assert_err(test, "Condition failed: `S + (move |()| 1 == 1)`");

    let test = || Ok(ensure!(S + |()| 1 == 1));
    assert_err(test, "Condition failed: `S + (|()| 1 == 1)`");
}

#[test]
fn test_unary() {
    let mut x = &1;
    let test = || Ok(ensure!(*x == 2));
    assert_err(test, "Condition failed: `*x == 2` (1 vs 2)");

    let test = || Ok(ensure!(!x == 1));
    assert_err(test, "Condition failed: `!x == 1` (-2 vs 1)");

    let test = || Ok(ensure!(-x == 1));
    assert_err(test, "Condition failed: `-x == 1` (-1 vs 1)");

    let test = || Ok(ensure!(&x == &&2));
    assert_err(test, "Condition failed: `&x == &&2` (1 vs 2)");

    let test = || Ok(ensure!(&mut x == *&&mut &2));
    assert_err(test, "Condition failed: `&mut x == *&&mut &2` (1 vs 2)");
}

#[test]
fn test_if() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {}.t(1) == 2));
    assert_err(test, "Condition failed: `if false { }.t(1) == 2` (1 vs 2)");

    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {} else {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if false { } else { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if false {} else if false {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if false { } else if false { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let 1 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 = 2 { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let 1 | 2 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 | 2 = 2 { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(if let | 1 | 2 = 2 {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `if let 1 | 2 = 2 { }.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_loop() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(1 + loop { break 1 } == 1));
    assert_err(
        test,
        // 1.54 puts a double space after loop
        "Condition failed: `1 + loop  { break 1  } == 1` (2 vs 1)\n\
         Condition failed: `1 + loop { break 1  } == 1` (2 vs 1)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(1 + 'a: loop { break 'a 1 } == 1));
    assert_err(
        test,
        // 1.54 puts a double space after loop
        "Condition failed: `1 + 'a: loop  { break 'a 1  } == 1` (2 vs 1)\n\
         Condition failed: `1 + 'a: loop { break 'a 1  } == 1` (2 vs 1)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(while false {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `while false { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(while let None = Some(1) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `while let None = Some(1) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for _x in iter::once(0) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for _x in iter::once(0) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for | _x in iter::once(0) {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for _x in iter::once(0) { }.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(for true | false in iter::empty() {}.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `for true | false in iter::empty() { }.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_match() {
    #[rustfmt::skip]
    let test = || Ok(ensure!(match 1 == 1 { true => 1, false => 0 } == 2));
    assert_err(
        test,
        "Condition failed: `match 1 == 1 { true => 1, false => 0, } == 2` (1 vs 2)",
    );
}

#[test]
fn test_atom() {
    let test = || Ok(ensure!([false, false].len() > 3));
    assert_err(
        test,
        "Condition failed: `[false, false].len() > 3` (2 vs 3)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!({ let x = 1; x } >= 3));
    assert_err(test, "Condition failed: `{ let x = 1; x } >= 3` (1 vs 3)");

    let test = || Ok(ensure!(S + async { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + async  { 1 } == true` (false vs true)",
    );

    let test = || Ok(ensure!(S + async move { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + async move  { 1 } == true` (false vs true)",
    );

    let test = || Ok(ensure!(S + unsafe { 1 } == true));
    assert_err(
        test,
        "Condition failed: `S + unsafe { 1 } == true` (false vs true)",
    );
}

#[test]
fn test_path() {
    let test = || Ok(ensure!(crate::S.t(1) == 2));
    assert_err(test, "Condition failed: `crate::S.t(1) == 2` (1 vs 2)");

    let test = || Ok(ensure!(::anyhow::Error::root_cause.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `::anyhow::Error::root_cause.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Error::msg::<&str>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(Error::msg::<&str,>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<&str>.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Error::msg::<<str as ToOwned>::Owned>.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Error::msg::<<str as ToOwned>::Owned>.t(1) == 2` (1 vs 2)",
    );

    let test = || Ok(ensure!(Chain::<'static>::new.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)",
    );

    #[rustfmt::skip]
    let test = || Ok(ensure!(Chain::<'static,>::new.t(1) == 2));
    assert_err(
        test,
        "Condition failed: `Chain::<'static>::new.t(1) == 2` (1 vs 2)",
    );
}

#[test]
fn test_macro() {
    let test = || Ok(ensure!(anyhow!("...").to_string().len() <= 1));
    assert_err(
        test,
        "Condition failed: `anyhow!(\"...\").to_string().len() <= 1` (3 vs 1)",
    );

    let test = || Ok(ensure!(vec![1].len() < 1));
    assert_err(test, "Condition failed: `vec![1].len() < 1` (1 vs 1)");

    let test = || Ok(ensure!(stringify! {} != ""));
    assert_err(
        test,
        "Condition failed: `stringify! { } != \"\"` (\"\" vs \"\")",
    );
}

#[test]
fn test_trailer() {
    let test = || Ok(ensure!((|| 1)() == 2));
    assert_err(test, "Condition failed: `(|| 1)() == 2` (1 vs 2)");

    let test = || Ok(ensure!(b"hmm"[1] == b'c'));
    assert_err(test, "Condition failed: `b\"hmm\"[1] == b'c'` (109 vs 99)");

    let test = || Ok(ensure!(PhantomData::<u8> {} != PhantomData));
    assert_err(
        test,
        "Condition failed: `PhantomData::<u8>{} != PhantomData` (PhantomData vs PhantomData)",
    );

    let result = Ok::<_, Error>(1);
    let test = || Ok(ensure!(result? == 2));
    assert_err(test, "Condition failed: `result? == 2` (1 vs 2)");

    let test = || Ok(ensure!((2, 3).1 == 2));
    assert_err(test, "Condition failed: `(2, 3).1 == 2` (3 vs 2)");

    let err = anyhow!("");
    let test = || Ok(ensure!(err.is::<&str>() == false));
    assert_err(
        test,
        "Condition failed: `err.is::<&str>() == false` (true vs false)",
    );

    let test = || Ok(ensure!(err.is::<<str as ToOwned>::Owned>() == true));
    assert_err(
        test,
        "Condition failed: `err.is::<<str as ToOwned>::Owned>() == true` (false vs true)",
    );
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do [Cap'n Proto code generation]
//! (https://capnproto.org/otherlang.html#how-to-write-compiler-plugins) within
//! a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from [schema.capnp]
/// (https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

#[derive(Copy, Clone)]
pub enum RustEdition {
    Rust2015,
    Rust2018,
}

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<'a, P>(&'a mut self, prefix: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<'a, P>(&'a mut self, dir: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import<'a>(&'a mut self) -> &'a mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command. Returns an error if `OUT_DIR` or a custom output directory was not set
    /// or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        let mut command = ::std::process::Command::new("capnp");
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<commit_msg>Remove unused RustEdition enum<commit_after>// Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
// Licensed under the MIT License:
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

//! # Cap'n Proto Schema Compiler Plugin Library
//!
//! This library allows you to do [Cap'n Proto code generation]
//! (https://capnproto.org/otherlang.html#how-to-write-compiler-plugins) within
//! a Cargo build. You still need the `capnp` binary (implemented in C++).
//! (If you use a package manager, try looking for a package called
//! `capnproto`.)
//!
//! In your Cargo.toml:
//!
//! ```ignore
//! [package]
//! build = "build.rs"
//!
//! [build-dependencies]
//! capnpc = "0.12"
//! ```
//!
//! In your build.rs:
//!
//! ```ignore
//! extern crate capnpc;
//!
//! fn main() {
//!     capnpc::CompilerCommand::new()
//!         .src_prefix("schema")
//!         .file("schema/foo.capnp")
//!         .file("schema/bar.capnp")
//!         .run().expect("schema compiler command");
//! }
//! ```
//!
//! This will be equivalent to executing the shell command
//!
//! ```ignore
//!   capnp compile -orust:$OUT_DIR --src-prefix=schema schema/foo.capnp schema/bar.capnp
//! ```
//!

extern crate capnp;

/// Code generated from [schema.capnp]
/// (https://github.com/capnproto/capnproto/blob/master/c%2B%2B/src/capnp/schema.capnp).
pub mod schema_capnp;

pub mod codegen;
pub mod codegen_types;
mod pointer_constants;

use std::path::{Path, PathBuf};

fn run_command(mut command: ::std::process::Command, path: &PathBuf) -> ::capnp::Result<()> {
    let mut p = command.spawn()?;
    crate::codegen::generate_code(p.stdout.take().unwrap(), path.as_path())?;
    let exit_status = p.wait()?;
    if !exit_status.success() {
        Err(::capnp::Error::failed(format!(
            "Non-success exit status: {}",
            exit_status
        )))
    } else {
        Ok(())
    }
}

/// A builder object for schema compiler commands.
pub struct CompilerCommand {
    files: Vec<PathBuf>,
    src_prefixes: Vec<PathBuf>,
    import_paths: Vec<PathBuf>,
    no_standard_import: bool,
    output_path: Option<PathBuf>,
}

impl CompilerCommand {
    /// Creates a new, empty command.
    pub fn new() -> CompilerCommand {
        CompilerCommand {
            files: Vec::new(),
            src_prefixes: Vec::new(),
            import_paths: Vec::new(),
            no_standard_import: false,
            output_path: None,
        }
    }

    /// Adds a file to be compiled.
    pub fn file<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.files.push(path.as_ref().to_path_buf());
        self
    }

    /// Adds a --src-prefix flag. For all files specified for compilation that start
    /// with `prefix`, removes the prefix when computing output filenames.
    pub fn src_prefix<'a, P>(&'a mut self, prefix: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.src_prefixes.push(prefix.as_ref().to_path_buf());
        self
    }

    /// Adds an --import_path flag. Adds `dir` to the list of directories searched
    /// for absolute imports.
    pub fn import_path<'a, P>(&'a mut self, dir: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.import_paths.push(dir.as_ref().to_path_buf());
        self
    }

    /// Adds the --no-standard-import flag, indicating that the default import paths of
    /// /usr/include and /usr/local/include should not bet included.
    pub fn no_standard_import<'a>(&'a mut self) -> &'a mut CompilerCommand {
        self.no_standard_import = true;
        self
    }

    ///Sets the output directory of generated code. Default is OUT_DIR
    pub fn output_path<'a, P>(&'a mut self, path: P) -> &'a mut CompilerCommand
    where
        P: AsRef<Path>,
    {
        self.output_path = Some(path.as_ref().to_path_buf());
        self
    }

    /// Runs the command. Returns an error if `OUT_DIR` or a custom output directory was not set
    /// or if `capnp compile` fails.
    pub fn run(&mut self) -> ::capnp::Result<()> {
        let mut command = ::std::process::Command::new("capnp");
        command.arg("compile").arg("-o").arg("-");

        if self.no_standard_import {
            command.arg("--no-standard-import");
        }

        for import_path in &self.import_paths {
            command.arg(&format!("--import-path={}", import_path.display()));
        }

        for src_prefix in &self.src_prefixes {
            command.arg(&format!("--src-prefix={}", src_prefix.display()));
        }

        for file in &self.files {
            command.arg(&format!("{}", file.display()));
        }

        let output_path = if let Some(output_path) = &self.output_path {
            output_path.clone()
        } else {
            // Try `OUT_DIR` by default
            PathBuf::from(::std::env::var("OUT_DIR").map_err(|error| {
                ::capnp::Error::failed(format!(
                    "Could not access `OUT_DIR` environment variable: {}. \
                     You might need to set it up or instead create you own output \
                     structure using `CompilerCommand::output_path`",
                    error
                ))
            })?)
        };

        command.stdout(::std::process::Stdio::piped());
        command.stderr(::std::process::Stdio::inherit());

        run_command(command, &output_path).map_err(|error| {
            ::capnp::Error::failed(format!(
                "Error while trying to execute `capnp compile`: {}.  \
                 Please verify that version 0.5.2 or higher of the capnp executable \
                 is installed on your system. See https://capnproto.org/install.html",
                error
            ))
        })
    }
}

#[test]
fn compiler_command_new_no_out_dir() {
    let error = CompilerCommand::new().run().unwrap_err().description;
    assert!(error.starts_with("Could not access `OUT_DIR` environment variable"));
}

#[test]
fn compiler_command_with_output_path_no_out_dir() {
    let error = CompilerCommand::new().output_path("foo").run().unwrap_err().description;
    assert!(error.starts_with("Error while trying to execute `capnp compile`"));
}
<|endoftext|>