<commit_before>use ast::LoopBodyOwner;
use ide_db::helpers::FamousDefs;
use stdx::format_to;
use syntax::{ast, AstNode};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: convert_for_to_iter_for_each
//
// Converts a for loop into a for_each loop on the Iterator.
//
// ```
// fn main() {
//     let x = vec![1, 2, 3];
//     for $0v in x {
//         let y = v * 2;
//     }
// }
// ```
// ->
// ```
// fn main() {
//     let x = vec![1, 2, 3];
//     x.into_iter().for_each(|v| {
//         let y = v * 2;
//     });
// }
// ```
pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
    let iterable = for_loop.iterable()?;
    let pat = for_loop.pat()?;
    let body = for_loop.loop_body()?;

    let mut buf = String::new();

    if impls_core_iter(&ctx.sema, &iterable) {
        buf += &iterable.to_string();
    } else {
        match iterable {
            ast::Expr::RefExpr(r) => {
                if r.mut_token().is_some() {
                    format_to!(buf, "{}.iter_mut()", r.expr()?);
                } else {
                    format_to!(buf, "{}.iter()", r.expr()?);
                }
            }
            _ => format_to!(buf, "{}.into_iter()", iterable),
        }
    }

    format_to!(buf, ".for_each(|{}| {});", pat, body);

    acc.add(
        AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite),
        "Convert a for loop into an Iterator::for_each",
        for_loop.syntax().text_range(),
        |builder| builder.replace(for_loop.syntax().text_range(), buf),
    )
}

fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
    let it_typ = if let Some(i) = sema.type_of_expr(iterable) {
        i
    } else {
        return false;
    };
    let module = if let Some(m) = sema.scope(iterable.syntax()).module() {
        m
    } else {
        return false;
    };
    let krate = module.krate();
    if let Some(iter_trait) = FamousDefs(sema, Some(krate)).core_iter_Iterator() {
        return it_typ.impls_trait(sema.db, iter_trait, &[]);
    }
    false
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    const EMPTY_ITER_FIXTURE: &'static str = r"
//- /lib.rs deps:core crate:empty_iter
pub struct EmptyIter;
impl Iterator for EmptyIter {
    type Item = usize;
    fn next(&mut self) -> Option<Self::Item> { None }
}

pub struct Empty;
impl Empty {
    pub fn iter(&self) -> EmptyIter { EmptyIter }
}
";

    #[test]
    fn test_not_for() {
        check_assist_not_applicable(
            convert_for_to_iter_for_each,
            r"
let mut x = vec![1, 2, 3];
x.iter_mut().$0for_each(|v| *v *= 2);
        ",
        )
    }

    #[test]
    fn test_simple_for() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    for $0v in x {
        v *= 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    x.into_iter().for_each(|v| {
        v *= 2;
    });
}",
        )
    }

    #[test]
    fn test_for_borrowed() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    for $0v in &x {
        let a = v * 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    x.iter().for_each(|v| {
        let a = v * 2;
    });
}",
        )
    }

    #[test]
    fn test_for_borrowed_mut() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    for $0v in &mut x {
        *v *= 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    x.iter_mut().for_each(|v| {
        *v *= 2;
    });
}",
        )
    }

    #[test]
    fn test_for_borrowed_mut_behind_var() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    let y = &mut x;
    for $0v in y {
        *v *= 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    let y = &mut x;
    y.into_iter().for_each(|v| {
        *v *= 2;
    });
}",
        )
    }

    #[test]
    fn test_take() {
        let before = r#"
use empty_iter::*;
fn main() {
    let x = Empty;
    for$0 a in x.iter().take(1) {
        println!("{}", a);
    }
}
"#;
        let after = r#"
use empty_iter::*;
fn main() {
    let x = Empty;
    x.iter().take(1).for_each(|a| {
        println!("{}", a);
    });
}
"#;
        let before = &format!(
            "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
            before,
            FamousDefs::FIXTURE,
            EMPTY_ITER_FIXTURE
        );
        check_assist(convert_for_to_iter_for_each, before, after);
    }
}
<commit_msg>Address review comments<commit_after>use ast::LoopBodyOwner;
use ide_db::helpers::FamousDefs;
use stdx::format_to;
use syntax::{ast, AstNode};

use crate::{AssistContext, AssistId, AssistKind, Assists};

// Assist: convert_for_to_iter_for_each
//
// Converts a for loop into a for_each loop on the Iterator.
//
// ```
// fn main() {
//     let x = vec![1, 2, 3];
//     for $0v in x {
//         let y = v * 2;
//     }
// }
// ```
// ->
// ```
// fn main() {
//     let x = vec![1, 2, 3];
//     x.into_iter().for_each(|v| {
//         let y = v * 2;
//     });
// }
// ```
pub(crate) fn convert_for_to_iter_for_each(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
    let for_loop = ctx.find_node_at_offset::<ast::ForExpr>()?;
    let iterable = for_loop.iterable()?;
    let pat = for_loop.pat()?;
    let body = for_loop.loop_body()?;

    acc.add(
        AssistId("convert_for_to_iter_for_each", AssistKind::RefactorRewrite),
        "Convert a for loop into an Iterator::for_each",
        for_loop.syntax().text_range(),
        |builder| {
            let mut buf = String::new();

            if let Some((expr_behind_ref, method)) =
                is_ref_and_impls_iter_method(&ctx.sema, &iterable)
            {
                // We have either "for x in &col" and col implements a method called iter
                //             or "for x in &mut col" and col implements a method called iter_mut
                format_to!(buf, "{}.{}()", expr_behind_ref, method);
            } else if impls_core_iter(&ctx.sema, &iterable) {
                format_to!(buf, "{}", iterable);
            } else {
                if let ast::Expr::RefExpr(_) = iterable {
                    format_to!(buf, "({}).into_iter()", iterable);
                } else {
                    format_to!(buf, "{}.into_iter()", iterable);
                }
            }

            format_to!(buf, ".for_each(|{}| {});", pat, body);

            builder.replace(for_loop.syntax().text_range(), buf)
        },
    )
}

/// If iterable is a reference where the expression behind the reference implements a method
/// returning an Iterator called iter or iter_mut (depending on the type of reference) then return
/// the expression behind the reference and the method name
fn is_ref_and_impls_iter_method(
    sema: &hir::Semantics<ide_db::RootDatabase>,
    iterable: &ast::Expr,
) -> Option<(ast::Expr, &'static str)> {
    let ref_expr = match iterable {
        ast::Expr::RefExpr(r) => r,
        _ => return None,
    };
    let wanted_method = if ref_expr.mut_token().is_some() { "iter_mut" } else { "iter" };
    let expr_behind_ref = ref_expr.expr()?;
    let typ = sema.type_of_expr(&expr_behind_ref)?;
    let scope = sema.scope(iterable.syntax());
    let krate = scope.module()?.krate();
    let traits_in_scope = scope.traits_in_scope();
    let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
    let has_wanted_method =
        typ.iterate_method_candidates(sema.db, krate, &traits_in_scope, None, |_, func| {
            if func.name(sema.db).to_string() != wanted_method {
                return None;
            }
            if func.ret_type(sema.db).impls_trait(sema.db, iter_trait, &[]) {
                return Some(());
            }
            None
        });
    has_wanted_method.and(Some((expr_behind_ref, wanted_method)))
}

/// Whether iterable implements core::Iterator
fn impls_core_iter(sema: &hir::Semantics<ide_db::RootDatabase>, iterable: &ast::Expr) -> bool {
    let it_typ = if let Some(i) = sema.type_of_expr(iterable) {
        i
    } else {
        return false;
    };
    let module = if let Some(m) = sema.scope(iterable.syntax()).module() {
        m
    } else {
        return false;
    };
    let krate = module.krate();
    if let Some(iter_trait) = FamousDefs(sema, Some(krate)).core_iter_Iterator() {
        return it_typ.impls_trait(sema.db, iter_trait, &[]);
    }
    false
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable};

    use super::*;

    const EMPTY_ITER_FIXTURE: &'static str = r"
//- /lib.rs deps:core crate:empty_iter
pub struct EmptyIter;
impl Iterator for EmptyIter {
    type Item = usize;
    fn next(&mut self) -> Option<Self::Item> { None }
}

pub struct Empty;
impl Empty {
    pub fn iter(&self) -> EmptyIter { EmptyIter }
    pub fn iter_mut(&self) -> EmptyIter { EmptyIter }
}

pub struct NoIterMethod;
";

    #[test]
    fn test_not_for() {
        check_assist_not_applicable(
            convert_for_to_iter_for_each,
            r"
let mut x = vec![1, 2, 3];
x.iter_mut().$0for_each(|v| *v *= 2);
        ",
        )
    }

    #[test]
    fn test_simple_for() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    for $0v in x {
        v *= 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    x.into_iter().for_each(|v| {
        v *= 2;
    });
}",
        )
    }

    #[test]
    fn test_for_borrowed() {
        let before = r"
use empty_iter::*;
fn main() {
    let x = Empty;
    for $0v in &x {
        let a = v * 2;
    }
}
";
        let before = &format!(
            "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
            before,
            FamousDefs::FIXTURE,
            EMPTY_ITER_FIXTURE
        );
        check_assist(
            convert_for_to_iter_for_each,
            before,
            r"
use empty_iter::*;
fn main() {
    let x = Empty;
    x.iter().for_each(|v| {
        let a = v * 2;
    });
}
",
        )
    }

    #[test]
    fn test_for_borrowed_no_iter_method() {
        let before = r"
use empty_iter::*;
fn main() {
    let x = NoIterMethod;
    for $0v in &x {
        let a = v * 2;
    }
}
";
        let before = &format!(
            "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
            before,
            FamousDefs::FIXTURE,
            EMPTY_ITER_FIXTURE
        );
        check_assist(
            convert_for_to_iter_for_each,
            before,
            r"
use empty_iter::*;
fn main() {
    let x = NoIterMethod;
    (&x).into_iter().for_each(|v| {
        let a = v * 2;
    });
}
",
        )
    }

    #[test]
    fn test_for_borrowed_mut() {
        let before = r"
use empty_iter::*;
fn main() {
    let x = Empty;
    for $0v in &mut x {
        let a = v * 2;
    }
}
";
        let before = &format!(
            "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
            before,
            FamousDefs::FIXTURE,
            EMPTY_ITER_FIXTURE
        );
        check_assist(
            convert_for_to_iter_for_each,
            before,
            r"
use empty_iter::*;
fn main() {
    let x = Empty;
    x.iter_mut().for_each(|v| {
        let a = v * 2;
    });
}
",
        )
    }

    #[test]
    fn test_for_borrowed_mut_behind_var() {
        check_assist(
            convert_for_to_iter_for_each,
            r"
fn main() {
    let x = vec![1, 2, 3];
    let y = &mut x;
    for $0v in y {
        *v *= 2;
    }
}",
            r"
fn main() {
    let x = vec![1, 2, 3];
    let y = &mut x;
    y.into_iter().for_each(|v| {
        *v *= 2;
    });
}",
        )
    }

    #[test]
    fn test_already_impls_iterator() {
        let before = r#"
use empty_iter::*;
fn main() {
    let x = Empty;
    for$0 a in x.iter().take(1) {
        println!("{}", a);
    }
}
"#;
        let after = r#"
use empty_iter::*;
fn main() {
    let x = Empty;
    x.iter().take(1).for_each(|a| {
        println!("{}", a);
    });
}
"#;
        let before = &format!(
            "//- /main.rs crate:main deps:core,empty_iter{}{}{}",
            before,
            FamousDefs::FIXTURE,
            EMPTY_ITER_FIXTURE
        );
        check_assist(convert_for_to_iter_for_each, before, after);
    }
}
<|endoftext|>


<commit_before>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  let e = Point2::new(p.x as i32, p.y as i32);
  let neighbours: Vec<Point2<i32>> = vec![Point2::new(e.x - 1, e.y),
                                          Point2::new(e.x - 1, e.y - 1),
                                          Point2::new(e.x, e.y - 1),
                                          Point2::new(e.x + 1, e.y),
                                          Point2::new(e.x + 1, e.y + 1),
                                          Point2::new(e.x, e.y + 1),
                                          Point2::new(e.x - 1, e.y + 1),
                                          Point2::new(e.x + 1, e.y - 1)
  ];

  neighbours.iter()
            .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
            .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
            .map(|p| (Point2::new(p.x, p.y), 1))
            .collect()
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = coords_to_tile_offset(end_point);

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles.to_vec()),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position/*, offset: &CharacterInputState*/) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS.to_vec())
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<commit_msg>Refactor path_finding.rs<commit_after>use cgmath::Point2;
use game::constants::{TERRAIN_OBJECTS, TILES_PCS_H, TILES_PCS_W};
use game::get_rand_from_range;
use graphics::coords_to_tile_offset;
use pathfinding::{astar::astar, utils::absdiff};
use shaders::Position;

fn tiles(p: &Point2<i32>, impassable_tiles: &[[usize; 2]]) -> Vec<(Point2<i32>, i32)> {
  let e = Point2::new(p.x as i32, p.y as i32);
  let neighbours: Vec<Point2<i32>> = vec![Point2::new(e.x - 1, e.y),
                                          Point2::new(e.x - 1, e.y - 1),
                                          Point2::new(e.x, e.y - 1),
                                          Point2::new(e.x + 1, e.y),
                                          Point2::new(e.x + 1, e.y + 1),
                                          Point2::new(e.x, e.y + 1),
                                          Point2::new(e.x - 1, e.y + 1),
                                          Point2::new(e.x + 1, e.y - 1)
  ];

  neighbours.iter()
            .filter(|ref e| e.x >= 0 && e.x < TILES_PCS_W as i32 && e.y >= 0 && e.y < TILES_PCS_H as i32)
            .filter(|ref e| !impassable_tiles.contains(&[e.x as usize, e.y as usize]))
            .map(|p| (Point2::new(p.x, p.y), 1))
            .collect()
}

pub fn calc_route(start_point: Position, end_point: Position, impassable_tiles: &[[usize; 2]]) -> Option<(Vec<Point2<i32>>, i32)> {
  let start = coords_to_tile_offset(start_point);
  let end = coords_to_tile_offset(end_point);

  astar(&start,
        |p: &Point2<i32>| tiles(p, &impassable_tiles),
        |p: &Point2<i32>| absdiff(p.x, end.x) + absdiff(p.y, end.y),
        |p: &Point2<i32>| p.x == end.x && p.y == end.y)
}

pub fn calc_next_movement(start_point: Position, end_point: Position) -> i32 {
  let next_step: Point2<i32> = calc_route(start_point, end_point, &TERRAIN_OBJECTS)
    .map_or_else(|| Point2::new(0 as i32, 0 as i32),
                 |(route, _)| {
                   if route.len() > 1 {
                     Point2::new(route[1].x as i32, route[1].y as i32)
                   } else {
                     Point2::new(route[0].x as i32, route[0].y as i32)
                   }
                 });

  let start = coords_to_tile_offset(start_point);
  let diff: (i32, i32) = (next_step.x - start.x, next_step.y - start.y);

  match diff {
    (1, 0) => 315,
    (1, 1) => 270,
    (0, 1) => 225,
    (-1, 1) => 180,
    (-1, 0) => 135,
    (-1, -1) => 90,
    (0, -1) => 45,
    (1, -1) => 0,
    _ => get_rand_from_range(0, 359),
  }
}
<|endoftext|>


<commit_before>use crate::utils::span_lint_and_sugg;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for fields in struct literals where shorthands
    /// could be used.
    ///
    /// **Why is this bad?** If the field and variable names are the same,
    /// the field name is redundant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let bar: u8 = 123;
    ///
    /// struct Foo {
    ///     bar: u8,
    /// }
    ///
    /// let foo = Foo { bar: bar };
    /// ```
    /// the last line can be simplified to
    /// ```ignore
    /// let foo = Foo { bar };
    /// ```
    pub REDUNDANT_FIELD_NAMES,
    style,
    "checks for fields in struct literals where shorthands could be used"
}

declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);

impl EarlyLintPass for RedundantFieldNames {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if let ExprKind::Struct(_, ref fields, _) = expr.kind {
            for field in fields {
                if field.is_shorthand {
                    continue;
                }
                if let ExprKind::Path(None, path) = &field.expr.kind {
                    if path.segments.len() == 1
                        && path.segments[0].ident == field.ident
                        && path.segments[0].args.is_none()
                    {
                        span_lint_and_sugg(
                            cx,
                            REDUNDANT_FIELD_NAMES,
                            field.span,
                            "redundant field names in struct initialization",
                            "replace it with",
                            field.ident.to_string(),
                            Applicability::MachineApplicable,
                        );
                    }
                }
            }
        }
    }
}
<commit_msg>Fix fallout in redundant_field_names<commit_after>use crate::utils::span_lint_and_sugg;
use rustc_ast::ast::{Expr, ExprKind};
use rustc_errors::Applicability;
use rustc_lint::{EarlyContext, EarlyLintPass};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};

declare_clippy_lint! {
    /// **What it does:** Checks for fields in struct literals where shorthands
    /// could be used.
    ///
    /// **Why is this bad?** If the field and variable names are the same,
    /// the field name is redundant.
    ///
    /// **Known problems:** None.
    ///
    /// **Example:**
    /// ```rust
    /// let bar: u8 = 123;
    ///
    /// struct Foo {
    ///     bar: u8,
    /// }
    ///
    /// let foo = Foo { bar: bar };
    /// ```
    /// the last line can be simplified to
    /// ```ignore
    /// let foo = Foo { bar };
    /// ```
    pub REDUNDANT_FIELD_NAMES,
    style,
    "checks for fields in struct literals where shorthands could be used"
}

declare_lint_pass!(RedundantFieldNames => [REDUNDANT_FIELD_NAMES]);

impl EarlyLintPass for RedundantFieldNames {
    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
        if in_external_macro(cx.sess, expr.span) {
            return;
        }
        if let ExprKind::Struct(_, ref fields, _) = expr.kind {
            for field in fields {
                if field.is_shorthand {
                    continue;
                }
                if let ExprKind::Path(None, path) = &field.expr.kind {
                    if path.segments.len() == 1
                        && path.segments[0].ident == field.ident
                        && path.segments[0].args.is_none()
                    {
                        span_lint_and_sugg(
                            cx,
                            REDUNDANT_FIELD_NAMES,
                            field.span,
                            "redundant field names in struct initialization",
                            "replace it with",
                            field.ident.to_string(),
                            Applicability::MachineApplicable,
                        );
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This library implements a schematized DB on top of [RocksDB](https://rocksdb.org/). It makes
//! sure all data passed in and out are structured according to predefined schemas and prevents
//! access to raw keys and values. This library also enforces a set of Libra specific DB options,
//! like custom comparators and schema-to-column-family mapping.
//!
//! It requires that different kinds of key-value pairs be stored in separate column
//! families.  To use this library to store a kind of key-value pairs, the user needs to use the
//! [`define_schema!`] macro to define the schema name, the types of key and value, and name of the
//! column family.

#[macro_use]
pub mod schema;

use crate::schema::{KeyCodec, Schema, SeekKeyCodec, ValueCodec};
use failure::prelude::*;
use rocksdb::{
    rocksdb_options::ColumnFamilyDescriptor, CFHandle, DBOptions, Range, Writable, WriteOptions,
};
use std::{
    collections::{BTreeMap, HashMap},
    iter::Iterator,
    marker::PhantomData,
    path::Path,
};

/// Type alias to `rocksdb::ColumnFamilyOptions`. See [`rocksdb doc`](https://github.com/pingcap/rust-rocksdb/blob/master/src/rocksdb_options.rs)
pub type ColumnFamilyOptions = rocksdb::ColumnFamilyOptions;
/// Type alias to `rocksdb::ReadOptions`. See [`rocksdb doc`](https://github.com/pingcap/rust-rocksdb/blob/master/src/rocksdb_options.rs)
pub type ReadOptions = rocksdb::ReadOptions;

/// Type alias to improve readability.
pub type ColumnFamilyName = &'static str;
/// Type alias to improve readability.
pub type ColumnFamilyOptionsMap = HashMap<ColumnFamilyName, ColumnFamilyOptions>;

/// Name for the `default` column family that's always open by RocksDB. We use it to store
/// [`LedgerInfo`](../types/ledger_info/struct.LedgerInfo.html).
pub const DEFAULT_CF_NAME: ColumnFamilyName = "default";

#[derive(Debug)]
enum WriteOp {
    Value(Vec<u8>),
    Deletion,
}

/// `SchemaBatch` holds a collection of updates that can be applied to a DB atomically. The updates
/// will be applied in the order in which they are added to the `SchemaBatch`.
#[derive(Debug, Default)]
pub struct SchemaBatch {
    rows: Vec<(ColumnFamilyName, Vec<u8> /* key */, WriteOp)>,
}

impl SchemaBatch {
    /// Creates an empty batch.
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds an insert/update operation to the batch.
    pub fn put<S: Schema>(&mut self, key: &S::Key, value: &S::Value) -> Result<()> {
        let key = <S::Key as KeyCodec<S>>::encode_key(key)?;
        let value = <S::Value as ValueCodec<S>>::encode_value(value)?;
        self.rows
            .push((S::COLUMN_FAMILY_NAME, key, WriteOp::Value(value)));
        Ok(())
    }

    /// Adds a delete operation to the batch.
    pub fn delete<S: Schema>(&mut self, key: &S::Key) -> Result<()> {
        let key = <S::Key as KeyCodec<S>>::encode_key(key)?;
        self.rows
            .push((S::COLUMN_FAMILY_NAME, key, WriteOp::Deletion));
        Ok(())
    }
}

/// DB Iterator parameterized on [`Schema`] that seeks with [`Schema::Key`] and yields
/// [`Schema::Key`] and [`Schema::Value`]
pub struct SchemaIterator<'a, S> {
    db_iter: rocksdb::DBIterator<&'a rocksdb::DB>,
    phantom: PhantomData<S>,
}

impl<'a, S> SchemaIterator<'a, S>
where
    S: Schema,
{
    fn new(db_iter: rocksdb::DBIterator<&'a rocksdb::DB>) -> Self {
        SchemaIterator {
            db_iter,
            phantom: PhantomData,
        }
    }

    /// Seeks to the first key.
    pub fn seek_to_first(&mut self) -> bool {
        self.db_iter.seek(rocksdb::SeekKey::Start)
    }

    /// Seeks to the last key.
    pub fn seek_to_last(&mut self) -> bool {
        self.db_iter.seek(rocksdb::SeekKey::End)
    }

    /// Seeks to the first key whose binary representation is equal to or greater than that of the
    /// `seek_key`.
    pub fn seek<SK>(&mut self, seek_key: &SK) -> Result<bool>
    where
        SK: SeekKeyCodec<S>,
    {
        let key = <SK as SeekKeyCodec<S>>::encode_seek_key(seek_key)?;
        Ok(self.db_iter.seek(rocksdb::SeekKey::Key(&key)))
    }

    /// Seeks to the last key whose binary representation is less than or equal to that of the
    /// `seek_key`.
    ///
    /// See example in [`RocksDB doc`](https://github.com/facebook/rocksdb/wiki/SeekForPrev).
    pub fn seek_for_prev<SK>(&mut self, seek_key: &SK) -> Result<bool>
    where
        SK: SeekKeyCodec<S>,
    {
        let key = <SK as SeekKeyCodec<S>>::encode_seek_key(seek_key)?;
        Ok(self.db_iter.seek_for_prev(rocksdb::SeekKey::Key(&key)))
    }
}

impl<'a, S> Iterator for SchemaIterator<'a, S>
where
    S: Schema,
{
    type Item = Result<(S::Key, S::Value)>;

    fn next(&mut self) -> Option<Self::Item> {
        self.db_iter.kv().map(|(raw_key, raw_value)| {
            self.db_iter.next();
            Ok((
                <S::Key as KeyCodec<S>>::decode_key(&raw_key)?,
                <S::Value as ValueCodec<S>>::decode_value(&raw_value)?,
            ))
        })
    }
}

/// Checks underlying Rocksdb instance existence by checking `CURRENT` file existence, the same way
/// Rocksdb adopts to detect db existence.
fn db_exists(path: &Path) -> bool {
    let rocksdb_current_file = path.join("CURRENT");
    rocksdb_current_file.is_file()
}

/// All the RocksDB methods return `std::result::Result<T, String>`. Since our methods return
/// `failure::Result<T>`, manual conversion is needed.
fn convert_rocksdb_err(msg: String) -> failure::Error {
    format_err!("RocksDB internal error: {}.", msg)
}

/// This DB is a schematized RocksDB wrapper where all data passed in and out are typed according to
/// [`Schema`]s.
#[derive(Debug)]
pub struct DB {
    inner: rocksdb::DB,
}

impl DB {
    /// Create db with all the column families provided if it doesn't exist at `path`; Otherwise,
    /// try to open it with all the column families.
    pub fn open<P: AsRef<Path>>(path: P, mut cf_opts_map: ColumnFamilyOptionsMap) -> Result<Self> {
        let mut db_opts = DBOptions::new();

        // For now we set the max total WAL size to be 1G. This config can be useful when column
        // families are updated at non-uniform frequencies.
        db_opts.set_max_total_wal_size(1 << 30);

        // If db exists, just open it with all cfs.
        if db_exists(path.as_ref()) {
            return DB::open_cf(db_opts, &path, cf_opts_map.into_iter().collect());
        }

        // If db doesn't exist, create a db first with all column families.
        db_opts.create_if_missing(true);

        let mut db = DB::open_cf(
            db_opts,
            path,
            vec![cf_opts_map
                .remove_entry(&DEFAULT_CF_NAME)
                .ok_or_else(|| format_err!("No \"default\" column family name found"))?],
        )?;
        cf_opts_map
            .into_iter()
            .map(|(cf_name, cf_opts)| db.create_cf((cf_name, cf_opts)))
            .collect::<Result<Vec<_>>>()?;
        Ok(db)
    }

    fn open_cf<'a, P, T>(opts: DBOptions, path: P, cfds: Vec<T>) -> Result<DB>
    where
        P: AsRef<Path>,
        T: Into<ColumnFamilyDescriptor<'a>>,
    {
        let inner = rocksdb::DB::open_cf(
            opts,
            path.as_ref().to_str().ok_or_else(|| {
                format_err!("Path {:?} can not be converted to string.", path.as_ref())
            })?,
            cfds,
        )
        .map_err(convert_rocksdb_err)?;

        Ok(DB { inner })
    }

    fn create_cf<'a, T>(&mut self, cfd: T) -> Result<()>
    where
        T: Into<ColumnFamilyDescriptor<'a>>,
    {
        let _cf_handle = self.inner.create_cf(cfd).map_err(convert_rocksdb_err)?;
        Ok(())
    }

    /// Reads single record by key.
    pub fn get<S: Schema>(&self, schema_key: &S::Key) -> Result<Option<S::Value>> {
        let k = <S::Key as KeyCodec<S>>::encode_key(&schema_key)?;
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;

        self.inner
            .get_cf(cf_handle, &k)
            .map_err(convert_rocksdb_err)?
            .map(|raw_value| <S::Value as ValueCodec<S>>::decode_value(&raw_value))
            .transpose()
    }

    /// Writes single record.
    pub fn put<S: Schema>(&self, key: &S::Key, value: &S::Value) -> Result<()> {
        let k = <S::Key as KeyCodec<S>>::encode_key(&key)?;
        let v = <S::Value as ValueCodec<S>>::encode_value(&value)?;
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;

        self.inner
            .put_cf_opt(cf_handle, &k, &v, &default_write_options())
            .map_err(convert_rocksdb_err)
    }

    /// Returns a [`SchemaIterator`] on a certain schema.
    pub fn iter<S: Schema>(&self, opts: ReadOptions) -> Result<SchemaIterator<S>> {
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;
        Ok(SchemaIterator::new(self.inner.iter_cf_opt(cf_handle, opts)))
    }

    /// Writes a group of records wrapped in a [`SchemaBatch`].
    pub fn write_schemas(&self, batch: SchemaBatch) -> Result<()> {
        let db_batch = rocksdb::WriteBatch::new();
        for (cf_name, key, write_op) in &batch.rows {
            let cf_handle = self.get_cf_handle(cf_name)?;
            match write_op {
                WriteOp::Value(value) => db_batch.put_cf(cf_handle, &key, &value),
                WriteOp::Deletion => db_batch.delete_cf(cf_handle, &key),
            }
            .map_err(convert_rocksdb_err)?;
        }

        self.inner
            .write_opt(&db_batch, &default_write_options())
            .map_err(convert_rocksdb_err)
    }

    fn get_cf_handle(&self, cf_name: &str) -> Result<&CFHandle> {
        self.inner.cf_handle(cf_name).ok_or_else(|| {
            format_err!(
                "DB::cf_handle not found for column family name: {}",
                cf_name
            )
        })
    }

    /// Returns the approximate size of each non-empty column family in bytes.
    pub fn get_approximate_sizes_cf(&self) -> Result<BTreeMap<String, u64>> {
        let mut cf_sizes = BTreeMap::new();

        for cf_name in self.inner.cf_names().into_iter().map(ToString::to_string) {
            let cf_handle = self.get_cf_handle(&cf_name)?;
            let first_key = match self.get_first_key(cf_handle) {
                Some(key) => key,
                None => {
                    cf_sizes.insert(cf_name, 0);
                    continue;
                }
            };
            let last_key = self
                .get_last_key(cf_handle)
                .ok_or_else(|| format_err!("Found first key but not last key."))?;
            let range = Range::new(&first_key, &last_key);
            let size = self
                .inner
                .get_approximate_sizes_cf(cf_handle, &[range])
                .pop()
                .ok_or_else(|| {
                    format_err!(
                        "Unable to get approximate size of {} column family.",
                        cf_name,
                    )
                })?;
            cf_sizes.insert(cf_name, size);
        }

        Ok(cf_sizes)
    }

    /// Returns the first key in given column family, if it exists.
    fn get_first_key(&self, cf_handle: &CFHandle) -> Option<Vec<u8>> {
        let mut iter = self.inner.iter_cf(cf_handle);
        iter.seek(rocksdb::SeekKey::Start);
        iter.kv().map(|(key, _value)| key)
    }

    /// Returns the last key in given column family, if it exists.
    fn get_last_key(&self, cf_handle: &CFHandle) -> Option<Vec<u8>> {
        let mut iter = self.inner.iter_cf(cf_handle);
        iter.seek(rocksdb::SeekKey::End);
        iter.kv().map(|(key, _value)| key)
    }

    /// Flushes all memtable data. If `sync` is true, the flush will wait until it's done. This is
    /// only used for testing `get_approximate_sizes_cf` in unit tests.
    pub fn flush_all(&self, sync: bool) -> Result<()> {
        for cf_name in self.inner.cf_names() {
            let cf_handle = self.get_cf_handle(cf_name)?;
            self.inner
                .flush_cf(cf_handle, sync)
                .map_err(convert_rocksdb_err)?;
        }
        Ok(())
    }
}

/// For now we always use synchronous writes. This makes sure that once the operation returns
/// `Ok(())` the data is persisted even if the machine crashes. In the future we might consider
/// selectively turning this off for some non-critical writes to improve performance.
fn default_write_options() -> WriteOptions {
    let mut opts = WriteOptions::new();
    opts.set_sync(true);
    opts
}
<commit_msg>[schemadb] use GetIntProperty to get live data size of some cf<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

//! This library implements a schematized DB on top of [RocksDB](https://rocksdb.org/). It makes
//! sure all data passed in and out are structured according to predefined schemas and prevents
//! access to raw keys and values. This library also enforces a set of Libra specific DB options,
//! like custom comparators and schema-to-column-family mapping.
//!
//! It requires that different kinds of key-value pairs be stored in separate column
//! families.  To use this library to store a kind of key-value pairs, the user needs to use the
//! [`define_schema!`] macro to define the schema name, the types of key and value, and name of the
//! column family.

#[macro_use]
pub mod schema;

use crate::schema::{KeyCodec, Schema, SeekKeyCodec, ValueCodec};
use failure::prelude::*;
use rocksdb::{
    rocksdb_options::ColumnFamilyDescriptor, CFHandle, DBOptions, Writable, WriteOptions,
};
use std::{
    collections::{BTreeMap, HashMap},
    iter::Iterator,
    marker::PhantomData,
    path::Path,
};

/// Type alias to `rocksdb::ColumnFamilyOptions`. See [`rocksdb doc`](https://github.com/pingcap/rust-rocksdb/blob/master/src/rocksdb_options.rs)
pub type ColumnFamilyOptions = rocksdb::ColumnFamilyOptions;
/// Type alias to `rocksdb::ReadOptions`. See [`rocksdb doc`](https://github.com/pingcap/rust-rocksdb/blob/master/src/rocksdb_options.rs)
pub type ReadOptions = rocksdb::ReadOptions;

/// Type alias to improve readability.
pub type ColumnFamilyName = &'static str;
/// Type alias to improve readability.
pub type ColumnFamilyOptionsMap = HashMap<ColumnFamilyName, ColumnFamilyOptions>;

/// Name for the `default` column family that's always open by RocksDB. We use it to store
/// [`LedgerInfo`](../types/ledger_info/struct.LedgerInfo.html).
pub const DEFAULT_CF_NAME: ColumnFamilyName = "default";

#[derive(Debug)]
enum WriteOp {
    Value(Vec<u8>),
    Deletion,
}

/// `SchemaBatch` holds a collection of updates that can be applied to a DB atomically. The updates
/// will be applied in the order in which they are added to the `SchemaBatch`.
#[derive(Debug, Default)]
pub struct SchemaBatch {
    rows: Vec<(ColumnFamilyName, Vec<u8> /* key */, WriteOp)>,
}

impl SchemaBatch {
    /// Creates an empty batch.
    pub fn new() -> Self {
        Self::default()
    }

    /// Adds an insert/update operation to the batch.
    pub fn put<S: Schema>(&mut self, key: &S::Key, value: &S::Value) -> Result<()> {
        let key = <S::Key as KeyCodec<S>>::encode_key(key)?;
        let value = <S::Value as ValueCodec<S>>::encode_value(value)?;
        self.rows
            .push((S::COLUMN_FAMILY_NAME, key, WriteOp::Value(value)));
        Ok(())
    }

    /// Adds a delete operation to the batch.
    pub fn delete<S: Schema>(&mut self, key: &S::Key) -> Result<()> {
        let key = <S::Key as KeyCodec<S>>::encode_key(key)?;
        self.rows
            .push((S::COLUMN_FAMILY_NAME, key, WriteOp::Deletion));
        Ok(())
    }
}

/// DB Iterator parameterized on [`Schema`] that seeks with [`Schema::Key`] and yields
/// [`Schema::Key`] and [`Schema::Value`]
pub struct SchemaIterator<'a, S> {
    db_iter: rocksdb::DBIterator<&'a rocksdb::DB>,
    phantom: PhantomData<S>,
}

impl<'a, S> SchemaIterator<'a, S>
where
    S: Schema,
{
    fn new(db_iter: rocksdb::DBIterator<&'a rocksdb::DB>) -> Self {
        SchemaIterator {
            db_iter,
            phantom: PhantomData,
        }
    }

    /// Seeks to the first key.
    pub fn seek_to_first(&mut self) -> bool {
        self.db_iter.seek(rocksdb::SeekKey::Start)
    }

    /// Seeks to the last key.
    pub fn seek_to_last(&mut self) -> bool {
        self.db_iter.seek(rocksdb::SeekKey::End)
    }

    /// Seeks to the first key whose binary representation is equal to or greater than that of the
    /// `seek_key`.
    pub fn seek<SK>(&mut self, seek_key: &SK) -> Result<bool>
    where
        SK: SeekKeyCodec<S>,
    {
        let key = <SK as SeekKeyCodec<S>>::encode_seek_key(seek_key)?;
        Ok(self.db_iter.seek(rocksdb::SeekKey::Key(&key)))
    }

    /// Seeks to the last key whose binary representation is less than or equal to that of the
    /// `seek_key`.
    ///
    /// See example in [`RocksDB doc`](https://github.com/facebook/rocksdb/wiki/SeekForPrev).
    pub fn seek_for_prev<SK>(&mut self, seek_key: &SK) -> Result<bool>
    where
        SK: SeekKeyCodec<S>,
    {
        let key = <SK as SeekKeyCodec<S>>::encode_seek_key(seek_key)?;
        Ok(self.db_iter.seek_for_prev(rocksdb::SeekKey::Key(&key)))
    }
}

impl<'a, S> Iterator for SchemaIterator<'a, S>
where
    S: Schema,
{
    type Item = Result<(S::Key, S::Value)>;

    fn next(&mut self) -> Option<Self::Item> {
        self.db_iter.kv().map(|(raw_key, raw_value)| {
            self.db_iter.next();
            Ok((
                <S::Key as KeyCodec<S>>::decode_key(&raw_key)?,
                <S::Value as ValueCodec<S>>::decode_value(&raw_value)?,
            ))
        })
    }
}

/// Checks underlying Rocksdb instance existence by checking `CURRENT` file existence, the same way
/// Rocksdb adopts to detect db existence.
fn db_exists(path: &Path) -> bool {
    let rocksdb_current_file = path.join("CURRENT");
    rocksdb_current_file.is_file()
}

/// All the RocksDB methods return `std::result::Result<T, String>`. Since our methods return
/// `failure::Result<T>`, manual conversion is needed.
fn convert_rocksdb_err(msg: String) -> failure::Error {
    format_err!("RocksDB internal error: {}.", msg)
}

/// This DB is a schematized RocksDB wrapper where all data passed in and out are typed according to
/// [`Schema`]s.
#[derive(Debug)]
pub struct DB {
    inner: rocksdb::DB,
}

impl DB {
    /// Create db with all the column families provided if it doesn't exist at `path`; Otherwise,
    /// try to open it with all the column families.
    pub fn open<P: AsRef<Path>>(path: P, mut cf_opts_map: ColumnFamilyOptionsMap) -> Result<Self> {
        let mut db_opts = DBOptions::new();

        // For now we set the max total WAL size to be 1G. This config can be useful when column
        // families are updated at non-uniform frequencies.
        db_opts.set_max_total_wal_size(1 << 30);

        // If db exists, just open it with all cfs.
        if db_exists(path.as_ref()) {
            return DB::open_cf(db_opts, &path, cf_opts_map.into_iter().collect());
        }

        // If db doesn't exist, create a db first with all column families.
        db_opts.create_if_missing(true);

        let mut db = DB::open_cf(
            db_opts,
            path,
            vec![cf_opts_map
                .remove_entry(&DEFAULT_CF_NAME)
                .ok_or_else(|| format_err!("No \"default\" column family name found"))?],
        )?;
        cf_opts_map
            .into_iter()
            .map(|(cf_name, cf_opts)| db.create_cf((cf_name, cf_opts)))
            .collect::<Result<Vec<_>>>()?;
        Ok(db)
    }

    fn open_cf<'a, P, T>(opts: DBOptions, path: P, cfds: Vec<T>) -> Result<DB>
    where
        P: AsRef<Path>,
        T: Into<ColumnFamilyDescriptor<'a>>,
    {
        let inner = rocksdb::DB::open_cf(
            opts,
            path.as_ref().to_str().ok_or_else(|| {
                format_err!("Path {:?} can not be converted to string.", path.as_ref())
            })?,
            cfds,
        )
        .map_err(convert_rocksdb_err)?;

        Ok(DB { inner })
    }

    fn create_cf<'a, T>(&mut self, cfd: T) -> Result<()>
    where
        T: Into<ColumnFamilyDescriptor<'a>>,
    {
        let _cf_handle = self.inner.create_cf(cfd).map_err(convert_rocksdb_err)?;
        Ok(())
    }

    /// Reads single record by key.
    pub fn get<S: Schema>(&self, schema_key: &S::Key) -> Result<Option<S::Value>> {
        let k = <S::Key as KeyCodec<S>>::encode_key(&schema_key)?;
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;

        self.inner
            .get_cf(cf_handle, &k)
            .map_err(convert_rocksdb_err)?
            .map(|raw_value| <S::Value as ValueCodec<S>>::decode_value(&raw_value))
            .transpose()
    }

    /// Writes single record.
    pub fn put<S: Schema>(&self, key: &S::Key, value: &S::Value) -> Result<()> {
        let k = <S::Key as KeyCodec<S>>::encode_key(&key)?;
        let v = <S::Value as ValueCodec<S>>::encode_value(&value)?;
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;

        self.inner
            .put_cf_opt(cf_handle, &k, &v, &default_write_options())
            .map_err(convert_rocksdb_err)
    }

    /// Returns a [`SchemaIterator`] on a certain schema.
    pub fn iter<S: Schema>(&self, opts: ReadOptions) -> Result<SchemaIterator<S>> {
        let cf_handle = self.get_cf_handle(S::COLUMN_FAMILY_NAME)?;
        Ok(SchemaIterator::new(self.inner.iter_cf_opt(cf_handle, opts)))
    }

    /// Writes a group of records wrapped in a [`SchemaBatch`].
    pub fn write_schemas(&self, batch: SchemaBatch) -> Result<()> {
        let db_batch = rocksdb::WriteBatch::new();
        for (cf_name, key, write_op) in &batch.rows {
            let cf_handle = self.get_cf_handle(cf_name)?;
            match write_op {
                WriteOp::Value(value) => db_batch.put_cf(cf_handle, &key, &value),
                WriteOp::Deletion => db_batch.delete_cf(cf_handle, &key),
            }
            .map_err(convert_rocksdb_err)?;
        }

        self.inner
            .write_opt(&db_batch, &default_write_options())
            .map_err(convert_rocksdb_err)
    }

    fn get_cf_handle(&self, cf_name: &str) -> Result<&CFHandle> {
        self.inner.cf_handle(cf_name).ok_or_else(|| {
            format_err!(
                "DB::cf_handle not found for column family name: {}",
                cf_name
            )
        })
    }

    /// Returns the approximate size of each non-empty column family in bytes.
    pub fn get_approximate_sizes_cf(&self) -> Result<BTreeMap<String, u64>> {
        let mut cf_sizes = BTreeMap::new();

        for cf_name in self.inner.cf_names().into_iter().map(ToString::to_string) {
            let cf_handle = self.get_cf_handle(&cf_name)?;
            let size = self
                .inner
                .get_property_int_cf(cf_handle, "rocksdb.estimate-live-data-size")
                .ok_or_else(|| {
                    format_err!(
                        "Unable to get approximate size of {} column family.",
                        cf_name,
                    )
                })?;
            cf_sizes.insert(cf_name, size);
        }

        Ok(cf_sizes)
    }

    /// Flushes all memtable data. If `sync` is true, the flush will wait until it's done. This is
    /// only used for testing `get_approximate_sizes_cf` in unit tests.
    pub fn flush_all(&self, sync: bool) -> Result<()> {
        for cf_name in self.inner.cf_names() {
            let cf_handle = self.get_cf_handle(cf_name)?;
            self.inner
                .flush_cf(cf_handle, sync)
                .map_err(convert_rocksdb_err)?;
        }
        Ok(())
    }
}

/// For now we always use synchronous writes. This makes sure that once the operation returns
/// `Ok(())` the data is persisted even if the machine crashes. In the future we might consider
/// selectively turning this off for some non-critical writes to improve performance.
fn default_write_options() -> WriteOptions {
    let mut opts = WriteOptions::new();
    opts.set_sync(true);
    opts
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Blockchain explorer module provides api for getting information about blocks and transactions
//! from the blockchain.

use serde_json::Value;

use std::cmp;

use storage::ListProof;
use crypto::Hash;
use blockchain::{Schema, Blockchain, Block, TxLocation};
use messages::Precommit;
// TODO: if explorer is usable anywhere else, remove `ApiError` dependencies (ECR-163).
use api::ApiError;
use helpers::Height;

/// Blockchain explorer.
#[derive(Debug)]
pub struct BlockchainExplorer<'a> {
    blockchain: &'a Blockchain,
}

/// Block information.
#[derive(Debug, Serialize)]
pub struct BlockInfo {
    /// Block header from blockchain.
    pub block: Block,
    /// List of precommit for this block.
    pub precommits: Vec<Precommit>,
    /// List of hashes for transactions that was executed into this block.
    pub txs: Vec<Hash>,
}

/// Transaction information.
#[derive(Debug, Serialize)]
pub struct TxInfo {
    /// `JSON` serialized transaction
    pub content: Value,
    /// Transaction location in block
    pub location: TxLocation,
    /// Proof that transaction really exist in database
    pub proof_to_block_merkle_root: ListProof<Hash>,
}

impl<'a> BlockchainExplorer<'a> {
    /// Creates a new `BlockchainExplorer` instance.
    pub fn new(blockchain: &'a Blockchain) -> Self {
        BlockchainExplorer { blockchain: blockchain }
    }

    /// Returns information about the transaction identified by the hash.
    pub fn tx_info(&self, tx_hash: &Hash) -> Result<Option<TxInfo>, ApiError> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let tx = schema.transactions().get(tx_hash);
        let res = match tx {
            None => None,
            Some(raw_tx) => {
                let box_transaction = self.blockchain.tx_from_raw(raw_tx.clone()).ok_or_else(|| {
                    ApiError::Service(format!("Service not found for tx: {:?}", raw_tx).into())
                })?;
                let content = box_transaction.info();

                let location = schema.tx_location_by_tx_hash().get(tx_hash).expect(
                    &format!(
                        "Not found tx_hash location: {:?}",
                        tx_hash
                    ),
                );

                let block_height = location.block_height();
                let tx_index = location.position_in_block();
                let proof = schema.block_txs(block_height).get_proof(tx_index);
                let tx_info = TxInfo {
                    content: content,
                    location: location,
                    proof_to_block_merkle_root: proof,
                };
                Some(tx_info)
            }
        };
        Ok(res)
    }

    /// Returns block information for the specified height or `None` if there is no such block.
    pub fn block_info(&self, height: Height) -> Option<BlockInfo> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let txs_table = schema.block_txs(height);
        let block_proof = schema.block_and_precommits(height);
        match block_proof {
            None => None,
            Some(proof) => {
                let bl = BlockInfo {
                    block: proof.block,
                    precommits: proof.precommits,
                    txs: txs_table.iter().collect(),
                };
                Some(bl)
            }
        }
    }

    /// Returns the list of blocks in the given range.
    pub fn blocks_range(
        &self,
        count: u64,
        upper: Option<u64>,
        skip_empty_blocks: bool,
    ) -> Vec<Block> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let hashes = schema.block_hashes_by_height();
        let blocks = schema.blocks();

        let max_len = hashes.len();
        let upper = upper.map(|x| cmp::min(x, max_len)).unwrap_or(max_len);
        let lower = upper.checked_sub(count).unwrap_or(0);

        let mut v = Vec::new();
        for height in (lower..upper).rev() {
            let block_txs = schema.block_txs(Height(height));
            if skip_empty_blocks && block_txs.is_empty() {
                continue;
            }
            let block_hash = hashes.get(height).expect(&format!(
                "Block not found, height:{:?}",
                height
            ));
            let block = blocks.get(&block_hash).expect(&format!(
                "Block not found, hash:{:?}",
                block_hash
            ));
            v.push(block)
        }
        v
    }
}
<commit_msg>Derive `Deserialize` for explorer datatypes<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Blockchain explorer module provides api for getting information about blocks and transactions
//! from the blockchain.

use serde_json::Value;

use std::cmp;

use storage::ListProof;
use crypto::Hash;
use blockchain::{Schema, Blockchain, Block, TxLocation};
use messages::Precommit;
// TODO: if explorer is usable anywhere else, remove `ApiError` dependencies (ECR-163).
use api::ApiError;
use helpers::Height;

/// Blockchain explorer.
#[derive(Debug)]
pub struct BlockchainExplorer<'a> {
    blockchain: &'a Blockchain,
}

/// Block information.
#[derive(Debug, Serialize, Deserialize)]
pub struct BlockInfo {
    /// Block header from blockchain.
    pub block: Block,
    /// List of precommit for this block.
    pub precommits: Vec<Precommit>,
    /// List of hashes for transactions that was executed into this block.
    pub txs: Vec<Hash>,
}

/// Transaction information.
#[derive(Debug, Serialize, Deserialize)]
pub struct TxInfo {
    /// `JSON` serialized transaction
    pub content: Value,
    /// Transaction location in block
    pub location: TxLocation,
    /// Proof that transaction really exist in database
    pub proof_to_block_merkle_root: ListProof<Hash>,
}

impl<'a> BlockchainExplorer<'a> {
    /// Creates a new `BlockchainExplorer` instance.
    pub fn new(blockchain: &'a Blockchain) -> Self {
        BlockchainExplorer { blockchain: blockchain }
    }

    /// Returns information about the transaction identified by the hash.
    pub fn tx_info(&self, tx_hash: &Hash) -> Result<Option<TxInfo>, ApiError> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let tx = schema.transactions().get(tx_hash);
        let res = match tx {
            None => None,
            Some(raw_tx) => {
                let box_transaction = self.blockchain.tx_from_raw(raw_tx.clone()).ok_or_else(|| {
                    ApiError::Service(format!("Service not found for tx: {:?}", raw_tx).into())
                })?;
                let content = box_transaction.info();

                let location = schema.tx_location_by_tx_hash().get(tx_hash).expect(
                    &format!(
                        "Not found tx_hash location: {:?}",
                        tx_hash
                    ),
                );

                let block_height = location.block_height();
                let tx_index = location.position_in_block();
                let proof = schema.block_txs(block_height).get_proof(tx_index);
                let tx_info = TxInfo {
                    content: content,
                    location: location,
                    proof_to_block_merkle_root: proof,
                };
                Some(tx_info)
            }
        };
        Ok(res)
    }

    /// Returns block information for the specified height or `None` if there is no such block.
    pub fn block_info(&self, height: Height) -> Option<BlockInfo> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let txs_table = schema.block_txs(height);
        let block_proof = schema.block_and_precommits(height);
        match block_proof {
            None => None,
            Some(proof) => {
                let bl = BlockInfo {
                    block: proof.block,
                    precommits: proof.precommits,
                    txs: txs_table.iter().collect(),
                };
                Some(bl)
            }
        }
    }

    /// Returns the list of blocks in the given range.
    pub fn blocks_range(
        &self,
        count: u64,
        upper: Option<u64>,
        skip_empty_blocks: bool,
    ) -> Vec<Block> {
        let b = self.blockchain.clone();
        let snapshot = b.snapshot();
        let schema = Schema::new(&snapshot);
        let hashes = schema.block_hashes_by_height();
        let blocks = schema.blocks();

        let max_len = hashes.len();
        let upper = upper.map(|x| cmp::min(x, max_len)).unwrap_or(max_len);
        let lower = upper.checked_sub(count).unwrap_or(0);

        let mut v = Vec::new();
        for height in (lower..upper).rev() {
            let block_txs = schema.block_txs(Height(height));
            if skip_empty_blocks && block_txs.is_empty() {
                continue;
            }
            let block_hash = hashes.get(height).expect(&format!(
                "Block not found, height:{:?}",
                height
            ));
            let block = blocks.get(&block_hash).expect(&format!(
                "Block not found, hash:{:?}",
                block_hash
            ));
            v.push(block)
        }
        v
    }
}
<|endoftext|>


<commit_before>use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::{HelperDef, HelperResult};
use crate::json::value::JsonTruthy;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Helper, RenderContext, Renderable};

#[derive(Clone, Copy)]
pub struct IfHelper {
    positive: bool,
}

impl HelperDef for IfHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry<'reg>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        let param = h
            .param(0)
            .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?;
        let include_zero = h
            .hash_get("includeZero")
            .and_then(|v| v.value().as_bool())
            .unwrap_or(false);

        let mut value = param.value().is_truthy(include_zero);

        if !self.positive {
            value = !value;
        }

        let tmpl = if value { h.template() } else { h.inverse() };
        match tmpl {
            Some(ref t) => t.render(r, ctx, rc, out),
            None => Ok(()),
        }
    }
}

pub static IF_HELPER: IfHelper = IfHelper { positive: true };
pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };

#[cfg(test)]
mod test {
    use crate::helpers::WITH_HELPER;
    use crate::registry::Registry;
    use serde_json::value::Value as Json;
    use std::str::FromStr;

    #[test]
    fn test_if() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{#if this}}hello{{/if}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{#unless this}}hello{{else}}world{{/unless}}")
            .is_ok());

        let r0 = handlebars.render("t0", &true);
        assert_eq!(r0.ok().unwrap(), "hello".to_string());

        let r1 = handlebars.render("t1", &true);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t0", &false);
        assert_eq!(r2.ok().unwrap(), "".to_string());
    }

    #[test]
    fn test_if_context() {
        let json_str = r#"{"a":{"b":99,"c":{"d": true}}}"#;
        let data = Json::from_str(json_str).unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_helper("with", Box::new(WITH_HELPER));
        assert!(handlebars
            .register_template_string("t0", "{{#if a.c.d}}hello {{a.b}}{{/if}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t1",
                "{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}"
            )
            .is_ok());

        let r0 = handlebars.render("t0", &data);
        assert_eq!(r0.unwrap(), "hello 99".to_string());

        let r1 = handlebars.render("t1", &data);
        assert_eq!(r1.unwrap(), "hello 99".to_string());
    }

    #[test]
    fn test_if_include_zero() {
        use std::f64;
        let handlebars = Registry::new();

        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template("{{#if a}}1{{else}}0{{/if}}", &json!({"a": 0}))
                .unwrap()
        );
        assert_eq!(
            "1".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({"a": 0})
                )
                .unwrap()
        );
        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({ "a": f64::NAN })
                )
                .unwrap()
        );
    }

    #[test]
    fn test_invisible_line_stripping() {
        let hbs = Registry::new();
        assert_eq!(
            "yes\n",
            hbs.render_template("{{#if a}}\nyes\n{{/if}}\n", &json!({"a": true}))
                .unwrap()
        );

        assert_eq!(
            "x\ny",
            hbs.render_template("{{#if a}}x{{/if}}\ny", &json!({"a": true}))
                .unwrap()
        );

        assert_eq!(
            "y\nz",
            hbs.render_template("{{#if a}}\nx\n{{^}}\ny\n{{/if}}\nz", &json!({"a": false}))
                .unwrap()
        );

        assert_eq!(
            r#"  foo
  bar
  baz"#,
            hbs.render_template(
                r#"{{~#if true}}
  foo
  bar
{{/if}}
  baz"#,
                &json!({})
            )
            .unwrap()
        );
    }
}
<commit_msg>(test) add a failure case when helper tag has a leading whitespace<commit_after>use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::{HelperDef, HelperResult};
use crate::json::value::JsonTruthy;
use crate::output::Output;
use crate::registry::Registry;
use crate::render::{Helper, RenderContext, Renderable};

#[derive(Clone, Copy)]
pub struct IfHelper {
    positive: bool,
}

impl HelperDef for IfHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        r: &'reg Registry<'reg>,
        ctx: &'rc Context,
        rc: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        let param = h
            .param(0)
            .ok_or_else(|| RenderError::new("Param not found for helper \"if\""))?;
        let include_zero = h
            .hash_get("includeZero")
            .and_then(|v| v.value().as_bool())
            .unwrap_or(false);

        let mut value = param.value().is_truthy(include_zero);

        if !self.positive {
            value = !value;
        }

        let tmpl = if value { h.template() } else { h.inverse() };
        match tmpl {
            Some(ref t) => t.render(r, ctx, rc, out),
            None => Ok(()),
        }
    }
}

pub static IF_HELPER: IfHelper = IfHelper { positive: true };
pub static UNLESS_HELPER: IfHelper = IfHelper { positive: false };

#[cfg(test)]
mod test {
    use crate::helpers::WITH_HELPER;
    use crate::registry::Registry;
    use serde_json::value::Value as Json;
    use std::str::FromStr;

    #[test]
    fn test_if() {
        let mut handlebars = Registry::new();
        assert!(handlebars
            .register_template_string("t0", "{{#if this}}hello{{/if}}")
            .is_ok());
        assert!(handlebars
            .register_template_string("t1", "{{#unless this}}hello{{else}}world{{/unless}}")
            .is_ok());

        let r0 = handlebars.render("t0", &true);
        assert_eq!(r0.ok().unwrap(), "hello".to_string());

        let r1 = handlebars.render("t1", &true);
        assert_eq!(r1.ok().unwrap(), "world".to_string());

        let r2 = handlebars.render("t0", &false);
        assert_eq!(r2.ok().unwrap(), "".to_string());
    }

    #[test]
    fn test_if_context() {
        let json_str = r#"{"a":{"b":99,"c":{"d": true}}}"#;
        let data = Json::from_str(json_str).unwrap();

        let mut handlebars = Registry::new();
        handlebars.register_helper("with", Box::new(WITH_HELPER));
        assert!(handlebars
            .register_template_string("t0", "{{#if a.c.d}}hello {{a.b}}{{/if}}")
            .is_ok());
        assert!(handlebars
            .register_template_string(
                "t1",
                "{{#with a}}{{#if c.d}}hello {{../a.b}}{{/if}}{{/with}}"
            )
            .is_ok());

        let r0 = handlebars.render("t0", &data);
        assert_eq!(r0.unwrap(), "hello 99".to_string());

        let r1 = handlebars.render("t1", &data);
        assert_eq!(r1.unwrap(), "hello 99".to_string());
    }

    #[test]
    fn test_if_include_zero() {
        use std::f64;
        let handlebars = Registry::new();

        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template("{{#if a}}1{{else}}0{{/if}}", &json!({"a": 0}))
                .unwrap()
        );
        assert_eq!(
            "1".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({"a": 0})
                )
                .unwrap()
        );
        assert_eq!(
            "0".to_owned(),
            handlebars
                .render_template(
                    "{{#if a includeZero=true}}1{{else}}0{{/if}}",
                    &json!({ "a": f64::NAN })
                )
                .unwrap()
        );
    }

    #[test]
    fn test_invisible_line_stripping() {
        let hbs = Registry::new();
        assert_eq!(
            "yes\n",
            hbs.render_template("{{#if a}}\nyes\n{{/if}}\n", &json!({"a": true}))
                .unwrap()
        );

        assert_eq!(
            "x\ny",
            hbs.render_template("{{#if a}}x{{/if}}\ny", &json!({"a": true}))
                .unwrap()
        );

        assert_eq!(
            "y\nz",
            hbs.render_template("{{#if a}}\nx\n{{^}}\ny\n{{/if}}\nz", &json!({"a": false}))
                .unwrap()
        );

        assert_eq!(
            r#"  foo
  bar
  baz"#,
            hbs.render_template(
                r#"  {{#if true}}
  foo
  bar
  {{/if}}
  baz"#,
                &json!({})
            )
            .unwrap()
        );
    }
}
<|endoftext|>


<commit_before>use proc_macro2;
use syn;

use diagnostic_shim::*;
use meta::*;
use model::*;
use util::*;

pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let model = Model::from_item(&item)?;
    let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to")
        .into_iter()
        .filter_map(
            |attr| match derive_belongs_to(&model, &item.generics, attr) {
                Ok(t) => Some(t),
                Err(e) => {
                    e.emit();
                    None
                }
            },
        );

    Ok(wrap_in_dummy_mod(
        model.dummy_mod_name("associations"),
        quote!(#(#tokens)*),
    ))
}

fn derive_belongs_to(
    model: &Model,
    generics: &syn::Generics,
    meta: MetaItem,
) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let AssociationOptions {
        parent_struct,
        foreign_key,
    } = AssociationOptions::from_meta(meta)?;
    let (_, ty_generics, _) = generics.split_for_impl();

    let foreign_key_field = model.find_column(&foreign_key)?;
    let struct_name = &model.name;
    let foreign_key_access = foreign_key_field.name.access();
    let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty);
    let table_name = model.table_name();

    let mut generics = generics.clone();

    // TODO: Remove this specialcasing as soon as we bump our minimal supported
    // rust version to >= 1.30.0 because this version will add
    // `impl<'a, T> From<&'a Option<T>> for Option<&'a T>` to the std-lib
    let foreign_key_expr = if is_option_ty(&foreign_key_field.ty) {
        quote!(self#foreign_key_access.as_ref().and_then(std::convert::Into::into))
    } else {
        quote!(std::convert::Into::into(&self#foreign_key_access))
    };

    generics.params.push(parse_quote!(__FK));
    {
        let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
        where_clause
            .predicates
            .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq));
        where_clause.predicates.push(
            parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>),
        );
        where_clause.predicates.push(
            parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable<Id = &'__a __FK>),
        );
    }

    let (impl_generics, _, where_clause) = generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics diesel::associations::BelongsTo<#parent_struct>
            for #struct_name #ty_generics
        #where_clause
        {
            type ForeignKey = __FK;
            type ForeignKeyColumn = #table_name::#foreign_key;

            fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> {
                #foreign_key_expr
            }

            fn foreign_key_column() -> Self::ForeignKeyColumn {
                #table_name::#foreign_key
            }
        }
    })
}

struct AssociationOptions {
    parent_struct: syn::Ident,
    foreign_key: syn::Ident,
}

impl AssociationOptions {
    fn from_meta(meta: MetaItem) -> Result<Self, Diagnostic> {
        let parent_struct = meta.nested()?
            .nth(0)
            .ok_or_else(|| meta.span())
            .and_then(|m| m.word().map_err(|_| m.span()))
            .map_err(|span| {
                span.error("Expected a struct name")
                    .help("e.g. `#[belongs_to(User)]`")
            })?;
        let foreign_key = meta.nested_item("foreign_key")
            .ok()
            .map(|i| i.ident_value())
            .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct)))?;

        let unrecognized_options = meta.nested()?.skip(1).filter(|n| n.name() != "foreign_key");
        for ignored in unrecognized_options {
            ignored
                .span()
                .warning(format!("Unrecognized option {}", ignored.name()))
                .emit();
        }

        Ok(Self {
            parent_struct,
            foreign_key,
        })
    }
}

fn infer_foreign_key(name: &syn::Ident) -> syn::Ident {
    let snake_case = camel_to_snake(&name.to_string());
    syn::Ident::new(&format!("{}_id", snake_case), name.span())
}
<commit_msg>Address review comments<commit_after>use proc_macro2;
use syn;

use diagnostic_shim::*;
use meta::*;
use model::*;
use util::*;

pub fn derive(item: syn::DeriveInput) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let model = Model::from_item(&item)?;
    let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to")
        .into_iter()
        .filter_map(
            |attr| match derive_belongs_to(&model, &item.generics, attr) {
                Ok(t) => Some(t),
                Err(e) => {
                    e.emit();
                    None
                }
            },
        );

    Ok(wrap_in_dummy_mod(
        model.dummy_mod_name("associations"),
        quote!(#(#tokens)*),
    ))
}

fn derive_belongs_to(
    model: &Model,
    generics: &syn::Generics,
    meta: MetaItem,
) -> Result<proc_macro2::TokenStream, Diagnostic> {
    let AssociationOptions {
        parent_struct,
        foreign_key,
    } = AssociationOptions::from_meta(meta)?;
    let (_, ty_generics, _) = generics.split_for_impl();

    let foreign_key_field = model.find_column(&foreign_key)?;
    let struct_name = &model.name;
    let foreign_key_access = foreign_key_field.name.access();
    let foreign_key_ty = inner_of_option_ty(&foreign_key_field.ty);
    let table_name = model.table_name();

    let mut generics = generics.clone();

    // TODO: Remove this special casing as soon as we bump our minimal supported
    // rust version to >= 1.30.0 because this version will add
    // `impl<'a, T> From<&'a Option<T>> for Option<&'a T>` to the std-lib
    let (foreign_key_expr, foreign_key_ty) = if is_option_ty(&foreign_key_field.ty) {
        (
            quote!(self#foreign_key_access.as_ref()),
            quote!(#foreign_key_ty),
        )
    } else {
        generics.params.push(parse_quote!(__FK));
        {
            let where_clause = generics.where_clause.get_or_insert(parse_quote!(where));
            where_clause
                .predicates
                .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq));
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>),
            );
            where_clause.predicates.push(
                parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable<Id = &'__a __FK>),
            );
        }

        (
            quote!(std::convert::Into::into(&self#foreign_key_access)),
            quote!(__FK),
        )
    };

    let (impl_generics, _, where_clause) = generics.split_for_impl();

    Ok(quote! {
        impl #impl_generics diesel::associations::BelongsTo<#parent_struct>
            for #struct_name #ty_generics
        #where_clause
        {
            type ForeignKey = #foreign_key_ty;
            type ForeignKeyColumn = #table_name::#foreign_key;

            fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> {
                #foreign_key_expr
            }

            fn foreign_key_column() -> Self::ForeignKeyColumn {
                #table_name::#foreign_key
            }
        }
    })
}

struct AssociationOptions {
    parent_struct: syn::Ident,
    foreign_key: syn::Ident,
}

impl AssociationOptions {
    fn from_meta(meta: MetaItem) -> Result<Self, Diagnostic> {
        let parent_struct = meta.nested()?
            .nth(0)
            .ok_or_else(|| meta.span())
            .and_then(|m| m.word().map_err(|_| m.span()))
            .map_err(|span| {
                span.error("Expected a struct name")
                    .help("e.g. `#[belongs_to(User)]`")
            })?;
        let foreign_key = meta.nested_item("foreign_key")
            .ok()
            .map(|i| i.ident_value())
            .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct)))?;

        let unrecognized_options = meta.nested()?.skip(1).filter(|n| n.name() != "foreign_key");
        for ignored in unrecognized_options {
            ignored
                .span()
                .warning(format!("Unrecognized option {}", ignored.name()))
                .emit();
        }

        Ok(Self {
            parent_struct,
            foreign_key,
        })
    }
}

fn infer_foreign_key(name: &syn::Ident) -> syn::Ident {
    let snake_case = camel_to_snake(&name.to_string());
    syn::Ident::new(&format!("{}_id", snake_case), name.span())
}
<|endoftext|>