<commit_before>use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::last_path_segment;
use rustc_hir::{
    intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, MatchSource, Mutability, Pat, PatField, PatKind,
    QPath, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty::subst::SubstsRef;
use rustc_middle::ty::{AdtDef, FieldDef, Ty, TyKind, VariantDef};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;
use std::iter;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for patterns that aren't exact representations of the types
    /// they are applied to.
    ///
    /// To satisfy this lint, you will have to adjust either the expression that is matched
    /// against or the pattern itself, as well as the bindings that are introduced by the
    /// adjusted patterns. For matching you will have to either dereference the expression
    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
    /// to use the inverse. You can leave them as plain bindings if you wish for the value
    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
    /// a reference into the matched structure.
    ///
    /// If you are looking for a way to learn about ownership semantics in more detail, it
    /// is recommended to look at IDE options available to you to highlight types, lifetimes
    /// and reference semantics in your code. The available tooling would expose these things
    /// in a general way even outside of the various pattern matching mechanics. Of course
    /// this lint can still be used to highlight areas of interest and ensure a good understanding
    /// of ownership semantics.
    ///
    /// ### Why is this bad?
    /// It isn't bad in general. But in some contexts it can be desirable
    /// because it increases ownership hints in the code, and will guard against some changes
    /// in ownership.
    ///
    /// ### Example
    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
    /// is bound to a shared borrow via `ref inner`.
    ///
    /// ```rust,ignore
    /// // Bad
    /// let value = &Some(Box::new(23));
    /// match value {
    ///     Some(inner) => println!("{}", inner),
    ///     None => println!("none"),
    /// }
    ///
    /// // Good
    /// let value = &Some(Box::new(23));
    /// match *value {
    ///     Some(ref inner) => println!("{}", inner),
    ///     None => println!("none"),
    /// }
    /// ```
    ///
    /// The following example demonstrates one of the advantages of the more verbose style.
    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
    /// accidentally modify the wrong part of the structure.
    ///
    /// ```rust,ignore
    /// // Bad
    /// let mut values = vec![(2, 3), (3, 4)];
    /// for (a, b) in &mut values {
    ///     *a += *b;
    /// }
    ///
    /// // Good
    /// let mut values = vec![(2, 3), (3, 4)];
    /// for &mut (ref mut a, b) in &mut values {
    ///     *a += b;
    /// }
    /// ```
    pub PATTERN_TYPE_MISMATCH,
    restriction,
    "type of pattern does not match the expression type"
}

declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);

impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
        if let StmtKind::Local(local) = stmt.kind {
            if let Some(init) = &local.init {
                if let Some(init_ty) = cx.typeck_results().node_type_opt(init.hir_id) {
                    let pat = &local.pat;
                    if in_external_macro(cx.sess(), pat.span) {
                        return;
                    }
                    let deref_possible = match local.source {
                        LocalSource::Normal => DerefPossible::Possible,
                        _ => DerefPossible::Impossible,
                    };
                    apply_lint(cx, pat, init_ty, deref_possible);
                }
            }
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind {
            if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) {
                'pattern_checks: for arm in arms {
                    let pat = &arm.pat;
                    if in_external_macro(cx.sess(), pat.span) {
                        continue 'pattern_checks;
                    }
                    if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
                        break 'pattern_checks;
                    }
                }
            }
        }
        if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
            if let Some(expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
                if in_external_macro(cx.sess(), let_pat.span) {
                    return;
                }
                apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible);
            }
        }
    }

    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        _: Span,
        hir_id: HirId,
    ) {
        if let Some(fn_sig) = cx.typeck_results().liberated_fn_sigs().get(hir_id) {
            for (param, ty) in iter::zip(body.params, fn_sig.inputs()) {
                apply_lint(cx, param.pat, ty, DerefPossible::Impossible);
            }
        }
    }
}

#[derive(Debug, Clone, Copy)]
enum DerefPossible {
    Possible,
    Impossible,
}

fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, expr_ty: Ty<'tcx>, deref_possible: DerefPossible) -> bool {
    let maybe_mismatch = find_first_mismatch(cx, pat, expr_ty, Level::Top);
    if let Some((span, mutability, level)) = maybe_mismatch {
        span_lint_and_help(
            cx,
            PATTERN_TYPE_MISMATCH,
            span,
            "type of pattern does not match the expression type",
            None,
            &format!(
                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
                match (deref_possible, level) {
                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
                    _ => "",
                },
                match mutability {
                    Mutability::Mut => "&mut _",
                    Mutability::Not => "&_",
                },
            ),
        );
        true
    } else {
        false
    }
}

#[derive(Debug, Copy, Clone)]
enum Level {
    Top,
    Lower,
}

#[allow(rustc::usage_of_ty_tykind)]
fn find_first_mismatch<'tcx>(
    cx: &LateContext<'tcx>,
    pat: &Pat<'_>,
    ty: Ty<'tcx>,
    level: Level,
) -> Option<(Span, Mutability, Level)> {
    if let PatKind::Ref(sub_pat, _) = pat.kind {
        if let TyKind::Ref(_, sub_ty, _) = ty.kind() {
            return find_first_mismatch(cx, sub_pat, sub_ty, Level::Lower);
        }
    }

    if let TyKind::Ref(_, _, mutability) = *ty.kind() {
        if is_non_ref_pattern(&pat.kind) {
            return Some((pat.span, mutability, level));
        }
    }

    if let PatKind::Struct(ref qpath, field_pats, _) = pat.kind {
        if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
            if let Some(variant) = get_variant(adt_def, qpath) {
                let field_defs = &variant.fields;
                return find_first_mismatch_in_struct(cx, field_pats, field_defs, substs_ref);
            }
        }
    }

    if let PatKind::TupleStruct(ref qpath, pats, _) = pat.kind {
        if let TyKind::Adt(adt_def, substs_ref) = ty.kind() {
            if let Some(variant) = get_variant(adt_def, qpath) {
                let field_defs = &variant.fields;
                let ty_iter = field_defs.iter().map(|field_def| field_def.ty(cx.tcx, substs_ref));
                return find_first_mismatch_in_tuple(cx, pats, ty_iter);
            }
        }
    }

    if let PatKind::Tuple(pats, _) = pat.kind {
        if let TyKind::Tuple(..) = ty.kind() {
            return find_first_mismatch_in_tuple(cx, pats, ty.tuple_fields());
        }
    }

    if let PatKind::Or(sub_pats) = pat.kind {
        for pat in sub_pats {
            let maybe_mismatch = find_first_mismatch(cx, pat, ty, level);
            if let Some(mismatch) = maybe_mismatch {
                return Some(mismatch);
            }
        }
    }

    None
}

fn get_variant<'a>(adt_def: &'a AdtDef, qpath: &QPath<'_>) -> Option<&'a VariantDef> {
    if adt_def.is_struct() {
        if let Some(variant) = adt_def.variants.iter().next() {
            return Some(variant);
        }
    }

    if adt_def.is_enum() {
        let pat_ident = last_path_segment(qpath).ident;
        for variant in &adt_def.variants {
            if variant.ident == pat_ident {
                return Some(variant);
            }
        }
    }

    None
}

fn find_first_mismatch_in_tuple<'tcx, I>(
    cx: &LateContext<'tcx>,
    pats: &[Pat<'_>],
    ty_iter_src: I,
) -> Option<(Span, Mutability, Level)>
where
    I: IntoIterator<Item = Ty<'tcx>>,
{
    let mut field_tys = ty_iter_src.into_iter();
    'fields: for pat in pats {
        let field_ty = if let Some(ty) = field_tys.next() {
            ty
        } else {
            break 'fields;
        };

        let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
        if let Some(mismatch) = maybe_mismatch {
            return Some(mismatch);
        }
    }

    None
}

fn find_first_mismatch_in_struct<'tcx>(
    cx: &LateContext<'tcx>,
    field_pats: &[PatField<'_>],
    field_defs: &[FieldDef],
    substs_ref: SubstsRef<'tcx>,
) -> Option<(Span, Mutability, Level)> {
    for field_pat in field_pats {
        'definitions: for field_def in field_defs {
            if field_pat.ident == field_def.ident {
                let field_ty = field_def.ty(cx.tcx, substs_ref);
                let pat = &field_pat.pat;
                let maybe_mismatch = find_first_mismatch(cx, pat, field_ty, Level::Lower);
                if let Some(mismatch) = maybe_mismatch {
                    return Some(mismatch);
                }
                break 'definitions;
            }
        }
    }

    None
}

fn is_non_ref_pattern(pat_kind: &PatKind<'_>) -> bool {
    match pat_kind {
        PatKind::Struct(..) | PatKind::Tuple(..) | PatKind::TupleStruct(..) | PatKind::Path(..) => true,
        PatKind::Or(sub_pats) => sub_pats.iter().any(|pat| is_non_ref_pattern(&pat.kind)),
        _ => false,
    }
}
<commit_msg>clippy: Fix pattern_type_mismatch for loop<commit_after>use clippy_utils::diagnostics::span_lint_and_help;
use rustc_hir::{
    intravisit, Body, Expr, ExprKind, FnDecl, HirId, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_middle::ty;
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::source_map::Span;

declare_clippy_lint! {
    /// ### What it does
    /// Checks for patterns that aren't exact representations of the types
    /// they are applied to.
    ///
    /// To satisfy this lint, you will have to adjust either the expression that is matched
    /// against or the pattern itself, as well as the bindings that are introduced by the
    /// adjusted patterns. For matching you will have to either dereference the expression
    /// with the `*` operator, or amend the patterns to explicitly match against `&<pattern>`
    /// or `&mut <pattern>` depending on the reference mutability. For the bindings you need
    /// to use the inverse. You can leave them as plain bindings if you wish for the value
    /// to be copied, but you must use `ref mut <variable>` or `ref <variable>` to construct
    /// a reference into the matched structure.
    ///
    /// If you are looking for a way to learn about ownership semantics in more detail, it
    /// is recommended to look at IDE options available to you to highlight types, lifetimes
    /// and reference semantics in your code. The available tooling would expose these things
    /// in a general way even outside of the various pattern matching mechanics. Of course
    /// this lint can still be used to highlight areas of interest and ensure a good understanding
    /// of ownership semantics.
    ///
    /// ### Why is this bad?
    /// It isn't bad in general. But in some contexts it can be desirable
    /// because it increases ownership hints in the code, and will guard against some changes
    /// in ownership.
    ///
    /// ### Example
    /// This example shows the basic adjustments necessary to satisfy the lint. Note how
    /// the matched expression is explicitly dereferenced with `*` and the `inner` variable
    /// is bound to a shared borrow via `ref inner`.
    ///
    /// ```rust,ignore
    /// // Bad
    /// let value = &Some(Box::new(23));
    /// match value {
    ///     Some(inner) => println!("{}", inner),
    ///     None => println!("none"),
    /// }
    ///
    /// // Good
    /// let value = &Some(Box::new(23));
    /// match *value {
    ///     Some(ref inner) => println!("{}", inner),
    ///     None => println!("none"),
    /// }
    /// ```
    ///
    /// The following example demonstrates one of the advantages of the more verbose style.
    /// Note how the second version uses `ref mut a` to explicitly declare `a` a shared mutable
    /// borrow, while `b` is simply taken by value. This ensures that the loop body cannot
    /// accidentally modify the wrong part of the structure.
    ///
    /// ```rust,ignore
    /// // Bad
    /// let mut values = vec![(2, 3), (3, 4)];
    /// for (a, b) in &mut values {
    ///     *a += *b;
    /// }
    ///
    /// // Good
    /// let mut values = vec![(2, 3), (3, 4)];
    /// for &mut (ref mut a, b) in &mut values {
    ///     *a += b;
    /// }
    /// ```
    pub PATTERN_TYPE_MISMATCH,
    restriction,
    "type of pattern does not match the expression type"
}

declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);

impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
        if let StmtKind::Local(local) = stmt.kind {
            if in_external_macro(cx.sess(), local.pat.span) {
                return;
            }
            let deref_possible = match local.source {
                LocalSource::Normal => DerefPossible::Possible,
                _ => DerefPossible::Impossible,
            };
            apply_lint(cx, local.pat, deref_possible);
        }
    }

    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
        if let ExprKind::Match(_, arms, _) = expr.kind {
            for arm in arms {
                let pat = &arm.pat;
                if apply_lint(cx, pat, DerefPossible::Possible) {
                    break;
                }
            }
        }
        if let ExprKind::Let(let_pat, ..) = expr.kind {
            apply_lint(cx, let_pat, DerefPossible::Possible);
        }
    }

    fn check_fn(
        &mut self,
        cx: &LateContext<'tcx>,
        _: intravisit::FnKind<'tcx>,
        _: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        _: Span,
        _: HirId,
    ) {
        for param in body.params {
            apply_lint(cx, param.pat, DerefPossible::Impossible);
        }
    }
}

#[derive(Debug, Clone, Copy)]
enum DerefPossible {
    Possible,
    Impossible,
}

fn apply_lint<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
    let maybe_mismatch = find_first_mismatch(cx, pat);
    if let Some((span, mutability, level)) = maybe_mismatch {
        span_lint_and_help(
            cx,
            PATTERN_TYPE_MISMATCH,
            span,
            "type of pattern does not match the expression type",
            None,
            &format!(
                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
                match (deref_possible, level) {
                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
                    _ => "",
                },
                match mutability {
                    Mutability::Mut => "&mut _",
                    Mutability::Not => "&_",
                },
            ),
        );
        true
    } else {
        false
    }
}

#[derive(Debug, Copy, Clone)]
enum Level {
    Top,
    Lower,
}

#[allow(rustc::usage_of_ty_tykind)]
fn find_first_mismatch<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'_>) -> Option<(Span, Mutability, Level)> {
    let mut result = None;
    pat.walk(|p| {
        if result.is_some() {
            return false;
        }
        if in_external_macro(cx.sess(), p.span) {
            return true;
        }
        let adjust_pat = match p.kind {
            PatKind::Or([p, ..]) => p,
            _ => p,
        };
        if let Some(adjustments) = cx.typeck_results().pat_adjustments().get(adjust_pat.hir_id) {
            if let [first, ..] = **adjustments {
                if let ty::Ref(.., mutability) = *first.kind() {
                    let level = if p.hir_id == pat.hir_id {
                        Level::Top
                    } else {
                        Level::Lower
                    };
                    result = Some((p.span, mutability, level));
                }
            }
        }
        result.is_none()
    });
    result
}
<|endoftext|>


<commit_before>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::cmp;
use std::collections::BTreeMap;
use tikv_util::collections::HashSet;
use txn_types::TimeStamp;

// Resolver resolves timestamps that guarantee no more commit will happen before
// the timestamp.
pub struct Resolver {
    // start_ts -> locked keys.
    locks: BTreeMap<TimeStamp, HashSet<Vec<u8>>>,
    // The timestamps that guarantees no more commit will happen before.
    // None if the resolver is not initialized.
    resolved_ts: Option<TimeStamp>,
    // The timestamps that advance the resolved_ts when there is no more write.
    min_ts: TimeStamp,
}

impl Resolver {
    pub fn new() -> Resolver {
        Resolver {
            locks: BTreeMap::new(),
            resolved_ts: None,
            min_ts: TimeStamp::zero(),
        }
    }

    pub fn init(&mut self) {
        self.resolved_ts = Some(TimeStamp::zero());
    }

    pub fn resolved_ts(&self) -> Option<TimeStamp> {
        self.resolved_ts
    }

    pub fn locks(&self) -> &BTreeMap<TimeStamp, HashSet<Vec<u8>>> {
        &self.locks
    }

    pub fn track_lock(&mut self, start_ts: TimeStamp, key: Vec<u8>) {
        self.locks.entry(start_ts).or_default().insert(key);
    }

    pub fn untrack_lock(
        &mut self,
        start_ts: TimeStamp,
        commit_ts: Option<TimeStamp>,
        key: Vec<u8>,
    ) {
        if let Some(commit_ts) = commit_ts {
            assert!(
                self.resolved_ts.map_or(true, |rts| commit_ts > rts),
                "{}@{}, commit@{} > {:?}",
                hex::encode_upper(key),
                start_ts,
                commit_ts,
                self.resolved_ts
            );
            assert!(
                commit_ts > self.min_ts,
                "{}@{}, commit@{} > {:?}",
                hex::encode_upper(key),
                start_ts,
                commit_ts,
                self.min_ts
            );
        }

        let entry = self.locks.get_mut(&start_ts);
        // It's possible that rollback happens on a not existing transaction.
        assert!(
            entry.is_some() || commit_ts.is_none(),
            "{}@{} is not tracked",
            hex::encode_upper(key),
            start_ts
        );
        if let Some(locked_keys) = entry {
            assert!(
                locked_keys.remove(&key),
                "{}@{} is not tracked, {:?}",
                hex::encode_upper(key),
                start_ts,
                locked_keys,
            );
            if locked_keys.is_empty() {
                self.locks.remove(&start_ts);
            }
        }
    }

    /// Try to advance resolved ts.
    ///
    /// `min_ts` advances the resolver even if there is no write.
    /// Return None means the resolver is not initialized.
    pub fn resolve(&mut self, min_ts: TimeStamp) -> Option<TimeStamp> {
        let old_resolved_ts = self.resolved_ts?;

        // Find the min start ts.
        let min_lock = self.locks.keys().next().cloned();
        let has_lock = min_lock.is_some();
        let min_start_ts = min_lock.unwrap_or(min_ts);

        // No more commit happens before the ts.
        let new_resolved_ts = cmp::min(min_start_ts, min_ts);
        // Resolved ts never decrease.
        self.resolved_ts = Some(cmp::max(old_resolved_ts, new_resolved_ts));

        let new_min_ts = if has_lock {
            // If there are some lock, the min_ts must be smaller than
            // the min start ts, so it guarantees to be smaller than
            // any late arriving commit ts.
            new_resolved_ts // cmp::min(min_start_ts, min_ts)
        } else {
            min_ts
        };
        // Min ts never decrease.
        self.min_ts = cmp::max(self.min_ts, new_min_ts);

        self.resolved_ts
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use txn_types::Key;

    #[derive(Clone)]
    enum Event {
        Lock(u64, Key),
        Unlock(u64, Option<u64>, Key),
        // min_ts, expect
        Resolve(u64, u64),
    }

    #[test]
    fn test_resolve() {
        let cases = vec![
            vec![Event::Lock(1, Key::from_raw(b"a")), Event::Resolve(2, 1)],
            vec![
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Unlock(1, Some(2), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
            ],
            vec![
                Event::Lock(3, Key::from_raw(b"a")),
                Event::Unlock(3, Some(4), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
            ],
            vec![
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Unlock(1, Some(2), Key::from_raw(b"a")),
                Event::Lock(1, Key::from_raw(b"b")),
                Event::Resolve(2, 1),
            ],
            vec![
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Unlock(2, Some(3), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
                // Pessimistic txn may write a smaller start_ts.
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Resolve(2, 2),
                Event::Unlock(1, Some(4), Key::from_raw(b"a")),
                Event::Resolve(3, 3),
            ],
            vec![
                Event::Unlock(1, None, Key::from_raw(b"a")),
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Unlock(2, None, Key::from_raw(b"a")),
                Event::Unlock(2, None, Key::from_raw(b"a")),
                Event::Resolve(3, 3),
            ],
            vec![
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Resolve(4, 2),
                Event::Unlock(2, Some(3), Key::from_raw(b"a")),
                Event::Resolve(5, 5),
            ],
        ];

        for (i, case) in cases.into_iter().enumerate() {
            let mut resolver = Resolver::new();
            resolver.init();
            for e in case.clone() {
                match e {
                    Event::Lock(start_ts, key) => {
                        resolver.track_lock(start_ts.into(), key.into_raw().unwrap())
                    }
                    Event::Unlock(start_ts, commit_ts, key) => resolver.untrack_lock(
                        start_ts.into(),
                        commit_ts.map(Into::into),
                        key.into_raw().unwrap(),
                    ),
                    Event::Resolve(min_ts, expect) => assert_eq!(
                        resolver.resolve(min_ts.into()).unwrap(),
                        expect.into(),
                        "case {}",
                        i
                    ),
                }
            }

            let mut resolver = Resolver::new();
            for e in case {
                match e {
                    Event::Lock(start_ts, key) => {
                        resolver.track_lock(start_ts.into(), key.into_raw().unwrap())
                    }
                    Event::Unlock(start_ts, commit_ts, key) => resolver.untrack_lock(
                        start_ts.into(),
                        commit_ts.map(Into::into),
                        key.into_raw().unwrap(),
                    ),
                    Event::Resolve(min_ts, _) => {
                        assert_eq!(resolver.resolve(min_ts.into()), None, "case {}", i)
                    }
                }
            }
        }
    }
}
<commit_msg>cdc: fix tracing the lock of rollback txn (#7233)<commit_after>// Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0.

use std::cmp;
use std::collections::BTreeMap;
use tikv_util::collections::HashSet;
use txn_types::TimeStamp;

// Resolver resolves timestamps that guarantee no more commit will happen before
// the timestamp.
pub struct Resolver {
    // start_ts -> locked keys.
    locks: BTreeMap<TimeStamp, HashSet<Vec<u8>>>,
    // The timestamps that guarantees no more commit will happen before.
    // None if the resolver is not initialized.
    resolved_ts: Option<TimeStamp>,
    // The timestamps that advance the resolved_ts when there is no more write.
    min_ts: TimeStamp,
}

impl Resolver {
    pub fn new() -> Resolver {
        Resolver {
            locks: BTreeMap::new(),
            resolved_ts: None,
            min_ts: TimeStamp::zero(),
        }
    }

    pub fn init(&mut self) {
        self.resolved_ts = Some(TimeStamp::zero());
    }

    pub fn resolved_ts(&self) -> Option<TimeStamp> {
        self.resolved_ts
    }

    pub fn locks(&self) -> &BTreeMap<TimeStamp, HashSet<Vec<u8>>> {
        &self.locks
    }

    pub fn track_lock(&mut self, start_ts: TimeStamp, key: Vec<u8>) {
        self.locks.entry(start_ts).or_default().insert(key);
    }

    pub fn untrack_lock(
        &mut self,
        start_ts: TimeStamp,
        commit_ts: Option<TimeStamp>,
        key: Vec<u8>,
    ) {
        if let Some(commit_ts) = commit_ts {
            assert!(
                self.resolved_ts.map_or(true, |rts| commit_ts > rts),
                "{}@{}, commit@{} > {:?}",
                hex::encode_upper(key),
                start_ts,
                commit_ts,
                self.resolved_ts
            );
            assert!(
                commit_ts > self.min_ts,
                "{}@{}, commit@{} > {:?}",
                hex::encode_upper(key),
                start_ts,
                commit_ts,
                self.min_ts
            );
        }

        let entry = self.locks.get_mut(&start_ts);
        // It's possible that rollback happens on a not existing transaction.
        assert!(
            entry.is_some() || commit_ts.is_none(),
            "{}@{}, commit@{} is not tracked",
            hex::encode_upper(key),
            start_ts,
            commit_ts.unwrap_or_else(TimeStamp::zero),
        );
        if let Some(locked_keys) = entry {
            assert!(
                locked_keys.remove(&key) || commit_ts.is_none(),
                "{}@{}, commit@{} is not tracked, {:?}",
                hex::encode_upper(key),
                start_ts,
                commit_ts.unwrap_or_else(TimeStamp::zero),
                locked_keys,
            );
            if locked_keys.is_empty() {
                self.locks.remove(&start_ts);
            }
        }
    }

    /// Try to advance resolved ts.
    ///
    /// `min_ts` advances the resolver even if there is no write.
    /// Return None means the resolver is not initialized.
    pub fn resolve(&mut self, min_ts: TimeStamp) -> Option<TimeStamp> {
        let old_resolved_ts = self.resolved_ts?;

        // Find the min start ts.
        let min_lock = self.locks.keys().next().cloned();
        let has_lock = min_lock.is_some();
        let min_start_ts = min_lock.unwrap_or(min_ts);

        // No more commit happens before the ts.
        let new_resolved_ts = cmp::min(min_start_ts, min_ts);
        // Resolved ts never decrease.
        self.resolved_ts = Some(cmp::max(old_resolved_ts, new_resolved_ts));

        let new_min_ts = if has_lock {
            // If there are some lock, the min_ts must be smaller than
            // the min start ts, so it guarantees to be smaller than
            // any late arriving commit ts.
            new_resolved_ts // cmp::min(min_start_ts, min_ts)
        } else {
            min_ts
        };
        // Min ts never decrease.
        self.min_ts = cmp::max(self.min_ts, new_min_ts);

        self.resolved_ts
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use txn_types::Key;

    #[derive(Clone)]
    enum Event {
        Lock(u64, Key),
        Unlock(u64, Option<u64>, Key),
        // min_ts, expect
        Resolve(u64, u64),
    }

    #[test]
    fn test_resolve() {
        let cases = vec![
            vec![Event::Lock(1, Key::from_raw(b"a")), Event::Resolve(2, 1)],
            vec![
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Unlock(1, Some(2), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
            ],
            vec![
                Event::Lock(3, Key::from_raw(b"a")),
                Event::Unlock(3, Some(4), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
            ],
            vec![
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Unlock(1, Some(2), Key::from_raw(b"a")),
                Event::Lock(1, Key::from_raw(b"b")),
                Event::Resolve(2, 1),
            ],
            vec![
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Unlock(2, Some(3), Key::from_raw(b"a")),
                Event::Resolve(2, 2),
                // Pessimistic txn may write a smaller start_ts.
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Resolve(2, 2),
                Event::Unlock(1, Some(4), Key::from_raw(b"a")),
                Event::Resolve(3, 3),
            ],
            vec![
                Event::Unlock(1, None, Key::from_raw(b"a")),
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Unlock(2, None, Key::from_raw(b"a")),
                Event::Unlock(2, None, Key::from_raw(b"a")),
                Event::Resolve(3, 3),
            ],
            vec![
                Event::Lock(2, Key::from_raw(b"a")),
                Event::Resolve(4, 2),
                Event::Unlock(2, Some(3), Key::from_raw(b"a")),
                Event::Resolve(5, 5),
            ],
            // Rollback may contain a key that is not locked.
            vec![
                Event::Lock(1, Key::from_raw(b"a")),
                Event::Unlock(1, None, Key::from_raw(b"b")),
                Event::Unlock(1, None, Key::from_raw(b"a")),
            ],
        ];

        for (i, case) in cases.into_iter().enumerate() {
            let mut resolver = Resolver::new();
            resolver.init();
            for e in case.clone() {
                match e {
                    Event::Lock(start_ts, key) => {
                        resolver.track_lock(start_ts.into(), key.into_raw().unwrap())
                    }
                    Event::Unlock(start_ts, commit_ts, key) => resolver.untrack_lock(
                        start_ts.into(),
                        commit_ts.map(Into::into),
                        key.into_raw().unwrap(),
                    ),
                    Event::Resolve(min_ts, expect) => assert_eq!(
                        resolver.resolve(min_ts.into()).unwrap(),
                        expect.into(),
                        "case {}",
                        i
                    ),
                }
            }

            let mut resolver = Resolver::new();
            for e in case {
                match e {
                    Event::Lock(start_ts, key) => {
                        resolver.track_lock(start_ts.into(), key.into_raw().unwrap())
                    }
                    Event::Unlock(start_ts, commit_ts, key) => resolver.untrack_lock(
                        start_ts.into(),
                        commit_ts.map(Into::into),
                        key.into_raw().unwrap(),
                    ),
                    Event::Resolve(min_ts, _) => {
                        assert_eq!(resolver.resolve(min_ts.into()), None, "case {}", i)
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! States elements can be in.

#![deny(missing_docs)]

bitflags! {
    #[doc = "Event-based element states."]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub flags ElementState: u32 {
        #[doc = "The mouse is down on this element. \
                 https://html.spec.whatwg.org/multipage/#selector-active \
                 FIXME(#7333): set/unset this when appropriate"]
        const IN_ACTIVE_STATE = 0x01,
        #[doc = "This element has focus. \
                 https://html.spec.whatwg.org/multipage/#selector-focus"]
        const IN_FOCUS_STATE = 0x02,
        #[doc = "The mouse is hovering over this element. \
                 https://html.spec.whatwg.org/multipage/#selector-hover"]
        const IN_HOVER_STATE = 0x04,
        #[doc = "Content is enabled (and can be disabled). \
                 http://www.whatwg.org/html/#selector-enabled"]
        const IN_ENABLED_STATE = 0x08,
        #[doc = "Content is disabled. \
                 http://www.whatwg.org/html/#selector-disabled"]
        const IN_DISABLED_STATE = 0x10,
        #[doc = "Content is checked. \
                 https://html.spec.whatwg.org/multipage/#selector-checked"]
        const IN_CHECKED_STATE = 0x20,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-indeterminate"]
        const IN_INDETERMINATE_STATE = 0x40,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-placeholder-shown"]
        const IN_PLACEHOLDER_SHOWN_STATE = 0x80,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-target"]
        const IN_TARGET_STATE = 0x100,
        #[doc = "https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class"]
        const IN_FULLSCREEN_STATE = 0x200,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-valid"]
        const IN_VALID_STATE = 0x400,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-invalid"]
        const IN_INVALID_STATE = 0x800,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-valid"]
        const IN_MOZ_UI_VALID_STATE = 0x1000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken"]
        const IN_BROKEN_STATE = 0x2000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-user-disabled"]
        const IN_USER_DISABLED_STATE = 0x4000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-suppressed"]
        const IN_SUPPRESSED_STATE = 0x8000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading"]
        const IN_LOADING_STATE = 0x10000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-blocked"]
        const IN_HANDLER_BLOCKED_STATE = 0x20000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-disabled"]
        const IN_HANDLER_DISABLED_STATE = 0x40000,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-crashed"]
        const IN_HANDLER_CRASHED_STATE = 0x80000,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-read-write"]
        const IN_READ_WRITE_STATE = 0x100000,
    }
}
<commit_msg>Auto merge of #16085 - bzbarsky:better-numbering-of-pseudoclasses, r=emilio<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! States elements can be in.

#![deny(missing_docs)]

bitflags! {
    #[doc = "Event-based element states."]
    #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
    pub flags ElementState: u32 {
        #[doc = "The mouse is down on this element. \
                 https://html.spec.whatwg.org/multipage/#selector-active \
                 FIXME(#7333): set/unset this when appropriate"]
        const IN_ACTIVE_STATE = 1 << 0,
        #[doc = "This element has focus. \
                 https://html.spec.whatwg.org/multipage/#selector-focus"]
        const IN_FOCUS_STATE = 1 << 1,
        #[doc = "The mouse is hovering over this element. \
                 https://html.spec.whatwg.org/multipage/#selector-hover"]
        const IN_HOVER_STATE = 1 << 2,
        #[doc = "Content is enabled (and can be disabled). \
                 http://www.whatwg.org/html/#selector-enabled"]
        const IN_ENABLED_STATE = 1 << 3,
        #[doc = "Content is disabled. \
                 http://www.whatwg.org/html/#selector-disabled"]
        const IN_DISABLED_STATE = 1 << 4,
        #[doc = "Content is checked. \
                 https://html.spec.whatwg.org/multipage/#selector-checked"]
        const IN_CHECKED_STATE = 1 << 5,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-indeterminate"]
        const IN_INDETERMINATE_STATE = 1 << 6,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-placeholder-shown"]
        const IN_PLACEHOLDER_SHOWN_STATE = 1 << 7,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-target"]
        const IN_TARGET_STATE = 1 << 8,
        #[doc = "https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class"]
        const IN_FULLSCREEN_STATE = 1 << 9,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-valid"]
        const IN_VALID_STATE = 1 << 10,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-invalid"]
        const IN_INVALID_STATE = 1 << 11,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-ui-valid"]
        const IN_MOZ_UI_VALID_STATE = 1 << 12,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-broken"]
        const IN_BROKEN_STATE = 1 << 13,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-user-disabled"]
        const IN_USER_DISABLED_STATE = 1 << 14,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-suppressed"]
        const IN_SUPPRESSED_STATE = 1 << 15,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-loading"]
        const IN_LOADING_STATE = 1 << 16,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-blocked"]
        const IN_HANDLER_BLOCKED_STATE = 1 << 17,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-disabled"]
        const IN_HANDLER_DISABLED_STATE = 1 << 18,
        #[doc = "Non-standard: https://developer.mozilla.org/en-US/docs/Web/CSS/:-moz-handler-crashed"]
        const IN_HANDLER_CRASHED_STATE = 1 << 19,
        #[doc = "https://html.spec.whatwg.org/multipage/#selector-read-write"]
        const IN_READ_WRITE_STATE = 1 << 20,
    }
}
<|endoftext|>


<commit_before>use ra_ide_db::{
    defs::Definition, imports_locator::ImportsLocator, search::Reference, RootDatabase,
};
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstNode, NameOwner},
    SourceFile, SyntaxNode, TextRange, TextSize,
};
use stdx::format_to;

use crate::{
    assist_context::{AssistBuilder, AssistDirector},
    utils::insert_use_statement,
    AssistContext, AssistId, Assists,
};
use ast::{ArgListOwner, VisibilityOwner};
use hir::{AsName, EnumVariant, Module, ModuleDef};
use ra_db::FileId;
use ra_fmt::leading_indent;
use rustc_hash::FxHashSet;

// Assist extract_struct_from_enum
//
// Extracts a struct from enum variant
//
// ```
// enum A { <|>One(u32, u32) }
// ```
// ->
// ```
// struct One(pub u32, pub u32);
//
// enum A { One(One) }"
// ```
pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
    let field_list = match variant.kind() {
        ast::StructKind::Tuple(field_list) => field_list,
        _ => return None,
    };
    let variant_name = variant.name()?.to_string();
    let enum_ast = variant.parent_enum();
    let enum_name = enum_ast.name()?.to_string();
    let visibility = enum_ast.visibility();
    let variant_hir = ctx.sema.to_def(&variant)?;

    if existing_struct_def(ctx.db, &variant_name, &variant_hir) {
        return None;
    }

    let target = variant.syntax().text_range();
    return acc.add_in_multiple_files(
        AssistId("extract_struct_from_enum_variant"),
        "Extract struct from enum variant",
        target,
        |edit| {
            let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
            let res = definition.find_usages(&ctx.db, None);
            let module_def = mod_def_for_target_module(ctx, &enum_name);
            let start_offset = variant.parent_enum().syntax().text_range().start();
            let mut visited_modules_set: FxHashSet<Module> = FxHashSet::default();
            visited_modules_set.insert(module_def.module(ctx.db).unwrap());
            for reference in res {
                let source_file = ctx.sema.parse(reference.file_range.file_id);
                update_reference(
                    ctx,
                    edit,
                    reference,
                    &source_file,
                    &module_def,
                    &mut visited_modules_set,
                );
            }
            extract_struct_def(
                edit,
                enum_ast.syntax(),
                &variant_name,
                &field_list.to_string(),
                start_offset,
                ctx.frange.file_id,
                &visibility,
            );
            let list_range = field_list.syntax().text_range();
            update_variant(edit, &variant_name, ctx.frange.file_id, list_range);
        },
    );
}

fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
    variant
        .parent_enum(db)
        .module(db)
        .scope(db, None)
        .into_iter()
        .any(|(name, _)| name.to_string() == variant_name.to_string())
}

fn mod_def_for_target_module(ctx: &AssistContext, enum_name: &str) -> ModuleDef {
    ImportsLocator::new(ctx.db).find_imports(enum_name).first().unwrap().left().unwrap()
}

fn insert_import(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    path: &ast::PathExpr,
    module: &Module,
    module_def: &ModuleDef,
    path_segment: ast::NameRef,
) -> Option<()> {
    let db = ctx.db;
    let mod_path = module.find_use_path(db, module_def.clone());
    if let Some(mut mod_path) = mod_path {
        mod_path.segments.pop();
        mod_path.segments.push(path_segment.as_name());
        insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
    }
    Some(())
}

fn extract_struct_def(
    edit: &mut AssistDirector,
    enum_ast: &SyntaxNode,
    variant_name: &str,
    variant_list: &str,
    start_offset: TextSize,
    file_id: FileId,
    visibility: &Option<ast::Visibility>,
) -> Option<()> {
    let visibility_string = if let Some(visibility) = visibility {
        format!("{} ", visibility.to_string())
    } else {
        "".to_string()
    };
    let mut buf = String::new();
    let indent = if let Some(indent) = leading_indent(enum_ast) {
        indent.to_string()
    } else {
        "".to_string()
    };

    format_to!(
        buf,
        r#"{}struct {}{};

{}"#,
        visibility_string,
        variant_name,
        list_with_visibility(variant_list),
        indent
    );
    edit.perform(file_id, |builder| {
        builder.insert(start_offset, buf);
    });
    Some(())
}

fn update_variant(
    edit: &mut AssistDirector,
    variant_name: &str,
    file_id: FileId,
    list_range: TextRange,
) -> Option<()> {
    let inside_variant_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    edit.perform(file_id, |builder| {
        builder.set_file(file_id);
        builder.replace(inside_variant_range, variant_name);
    });
    Some(())
}

fn update_reference(
    ctx: &AssistContext,
    edit: &mut AssistDirector,
    reference: Reference,
    source_file: &SourceFile,
    module_def: &ModuleDef,
    visited_modules_set: &mut FxHashSet<Module>,
) -> Option<()> {
    let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
        source_file.syntax(),
        reference.file_range.range.start(),
    )?;
    let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
    let list = call.arg_list()?;
    let segment = path_expr.path()?.segment()?;
    let list_range = list.syntax().text_range();
    let inside_list_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    edit.perform(reference.file_range.file_id, |builder| {
        let module = ctx.sema.scope(&path_expr.syntax()).module().unwrap();
        if !visited_modules_set.contains(&module) {
            if insert_import(
                ctx,
                builder,
                &path_expr,
                &module,
                module_def,
                segment.name_ref().unwrap(),
            )
            .is_some()
            {
                visited_modules_set.insert(module);
            }
        }
        builder.replace(inside_list_range, format!("{}{}", segment, list));
    });
    Some(())
}

fn list_with_visibility(list: &str) -> String {
    list.split(',')
        .map(|part| {
            let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
            let mut mod_part = part.trim().to_string();
            mod_part.insert_str(index, "pub ");
            mod_part
        })
        .collect::<Vec<String>>()
        .join(", ")
}

#[cfg(test)]
mod tests {

    use crate::{
        tests::{check_assist, check_assist_not_applicable},
        utils::FamousDefs,
    };

    use super::*;

    #[test]
    fn test_extract_struct_several_fields() {
        check_assist(
            extract_struct_from_enum,
            "enum A { <|>One(u32, u32) }",
            r#"struct One(pub u32, pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_one_field() {
        check_assist(
            extract_struct_from_enum,
            "enum A { <|>One(u32) }",
            r#"struct One(pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_pub_visibility() {
        check_assist(
            extract_struct_from_enum,
            "pub enum A { <|>One(u32, u32) }",
            r#"pub struct One(pub u32, pub u32);

pub enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_with_complex_imports() {
        check_assist(
            extract_struct_from_enum,
            r#"mod my_mod {
    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(1, 1);
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(1, 1);
        }

        pub enum MyEnum {
            <|>MyField(u8, u8),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
}"#,
            r#"use my_mod::my_other_mod::MyField;

mod my_mod {
    use my_other_mod::MyField;

    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(MyField(1, 1));
        }

        pub struct MyField(pub u8, pub u8);

        pub enum MyEnum {
            MyField(MyField),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
}"#,
        );
    }

    fn check_not_applicable(ra_fixture: &str) {
        let fixture =
            format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
        check_assist_not_applicable(extract_struct_from_enum, &fixture)
    }

    #[test]
    fn test_extract_enum_not_applicable_for_element_with_no_fields() {
        check_not_applicable("enum A { <|>One }");
    }

    #[test]
    fn test_extract_enum_not_applicable_if_struct_exists() {
        check_not_applicable(
            r#"struct One;
        enum A { <|>One(u8) }"#,
        );
    }
}
<commit_msg>Remove unwraps where possible<commit_after>use ra_ide_db::{
    defs::Definition, imports_locator::ImportsLocator, search::Reference, RootDatabase,
};
use ra_syntax::{
    algo::find_node_at_offset,
    ast::{self, AstNode, NameOwner},
    SourceFile, SyntaxNode, TextRange, TextSize,
};
use stdx::format_to;

use crate::{
    assist_context::{AssistBuilder, AssistDirector},
    utils::insert_use_statement,
    AssistContext, AssistId, Assists,
};
use ast::{ArgListOwner, VisibilityOwner};
use hir::{AsName, EnumVariant, Module, ModuleDef};
use ra_db::FileId;
use ra_fmt::leading_indent;
use rustc_hash::FxHashSet;

// Assist extract_struct_from_enum
//
// Extracts a struct from enum variant
//
// ```
// enum A { <|>One(u32, u32) }
// ```
// ->
// ```
// struct One(pub u32, pub u32);
//
// enum A { One(One) }"
// ```
pub(crate) fn extract_struct_from_enum(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let variant = ctx.find_node_at_offset::<ast::EnumVariant>()?;
    let field_list = match variant.kind() {
        ast::StructKind::Tuple(field_list) => field_list,
        _ => return None,
    };
    let variant_name = variant.name()?.to_string();
    let variant_hir = ctx.sema.to_def(&variant)?;
    if existing_struct_def(ctx.db, &variant_name, &variant_hir) {
        return None;
    }
    let enum_ast = variant.parent_enum();
    let enum_name = enum_ast.name()?.to_string();
    let visibility = enum_ast.visibility();
    let current_module_def =
        ImportsLocator::new(ctx.db).find_imports(&enum_name).first()?.left()?;
    let current_module = current_module_def.module(ctx.db)?;
    let target = variant.syntax().text_range();
    return acc.add_in_multiple_files(
        AssistId("extract_struct_from_enum_variant"),
        "Extract struct from enum variant",
        target,
        |edit| {
            let definition = Definition::ModuleDef(ModuleDef::EnumVariant(variant_hir));
            let res = definition.find_usages(&ctx.db, None);
            let start_offset = variant.parent_enum().syntax().text_range().start();
            let mut visited_modules_set: FxHashSet<Module> = FxHashSet::default();
            visited_modules_set.insert(current_module);
            for reference in res {
                let source_file = ctx.sema.parse(reference.file_range.file_id);
                update_reference(
                    ctx,
                    edit,
                    reference,
                    &source_file,
                    &current_module_def,
                    &mut visited_modules_set,
                );
            }
            extract_struct_def(
                edit,
                enum_ast.syntax(),
                &variant_name,
                &field_list.to_string(),
                start_offset,
                ctx.frange.file_id,
                &visibility,
            );
            let list_range = field_list.syntax().text_range();
            update_variant(edit, &variant_name, ctx.frange.file_id, list_range);
        },
    );
}

fn existing_struct_def(db: &RootDatabase, variant_name: &str, variant: &EnumVariant) -> bool {
    variant
        .parent_enum(db)
        .module(db)
        .scope(db, None)
        .into_iter()
        .any(|(name, _)| name.to_string() == variant_name.to_string())
}

fn insert_import(
    ctx: &AssistContext,
    builder: &mut AssistBuilder,
    path: &ast::PathExpr,
    module: &Module,
    module_def: &ModuleDef,
    path_segment: ast::NameRef,
) -> Option<()> {
    let db = ctx.db;
    let mod_path = module.find_use_path(db, module_def.clone());
    if let Some(mut mod_path) = mod_path {
        mod_path.segments.pop();
        mod_path.segments.push(path_segment.as_name());
        insert_use_statement(path.syntax(), &mod_path, ctx, builder.text_edit_builder());
    }
    Some(())
}

fn extract_struct_def(
    edit: &mut AssistDirector,
    enum_ast: &SyntaxNode,
    variant_name: &str,
    variant_list: &str,
    start_offset: TextSize,
    file_id: FileId,
    visibility: &Option<ast::Visibility>,
) -> Option<()> {
    let visibility_string = if let Some(visibility) = visibility {
        format!("{} ", visibility.to_string())
    } else {
        "".to_string()
    };
    let mut buf = String::new();
    let indent = if let Some(indent) = leading_indent(enum_ast) {
        indent.to_string()
    } else {
        "".to_string()
    };

    format_to!(
        buf,
        r#"{}struct {}{};

{}"#,
        visibility_string,
        variant_name,
        list_with_visibility(variant_list),
        indent
    );
    edit.perform(file_id, |builder| {
        builder.insert(start_offset, buf);
    });
    Some(())
}

fn update_variant(
    edit: &mut AssistDirector,
    variant_name: &str,
    file_id: FileId,
    list_range: TextRange,
) -> Option<()> {
    let inside_variant_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    edit.perform(file_id, |builder| {
        builder.set_file(file_id);
        builder.replace(inside_variant_range, variant_name);
    });
    Some(())
}

fn update_reference(
    ctx: &AssistContext,
    edit: &mut AssistDirector,
    reference: Reference,
    source_file: &SourceFile,
    module_def: &ModuleDef,
    visited_modules_set: &mut FxHashSet<Module>,
) -> Option<()> {
    let path_expr: ast::PathExpr = find_node_at_offset::<ast::PathExpr>(
        source_file.syntax(),
        reference.file_range.range.start(),
    )?;
    let call = path_expr.syntax().parent().and_then(ast::CallExpr::cast)?;
    let list = call.arg_list()?;
    let segment = path_expr.path()?.segment()?;
    let segment_name = segment.name_ref()?;
    let module = ctx.sema.scope(&path_expr.syntax()).module()?;
    let list_range = list.syntax().text_range();
    let inside_list_range = TextRange::new(
        list_range.start().checked_add(TextSize::from(1))?,
        list_range.end().checked_sub(TextSize::from(1))?,
    );
    edit.perform(reference.file_range.file_id, |builder| {
        if !visited_modules_set.contains(&module) {
            if insert_import(ctx, builder, &path_expr, &module, module_def, segment_name).is_some()
            {
                visited_modules_set.insert(module);
            }
        }
        builder.replace(inside_list_range, format!("{}{}", segment, list));
    });
    Some(())
}

fn list_with_visibility(list: &str) -> String {
    list.split(',')
        .map(|part| {
            let index = if part.chars().next().unwrap() == '(' { 1usize } else { 0 };
            let mut mod_part = part.trim().to_string();
            mod_part.insert_str(index, "pub ");
            mod_part
        })
        .collect::<Vec<String>>()
        .join(", ")
}

#[cfg(test)]
mod tests {

    use crate::{
        tests::{check_assist, check_assist_not_applicable},
        utils::FamousDefs,
    };

    use super::*;

    #[test]
    fn test_extract_struct_several_fields() {
        check_assist(
            extract_struct_from_enum,
            "enum A { <|>One(u32, u32) }",
            r#"struct One(pub u32, pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_one_field() {
        check_assist(
            extract_struct_from_enum,
            "enum A { <|>One(u32) }",
            r#"struct One(pub u32);

enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_pub_visibility() {
        check_assist(
            extract_struct_from_enum,
            "pub enum A { <|>One(u32, u32) }",
            r#"pub struct One(pub u32, pub u32);

pub enum A { One(One) }"#,
        );
    }

    #[test]
    fn test_extract_struct_with_complex_imports() {
        check_assist(
            extract_struct_from_enum,
            r#"mod my_mod {
    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(1, 1);
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(1, 1);
        }

        pub enum MyEnum {
            <|>MyField(u8, u8),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(1, 1);
}"#,
            r#"use my_mod::my_other_mod::MyField;

mod my_mod {
    use my_other_mod::MyField;

    fn another_fn() {
        let m = my_other_mod::MyEnum::MyField(MyField(1, 1));
    }

    pub mod my_other_mod {
        fn another_fn() {
            let m = MyEnum::MyField(MyField(1, 1));
        }

        pub struct MyField(pub u8, pub u8);

        pub enum MyEnum {
            MyField(MyField),
        }
    }
}

fn another_fn() {
    let m = my_mod::my_other_mod::MyEnum::MyField(MyField(1, 1));
}"#,
        );
    }

    fn check_not_applicable(ra_fixture: &str) {
        let fixture =
            format!("//- main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
        check_assist_not_applicable(extract_struct_from_enum, &fixture)
    }

    #[test]
    fn test_extract_enum_not_applicable_for_element_with_no_fields() {
        check_not_applicable("enum A { <|>One }");
    }

    #[test]
    fn test_extract_enum_not_applicable_if_struct_exists() {
        check_not_applicable(
            r#"struct One;
        enum A { <|>One(u8) }"#,
        );
    }
}
<|endoftext|>


<commit_before>//! Byte swap intrinsics.
#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

/// Returns an integer with the reversed byte order of x
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_bswap)
#[inline]
#[cfg_attr(test, assert_instr(bswap))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _bswap(x: i32) -> i32 {
    bswap_i32(x)
}

#[allow(improper_ctypes)]
extern "C" {
    #[link_name = "llvm.bswap.i32"]
    fn bswap_i32(x: i32) -> i32;
}

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

    #[test]
    fn test_bswap() {
        unsafe {
            assert_eq!(_bswap(0x0EADBE0F), 0x0FBEAD0E);
            assert_eq!(_bswap(0x00000000), 0x00000000);
        }
    }
}
<commit_msg>Use <i32>::swap_bytes instead of llvm.bswap.i32<commit_after>//! Byte swap intrinsics.
#![allow(clippy::module_name_repetitions)]

#[cfg(test)]
use stdarch_test::assert_instr;

/// Returns an integer with the reversed byte order of x
///
/// [Intel's documentation](https://software.intel.com/sites/landingpage/IntrinsicsGuide/#text=_bswap)
#[inline]
#[cfg_attr(test, assert_instr(bswap))]
#[stable(feature = "simd_x86", since = "1.27.0")]
pub unsafe fn _bswap(x: i32) -> i32 {
    x.swap_bytes()
}

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

    #[test]
    fn test_bswap() {
        unsafe {
            assert_eq!(_bswap(0x0EADBE0F), 0x0FBEAD0E);
            assert_eq!(_bswap(0x00000000), 0x00000000);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>completed functions<commit_after>

//! expression returns a value while statement do not

fn main() {
    println!("2 + 3 = {}", sum(2, 3));
    println!("2 ** 2 = {}", square(2));

    // this is nice
    let c: fn(i32) -> i32 = cubeme;

    // let c = cubeme;

    println!("2 ** 3 = {}", c(2));

    // panic only after completing above task
    // divergent();

    // it can be used as any type
    let att = divergent();
}

// parameters must declare types
// return type is optional
fn sum(a: u32, b: u32) -> u32 {
    // last expression always returns
    // should not use ; which gives ()
    a + b
}

fn square(a: u32) -> u32 {
    // rust has return also for some of the cases
    // power operation needs importing from std lib
    return a * a;
}

fn divergent() -> ! {
    // this function doesnot returns
    // makes a crash
    panic!("I am having panic attack :-(");
}

fn cubeme(a: i32) -> i32 {
    a * a * a
}

<|endoftext|>


<commit_before>struct Solution;

impl Solution {
    pub fn is_match(s: String, p: String) -> bool {
        Self::is_match_bytes(s.as_bytes(), p.as_bytes())
    }

    fn is_match_bytes(s: &[u8], p: &[u8]) -> bool {
        if p.len() == 0 {
            return s.len() == 0;
        }

        let first_match = s.len() != 0 && (s[0] == p[0] || p[0] == '.' as u8);

        if p.len() > 1 && p[1] == '*' as u8 {
            return (first_match && Self::is_match_bytes(&s[1..], p))
                || (Self::is_match_bytes(s, &p[2..]));
        }

        first_match && Self::is_match_bytes(&s[1..], &p[1..])
    }

    pub fn is_match2(s: String, p: String) -> bool {
        let mut memo = vec![vec![None; p.len() + 1]; s.len() + 1];
        Self::dp2(s.as_bytes(), p.as_bytes(), 0, 0, &mut memo)
    }

    fn dp2(s: &[u8], p: &[u8], i: usize, j: usize, memo: &mut Vec<Vec<Option<bool>>>) -> bool {
        if memo[i][j].is_some() {
            return memo[i][j].unwrap();
        }

        let ans;
        if j == p.len() {
            ans = i == s.len();
        } else {
            let first_match = i < s.len() && (s[i] == p[j] || p[j] == '.' as u8);
            if j + 1 < p.len() && p[j + 1] == '*' as u8 {
                ans = (first_match && Self::dp2(s, p, i + 1, j, memo))
                    || (Self::dp2(s, p, i, j + 2, memo))
            } else {
                ans = first_match && Self::dp2(s, p, i + 1, j + 1, memo)
            }
        }

        memo[i][j] = Some(ans);
        ans
    }

    pub fn is_match3(s: String, p: String) -> bool {
        let s = s.as_bytes();
        let p = p.as_bytes();
        let (slen, plen) = (s.len(), p.len());
        let mut memo = vec![vec![None; plen + 1]; slen + 1];
        memo[slen][plen] = Some(true);

        for i in (0..=slen).rev() {
            for j in (0..plen).rev() {
                let first_match = i < slen && (s[i] == p[j] || p[j] == '.' as u8);
                if j + 1 < plen && p[j + 1] == '*' as u8 {
                    memo[i][j] = Some(
                        (first_match && memo[i + 1][j] == Some(true))
                            || memo[i][j + 2] == Some(true),
                    );
                } else {
                    memo[i][j] = Some(first_match && memo[i + 1][j + 1] == Some(true));
                }
            }
        }

        memo[0][0] == Some(true)
    }
}

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

    #[test]
    fn test_is_match() {
        assert_eq!(Solution::is_match("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
    }

    #[test]
    fn test_is_match2() {
        assert_eq!(Solution::is_match2("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match2("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match2("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
        assert_eq!(
            Solution::is_match2("ab".to_owned(), ".*c".to_owned()),
            false
        );
    }

    #[test]
    fn test_is_match3() {
        assert_eq!(Solution::is_match3("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match3("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match3("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
        assert_eq!(
            Solution::is_match3("ab".to_owned(), ".*c".to_owned()),
            false
        );
    }
}
<commit_msg>再做一次：10-正则表达式匹配。<commit_after>struct Solution;

impl Solution {
    pub fn is_match(s: String, p: String) -> bool {
        Self::is_match_bytes(s.as_bytes(), p.as_bytes())
    }

    fn is_match_bytes(s: &[u8], p: &[u8]) -> bool {
        if p.len() == 0 {
            return s.len() == 0;
        }

        let first_match = s.len() != 0 && (s[0] == p[0] || p[0] == '.' as u8);

        if p.len() > 1 && p[1] == '*' as u8 {
            return (first_match && Self::is_match_bytes(&s[1..], p))
                || (Self::is_match_bytes(s, &p[2..]));
        }

        first_match && Self::is_match_bytes(&s[1..], &p[1..])
    }

    pub fn is_match2(s: String, p: String) -> bool {
        let mut memo = vec![vec![None; p.len() + 1]; s.len() + 1];
        Self::dp2(s.as_bytes(), p.as_bytes(), 0, 0, &mut memo)
    }

    fn dp2(s: &[u8], p: &[u8], i: usize, j: usize, memo: &mut Vec<Vec<Option<bool>>>) -> bool {
        if memo[i][j].is_some() {
            return memo[i][j].unwrap();
        }

        let ans;
        if j == p.len() {
            ans = i == s.len();
        } else {
            let first_match = i < s.len() && (s[i] == p[j] || p[j] == '.' as u8);
            if j + 1 < p.len() && p[j + 1] == '*' as u8 {
                ans = (first_match && Self::dp2(s, p, i + 1, j, memo))
                    || (Self::dp2(s, p, i, j + 2, memo))
            } else {
                ans = first_match && Self::dp2(s, p, i + 1, j + 1, memo)
            }
        }

        memo[i][j] = Some(ans);
        ans
    }

    pub fn is_match3(s: String, p: String) -> bool {
        let s = s.as_bytes();
        let p = p.as_bytes();
        let (slen, plen) = (s.len(), p.len());
        let mut memo = vec![vec![None; plen + 1]; slen + 1]; // 这里不需要用 Option, 见 is_match4
        memo[slen][plen] = Some(true);

        for i in (0..=slen).rev() {
            for j in (0..plen).rev() {
                let first_match = i < slen && (s[i] == p[j] || p[j] == '.' as u8);
                if j + 1 < plen && p[j + 1] == '*' as u8 {
                    memo[i][j] = Some(
                        (first_match && memo[i + 1][j] == Some(true))
                            || memo[i][j + 2] == Some(true),
                    );
                } else {
                    memo[i][j] = Some(first_match && memo[i + 1][j + 1] == Some(true));
                }
                // println!("m[{}][{}] = {:?}", i, j, memo[i][j]);
            }
        }

        memo[0][0] == Some(true)
    }

    // 2020-06-20 再做一次，独立完成。
    pub fn is_match4(s: String, p: String) -> bool {
        let s = s.as_bytes();
        let p = p.as_bytes();

        let mut memo = vec![vec![false; p.len() + 1]; s.len() + 1];
        memo[s.len()][p.len()] = true;

        for i in (0..=s.len()).rev() {
            for j in (0..p.len()).rev() {
                // 现在要计算 memo[i][j]
                let first_match = i < s.len() && (s[i] == p[j] || (p[j] == '.' as u8));
                if j + 1 < p.len() && p[j + 1] == '*' as u8 {
                    memo[i][j] = memo[i][j + 2] || (first_match && memo[i + 1][j]);
                } else {
                    memo[i][j] = first_match && memo[i + 1][j + 1];
                }
                // println!("m[{}][{}] = {}", i, j, memo[i][j]);
            }
        }
        memo[0][0]
    }
}

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

    #[test]
    fn test_is_match() {
        assert_eq!(Solution::is_match("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
    }

    #[test]
    fn test_is_match2() {
        assert_eq!(Solution::is_match2("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match2("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match2("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
        assert_eq!(
            Solution::is_match2("ab".to_owned(), ".*c".to_owned()),
            false
        );
    }

    #[test]
    fn test_is_match3() {
        assert_eq!(Solution::is_match3("aa".to_owned(), "a*".to_owned()), true);
        assert_eq!(
            Solution::is_match3("aab".to_owned(), "c*a*b".to_owned()),
            true
        );
        assert_eq!(
            Solution::is_match3("mississippi".to_owned(), "mis*is*p*".to_owned()),
            false
        );
        assert_eq!(
            Solution::is_match3("ab".to_owned(), ".*c".to_owned()),
            false
        );
    }

    #[test]
    fn test_is_match4() {
        assert_eq!(Solution::is_match4("ab".to_owned(), ".*".to_owned()), true);
        // assert_eq!(Solution::is_match3("ab".to_owned(), ".*".to_owned()), true);
    }
}
<|endoftext|>


<commit_before>use errors::prelude::*;
use rusqlite::types::ToSql;
use services::wallet::language::{Operator, TagName, TargetValue};

// Translates Wallet Query Language to SQL
// WQL input is provided as a reference to a top level Operator
// Result is a tuple of query string and query arguments
pub fn wql_to_sql<'a>(class: &'a Vec<u8>, op: &'a Operator, _options: Option<&str>) -> Result<(String, Vec<&'a ToSql>), IndyError> {
    let mut arguments: Vec<&ToSql> = Vec::new();
    arguments.push(class);
    let clause_string = operator_to_sql(op, &mut arguments)?;
    let mut query_string = "SELECT i.id, i.name, i.value, i.key, i.type FROM items as i WHERE i.type = ?".to_string();
    if !clause_string.is_empty() {
        query_string.push_str(" AND ");
        query_string.push_str(&clause_string);
    }
    Ok((query_string, arguments))
}

pub fn wql_to_sql_count<'a>(class: &'a Vec<u8>, op: &'a Operator) -> Result<(String, Vec<&'a ToSql>), IndyError> {
    let mut arguments: Vec<&ToSql> = Vec::new();
    arguments.push(class);
    let clause_string = operator_to_sql(op, &mut arguments)?;
    let mut query_string = "SELECT count(*) FROM items as i WHERE i.type = ?".to_string();
    if !clause_string.is_empty() {
        query_string.push_str(" AND ");
        query_string.push_str(&clause_string);
    }
    Ok((query_string, arguments))
}

fn operator_to_sql<'a>(op: &'a Operator, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match *op {
        Operator::Eq(ref tag_name, ref target_value) => eq_to_sql(tag_name, target_value, arguments),
        Operator::Neq(ref tag_name, ref target_value) => neq_to_sql(tag_name, target_value, arguments),
        Operator::Gt(ref tag_name, ref target_value) => gt_to_sql(tag_name, target_value, arguments),
        Operator::Gte(ref tag_name, ref target_value) => gte_to_sql(tag_name, target_value, arguments),
        Operator::Lt(ref tag_name, ref target_value) => lt_to_sql(tag_name, target_value, arguments),
        Operator::Lte(ref tag_name, ref target_value) => lte_to_sql(tag_name, target_value, arguments),
        Operator::Like(ref tag_name, ref target_value) => like_to_sql(tag_name, target_value, arguments),
        Operator::In(ref tag_name, ref target_values) => in_to_sql(tag_name, target_values, arguments),
        Operator::And(ref suboperators) => and_to_sql(suboperators, arguments),
        Operator::Or(ref suboperators) => or_to_sql(suboperators, arguments),
        Operator::Not(ref suboperator) => not_to_sql(suboperator, arguments),
    }
}

fn eq_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value = ?))".to_string())
        },
        (&TagName::EncryptedTagName(ref queried_name), &TargetValue::Encrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value = ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for equality operator"))
    }
}

fn neq_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value != ?))".to_string())
        },
        (&TagName::EncryptedTagName(ref queried_name), &TargetValue::Encrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value != ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for inequality operator"))
    }
}

fn gt_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value > ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $gt operator"))
    }
}

fn gte_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value >= ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $gte operator"))
    }
}

fn lt_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value < ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $lt operator"))
    }
}

fn lte_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value <= ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $lte operator"))
    }
}

fn like_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value LIKE ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $like operator"))
    }
}

fn in_to_sql<'a>(name: &'a TagName, values: &'a Vec<TargetValue>, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let mut in_string = String::new();
    match *name {
        TagName::PlainTagName(ref queried_name) => {
            in_string.push_str("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value IN (");
            arguments.push(queried_name);

            for (index, value) in values.iter().enumerate() {
                if let TargetValue::Unencrypted(ref target) = *value {
                    in_string.push_str("?");
                    arguments.push(target);
                    if index < values.len() - 1 {
                        in_string.push(',');
                    }
                } else {
                    return Err(err_msg(IndyErrorKind::WalletQueryError, "Encrypted tag value in $in for nonencrypted tag name"))
                }
            }

            Ok(in_string + ")))")
        },
        TagName::EncryptedTagName(ref queried_name) => {
            in_string.push_str("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value IN (");
            arguments.push(queried_name);
            let index_before_last = values.len() - 2;

            for (index, value) in values.iter().enumerate() {
                if let TargetValue::Encrypted(ref target) = *value {
                    in_string.push_str("?");
                    arguments.push(target);
                    if index <= index_before_last {
                        in_string.push(',');
                    }
                } else {
                    return Err(err_msg(IndyErrorKind::WalletQueryError, "Unencrypted tag value in $in for encrypted tag name"))
                }
            }

            Ok(in_string + ")))")
        },
    }
}

fn and_to_sql<'a>(suboperators: &'a [Operator], arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    join_operators(suboperators, " AND ", arguments)
}

fn or_to_sql<'a>(suboperators: &'a [Operator], arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    join_operators(suboperators, " OR ", arguments)
}

fn not_to_sql<'a>(suboperator: &'a Operator, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let suboperator_string = operator_to_sql(suboperator, arguments)?;
    Ok("NOT (".to_string() + &suboperator_string + ")")
}

fn join_operators<'a>(operators: &'a [Operator], join_str: &str, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let mut s = String::new();
    if !operators.is_empty() {
        s.push('(');
        for (index, operator) in operators.iter().enumerate() {
            let operator_string = operator_to_sql(operator, arguments)?;
            s.push_str(&operator_string);
            if index < operators.len() - 1 {
                s.push_str(join_str);
            }
        }
        s.push(')');
    }
    Ok(s)
}

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

    #[test]
    fn simple_and() {
        let condition_1 = Operator::And(vec![
            Operator::Eq(TagName::EncryptedTagName(vec![1,2,3]), TargetValue::Encrypted(vec![4,5,6])),
            Operator::Eq(TagName::PlainTagName(vec![7,8,9]), TargetValue::Unencrypted("spam".to_string())),
        ]);
        let condition_2 = Operator::And(vec![
            Operator::Eq(TagName::EncryptedTagName(vec![10,11,12]), TargetValue::Encrypted(vec![13,14,15])),
            Operator::Not(Box::new(Operator::Eq(TagName::PlainTagName(vec![16,17,18]), TargetValue::Unencrypted("eggs".to_string()))))
        ]);
        let query = Operator::Or(vec![condition_1, condition_2]);
        let class = vec![100,100,100];
        let (_query, _arguments) = wql_to_sql(&class, &query, None).unwrap();
    }
}<commit_msg>push_str is fastest, also with_capacity<commit_after>use errors::prelude::*;
use rusqlite::types::ToSql;
use services::wallet::language::{Operator, TagName, TargetValue};

// Translates Wallet Query Language to SQL
// WQL input is provided as a reference to a top level Operator
// Result is a tuple of query string and query arguments
pub fn wql_to_sql<'a>(class: &'a Vec<u8>, op: &'a Operator, _options: Option<&str>) -> Result<(String, Vec<&'a ToSql>), IndyError> {
    let mut arguments: Vec<&ToSql> = Vec::new();
    arguments.push(class);
    let clause_string = operator_to_sql(op, &mut arguments)?;
    const BASE: &str = "SELECT i.id, i.name, i.value, i.key, i.type FROM items as i WHERE i.type = ?";
    if !clause_string.is_empty() {
        let mut query_string = String::with_capacity(BASE.len() + 5 + clause_string.len());
        query_string.push_str(BASE);
        query_string.push_str(" AND ");
        query_string.push_str(&clause_string);
        Ok((query_string, arguments))
    } else {
        Ok((BASE.to_string(), arguments))
    }

}

pub fn wql_to_sql_count<'a>(class: &'a Vec<u8>, op: &'a Operator) -> Result<(String, Vec<&'a ToSql>), IndyError> {
    let mut arguments: Vec<&ToSql> = Vec::new();
    arguments.push(class);
    let clause_string = operator_to_sql(op, &mut arguments)?;
    let mut query_string = "SELECT count(*) FROM items as i WHERE i.type = ?".to_string();
    if !clause_string.is_empty() {
        query_string.push_str(" AND ");
        query_string.push_str(&clause_string);
    }
    Ok((query_string, arguments))
}

fn operator_to_sql<'a>(op: &'a Operator, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match *op {
        Operator::Eq(ref tag_name, ref target_value) => eq_to_sql(tag_name, target_value, arguments),
        Operator::Neq(ref tag_name, ref target_value) => neq_to_sql(tag_name, target_value, arguments),
        Operator::Gt(ref tag_name, ref target_value) => gt_to_sql(tag_name, target_value, arguments),
        Operator::Gte(ref tag_name, ref target_value) => gte_to_sql(tag_name, target_value, arguments),
        Operator::Lt(ref tag_name, ref target_value) => lt_to_sql(tag_name, target_value, arguments),
        Operator::Lte(ref tag_name, ref target_value) => lte_to_sql(tag_name, target_value, arguments),
        Operator::Like(ref tag_name, ref target_value) => like_to_sql(tag_name, target_value, arguments),
        Operator::In(ref tag_name, ref target_values) => in_to_sql(tag_name, target_values, arguments),
        Operator::And(ref suboperators) => and_to_sql(suboperators, arguments),
        Operator::Or(ref suboperators) => or_to_sql(suboperators, arguments),
        Operator::Not(ref suboperator) => not_to_sql(suboperator, arguments),
    }
}

fn eq_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value = ?))".to_string())
        },
        (&TagName::EncryptedTagName(ref queried_name), &TargetValue::Encrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value = ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for equality operator"))
    }
}

fn neq_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value != ?))".to_string())
        },
        (&TagName::EncryptedTagName(ref queried_name), &TargetValue::Encrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value != ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for inequality operator"))
    }
}

fn gt_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value > ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $gt operator"))
    }
}

fn gte_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value >= ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $gte operator"))
    }
}

fn lt_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value < ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $lt operator"))
    }
}

fn lte_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value <= ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $lte operator"))
    }
}

fn like_to_sql<'a>(name: &'a TagName, value: &'a TargetValue, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    match (name, value) {
        (&TagName::PlainTagName(ref queried_name), &TargetValue::Unencrypted(ref queried_value)) => {
            arguments.push(queried_name);
            arguments.push(queried_value);
            Ok("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value LIKE ?))".to_string())
        },
        _ => Err(err_msg(IndyErrorKind::WalletQueryError, "Invalid combination of tag name and value for $like operator"))
    }
}

fn in_to_sql<'a>(name: &'a TagName, values: &'a Vec<TargetValue>, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let mut in_string = String::new();
    match *name {
        TagName::PlainTagName(ref queried_name) => {
            in_string.push_str("(i.id in (SELECT item_id FROM tags_plaintext WHERE name = ? AND value IN (");
            arguments.push(queried_name);

            for (index, value) in values.iter().enumerate() {
                if let TargetValue::Unencrypted(ref target) = *value {
                    in_string.push_str("?");
                    arguments.push(target);
                    if index < values.len() - 1 {
                        in_string.push(',');
                    }
                } else {
                    return Err(err_msg(IndyErrorKind::WalletQueryError, "Encrypted tag value in $in for nonencrypted tag name"))
                }
            }

            Ok(in_string + ")))")
        },
        TagName::EncryptedTagName(ref queried_name) => {
            in_string.push_str("(i.id in (SELECT item_id FROM tags_encrypted WHERE name = ? AND value IN (");
            arguments.push(queried_name);
            let index_before_last = values.len() - 2;

            for (index, value) in values.iter().enumerate() {
                if let TargetValue::Encrypted(ref target) = *value {
                    in_string.push_str("?");
                    arguments.push(target);
                    if index <= index_before_last {
                        in_string.push(',');
                    }
                } else {
                    return Err(err_msg(IndyErrorKind::WalletQueryError, "Unencrypted tag value in $in for encrypted tag name"))
                }
            }

            Ok(in_string + ")))")
        },
    }
}

fn and_to_sql<'a>(suboperators: &'a [Operator], arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    join_operators(suboperators, " AND ", arguments)
}

fn or_to_sql<'a>(suboperators: &'a [Operator], arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    join_operators(suboperators, " OR ", arguments)
}

fn not_to_sql<'a>(suboperator: &'a Operator, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let suboperator_string = operator_to_sql(suboperator, arguments)?;
    Ok("NOT (".to_string() + &suboperator_string + ")")
}

fn join_operators<'a>(operators: &'a [Operator], join_str: &str, arguments: &mut Vec<&'a ToSql>) -> IndyResult<String> {
    let mut s = String::new();
    if !operators.is_empty() {
        s.push('(');
        for (index, operator) in operators.iter().enumerate() {
            let operator_string = operator_to_sql(operator, arguments)?;
            s.push_str(&operator_string);
            if index < operators.len() - 1 {
                s.push_str(join_str);
            }
        }
        s.push(')');
    }
    Ok(s)
}

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

    #[test]
    fn simple_and() {
        let condition_1 = Operator::And(vec![
            Operator::Eq(TagName::EncryptedTagName(vec![1,2,3]), TargetValue::Encrypted(vec![4,5,6])),
            Operator::Eq(TagName::PlainTagName(vec![7,8,9]), TargetValue::Unencrypted("spam".to_string())),
        ]);
        let condition_2 = Operator::And(vec![
            Operator::Eq(TagName::EncryptedTagName(vec![10,11,12]), TargetValue::Encrypted(vec![13,14,15])),
            Operator::Not(Box::new(Operator::Eq(TagName::PlainTagName(vec![16,17,18]), TargetValue::Unencrypted("eggs".to_string()))))
        ]);
        let query = Operator::Or(vec![condition_1, condition_2]);
        let class = vec![100,100,100];
        let (_query, _arguments) = wql_to_sql(&class, &query, None).unwrap();
    }
}<|endoftext|>