<commit_before>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::attr;
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1 || levenstein_not_1(&interned_name, &existing_name.interned.as_str()) {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1 || levenstein_not_1(&existing_name.interned.as_str(), &interned_name) {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<commit_msg>Fix sync fallout (fmt)<commit_after>use crate::utils::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
    Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, Item, ItemKind, Local, MacCall, Pat, PatKind,
};
use rustc_ast::attr;
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::source_map::Span;
use rustc_span::symbol::{Ident, Symbol};
use std::cmp::Ordering;

declare_clippy_lint! {
    /// **What it does:** Checks for names that are very similar and thus confusing.
    ///
    /// **Why is this bad?** It's hard to distinguish between names that differ only
    /// by a single character.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let checked_exp = something;
    /// let checked_expr = something_else;
    /// ```
    pub SIMILAR_NAMES,
    pedantic,
    "similarly named items and bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks for too many variables whose name consists of a
    /// single character.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```ignore
    /// let (a, b, c, d, e, f, g) = (...);
    /// ```
    pub MANY_SINGLE_CHAR_NAMES,
    style,
    "too many single character bindings"
}

declare_clippy_lint! {
    /// **What it does:** Checks if you have variables whose name consists of just
    /// underscores and digits.
    ///
    /// **Why is this bad?** It's hard to memorize what a variable means without a
    /// descriptive name.
    ///
    /// **Known problems:** None?
    ///
    /// **Example:**
    /// ```rust
    /// let _1 = 1;
    /// let ___1 = 1;
    /// let __1___2 = 11;
    /// ```
    pub JUST_UNDERSCORES_AND_DIGITS,
    style,
    "unclear name"
}

#[derive(Copy, Clone)]
pub struct NonExpressiveNames {
    pub single_char_binding_names_threshold: u64,
}

impl_lint_pass!(NonExpressiveNames => [SIMILAR_NAMES, MANY_SINGLE_CHAR_NAMES, JUST_UNDERSCORES_AND_DIGITS]);

struct ExistingName {
    interned: Symbol,
    span: Span,
    len: usize,
    exemptions: &'static [&'static str],
}

struct SimilarNamesLocalVisitor<'a, 'tcx> {
    names: Vec<ExistingName>,
    cx: &'a EarlyContext<'tcx>,
    lint: &'a NonExpressiveNames,

    /// A stack of scopes containing the single-character bindings in each scope.
    single_char_names: Vec<Vec<Ident>>,
}

impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
    fn check_single_char_names(&self) {
        let num_single_char_names = self.single_char_names.iter().flatten().count();
        let threshold = self.lint.single_char_binding_names_threshold;
        if num_single_char_names as u64 > threshold {
            let span = self
                .single_char_names
                .iter()
                .flatten()
                .map(|ident| ident.span)
                .collect::<Vec<_>>();
            span_lint(
                self.cx,
                MANY_SINGLE_CHAR_NAMES,
                span,
                &format!(
                    "{} bindings with single-character names in scope",
                    num_single_char_names
                ),
            );
        }
    }
}

// this list contains lists of names that are allowed to be similar
// the assumption is that no name is ever contained in multiple lists.
#[rustfmt::skip]
const ALLOWED_TO_BE_SIMILAR: &[&[&str]] = &[
    &["parsed", "parser"],
    &["lhs", "rhs"],
    &["tx", "rx"],
    &["set", "get"],
    &["args", "arms"],
    &["qpath", "path"],
    &["lit", "lint"],
];

struct SimilarNamesNameVisitor<'a, 'tcx, 'b>(&'b mut SimilarNamesLocalVisitor<'a, 'tcx>);

impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn visit_pat(&mut self, pat: &'tcx Pat) {
        match pat.kind {
            PatKind::Ident(_, ident, _) => {
                if !pat.span.from_expansion() {
                    self.check_ident(ident);
                }
            },
            PatKind::Struct(_, ref fields, _) => {
                for field in fields {
                    if !field.is_shorthand {
                        self.visit_pat(&field.pat);
                    }
                }
            },
            // just go through the first pattern, as either all patterns
            // bind the same bindings or rustc would have errored much earlier
            PatKind::Or(ref pats) => self.visit_pat(&pats[0]),
            _ => walk_pat(self, pat),
        }
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

#[must_use]
fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
    for &list in ALLOWED_TO_BE_SIMILAR {
        if allowed_to_be_similar(interned_name, list) {
            return Some(list);
        }
    }
    None
}

#[must_use]
fn allowed_to_be_similar(interned_name: &str, list: &[&str]) -> bool {
    list.iter()
        .any(|&name| interned_name.starts_with(name) || interned_name.ends_with(name))
}

impl<'a, 'tcx, 'b> SimilarNamesNameVisitor<'a, 'tcx, 'b> {
    fn check_short_ident(&mut self, ident: Ident) {
        // Ignore shadowing
        if self
            .0
            .single_char_names
            .iter()
            .flatten()
            .any(|id| id.name == ident.name)
        {
            return;
        }

        if let Some(scope) = &mut self.0.single_char_names.last_mut() {
            scope.push(ident);
        }
    }

    #[allow(clippy::too_many_lines)]
    fn check_ident(&mut self, ident: Ident) {
        let interned_name = ident.name.as_str();
        if interned_name.chars().any(char::is_uppercase) {
            return;
        }
        if interned_name.chars().all(|c| c.is_digit(10) || c == '_') {
            span_lint(
                self.0.cx,
                JUST_UNDERSCORES_AND_DIGITS,
                ident.span,
                "consider choosing a more descriptive name",
            );
            return;
        }
        let count = interned_name.chars().count();
        if count < 3 {
            if count == 1 {
                self.check_short_ident(ident);
            }
            return;
        }
        for existing_name in &self.0.names {
            if allowed_to_be_similar(&interned_name, existing_name.exemptions) {
                continue;
            }
            let mut split_at = None;
            match existing_name.len.cmp(&count) {
                Ordering::Greater => {
                    if existing_name.len - count != 1
                        || levenstein_not_1(&interned_name, &existing_name.interned.as_str())
                    {
                        continue;
                    }
                },
                Ordering::Less => {
                    if count - existing_name.len != 1
                        || levenstein_not_1(&existing_name.interned.as_str(), &interned_name)
                    {
                        continue;
                    }
                },
                Ordering::Equal => {
                    let mut interned_chars = interned_name.chars();
                    let interned_str = existing_name.interned.as_str();
                    let mut existing_chars = interned_str.chars();
                    let first_i = interned_chars.next().expect("we know we have at least one char");
                    let first_e = existing_chars.next().expect("we know we have at least one char");
                    let eq_or_numeric = |(a, b): (char, char)| a == b || a.is_numeric() && b.is_numeric();

                    if eq_or_numeric((first_i, first_e)) {
                        let last_i = interned_chars.next_back().expect("we know we have at least two chars");
                        let last_e = existing_chars.next_back().expect("we know we have at least two chars");
                        if eq_or_numeric((last_i, last_e)) {
                            if interned_chars
                                .zip(existing_chars)
                                .filter(|&ie| !eq_or_numeric(ie))
                                .count()
                                != 1
                            {
                                continue;
                            }
                        } else {
                            let second_last_i = interned_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            let second_last_e = existing_chars
                                .next_back()
                                .expect("we know we have at least three chars");
                            if !eq_or_numeric((second_last_i, second_last_e))
                                || second_last_i == '_'
                                || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                            {
                                // allowed similarity foo_x, foo_y
                                // or too many chars differ (foo_x, boo_y) or (foox, booy)
                                continue;
                            }
                            split_at = interned_name.char_indices().rev().next().map(|(i, _)| i);
                        }
                    } else {
                        let second_i = interned_chars.next().expect("we know we have at least two chars");
                        let second_e = existing_chars.next().expect("we know we have at least two chars");
                        if !eq_or_numeric((second_i, second_e))
                            || second_i == '_'
                            || !interned_chars.zip(existing_chars).all(eq_or_numeric)
                        {
                            // allowed similarity x_foo, y_foo
                            // or too many chars differ (x_foo, y_boo) or (xfoo, yboo)
                            continue;
                        }
                        split_at = interned_name.chars().next().map(char::len_utf8);
                    }
                },
            }
            span_lint_and_then(
                self.0.cx,
                SIMILAR_NAMES,
                ident.span,
                "binding's name is too similar to existing binding",
                |diag| {
                    diag.span_note(existing_name.span, "existing binding defined here");
                    if let Some(split) = split_at {
                        diag.span_help(
                            ident.span,
                            &format!(
                                "separate the discriminating character by an \
                                 underscore like: `{}_{}`",
                                &interned_name[..split],
                                &interned_name[split..]
                            ),
                        );
                    }
                },
            );
            return;
        }
        self.0.names.push(ExistingName {
            exemptions: get_exemptions(&interned_name).unwrap_or(&[]),
            interned: ident.name,
            span: ident.span,
            len: count,
        });
    }
}

impl<'a, 'b> SimilarNamesLocalVisitor<'a, 'b> {
    /// ensure scoping rules work
    fn apply<F: for<'c> Fn(&'c mut Self)>(&mut self, f: F) {
        let n = self.names.len();
        let single_char_count = self.single_char_names.len();
        f(self);
        self.names.truncate(n);
        self.single_char_names.truncate(single_char_count);
    }
}

impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
    fn visit_local(&mut self, local: &'tcx Local) {
        if let Some(ref init) = local.init {
            self.apply(|this| walk_expr(this, &**init));
        }
        // add the pattern after the expression because the bindings aren't available
        // yet in the init
        // expression
        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
    }
    fn visit_block(&mut self, blk: &'tcx Block) {
        self.single_char_names.push(vec![]);

        self.apply(|this| walk_block(this, blk));

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_arm(&mut self, arm: &'tcx Arm) {
        self.single_char_names.push(vec![]);

        self.apply(|this| {
            SimilarNamesNameVisitor(this).visit_pat(&arm.pat);
            this.apply(|this| walk_expr(this, &arm.body));
        });

        self.check_single_char_names();
        self.single_char_names.pop();
    }
    fn visit_item(&mut self, _: &Item) {
        // do not recurse into inner items
    }
    fn visit_mac(&mut self, _mac: &MacCall) {
        // do not check macs
    }
}

impl EarlyLintPass for NonExpressiveNames {
    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let ItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }

    fn check_impl_item(&mut self, cx: &EarlyContext<'_>, item: &AssocItem) {
        if in_external_macro(cx.sess, item.span) {
            return;
        }

        if let AssocItemKind::Fn(_, ref sig, _, Some(ref blk)) = item.kind {
            do_check(self, cx, &item.attrs, &sig.decl, blk);
        }
    }
}

fn do_check(lint: &mut NonExpressiveNames, cx: &EarlyContext<'_>, attrs: &[Attribute], decl: &FnDecl, blk: &Block) {
    if !attr::contains_name(attrs, sym!(test)) {
        let mut visitor = SimilarNamesLocalVisitor {
            names: Vec::new(),
            cx,
            lint,
            single_char_names: vec![vec![]],
        };

        // initialize with function arguments
        for arg in &decl.inputs {
            SimilarNamesNameVisitor(&mut visitor).visit_pat(&arg.pat);
        }
        // walk all other bindings
        walk_block(&mut visitor, blk);

        visitor.check_single_char_names();
    }
}

/// Precondition: `a_name.chars().count() < b_name.chars().count()`.
#[must_use]
fn levenstein_not_1(a_name: &str, b_name: &str) -> bool {
    debug_assert!(a_name.chars().count() < b_name.chars().count());
    let mut a_chars = a_name.chars();
    let mut b_chars = b_name.chars();
    while let (Some(a), Some(b)) = (a_chars.next(), b_chars.next()) {
        if a == b {
            continue;
        }
        if let Some(b2) = b_chars.next() {
            // check if there's just one character inserted
            return a != b2 || a_chars.ne(b_chars);
        } else {
            // tuple
            // ntuple
            return true;
        }
    }
    // for item in items
    true
}
<|endoftext|>


<commit_before>use super::{Clock, Reference};
use crate::lib::*;
use parking_lot::Mutex;
use std::time::SystemTime;

/// The default clock reference point in time: [`Instant`].
// TODO: remove this in favor of `DefaultClock::Instant`.
pub type DefaultReference = Instant;

/// The default clock that reports [`Instant`]s.
pub type DefaultClock = MonotonicClock;

/// A mock implementation of a clock tracking [`Instant`]s. All it
/// does is keep track of what "now" is by allowing the program to
/// increment the current time (taken at time of construction) by some
/// arbitrary [`Duration`].
#[derive(Debug, Clone)]
pub struct FakeAbsoluteClock {
    now: Arc<Mutex<Instant>>,
}

impl Default for FakeAbsoluteClock {
    fn default() -> Self {
        FakeAbsoluteClock {
            now: Arc::new(Mutex::new(Instant::now())),
        }
    }
}

impl FakeAbsoluteClock {
    /// Advances the fake clock by the given amount.
    pub fn advance(&mut self, by: Duration) {
        *(self.now.lock()) += by
    }
}

impl Clock for FakeAbsoluteClock {
    type Instant = Instant;

    fn now(&self) -> Self::Instant {
        *self.now.lock()
    }
}

/// The monotonic clock implemented by [`Instant`].
#[derive(Clone, Debug)]
pub struct MonotonicClock();

impl Default for MonotonicClock {
    fn default() -> Self {
        MonotonicClock()
    }
}

impl Reference for Instant {
    fn duration_since(&self, earlier: Self) -> Duration {
        *self - earlier
    }
}

impl Clock for MonotonicClock {
    type Instant = Instant;

    fn now(&self) -> Self::Instant {
        Instant::now()
    }
}

/// The non-monotonic clock implemented by [`SystemTime`].
#[derive(Clone, Debug)]
pub struct SystemClock();

impl Default for SystemClock {
    fn default() -> Self {
        SystemClock()
    }
}

impl Reference for SystemTime {
    /// Returns the difference in times between the two
    /// SystemTimes. Due to the fallible nature of SystemTimes,
    /// returns the zero duration if a negative duration would
    /// result (e.g. due to system clock adjustments).
    fn duration_since(&self, earlier: Self) -> Duration {
        self.duration_since(earlier)
            .unwrap_or_else(|_| Duration::new(0, 0))
    }
}

impl Clock for SystemClock {
    type Instant = SystemTime;

    fn now(&self) -> Self::Instant {
        SystemTime::now()
    }
}
<commit_msg>Derive Default for the std clocks<commit_after>use super::{Clock, Reference};
use crate::lib::*;
use parking_lot::Mutex;
use std::time::SystemTime;

/// The default clock reference point in time: [`Instant`].
// TODO: remove this in favor of `DefaultClock::Instant`.
pub type DefaultReference = Instant;

/// The default clock that reports [`Instant`]s.
pub type DefaultClock = MonotonicClock;

/// A mock implementation of a clock tracking [`Instant`]s. All it
/// does is keep track of what "now" is by allowing the program to
/// increment the current time (taken at time of construction) by some
/// arbitrary [`Duration`].
#[derive(Debug, Clone)]
pub struct FakeAbsoluteClock {
    now: Arc<Mutex<Instant>>,
}

impl Default for FakeAbsoluteClock {
    fn default() -> Self {
        FakeAbsoluteClock {
            now: Arc::new(Mutex::new(Instant::now())),
        }
    }
}

impl FakeAbsoluteClock {
    /// Advances the fake clock by the given amount.
    pub fn advance(&mut self, by: Duration) {
        *(self.now.lock()) += by
    }
}

impl Clock for FakeAbsoluteClock {
    type Instant = Instant;

    fn now(&self) -> Self::Instant {
        *self.now.lock()
    }
}

/// The monotonic clock implemented by [`Instant`].
#[derive(Clone, Debug, Default)]
pub struct MonotonicClock();

impl Reference for Instant {
    fn duration_since(&self, earlier: Self) -> Duration {
        *self - earlier
    }
}

impl Clock for MonotonicClock {
    type Instant = Instant;

    fn now(&self) -> Self::Instant {
        Instant::now()
    }
}

/// The non-monotonic clock implemented by [`SystemTime`].
#[derive(Clone, Debug, Default)]
pub struct SystemClock();

impl Reference for SystemTime {
    /// Returns the difference in times between the two
    /// SystemTimes. Due to the fallible nature of SystemTimes,
    /// returns the zero duration if a negative duration would
    /// result (e.g. due to system clock adjustments).
    fn duration_since(&self, earlier: Self) -> Duration {
        self.duration_since(earlier)
            .unwrap_or_else(|_| Duration::new(0, 0))
    }
}

impl Clock for SystemClock {
    type Instant = SystemTime;

    fn now(&self) -> Self::Instant {
        SystemTime::now()
    }
}
<|endoftext|>


<commit_before>#[cfg(not(windows))]
compile_error!("This demo only runs on Windows.");

extern crate direct_composition;
extern crate euclid;
extern crate gleam;
extern crate webrender;
extern crate winit;

use direct_composition::DirectComposition;
use webrender::api;
use winit::os::windows::WindowExt;

fn main() {
    let mut events_loop = winit::EventsLoop::new();
    let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy() });

    let window = winit::WindowBuilder::new()
        .with_title("Hello, world!")
        .with_dimensions(1024, 768)
        .build(&events_loop)
        .unwrap();

    let composition = direct_composition_from_window(&window);
    let factor = window.hidpi_factor();

    let mut clicks: usize = 0;
    let mut offset_y = 100.;
    let size = api::DeviceUintSize::new;
    let mut rects = [
        Rectangle::new(&composition, &notifier, factor, size(300, 200), 0., 0.2, 0.4, 1.),
        Rectangle::new(&composition, &notifier, factor, size(400, 300), 0., 0.5, 0., 0.5),
    ];
    rects[0].render(factor);
    rects[1].render(factor);

    // FIXME: what shows up on screen for each visual seems to be one frame late?
    rects[0].render(factor);
    rects[1].render(factor);

    rects[0].visual.set_offset_x(100.);
    rects[0].visual.set_offset_y(50.);

    rects[1].visual.set_offset_x(200.);
    rects[1].visual.set_offset_y(offset_y);

    composition.commit();

    events_loop.run_forever(|event| {
        if let winit::Event::WindowEvent { event, .. } = event {
            match event {
                winit::WindowEvent::Closed => {
                    return winit::ControlFlow::Break
                }
                winit::WindowEvent::MouseWheel { delta, .. } => {
                    let dy = match delta {
                        winit::MouseScrollDelta::LineDelta(_, dy) => dy,
                        winit::MouseScrollDelta::PixelDelta(_, dy) => dy,
                    };
                    offset_y = (offset_y - 10. * dy).max(0.).min(468.);

                    rects[1].visual.set_offset_y(offset_y);
                    composition.commit();
                }
                winit::WindowEvent::MouseInput {
                    button: winit::MouseButton::Left,
                    state: winit::ElementState::Pressed,
                    ..
                } => {
                    clicks += 1;
                    let rect = &mut rects[clicks % 2];
                    rect.color.g += 0.1;
                    rect.color.g %= 1.;
                    rect.render(factor)
                }
                _ => {}
            }
        }
        winit::ControlFlow::Continue
    });
}

fn direct_composition_from_window(window: &winit::Window) -> DirectComposition {
    unsafe {
        DirectComposition::new(window.get_hwnd() as _)
    }
}

struct Rectangle {
    visual: direct_composition::AngleVisual,
    renderer: Option<webrender::Renderer>,
    api: api::RenderApi,
    document_id: api::DocumentId,
    size: api::DeviceUintSize,
    color: api::ColorF,
}

impl Rectangle {
    fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
           device_pixel_ratio: f32, size: api::DeviceUintSize, r: f32, g: f32, b: f32, a: f32)
           -> Self {
        let visual = composition.create_angle_visual(size.width, size.height);
        visual.make_current();

        let (renderer, sender) = webrender::Renderer::new(
            composition.gleam.clone(),
            notifier.clone(),
            webrender::RendererOptions {
                clear_color: Some(api::ColorF::new(0., 0., 0., 0.)),
                device_pixel_ratio,
                ..webrender::RendererOptions::default()
            },
        ).unwrap();
        let api = sender.create_api();

       Rectangle {
            visual,
            renderer: Some(renderer),
            document_id: api.add_document(size, 0),
            api,
            size,
            color: api::ColorF { r, g, b, a },
        }
    }

    fn render(&mut self, device_pixel_ratio: f32) {
        self.visual.make_current();

        let pipeline_id = api::PipelineId(0, 0);
        let layout_size = self.size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
        let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size);

        let rect = euclid::TypedRect::new(euclid::TypedPoint2D::zero(), layout_size);
        builder.push_rect(
            &api::PrimitiveInfo::with_clip(
                rect,
                api::LocalClip::RoundedRect(rect, api::ComplexClipRegion::new(
                    rect, api::BorderRadius::uniform(20.), api::ClipMode::Clip,
                ))
            ),
            self.color,
        );

        let mut transaction = api::Transaction::new();
        transaction.set_display_list(
            api::Epoch(0),
            None,
            layout_size,
            builder.finalize(),
            true,
        );
        transaction.set_root_pipeline(pipeline_id);
        transaction.generate_frame();
        self.api.send_transaction(self.document_id, transaction);
        let renderer = self.renderer.as_mut().unwrap();
        renderer.update();
        renderer.render(self.size).unwrap();
        let _ = renderer.flush_pipeline_info();
        self.visual.present();
    }
}

impl Drop for Rectangle {
    fn drop(&mut self) {
        self.renderer.take().unwrap().deinit()
    }
}

#[derive(Clone)]
struct Notifier {
    events_proxy: winit::EventsLoopProxy,
}

impl api::RenderNotifier for Notifier {
    fn clone(&self) -> Box<api::RenderNotifier> {
        Box::new(Clone::clone(self))
    }

    fn wake_up(&self) {
        let _ = self.events_proxy.wakeup();
    }

    fn new_document_ready(&self, _: api::DocumentId, _: bool, _: bool) {
        self.wake_up();
    }
}
<commit_msg>Block until the pipeline is ready, before rendering.<commit_after>#[cfg(not(windows))]
compile_error!("This demo only runs on Windows.");

extern crate direct_composition;
extern crate euclid;
extern crate gleam;
extern crate webrender;
extern crate winit;

use direct_composition::DirectComposition;
use std::sync::mpsc;
use webrender::api;
use winit::os::windows::WindowExt;

fn main() {
    let mut events_loop = winit::EventsLoop::new();

    let (tx, rx) = mpsc::channel();
    let notifier = Box::new(Notifier { events_proxy: events_loop.create_proxy(), tx });

    let window = winit::WindowBuilder::new()
        .with_title("WebRender + ANGLE + DirectComposition")
        .with_dimensions(1024, 768)
        .build(&events_loop)
        .unwrap();

    let composition = direct_composition_from_window(&window);
    let factor = window.hidpi_factor();

    let mut clicks: usize = 0;
    let mut offset_y = 100.;
    let size = api::DeviceUintSize::new;
    let mut rects = [
        Rectangle::new(&composition, &notifier, factor, size(300, 200), 0., 0.2, 0.4, 1.),
        Rectangle::new(&composition, &notifier, factor, size(400, 300), 0., 0.5, 0., 0.5),
    ];
    rects[0].render(factor, &rx);
    rects[1].render(factor, &rx);

    rects[0].visual.set_offset_x(100.);
    rects[0].visual.set_offset_y(50.);

    rects[1].visual.set_offset_x(200.);
    rects[1].visual.set_offset_y(offset_y);

    composition.commit();

    events_loop.run_forever(|event| {
        if let winit::Event::WindowEvent { event, .. } = event {
            match event {
                winit::WindowEvent::Closed => {
                    return winit::ControlFlow::Break
                }
                winit::WindowEvent::MouseWheel { delta, .. } => {
                    let dy = match delta {
                        winit::MouseScrollDelta::LineDelta(_, dy) => dy,
                        winit::MouseScrollDelta::PixelDelta(_, dy) => dy,
                    };
                    offset_y = (offset_y - 10. * dy).max(0.).min(468.);

                    rects[1].visual.set_offset_y(offset_y);
                    composition.commit();
                }
                winit::WindowEvent::MouseInput {
                    button: winit::MouseButton::Left,
                    state: winit::ElementState::Pressed,
                    ..
                } => {
                    clicks += 1;
                    let rect = &mut rects[clicks % 2];
                    rect.color.g += 0.1;
                    rect.color.g %= 1.;
                    rect.render(factor, &rx)
                }
                _ => {}
            }
        }
        winit::ControlFlow::Continue
    });
}

fn direct_composition_from_window(window: &winit::Window) -> DirectComposition {
    unsafe {
        DirectComposition::new(window.get_hwnd() as _)
    }
}

struct Rectangle {
    visual: direct_composition::AngleVisual,
    renderer: Option<webrender::Renderer>,
    api: api::RenderApi,
    document_id: api::DocumentId,
    size: api::DeviceUintSize,
    color: api::ColorF,
}

impl Rectangle {
    fn new(composition: &DirectComposition, notifier: &Box<Notifier>,
           device_pixel_ratio: f32, size: api::DeviceUintSize, r: f32, g: f32, b: f32, a: f32)
           -> Self {
        let visual = composition.create_angle_visual(size.width, size.height);
        visual.make_current();

        let (renderer, sender) = webrender::Renderer::new(
            composition.gleam.clone(),
            notifier.clone(),
            webrender::RendererOptions {
                clear_color: Some(api::ColorF::new(0., 0., 0., 0.)),
                device_pixel_ratio,
                ..webrender::RendererOptions::default()
            },
        ).unwrap();
        let api = sender.create_api();

       Rectangle {
            visual,
            renderer: Some(renderer),
            document_id: api.add_document(size, 0),
            api,
            size,
            color: api::ColorF { r, g, b, a },
        }
    }

    fn render(&mut self, device_pixel_ratio: f32, rx: &mpsc::Receiver<()>) {
        self.visual.make_current();

        let pipeline_id = api::PipelineId(0, 0);
        let layout_size = self.size.to_f32() / euclid::TypedScale::new(device_pixel_ratio);
        let mut builder = api::DisplayListBuilder::new(pipeline_id, layout_size);

        let rect = euclid::TypedRect::new(euclid::TypedPoint2D::zero(), layout_size);
        builder.push_rect(
            &api::PrimitiveInfo::with_clip(
                rect,
                api::LocalClip::RoundedRect(rect, api::ComplexClipRegion::new(
                    rect, api::BorderRadius::uniform(20.), api::ClipMode::Clip,
                ))
            ),
            self.color,
        );

        let mut transaction = api::Transaction::new();
        transaction.set_display_list(
            api::Epoch(0),
            None,
            layout_size,
            builder.finalize(),
            true,
        );
        transaction.set_root_pipeline(pipeline_id);
        transaction.generate_frame();
        self.api.send_transaction(self.document_id, transaction);
        rx.recv().unwrap();
        let renderer = self.renderer.as_mut().unwrap();
        renderer.update();
        renderer.render(self.size).unwrap();
        let _ = renderer.flush_pipeline_info();
        self.visual.present();
    }
}

impl Drop for Rectangle {
    fn drop(&mut self) {
        self.renderer.take().unwrap().deinit()
    }
}

#[derive(Clone)]
struct Notifier {
    events_proxy: winit::EventsLoopProxy,
    tx: mpsc::Sender<()>,
}

impl api::RenderNotifier for Notifier {
    fn clone(&self) -> Box<api::RenderNotifier> {
        Box::new(Clone::clone(self))
    }

    fn wake_up(&self) {
        self.tx.send(()).unwrap();
        let _ = self.events_proxy.wakeup();
    }

    fn new_document_ready(&self, _: api::DocumentId, _: bool, _: bool) {
        self.wake_up();
    }
}
<|endoftext|>


<commit_before>#![allow(unstable)]

extern crate cql_ffi;

use std::ffi::CString;
use std::slice;

use cql_ffi::*;

use std::num::ToPrimitive;

static NUM_CONCURRENT_REQUESTS:usize = 1000;

fn print_error(future:&mut CassFuture) {unsafe{
    let message = cass_future_error_message(future);
    let message = slice::from_raw_buf(&message.data,message.length as usize);
    println!("Error: {:?}", message);
}}

fn create_cluster() -> *mut CassCluster {
    let cluster = unsafe{cass_cluster_new()};
    unsafe{cass_cluster_set_contact_points(cluster, "127.0.0.1,127.0.0.2,127.0.0.3".as_ptr() as *const i8)};
    cluster
}

fn connect_session(session:&mut CassSession, cluster:&mut CassCluster) -> CassError {unsafe{
    let future = &mut *cass_session_connect(session, cluster);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    if rc != CASS_OK {
        print_error(future);
    }
    cass_future_free(future);
    return rc;
}}

fn execute_query(session: &mut CassSession, query: &str) -> CassError {unsafe{
    let statement = cass_statement_new(cass_string_init(cass_string_init(CString::from_slice(query.as_bytes()).as_ptr()).data), 0);
    let future = &mut *cass_session_execute(session, statement);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    if rc != CASS_OK {
        print_error(future);
    }
    cass_future_free(future);
    cass_statement_free(statement);
    return rc;
}}

fn insert_into_async(session: &mut CassSession, key:&str) {unsafe{
    let query="INSERT INTO examples.async (key, bln, flt, dbl, i32, i64) VALUES (?, ?, ?, ?, ?, ?);";

    let mut futures = Vec::<*mut CassFuture>::new();
    for i in (0..NUM_CONCURRENT_REQUESTS) {
    let statement = cass_statement_new(cass_string_init(cass_string_init(CString::from_slice(query.as_bytes()).as_ptr()).data), 6);
        let key = format!("key{}",i).as_ptr() as *const i8;
        cass_statement_bind_string(statement, 0, cass_string_init(key));
        cass_statement_bind_bool(statement, 1, if i % 2 == 0 {cass_true} else {cass_false});
        cass_statement_bind_float(statement, 2, i.to_f32().unwrap() / 2.0f32);
        cass_statement_bind_double(statement, 3, i.to_f64().unwrap() / 200.0);
        cass_statement_bind_int32(statement, 4, i.to_i32().unwrap() * 10);
        cass_statement_bind_int64(statement, 5, i.to_i64().unwrap() * 100);
        futures.push(cass_session_execute(session, statement));
        cass_statement_free(statement);
    }
    for mut future in futures.iter_mut() {
        cass_future_wait(*future);
        let rc = cass_future_error_code(*future);
        if rc != CASS_OK {
            print_error(&mut**future);
        }
        cass_future_free(*future);
    }
}}

pub fn main() {unsafe{
    let cluster = create_cluster();
    let session = cass_session_new();
    if connect_session(&mut*session, &mut*cluster) != CASS_OK {
        cass_cluster_free(cluster);
        cass_session_free(session);
        panic!("couldn't connect");
    }
    execute_query(&mut*session, "CREATE KEYSPACE IF NOT EXISTS examples WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '1' };");
    execute_query(&mut*session, "CREATE TABLE IF NOT EXISTS examples.async (key text, bln boolean, flt float, dbl double, i32 int, i64 bigint, PRIMARY KEY (key));");
    execute_query(&mut*session, "USE examples");
    insert_into_async(&mut*session, "test");
    let close_future = cass_session_close(session);
    cass_future_wait(close_future);
    cass_future_free(close_future);
    cass_cluster_free(cluster);
    cass_session_free(session);
}}
<commit_msg>breaks the compiler<commit_after>#![allow(unstable)]

extern crate cql_ffi;

use std::ffi::CString;
use std::slice;

use cql_ffi::*;

use std::num::ToPrimitive;

static NUM_CONCURRENT_REQUESTS:usize = 1000;

fn print_error(future:&mut CassFuture) {unsafe{
    let message = cass_future_error_message(future);
    let message = slice::from_raw_buf(&message.data,message.length as usize);
    println!("Error: {:?}", message);
}}

fn create_cluster() -> *mut CassCluster {
    let cluster = unsafe{cass_cluster_new()};
    unsafe{cass_cluster_set_contact_points(cluster, "127.0.0.1,127.0.0.2,127.0.0.3".as_ptr() as *const i8)};
    cluster
}

fn connect_session(session:&mut CassSession, cluster:&mut CassCluster) -> CassError {unsafe{
    let future = &mut *cass_session_connect(session, cluster);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    match rc {
        CASS_OK => {},
        _=> print_error(future)
    }
    cass_future_free(future);
    rc
}}

fn execute_query(session: &mut CassSession, query: &str) -> CassError {unsafe{
    let statement = cass_statement_new(cass_string_init(cass_string_init(CString::from_slice(query.as_bytes()).as_ptr()).data), 0);
    let future = &mut *cass_session_execute(session, statement);
    cass_future_wait(future);
    let rc = cass_future_error_code(future);
    if rc != CASS_OK {
        print_error(future);
    }
    cass_future_free(future);
    cass_statement_free(statement);
    return rc;
}}

fn insert_into_async(session: &mut CassSession, key:&str) {unsafe{
    let query="INSERT INTO examples.async (key, bln, flt, dbl, i32, i64) VALUES (?, ?, ?, ?, ?, ?);";

    let mut futures = Vec::<CassFuture>::new();
    for i in (0..NUM_CONCURRENT_REQUESTS) {
        let statement = cass_statement_new(cass_string_init(cass_string_init(CString::from_slice(query.as_bytes()).as_ptr()).data), 6);
        let key = format!("{}{}", key, i).as_ptr() as *const i8;
        cass_statement_bind_string(statement, 0, cass_string_init(key));
        cass_statement_bind_bool(statement, 1, if i % 2 == 0 {cass_true} else {cass_false});
        cass_statement_bind_float(statement, 2, i.to_f32().unwrap() / 2.0f32);
        cass_statement_bind_double(statement, 3, i.to_f64().unwrap() / 200.0);
        cass_statement_bind_int32(statement, 4, i.to_i32().unwrap() * 10);
        cass_statement_bind_int64(statement, 5, i.to_i64().unwrap() * 100);
        futures.push(*cass_session_execute(session, statement));
        cass_statement_free(statement);
    }
    for mut future in futures.iter_mut() {
        cass_future_wait(future);
        let rc = cass_future_error_code(future);
        if rc != CASS_OK {
            print_error(&mut*future);
        }
        cass_future_free(future);
    }
}}

pub fn main() {unsafe{
    let (cluster,session) = (create_cluster(),cass_session_new());
    match connect_session(&mut*session, &mut*cluster) {
        CASS_OK => {},
        _ => {
            cass_cluster_free(cluster);
            cass_session_free(session);
            panic!("couldn't connect");
        }
    }
    execute_query(&mut*session, "CREATE KEYSPACE IF NOT EXISTS examples WITH replication = { 'class': 'SimpleStrategy', 'replication_factor': '1' };");
    execute_query(&mut*session, "CREATE TABLE IF NOT EXISTS examples.async (key text, bln boolean, flt float, dbl double, i32 int, i64 bigint, PRIMARY KEY (key));");
    execute_query(&mut*session, "USE examples");
    insert_into_async(&mut*session, "test");
    let close_future = cass_session_close(session);
    cass_future_wait(close_future);
    cass_future_free(close_future);
    cass_cluster_free(cluster);
    cass_session_free(session);
}}
<|endoftext|>


<commit_before>//! Getting the Git status of files and directories.

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

use log::*;

use crate::fs::fields as f;

/// A **Git cache** is assembled based on the user’s input arguments.
///
/// This uses vectors to avoid the overhead of hashing: it’s not worth it when the
/// expected number of Git repositories per exa invocation is 0 or 1...
pub struct GitCache {

    /// A list of discovered Git repositories and their paths.
    repos: Vec<GitRepo>,

    /// Paths that we’ve confirmed do not have Git repositories underneath them.
    misses: Vec<PathBuf>,
}

impl GitCache {
    pub fn has_anything_for(&self, index: &Path) -> bool {
        self.repos.iter().any(|e| e.has_path(index))
    }

    pub fn get(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        self.repos.iter()
            .find(|e| e.has_path(index))
            .map(|repo| repo.search(index, prefix_lookup))
            .unwrap_or_default()
    }
}

use std::iter::FromIterator;
impl FromIterator<PathBuf> for GitCache {
    fn from_iter<I>(iter: I) -> Self
    where I: IntoIterator<Item=PathBuf>
    {
        let iter = iter.into_iter();
        let mut git = Self {
            repos: Vec::with_capacity(iter.size_hint().0),
            misses: Vec::new(),
        };

        for path in iter {
            if git.misses.contains(&path) {
                debug!("Skipping {:?} because it already came back Gitless", path);
            }
            else if git.repos.iter().any(|e| e.has_path(&path)) {
                debug!("Skipping {:?} because we already queried it", path);
            }
            else {
                match GitRepo::discover(path) {
                    Ok(r) => {
                        if let Some(r2) = git.repos.iter_mut().find(|e| e.has_workdir(&r.workdir)) {
                            debug!("Adding to existing repo (workdir matches with {:?})", r2.workdir);
                            r2.extra_paths.push(r.original_path);
                            continue;
                        }

                        debug!("Discovered new Git repo");
                        git.repos.push(r);
                    }
                    Err(miss) => {
                        git.misses.push(miss)
                    }
                }
            }
        }

        git
    }
}

/// A **Git repository** is one we’ve discovered somewhere on the filesystem.
pub struct GitRepo {

    /// The queryable contents of the repository: either a `git2` repo, or the
    /// cached results from when we queried it last time.
    contents: Mutex<GitContents>,

    /// The working directory of this repository.
    /// This is used to check whether two repositories are the same.
    workdir: PathBuf,

    /// The path that was originally checked to discover this repository.
    /// This is as important as the extra_paths (it gets checked first), but
    /// is separate to avoid having to deal with a non-empty Vec.
    original_path: PathBuf,

    /// Any other paths that were checked only to result in this same
    /// repository.
    extra_paths: Vec<PathBuf>,
}

/// A repository’s queried state.
enum GitContents {

    /// All the interesting Git stuff goes through this.
    Before {
        repo: git2::Repository,
    },

    /// Temporary value used in `repo_to_statuses` so we can move the
    /// repository out of the `Before` variant.
    Processing,

    /// The data we’ve extracted from the repository, but only after we’ve
    /// actually done so.
    After {
        statuses: Git,
    },
}

impl GitRepo {

    /// Searches through this repository for a path (to a file or directory,
    /// depending on the prefix-lookup flag) and returns its Git status.
    ///
    /// Actually querying the `git2` repository for the mapping of paths to
    /// Git statuses is only done once, and gets cached so we don’t need to
    /// re-query the entire repository the times after that.
    ///
    /// The temporary `Processing` enum variant is used after the `git2`
    /// repository is moved out, but before the results have been moved in!
    /// See <https://stackoverflow.com/q/45985827/3484614>
    fn search(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        use std::mem::replace;

        let mut contents = self.contents.lock().unwrap();
        if let GitContents::After { ref statuses } = *contents {
            debug!("Git repo {:?} has been found in cache", &self.workdir);
            return statuses.status(index, prefix_lookup);
        }

        debug!("Querying Git repo {:?} for the first time", &self.workdir);
        let repo = replace(&mut *contents, GitContents::Processing).inner_repo();
        let statuses = repo_to_statuses(&repo, &self.workdir);
        let result = statuses.status(index, prefix_lookup);
        let _processing = replace(&mut *contents, GitContents::After { statuses });
        result
    }

    /// Whether this repository has the given working directory.
    fn has_workdir(&self, path: &Path) -> bool {
        self.workdir == path
    }

    /// Whether this repository cares about the given path at all.
    fn has_path(&self, path: &Path) -> bool {
        path.starts_with(&self.original_path) || self.extra_paths.iter().any(|e| path.starts_with(e))
    }

    /// Searches for a Git repository at any point above the given path.
    /// Returns the original buffer if none is found.
    fn discover(path: PathBuf) -> Result<Self, PathBuf> {
        info!("Searching for Git repository above {:?}", path);
        let repo = match git2::Repository::discover(&path) {
            Ok(r) => r,
            Err(e) => {
                error!("Error discovering Git repositories: {:?}", e);
                return Err(path);
            }
        };

        if let Some(workdir) = repo.workdir() {
            let workdir = workdir.to_path_buf();
            let contents = Mutex::new(GitContents::Before { repo });
            Ok(Self { contents, workdir, original_path: path, extra_paths: Vec::new() })
        }
        else {
            warn!("Repository has no workdir?");
            Err(path)
        }
    }
}

impl GitContents {
    /// Assumes that the repository hasn’t been queried, and extracts it
    /// (consuming the value) if it has. This is needed because the entire
    /// enum variant gets replaced when a repo is queried (see above).
    fn inner_repo(self) -> git2::Repository {
        if let Self::Before { repo } = self {
            repo
        }
        else {
            unreachable!("Tried to extract a non-Repository")
        }
    }
}

/// Iterates through a repository’s statuses, consuming it and returning the
/// mapping of files to their Git status.
/// We will have already used the working directory at this point, so it gets
/// passed in rather than deriving it from the `Repository` again.
fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git {
    let mut statuses = Vec::new();

    info!("Getting Git statuses for repo with workdir {:?}", workdir);
    match repo.statuses(None) {
        Ok(es) => {
            for e in es.iter() {
                let path = workdir.join(Path::new(e.path().unwrap()));
                let elem = (path, e.status());
                statuses.push(elem);
            }
        }
        Err(e) => {
            error!("Error looking up Git statuses: {:?}", e);
        }
    }

    Git { statuses }
}

// The `repo.statuses` call above takes a long time. exa debug output:
//
//   20.311276  INFO:exa::fs::feature::git: Getting Git statuses for repo with workdir "/vagrant/"
//   20.799610  DEBUG:exa::output::table: Getting Git status for file "./Cargo.toml"
//
// Even inserting another logging line immediately afterwards doesn’t make it
// look any faster.

/// Container of Git statuses for all the files in this folder’s Git repository.
struct Git {
    statuses: Vec<(PathBuf, git2::Status)>,
}

impl Git {

    /// Get either the file or directory status for the given path.
    /// “Prefix lookup” means that it should report an aggregate status of all
    /// paths starting with the given prefix (in other words, a directory).
    fn status(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        if prefix_lookup { self.dir_status(index) }
                    else { self.file_status(index) }
    }

    /// Get the status for the file at the given path.
    fn file_status(&self, file: &Path) -> f::Git {
        let path = reorient(file);

        self.statuses.iter()
            .find(|p| p.0.as_path() == path)
            .map(|&(_, s)| f::Git { staged: index_status(s), unstaged: working_tree_status(s) })
            .unwrap_or_default()
    }

    /// Get the combined status for all the files whose paths begin with the
    /// path that gets passed in. This is used for getting the status of
    /// directories, which don’t really have an ‘official’ status.
    fn dir_status(&self, dir: &Path) -> f::Git {
        let path = reorient(dir);

        let s = self.statuses.iter()
                    .filter(|p| p.0.starts_with(&path))
                    .fold(git2::Status::empty(), |a, b| a | b.1);

        let staged = index_status(s);
        let unstaged = working_tree_status(s);
        f::Git { staged, unstaged }
    }
}

/// Converts a path to an absolute path based on the current directory.
/// Paths need to be absolute for them to be compared properly, otherwise
/// you’d ask a repo about “./README.md” but it only knows about
/// “/vagrant/README.md”, prefixed by the workdir.
fn reorient(path: &Path) -> PathBuf {
    use std::env::current_dir;

    // TODO: I’m not 100% on this func tbh
    let path = match current_dir() {
        Err(_)   => Path::new(".").join(&path),
        Ok(dir)  => dir.join(&path),
    };

    path.canonicalize().unwrap_or(path)
}

/// The character to display if the file has been modified, but not staged.
fn working_tree_status(status: git2::Status) -> f::GitStatus {
    match status {
        s if s.contains(git2::Status::WT_NEW)         => f::GitStatus::New,
        s if s.contains(git2::Status::WT_MODIFIED)    => f::GitStatus::Modified,
        s if s.contains(git2::Status::WT_DELETED)     => f::GitStatus::Deleted,
        s if s.contains(git2::Status::WT_RENAMED)     => f::GitStatus::Renamed,
        s if s.contains(git2::Status::WT_TYPECHANGE)  => f::GitStatus::TypeChange,
        s if s.contains(git2::Status::IGNORED)        => f::GitStatus::Ignored,
        s if s.contains(git2::Status::CONFLICTED)     => f::GitStatus::Conflicted,
        _                                             => f::GitStatus::NotModified,
    }
}

/// The character to display if the file has been modified and the change
/// has been staged.
fn index_status(status: git2::Status) -> f::GitStatus {
    match status {
        s if s.contains(git2::Status::INDEX_NEW)         => f::GitStatus::New,
        s if s.contains(git2::Status::INDEX_MODIFIED)    => f::GitStatus::Modified,
        s if s.contains(git2::Status::INDEX_DELETED)     => f::GitStatus::Deleted,
        s if s.contains(git2::Status::INDEX_RENAMED)     => f::GitStatus::Renamed,
        s if s.contains(git2::Status::INDEX_TYPECHANGE)  => f::GitStatus::TypeChange,
        _                                                => f::GitStatus::NotModified,
    }
}
<commit_msg>Fix --git deducing ignored state in unintuitive way<commit_after>//! Getting the Git status of files and directories.

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

use log::*;

use crate::fs::fields as f;

/// A **Git cache** is assembled based on the user’s input arguments.
///
/// This uses vectors to avoid the overhead of hashing: it’s not worth it when the
/// expected number of Git repositories per exa invocation is 0 or 1...
pub struct GitCache {

    /// A list of discovered Git repositories and their paths.
    repos: Vec<GitRepo>,

    /// Paths that we’ve confirmed do not have Git repositories underneath them.
    misses: Vec<PathBuf>,
}

impl GitCache {
    pub fn has_anything_for(&self, index: &Path) -> bool {
        self.repos.iter().any(|e| e.has_path(index))
    }

    pub fn get(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        self.repos.iter()
            .find(|e| e.has_path(index))
            .map(|repo| repo.search(index, prefix_lookup))
            .unwrap_or_default()
    }
}

use std::iter::FromIterator;
impl FromIterator<PathBuf> for GitCache {
    fn from_iter<I>(iter: I) -> Self
    where I: IntoIterator<Item=PathBuf>
    {
        let iter = iter.into_iter();
        let mut git = Self {
            repos: Vec::with_capacity(iter.size_hint().0),
            misses: Vec::new(),
        };

        for path in iter {
            if git.misses.contains(&path) {
                debug!("Skipping {:?} because it already came back Gitless", path);
            }
            else if git.repos.iter().any(|e| e.has_path(&path)) {
                debug!("Skipping {:?} because we already queried it", path);
            }
            else {
                match GitRepo::discover(path) {
                    Ok(r) => {
                        if let Some(r2) = git.repos.iter_mut().find(|e| e.has_workdir(&r.workdir)) {
                            debug!("Adding to existing repo (workdir matches with {:?})", r2.workdir);
                            r2.extra_paths.push(r.original_path);
                            continue;
                        }

                        debug!("Discovered new Git repo");
                        git.repos.push(r);
                    }
                    Err(miss) => {
                        git.misses.push(miss)
                    }
                }
            }
        }

        git
    }
}

/// A **Git repository** is one we’ve discovered somewhere on the filesystem.
pub struct GitRepo {

    /// The queryable contents of the repository: either a `git2` repo, or the
    /// cached results from when we queried it last time.
    contents: Mutex<GitContents>,

    /// The working directory of this repository.
    /// This is used to check whether two repositories are the same.
    workdir: PathBuf,

    /// The path that was originally checked to discover this repository.
    /// This is as important as the extra_paths (it gets checked first), but
    /// is separate to avoid having to deal with a non-empty Vec.
    original_path: PathBuf,

    /// Any other paths that were checked only to result in this same
    /// repository.
    extra_paths: Vec<PathBuf>,
}

/// A repository’s queried state.
enum GitContents {

    /// All the interesting Git stuff goes through this.
    Before {
        repo: git2::Repository,
    },

    /// Temporary value used in `repo_to_statuses` so we can move the
    /// repository out of the `Before` variant.
    Processing,

    /// The data we’ve extracted from the repository, but only after we’ve
    /// actually done so.
    After {
        statuses: Git,
    },
}

impl GitRepo {

    /// Searches through this repository for a path (to a file or directory,
    /// depending on the prefix-lookup flag) and returns its Git status.
    ///
    /// Actually querying the `git2` repository for the mapping of paths to
    /// Git statuses is only done once, and gets cached so we don’t need to
    /// re-query the entire repository the times after that.
    ///
    /// The temporary `Processing` enum variant is used after the `git2`
    /// repository is moved out, but before the results have been moved in!
    /// See <https://stackoverflow.com/q/45985827/3484614>
    fn search(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        use std::mem::replace;

        let mut contents = self.contents.lock().unwrap();
        if let GitContents::After { ref statuses } = *contents {
            debug!("Git repo {:?} has been found in cache", &self.workdir);
            return statuses.status(index, prefix_lookup);
        }

        debug!("Querying Git repo {:?} for the first time", &self.workdir);
        let repo = replace(&mut *contents, GitContents::Processing).inner_repo();
        let statuses = repo_to_statuses(&repo, &self.workdir);
        let result = statuses.status(index, prefix_lookup);
        let _processing = replace(&mut *contents, GitContents::After { statuses });
        result
    }

    /// Whether this repository has the given working directory.
    fn has_workdir(&self, path: &Path) -> bool {
        self.workdir == path
    }

    /// Whether this repository cares about the given path at all.
    fn has_path(&self, path: &Path) -> bool {
        path.starts_with(&self.original_path) || self.extra_paths.iter().any(|e| path.starts_with(e))
    }

    /// Searches for a Git repository at any point above the given path.
    /// Returns the original buffer if none is found.
    fn discover(path: PathBuf) -> Result<Self, PathBuf> {
        info!("Searching for Git repository above {:?}", path);
        let repo = match git2::Repository::discover(&path) {
            Ok(r) => r,
            Err(e) => {
                error!("Error discovering Git repositories: {:?}", e);
                return Err(path);
            }
        };

        if let Some(workdir) = repo.workdir() {
            let workdir = workdir.to_path_buf();
            let contents = Mutex::new(GitContents::Before { repo });
            Ok(Self { contents, workdir, original_path: path, extra_paths: Vec::new() })
        }
        else {
            warn!("Repository has no workdir?");
            Err(path)
        }
    }
}

impl GitContents {
    /// Assumes that the repository hasn’t been queried, and extracts it
    /// (consuming the value) if it has. This is needed because the entire
    /// enum variant gets replaced when a repo is queried (see above).
    fn inner_repo(self) -> git2::Repository {
        if let Self::Before { repo } = self {
            repo
        }
        else {
            unreachable!("Tried to extract a non-Repository")
        }
    }
}

/// Iterates through a repository’s statuses, consuming it and returning the
/// mapping of files to their Git status.
/// We will have already used the working directory at this point, so it gets
/// passed in rather than deriving it from the `Repository` again.
fn repo_to_statuses(repo: &git2::Repository, workdir: &Path) -> Git {
    let mut statuses = Vec::new();

    info!("Getting Git statuses for repo with workdir {:?}", workdir);
    match repo.statuses(None) {
        Ok(es) => {
            for e in es.iter() {
                let path = workdir.join(Path::new(e.path().unwrap()));
                let elem = (path, e.status());
                statuses.push(elem);
            }
        }
        Err(e) => {
            error!("Error looking up Git statuses: {:?}", e);
        }
    }

    Git { statuses }
}

// The `repo.statuses` call above takes a long time. exa debug output:
//
//   20.311276  INFO:exa::fs::feature::git: Getting Git statuses for repo with workdir "/vagrant/"
//   20.799610  DEBUG:exa::output::table: Getting Git status for file "./Cargo.toml"
//
// Even inserting another logging line immediately afterwards doesn’t make it
// look any faster.

/// Container of Git statuses for all the files in this folder’s Git repository.
struct Git {
    statuses: Vec<(PathBuf, git2::Status)>,
}

impl Git {

    /// Get either the file or directory status for the given path.
    /// “Prefix lookup” means that it should report an aggregate status of all
    /// paths starting with the given prefix (in other words, a directory).
    fn status(&self, index: &Path, prefix_lookup: bool) -> f::Git {
        if prefix_lookup { self.dir_status(index) }
                    else { self.file_status(index) }
    }

    /// Get the status for the file at the given path.
    fn file_status(&self, file: &Path) -> f::Git {
        let path = reorient(file);

        self.statuses.iter()
            .find(|p| p.0.as_path() == path)
            .map(|&(_, s)| f::Git { staged: index_status(s), unstaged: working_tree_status(s) })
            .unwrap_or_default()
    }

    /// Get the combined, user-facing status of a file or directory.
    /// Statuses are aggregating (for example, a directory is considered
    /// modified if any file under it has the status modified), except
    /// for ignored which applies to files under (for example, a file is
    /// considered ignored if one of its parent directories is ignored)
    fn dir_status(&self, dir: &Path) -> f::Git {
        let path = reorient(dir);

        let s = self.statuses.iter()
            .filter(|p| if p.1 == git2::Status::IGNORED {
                path.starts_with(&p.0)
            } else {
                p.0.starts_with(&path)
            })
            .fold(git2::Status::empty(), |a, b| a | b.1);

        let staged = index_status(s);
        let unstaged = working_tree_status(s);
        f::Git { staged, unstaged }
    }
}

/// Converts a path to an absolute path based on the current directory.
/// Paths need to be absolute for them to be compared properly, otherwise
/// you’d ask a repo about “./README.md” but it only knows about
/// “/vagrant/README.md”, prefixed by the workdir.
fn reorient(path: &Path) -> PathBuf {
    use std::env::current_dir;

    // TODO: I’m not 100% on this func tbh
    let path = match current_dir() {
        Err(_)   => Path::new(".").join(&path),
        Ok(dir)  => dir.join(&path),
    };

    path.canonicalize().unwrap_or(path)
}

/// The character to display if the file has been modified, but not staged.
fn working_tree_status(status: git2::Status) -> f::GitStatus {
    match status {
        s if s.contains(git2::Status::WT_NEW)         => f::GitStatus::New,
        s if s.contains(git2::Status::WT_MODIFIED)    => f::GitStatus::Modified,
        s if s.contains(git2::Status::WT_DELETED)     => f::GitStatus::Deleted,
        s if s.contains(git2::Status::WT_RENAMED)     => f::GitStatus::Renamed,
        s if s.contains(git2::Status::WT_TYPECHANGE)  => f::GitStatus::TypeChange,
        s if s.contains(git2::Status::IGNORED)        => f::GitStatus::Ignored,
        s if s.contains(git2::Status::CONFLICTED)     => f::GitStatus::Conflicted,
        _                                             => f::GitStatus::NotModified,
    }
}

/// The character to display if the file has been modified and the change
/// has been staged.
fn index_status(status: git2::Status) -> f::GitStatus {
    match status {
        s if s.contains(git2::Status::INDEX_NEW)         => f::GitStatus::New,
        s if s.contains(git2::Status::INDEX_MODIFIED)    => f::GitStatus::Modified,
        s if s.contains(git2::Status::INDEX_DELETED)     => f::GitStatus::Deleted,
        s if s.contains(git2::Status::INDEX_RENAMED)     => f::GitStatus::Renamed,
        s if s.contains(git2::Status::INDEX_TYPECHANGE)  => f::GitStatus::TypeChange,
        _                                                => f::GitStatus::NotModified,
    }
}
<|endoftext|>


<commit_before>use crate::cpp_code_generator;
use crate::cpp_code_generator::generate_cpp_type_size_requester;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_code_generator;
use crate::versions;
use pathdiff::diff_paths;
use ritual_common::errors::{err_msg, Result};
use ritual_common::file_utils::copy_file;
use ritual_common::file_utils::copy_recursively;
use ritual_common::file_utils::create_dir;
use ritual_common::file_utils::create_dir_all;
use ritual_common::file_utils::create_file;
use ritual_common::file_utils::path_to_str;
use ritual_common::file_utils::read_dir;
use ritual_common::file_utils::remove_dir_all;
use ritual_common::file_utils::repo_crate_local_path;
use ritual_common::file_utils::save_json;
use ritual_common::file_utils::save_toml;
use ritual_common::toml;
use ritual_common::utils::run_command;
use ritual_common::utils::MapIfOk;
use ritual_common::BuildScriptData;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Merges `a` and `b` recursively. `b` take precedence over `a`.
fn recursive_merge_toml(a: toml::Value, b: toml::Value) -> toml::Value {
    if a.same_type(&b) {
        if let toml::Value::Array(mut a_array) = a {
            if let toml::Value::Array(mut b_array) = b {
                a_array.append(&mut b_array);
                toml::Value::Array(a_array)
            } else {
                unreachable!()
            }
        } else if let toml::Value::Table(mut a_table) = a {
            if let toml::Value::Table(b_table) = b {
                for (key, value) in b_table {
                    if let Some(old_value) = a_table.remove(&key) {
                        a_table.insert(key, recursive_merge_toml(old_value, value));
                    } else {
                        a_table.insert(key, value);
                    }
                }
                toml::Value::Table(a_table)
            } else {
                unreachable!()
            }
        } else {
            b
        }
    } else {
        b
    }
}

/// Generates `Cargo.toml` file and skeleton of the crate.
/// If a crate template was supplied, files from it are
/// copied to the output location.
fn generate_crate_template(data: &mut ProcessorData) -> Result<()> {
    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let template_build_rs_path =
        data.config
            .crate_template_path()
            .as_ref()
            .and_then(|crate_template_path| {
                let template_build_rs_path = crate_template_path.join("build.rs");
                if template_build_rs_path.exists() {
                    Some(template_build_rs_path)
                } else {
                    None
                }
            });
    let output_build_rs_path = output_path.join("build.rs");
    if let Some(ref template_build_rs_path) = template_build_rs_path {
        copy_file(template_build_rs_path, output_build_rs_path)?;
    } else {
        let mut rustfmt_file = create_file(&output_build_rs_path)?;
        rustfmt_file.write(include_str!("../templates/crate/build.rs"))?;
    }
    let cargo_toml_data = {
        let package = toml::value::Value::Table({
            let mut table = toml::value::Table::new();
            table.insert(
                "name".to_string(),
                toml::Value::String(data.config.crate_properties().name().into()),
            );
            table.insert(
                "version".to_string(),
                toml::Value::String(data.config.crate_properties().version().into()),
            );
            table.insert(
                "build".to_string(),
                toml::Value::String("build.rs".to_string()),
            );
            table.insert(
                "edition".to_string(),
                toml::Value::String("2018".to_string()),
            );
            table
        });
        let dep_value = |version: &str, local_path: Option<PathBuf>| -> Result<toml::Value> {
            Ok(
                if local_path.is_none() || !data.workspace.config().write_dependencies_local_paths {
                    toml::Value::String(version.to_string())
                } else {
                    toml::Value::Table({
                        let mut value = toml::value::Table::new();
                        value.insert(
                            "version".to_string(),
                            toml::Value::String(version.to_string()),
                        );
                        value.insert(
                            "path".to_string(),
                            toml::Value::String(
                                path_to_str(&local_path.expect("checked above"))?.to_string(),
                            ),
                        );
                        value
                    })
                },
            )
        };
        let dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_dependencies()
            {
                table.insert(
                    "cpp_utils".to_string(),
                    dep_value(
                        versions::CPP_UTILS_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("cpp_utils")?)
                        } else {
                            None
                        },
                    )?,
                );
                for dep in data.dep_databases {
                    let relative_path =
                        diff_paths(&data.workspace.crate_path(&dep.crate_name)?, &output_path)
                            .ok_or_else(|| {
                                err_msg("failed to get relative path to the dependency")
                            })?;

                    table.insert(
                        dep.crate_name.clone(),
                        dep_value(&dep.crate_version, Some(relative_path))?,
                    );
                }
            }
            for dep in data.config.crate_properties().dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let build_dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_build_dependencies()
            {
                table.insert(
                    "ritual_build".to_string(),
                    dep_value(
                        versions::RITUAL_BUILD_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("ritual_build")?)
                        } else {
                            None
                        },
                    )?,
                );
            }
            for dep in data.config.crate_properties().build_dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let mut table = toml::value::Table::new();
        table.insert("package".to_string(), package);
        table.insert("dependencies".to_string(), dependencies);
        table.insert("build-dependencies".to_string(), build_dependencies);
        recursive_merge_toml(
            toml::Value::Table(table),
            toml::Value::Table(data.config.crate_properties().custom_fields().clone()),
        )
    };
    save_toml(output_path.join("Cargo.toml"), &cargo_toml_data)?;

    if let Some(ref template_path) = data.config.crate_template_path() {
        for item in read_dir(template_path)? {
            let item = item?;
            let target = output_path.join(item.file_name());
            remove_dir_all(&target)?;
            copy_recursively(&item.path(), &target)?;
        }
    }
    if !output_path.join("src").exists() {
        create_dir_all(output_path.join("src"))?;
    }
    Ok(())
}

/// Generates main files and directories of the library.
fn generate_c_lib_template(
    lib_name: &str,
    lib_path: &Path,
    global_header_name: &str,
    include_directives: &[PathBuf],
) -> Result<()> {
    let name_upper = lib_name.to_uppercase();
    let cmakelists_path = lib_path.join("CMakeLists.txt");
    let mut cmakelists_file = create_file(&cmakelists_path)?;

    cmakelists_file.write(format!(
        include_str!("../templates/c_lib/CMakeLists.txt"),
        lib_name_lowercase = lib_name,
        lib_name_uppercase = name_upper
    ))?;

    let include_directives_code = include_directives
        .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
        .join("\n");

    let global_header_path = lib_path.join(&global_header_name);
    let mut global_header_file = create_file(&global_header_path)?;
    global_header_file.write(format!(
        include_str!("../templates/c_lib/global.h"),
        include_directives_code = include_directives_code
    ))?;
    Ok(())
}

fn run(data: &mut ProcessorData) -> Result<()> {
    generate_crate_template(data)?;

    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let c_lib_path = output_path.join("c_lib");
    if !c_lib_path.exists() {
        create_dir(&c_lib_path)?;
    }
    let c_lib_name = format!("{}_c", data.config.crate_properties().name());
    let global_header_name = format!("{}_global.h", c_lib_name);
    generate_c_lib_template(
        &c_lib_name,
        &c_lib_path,
        &global_header_name,
        data.config.include_directives(),
    )?;

    cpp_code_generator::generate_cpp_file(
        &data.current_database.cpp_items,
        &c_lib_path.join("file1.cpp"),
        &global_header_name,
    )?;

    let file = create_file(c_lib_path.join("sized_types.cxx"))?;
    generate_cpp_type_size_requester(
        &data.current_database.rust_database,
        data.config.include_directives(),
        file,
    )?;

    rust_code_generator::generate(
        data.config.crate_properties().name(),
        &data.current_database.rust_database,
        &output_path.join("src"),
    )?;

    run_command(Command::new("cargo").arg("fmt").current_dir(&output_path))?;
    run_command(
        Command::new("rustfmt")
            .arg("src/ffi.in.rs")
            .current_dir(&output_path),
    )?;

    save_json(
        output_path.join("build_script_data.json"),
        &BuildScriptData {
            cpp_build_config: data.config.cpp_build_config().clone(),
            cpp_wrapper_lib_name: c_lib_name,
            cpp_lib_version: data.config.cpp_lib_version().map(|s| s.to_string()),
        },
    )?;
    Ok(())
}

pub fn crate_writer_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new_const("crate_writer", run)
}
<commit_msg>fix deleting non-existent dirs in crate writer<commit_after>use crate::cpp_code_generator;
use crate::cpp_code_generator::generate_cpp_type_size_requester;
use crate::processor::ProcessingStep;
use crate::processor::ProcessorData;
use crate::rust_code_generator;
use crate::versions;
use pathdiff::diff_paths;
use ritual_common::errors::{err_msg, Result};
use ritual_common::file_utils::copy_file;
use ritual_common::file_utils::copy_recursively;
use ritual_common::file_utils::create_dir;
use ritual_common::file_utils::create_dir_all;
use ritual_common::file_utils::create_file;
use ritual_common::file_utils::path_to_str;
use ritual_common::file_utils::read_dir;
use ritual_common::file_utils::remove_dir_all;
use ritual_common::file_utils::repo_crate_local_path;
use ritual_common::file_utils::save_json;
use ritual_common::file_utils::save_toml;
use ritual_common::toml;
use ritual_common::utils::run_command;
use ritual_common::utils::MapIfOk;
use ritual_common::BuildScriptData;
use std::path::Path;
use std::path::PathBuf;
use std::process::Command;

/// Merges `a` and `b` recursively. `b` take precedence over `a`.
fn recursive_merge_toml(a: toml::Value, b: toml::Value) -> toml::Value {
    if a.same_type(&b) {
        if let toml::Value::Array(mut a_array) = a {
            if let toml::Value::Array(mut b_array) = b {
                a_array.append(&mut b_array);
                toml::Value::Array(a_array)
            } else {
                unreachable!()
            }
        } else if let toml::Value::Table(mut a_table) = a {
            if let toml::Value::Table(b_table) = b {
                for (key, value) in b_table {
                    if let Some(old_value) = a_table.remove(&key) {
                        a_table.insert(key, recursive_merge_toml(old_value, value));
                    } else {
                        a_table.insert(key, value);
                    }
                }
                toml::Value::Table(a_table)
            } else {
                unreachable!()
            }
        } else {
            b
        }
    } else {
        b
    }
}

/// Generates `Cargo.toml` file and skeleton of the crate.
/// If a crate template was supplied, files from it are
/// copied to the output location.
fn generate_crate_template(data: &mut ProcessorData) -> Result<()> {
    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let template_build_rs_path =
        data.config
            .crate_template_path()
            .as_ref()
            .and_then(|crate_template_path| {
                let template_build_rs_path = crate_template_path.join("build.rs");
                if template_build_rs_path.exists() {
                    Some(template_build_rs_path)
                } else {
                    None
                }
            });
    let output_build_rs_path = output_path.join("build.rs");
    if let Some(ref template_build_rs_path) = template_build_rs_path {
        copy_file(template_build_rs_path, output_build_rs_path)?;
    } else {
        let mut rustfmt_file = create_file(&output_build_rs_path)?;
        rustfmt_file.write(include_str!("../templates/crate/build.rs"))?;
    }
    let cargo_toml_data = {
        let package = toml::value::Value::Table({
            let mut table = toml::value::Table::new();
            table.insert(
                "name".to_string(),
                toml::Value::String(data.config.crate_properties().name().into()),
            );
            table.insert(
                "version".to_string(),
                toml::Value::String(data.config.crate_properties().version().into()),
            );
            table.insert(
                "build".to_string(),
                toml::Value::String("build.rs".to_string()),
            );
            table.insert(
                "edition".to_string(),
                toml::Value::String("2018".to_string()),
            );
            table
        });
        let dep_value = |version: &str, local_path: Option<PathBuf>| -> Result<toml::Value> {
            Ok(
                if local_path.is_none() || !data.workspace.config().write_dependencies_local_paths {
                    toml::Value::String(version.to_string())
                } else {
                    toml::Value::Table({
                        let mut value = toml::value::Table::new();
                        value.insert(
                            "version".to_string(),
                            toml::Value::String(version.to_string()),
                        );
                        value.insert(
                            "path".to_string(),
                            toml::Value::String(
                                path_to_str(&local_path.expect("checked above"))?.to_string(),
                            ),
                        );
                        value
                    })
                },
            )
        };
        let dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_dependencies()
            {
                table.insert(
                    "cpp_utils".to_string(),
                    dep_value(
                        versions::CPP_UTILS_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("cpp_utils")?)
                        } else {
                            None
                        },
                    )?,
                );
                for dep in data.dep_databases {
                    let relative_path =
                        diff_paths(&data.workspace.crate_path(&dep.crate_name)?, &output_path)
                            .ok_or_else(|| {
                                err_msg("failed to get relative path to the dependency")
                            })?;

                    table.insert(
                        dep.crate_name.clone(),
                        dep_value(&dep.crate_version, Some(relative_path))?,
                    );
                }
            }
            for dep in data.config.crate_properties().dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let build_dependencies = toml::Value::Table({
            let mut table = toml::value::Table::new();
            if !data
                .config
                .crate_properties()
                .should_remove_default_build_dependencies()
            {
                table.insert(
                    "ritual_build".to_string(),
                    dep_value(
                        versions::RITUAL_BUILD_VERSION,
                        if data.workspace.config().write_dependencies_local_paths {
                            Some(repo_crate_local_path("ritual_build")?)
                        } else {
                            None
                        },
                    )?,
                );
            }
            for dep in data.config.crate_properties().build_dependencies() {
                table.insert(
                    dep.name().to_string(),
                    dep_value(dep.version(), dep.local_path().cloned())?,
                );
            }
            table
        });
        let mut table = toml::value::Table::new();
        table.insert("package".to_string(), package);
        table.insert("dependencies".to_string(), dependencies);
        table.insert("build-dependencies".to_string(), build_dependencies);
        recursive_merge_toml(
            toml::Value::Table(table),
            toml::Value::Table(data.config.crate_properties().custom_fields().clone()),
        )
    };
    save_toml(output_path.join("Cargo.toml"), &cargo_toml_data)?;

    if let Some(ref template_path) = data.config.crate_template_path() {
        for item in read_dir(template_path)? {
            let item = item?;
            let target = output_path.join(item.file_name());
            if target.exists() {
                remove_dir_all(&target)?;
            }
            copy_recursively(&item.path(), &target)?;
        }
    }
    if !output_path.join("src").exists() {
        create_dir_all(output_path.join("src"))?;
    }
    Ok(())
}

/// Generates main files and directories of the library.
fn generate_c_lib_template(
    lib_name: &str,
    lib_path: &Path,
    global_header_name: &str,
    include_directives: &[PathBuf],
) -> Result<()> {
    let name_upper = lib_name.to_uppercase();
    let cmakelists_path = lib_path.join("CMakeLists.txt");
    let mut cmakelists_file = create_file(&cmakelists_path)?;

    cmakelists_file.write(format!(
        include_str!("../templates/c_lib/CMakeLists.txt"),
        lib_name_lowercase = lib_name,
        lib_name_uppercase = name_upper
    ))?;

    let include_directives_code = include_directives
        .map_if_ok(|d| -> Result<_> { Ok(format!("#include \"{}\"", path_to_str(d)?)) })?
        .join("\n");

    let global_header_path = lib_path.join(&global_header_name);
    let mut global_header_file = create_file(&global_header_path)?;
    global_header_file.write(format!(
        include_str!("../templates/c_lib/global.h"),
        include_directives_code = include_directives_code
    ))?;
    Ok(())
}

fn run(data: &mut ProcessorData) -> Result<()> {
    generate_crate_template(data)?;

    let output_path = data
        .workspace
        .crate_path(data.config.crate_properties().name())?;

    let c_lib_path = output_path.join("c_lib");
    if !c_lib_path.exists() {
        create_dir(&c_lib_path)?;
    }
    let c_lib_name = format!("{}_c", data.config.crate_properties().name());
    let global_header_name = format!("{}_global.h", c_lib_name);
    generate_c_lib_template(
        &c_lib_name,
        &c_lib_path,
        &global_header_name,
        data.config.include_directives(),
    )?;

    cpp_code_generator::generate_cpp_file(
        &data.current_database.cpp_items,
        &c_lib_path.join("file1.cpp"),
        &global_header_name,
    )?;

    let file = create_file(c_lib_path.join("sized_types.cxx"))?;
    generate_cpp_type_size_requester(
        &data.current_database.rust_database,
        data.config.include_directives(),
        file,
    )?;

    rust_code_generator::generate(
        data.config.crate_properties().name(),
        &data.current_database.rust_database,
        &output_path.join("src"),
    )?;

    run_command(Command::new("cargo").arg("fmt").current_dir(&output_path))?;
    run_command(
        Command::new("rustfmt")
            .arg("src/ffi.in.rs")
            .current_dir(&output_path),
    )?;

    save_json(
        output_path.join("build_script_data.json"),
        &BuildScriptData {
            cpp_build_config: data.config.cpp_build_config().clone(),
            cpp_wrapper_lib_name: c_lib_name,
            cpp_lib_version: data.config.cpp_lib_version().map(|s| s.to_string()),
        },
    )?;
    Ok(())
}

pub fn crate_writer_step() -> ProcessingStep {
    // TODO: set dependencies
    ProcessingStep::new_const("crate_writer", run)
}
<|endoftext|>