<commit_before>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains the "canonicalizer" itself.
//!
//! For an overview of what canonicaliation is and how it fits into
//! rustc, check out the [chapter in the rustc guide][c].
//!
//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html

use infer::canonical::{
    Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
    SmallCanonicalVarValues,
};
use infer::InferCtxt;
use std::sync::atomic::Ordering;
use ty::fold::{TypeFoldable, TypeFolder};
use ty::subst::Kind;
use ty::{self, CanonicalVar, Lift, List, Ty, TyCtxt, TypeFlags};

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use smallvec::SmallVec;

impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
    /// Canonicalizes a query value `V`. When we canonicalize a query,
    /// we not only canonicalize unbound inference variables, but we
    /// *also* replace all free regions whatsoever. So for example a
    /// query like `T: Trait<'static>` would be canonicalized to
    ///
    /// ```text
    /// T: Trait<'?0>
    /// ```
    ///
    /// with a mapping M that maps `'?0` to `'static`.
    ///
    /// To get a good understanding of what is happening here, check
    /// out the [chapter in the rustc guide][c].
    ///
    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query
    pub fn canonicalize_query<V>(
        &self,
        value: &V,
        var_values: &mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        self.tcx
            .sess
            .perf_stats
            .queries_canonicalized
            .fetch_add(1, Ordering::Relaxed);

        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: true,
                other_free_regions: true,
            },
            var_values,
        )
    }

    /// Canonicalizes a query *response* `V`. When we canonicalize a
    /// query response, we only canonicalize unbound inference
    /// variables, and we leave other free regions alone. So,
    /// continuing with the example from `canonicalize_query`, if
    /// there was an input query `T: Trait<'static>`, it would have
    /// been canonicalized to
    ///
    /// ```text
    /// T: Trait<'?0>
    /// ```
    ///
    /// with a mapping M that maps `'?0` to `'static`. But if we found that there
    /// exists only one possible impl of `Trait`, and it looks like
    ///
    ///     impl<T> Trait<'static> for T { .. }
    ///
    /// then we would prepare a query result R that (among other
    /// things) includes a mapping to `'?0 := 'static`. When
    /// canonicalizing this query result R, we would leave this
    /// reference to `'static` alone.
    ///
    /// To get a good understanding of what is happening here, check
    /// out the [chapter in the rustc guide][c].
    ///
    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result
    pub fn canonicalize_response<V>(&self, value: &V) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        let mut var_values = SmallVec::new();
        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: false,
                other_free_regions: false,
            },
            &mut var_values,
        )
    }

    /// A hacky variant of `canonicalize_query` that does not
    /// canonicalize `'static`.  Unfortunately, the existing leak
    /// check treaks `'static` differently in some cases (see also
    /// #33684), so if we are performing an operation that may need to
    /// prove "leak-check" related things, we leave `'static`
    /// alone.
    ///
    /// FIXME(#48536) -- once we have universes, we can remove this and just use
    /// `canonicalize_query`.
    pub fn canonicalize_hr_query_hack<V>(
        &self,
        value: &V,
        var_values: &mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        self.tcx
            .sess
            .perf_stats
            .queries_canonicalized
            .fetch_add(1, Ordering::Relaxed);

        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: false,
                other_free_regions: true,
            },
            var_values,
        )
    }
}

/// If this flag is true, then all free regions will be replaced with
/// a canonical var. This is used to make queries as generic as
/// possible. For example, the query `F: Foo<'static>` would be
/// canonicalized to `F: Foo<'0>`.
struct CanonicalizeRegionMode {
    static_region: bool,
    other_free_regions: bool,
}

impl CanonicalizeRegionMode {
    fn any(&self) -> bool {
        self.static_region || self.other_free_regions
    }
}

struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
    infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
    variables: SmallVec<[CanonicalVarInfo; 8]>,
    var_values: &'cx mut SmallCanonicalVarValues<'tcx>,
    // Note that indices is only used once `var_values` is big enough to be
    // heap-allocated.
    indices: FxHashMap<Kind<'tcx>, CanonicalVar>,
    canonicalize_region_mode: CanonicalizeRegionMode,
    needs_canonical_flags: TypeFlags,
}

impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
        self.tcx
    }

    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
        match *r {
            ty::ReLateBound(..) => {
                // leave bound regions alone
                r
            }

            ty::ReVar(vid) => {
                let r = self.infcx
                    .unwrap()
                    .borrow_region_constraints()
                    .opportunistic_resolve_var(self.tcx, vid);
                let info = CanonicalVarInfo {
                    kind: CanonicalVarKind::Region,
                };
                debug!(
                    "canonical: region var found with vid {:?}, \
                     opportunistically resolved to {:?}",
                    vid, r
                );
                let cvar = self.canonical_var(info, r.into());
                self.tcx().mk_region(ty::ReCanonical(cvar))
            }

            ty::ReStatic => {
                if self.canonicalize_region_mode.static_region {
                    let info = CanonicalVarInfo {
                        kind: CanonicalVarKind::Region,
                    };
                    let cvar = self.canonical_var(info, r.into());
                    self.tcx().mk_region(ty::ReCanonical(cvar))
                } else {
                    r
                }
            }

            ty::ReEarlyBound(..)
            | ty::ReFree(_)
            | ty::ReScope(_)
            | ty::RePlaceholder(..)
            | ty::ReEmpty
            | ty::ReErased => {
                if self.canonicalize_region_mode.other_free_regions {
                    let info = CanonicalVarInfo {
                        kind: CanonicalVarKind::Region,
                    };
                    let cvar = self.canonical_var(info, r.into());
                    self.tcx().mk_region(ty::ReCanonical(cvar))
                } else {
                    r
                }
            }

            ty::ReClosureBound(..) | ty::ReCanonical(_) => {
                bug!("canonical region encountered during canonicalization")
            }
        }
    }

    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
        match t.sty {
            ty::Infer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t),

            ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t),

            ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t),

            ty::Infer(ty::FreshTy(_))
            | ty::Infer(ty::FreshIntTy(_))
            | ty::Infer(ty::FreshFloatTy(_)) => {
                bug!("encountered a fresh type during canonicalization")
            }

            ty::Infer(ty::CanonicalTy(_)) => {
                bug!("encountered a canonical type during canonicalization")
            }

            ty::Closure(..)
            | ty::Generator(..)
            | ty::GeneratorWitness(..)
            | ty::Bool
            | ty::Char
            | ty::Int(..)
            | ty::Uint(..)
            | ty::Float(..)
            | ty::Adt(..)
            | ty::Str
            | ty::Error
            | ty::Array(..)
            | ty::Slice(..)
            | ty::RawPtr(..)
            | ty::Ref(..)
            | ty::FnDef(..)
            | ty::FnPtr(_)
            | ty::Dynamic(..)
            | ty::Never
            | ty::Tuple(..)
            | ty::Projection(..)
            | ty::UnnormalizedProjection(..)
            | ty::Foreign(..)
            | ty::Param(..)
            | ty::Opaque(..) => {
                if t.flags.intersects(self.needs_canonical_flags) {
                    t.super_fold_with(self)
                } else {
                    t
                }
            }
        }
    }
}

impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
    /// The main `canonicalize` method, shared impl of
    /// `canonicalize_query` and `canonicalize_response`.
    fn canonicalize<V>(
        value: &V,
        infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
        canonicalize_region_mode: CanonicalizeRegionMode,
        var_values: &'cx mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        debug_assert!(
            !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS),
            "canonicalizing a canonical value: {:?}",
            value,
        );

        let needs_canonical_flags = if canonicalize_region_mode.any() {
            TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX
        } else {
            TypeFlags::KEEP_IN_LOCAL_TCX
        };

        let gcx = tcx.global_tcx();

        // Fast path: nothing that needs to be canonicalized.
        if !value.has_type_flags(needs_canonical_flags) {
            let out_value = gcx.lift(value).unwrap();
            let canon_value = Canonical {
                variables: List::empty(),
                value: out_value,
            };
            return canon_value;
        }

        let mut canonicalizer = Canonicalizer {
            infcx,
            tcx,
            canonicalize_region_mode,
            needs_canonical_flags,
            variables: SmallVec::new(),
            var_values,
            indices: FxHashMap::default(),
        };
        let out_value = value.fold_with(&mut canonicalizer);

        // Once we have canonicalized `out_value`, it should not
        // contain anything that ties it to this inference context
        // anymore, so it should live in the global arena.
        let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
            bug!(
                "failed to lift `{:?}`, canonicalized from `{:?}`",
                out_value,
                value
            )
        });

        let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);

        Canonical {
            variables: canonical_variables,
            value: out_value,
        }
    }

    /// Creates a canonical variable replacing `kind` from the input,
    /// or returns an existing variable if `kind` has already been
    /// seen. `kind` is expected to be an unbound variable (or
    /// potentially a free region).
    fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar {
        let Canonicalizer {
            variables,
            var_values,
            indices,
            ..
        } = self;

        // This code is hot. `variables` and `var_values` are usually small
        // (fewer than 8 elements ~95% of the time). They are SmallVec's to
        // avoid allocations in those cases. We also don't use `indices` to
        // determine if a kind has been seen before until the limit of 8 has
        // been exceeded, to also avoid allocations for `indices`.
        if !var_values.spilled() {
            // `var_values` is stack-allocated. `indices` isn't used yet. Do a
            // direct linear search of `var_values`.
            if let Some(idx) = var_values.iter().position(|&k| k == kind) {
                // `kind` is already present in `var_values`.
                CanonicalVar::new(idx)
            } else {
                // `kind` isn't present in `var_values`. Append it. Likewise
                // for `info` and `variables`.
                variables.push(info);
                var_values.push(kind);
                assert_eq!(variables.len(), var_values.len());

                // If `var_values` has become big enough to be heap-allocated,
                // fill up `indices` to facilitate subsequent lookups.
                if var_values.spilled() {
                    assert!(indices.is_empty());
                    *indices = var_values
                        .iter()
                        .enumerate()
                        .map(|(i, &kind)| (kind, CanonicalVar::new(i)))
                        .collect();
                }
                // The cv is the index of the appended element.
                CanonicalVar::new(var_values.len() - 1)
            }
        } else {
            // `var_values` is large. Do a hashmap search via `indices`.
            *indices.entry(kind).or_insert_with(|| {
                variables.push(info);
                var_values.push(kind);
                assert_eq!(variables.len(), var_values.len());
                CanonicalVar::new(variables.len() - 1)
            })
        }
    }

    /// Given a type variable `ty_var` of the given kind, first check
    /// if `ty_var` is bound to anything; if so, canonicalize
    /// *that*. Otherwise, create a new canonical variable for
    /// `ty_var`.
    fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> {
        let infcx = self.infcx.expect("encountered ty-var without infcx");
        let bound_to = infcx.shallow_resolve(ty_var);
        if bound_to != ty_var {
            self.fold_ty(bound_to)
        } else {
            let info = CanonicalVarInfo {
                kind: CanonicalVarKind::Ty(ty_kind),
            };
            let cvar = self.canonical_var(info, ty_var.into());
            self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar))
        }
    }
}
<commit_msg>introduce `canonical_var_for_region` helper fn<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module contains the "canonicalizer" itself.
//!
//! For an overview of what canonicaliation is and how it fits into
//! rustc, check out the [chapter in the rustc guide][c].
//!
//! [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html

use infer::canonical::{
    Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized,
    SmallCanonicalVarValues,
};
use infer::InferCtxt;
use std::sync::atomic::Ordering;
use ty::fold::{TypeFoldable, TypeFolder};
use ty::subst::Kind;
use ty::{self, CanonicalVar, Lift, List, Ty, TyCtxt, TypeFlags};

use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::indexed_vec::Idx;
use smallvec::SmallVec;

impl<'cx, 'gcx, 'tcx> InferCtxt<'cx, 'gcx, 'tcx> {
    /// Canonicalizes a query value `V`. When we canonicalize a query,
    /// we not only canonicalize unbound inference variables, but we
    /// *also* replace all free regions whatsoever. So for example a
    /// query like `T: Trait<'static>` would be canonicalized to
    ///
    /// ```text
    /// T: Trait<'?0>
    /// ```
    ///
    /// with a mapping M that maps `'?0` to `'static`.
    ///
    /// To get a good understanding of what is happening here, check
    /// out the [chapter in the rustc guide][c].
    ///
    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query
    pub fn canonicalize_query<V>(
        &self,
        value: &V,
        var_values: &mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        self.tcx
            .sess
            .perf_stats
            .queries_canonicalized
            .fetch_add(1, Ordering::Relaxed);

        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: true,
                other_free_regions: true,
            },
            var_values,
        )
    }

    /// Canonicalizes a query *response* `V`. When we canonicalize a
    /// query response, we only canonicalize unbound inference
    /// variables, and we leave other free regions alone. So,
    /// continuing with the example from `canonicalize_query`, if
    /// there was an input query `T: Trait<'static>`, it would have
    /// been canonicalized to
    ///
    /// ```text
    /// T: Trait<'?0>
    /// ```
    ///
    /// with a mapping M that maps `'?0` to `'static`. But if we found that there
    /// exists only one possible impl of `Trait`, and it looks like
    ///
    ///     impl<T> Trait<'static> for T { .. }
    ///
    /// then we would prepare a query result R that (among other
    /// things) includes a mapping to `'?0 := 'static`. When
    /// canonicalizing this query result R, we would leave this
    /// reference to `'static` alone.
    ///
    /// To get a good understanding of what is happening here, check
    /// out the [chapter in the rustc guide][c].
    ///
    /// [c]: https://rust-lang-nursery.github.io/rustc-guide/traits/canonicalization.html#canonicalizing-the-query-result
    pub fn canonicalize_response<V>(&self, value: &V) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        let mut var_values = SmallVec::new();
        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: false,
                other_free_regions: false,
            },
            &mut var_values,
        )
    }

    /// A hacky variant of `canonicalize_query` that does not
    /// canonicalize `'static`.  Unfortunately, the existing leak
    /// check treaks `'static` differently in some cases (see also
    /// #33684), so if we are performing an operation that may need to
    /// prove "leak-check" related things, we leave `'static`
    /// alone.
    ///
    /// FIXME(#48536) -- once we have universes, we can remove this and just use
    /// `canonicalize_query`.
    pub fn canonicalize_hr_query_hack<V>(
        &self,
        value: &V,
        var_values: &mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        self.tcx
            .sess
            .perf_stats
            .queries_canonicalized
            .fetch_add(1, Ordering::Relaxed);

        Canonicalizer::canonicalize(
            value,
            Some(self),
            self.tcx,
            CanonicalizeRegionMode {
                static_region: false,
                other_free_regions: true,
            },
            var_values,
        )
    }
}

/// If this flag is true, then all free regions will be replaced with
/// a canonical var. This is used to make queries as generic as
/// possible. For example, the query `F: Foo<'static>` would be
/// canonicalized to `F: Foo<'0>`.
struct CanonicalizeRegionMode {
    static_region: bool,
    other_free_regions: bool,
}

impl CanonicalizeRegionMode {
    fn any(&self) -> bool {
        self.static_region || self.other_free_regions
    }
}

struct Canonicalizer<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
    infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
    variables: SmallVec<[CanonicalVarInfo; 8]>,
    var_values: &'cx mut SmallCanonicalVarValues<'tcx>,
    // Note that indices is only used once `var_values` is big enough to be
    // heap-allocated.
    indices: FxHashMap<Kind<'tcx>, CanonicalVar>,
    canonicalize_region_mode: CanonicalizeRegionMode,
    needs_canonical_flags: TypeFlags,
}

impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for Canonicalizer<'cx, 'gcx, 'tcx> {
    fn tcx<'b>(&'b self) -> TyCtxt<'b, 'gcx, 'tcx> {
        self.tcx
    }

    fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
        match *r {
            ty::ReLateBound(..) => {
                // leave bound regions alone
                r
            }

            ty::ReVar(vid) => {
                let r = self.infcx
                    .unwrap()
                    .borrow_region_constraints()
                    .opportunistic_resolve_var(self.tcx, vid);
                debug!(
                    "canonical: region var found with vid {:?}, \
                     opportunistically resolved to {:?}",
                    vid, r
                );
                self.canonical_var_for_region(r)
            }

            ty::ReStatic => {
                if self.canonicalize_region_mode.static_region {
                    self.canonical_var_for_region(r)
                } else {
                    r
                }
            }

            ty::ReEarlyBound(..)
            | ty::ReFree(_)
            | ty::ReScope(_)
            | ty::RePlaceholder(..)
            | ty::ReEmpty
            | ty::ReErased => {
                if self.canonicalize_region_mode.other_free_regions {
                    self.canonical_var_for_region(r)
                } else {
                    r
                }
            }

            ty::ReClosureBound(..) | ty::ReCanonical(_) => {
                bug!("canonical region encountered during canonicalization")
            }
        }
    }

    fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
        match t.sty {
            ty::Infer(ty::TyVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::General, t),

            ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Int, t),

            ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var(CanonicalTyVarKind::Float, t),

            ty::Infer(ty::FreshTy(_))
            | ty::Infer(ty::FreshIntTy(_))
            | ty::Infer(ty::FreshFloatTy(_)) => {
                bug!("encountered a fresh type during canonicalization")
            }

            ty::Infer(ty::CanonicalTy(_)) => {
                bug!("encountered a canonical type during canonicalization")
            }

            ty::Closure(..)
            | ty::Generator(..)
            | ty::GeneratorWitness(..)
            | ty::Bool
            | ty::Char
            | ty::Int(..)
            | ty::Uint(..)
            | ty::Float(..)
            | ty::Adt(..)
            | ty::Str
            | ty::Error
            | ty::Array(..)
            | ty::Slice(..)
            | ty::RawPtr(..)
            | ty::Ref(..)
            | ty::FnDef(..)
            | ty::FnPtr(_)
            | ty::Dynamic(..)
            | ty::Never
            | ty::Tuple(..)
            | ty::Projection(..)
            | ty::UnnormalizedProjection(..)
            | ty::Foreign(..)
            | ty::Param(..)
            | ty::Opaque(..) => {
                if t.flags.intersects(self.needs_canonical_flags) {
                    t.super_fold_with(self)
                } else {
                    t
                }
            }
        }
    }
}

impl<'cx, 'gcx, 'tcx> Canonicalizer<'cx, 'gcx, 'tcx> {
    /// The main `canonicalize` method, shared impl of
    /// `canonicalize_query` and `canonicalize_response`.
    fn canonicalize<V>(
        value: &V,
        infcx: Option<&'cx InferCtxt<'cx, 'gcx, 'tcx>>,
        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
        canonicalize_region_mode: CanonicalizeRegionMode,
        var_values: &'cx mut SmallCanonicalVarValues<'tcx>,
    ) -> Canonicalized<'gcx, V>
    where
        V: TypeFoldable<'tcx> + Lift<'gcx>,
    {
        debug_assert!(
            !value.has_type_flags(TypeFlags::HAS_CANONICAL_VARS),
            "canonicalizing a canonical value: {:?}",
            value,
        );

        let needs_canonical_flags = if canonicalize_region_mode.any() {
            TypeFlags::HAS_FREE_REGIONS | TypeFlags::KEEP_IN_LOCAL_TCX
        } else {
            TypeFlags::KEEP_IN_LOCAL_TCX
        };

        let gcx = tcx.global_tcx();

        // Fast path: nothing that needs to be canonicalized.
        if !value.has_type_flags(needs_canonical_flags) {
            let out_value = gcx.lift(value).unwrap();
            let canon_value = Canonical {
                variables: List::empty(),
                value: out_value,
            };
            return canon_value;
        }

        let mut canonicalizer = Canonicalizer {
            infcx,
            tcx,
            canonicalize_region_mode,
            needs_canonical_flags,
            variables: SmallVec::new(),
            var_values,
            indices: FxHashMap::default(),
        };
        let out_value = value.fold_with(&mut canonicalizer);

        // Once we have canonicalized `out_value`, it should not
        // contain anything that ties it to this inference context
        // anymore, so it should live in the global arena.
        let out_value = gcx.lift(&out_value).unwrap_or_else(|| {
            bug!(
                "failed to lift `{:?}`, canonicalized from `{:?}`",
                out_value,
                value
            )
        });

        let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.variables);

        Canonical {
            variables: canonical_variables,
            value: out_value,
        }
    }

    /// Creates a canonical variable replacing `kind` from the input,
    /// or returns an existing variable if `kind` has already been
    /// seen. `kind` is expected to be an unbound variable (or
    /// potentially a free region).
    fn canonical_var(&mut self, info: CanonicalVarInfo, kind: Kind<'tcx>) -> CanonicalVar {
        let Canonicalizer {
            variables,
            var_values,
            indices,
            ..
        } = self;

        // This code is hot. `variables` and `var_values` are usually small
        // (fewer than 8 elements ~95% of the time). They are SmallVec's to
        // avoid allocations in those cases. We also don't use `indices` to
        // determine if a kind has been seen before until the limit of 8 has
        // been exceeded, to also avoid allocations for `indices`.
        if !var_values.spilled() {
            // `var_values` is stack-allocated. `indices` isn't used yet. Do a
            // direct linear search of `var_values`.
            if let Some(idx) = var_values.iter().position(|&k| k == kind) {
                // `kind` is already present in `var_values`.
                CanonicalVar::new(idx)
            } else {
                // `kind` isn't present in `var_values`. Append it. Likewise
                // for `info` and `variables`.
                variables.push(info);
                var_values.push(kind);
                assert_eq!(variables.len(), var_values.len());

                // If `var_values` has become big enough to be heap-allocated,
                // fill up `indices` to facilitate subsequent lookups.
                if var_values.spilled() {
                    assert!(indices.is_empty());
                    *indices = var_values
                        .iter()
                        .enumerate()
                        .map(|(i, &kind)| (kind, CanonicalVar::new(i)))
                        .collect();
                }
                // The cv is the index of the appended element.
                CanonicalVar::new(var_values.len() - 1)
            }
        } else {
            // `var_values` is large. Do a hashmap search via `indices`.
            *indices.entry(kind).or_insert_with(|| {
                variables.push(info);
                var_values.push(kind);
                assert_eq!(variables.len(), var_values.len());
                CanonicalVar::new(variables.len() - 1)
            })
        }
    }

    fn canonical_var_for_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
        let info = CanonicalVarInfo {
            kind: CanonicalVarKind::Region,
        };
        let cvar = self.canonical_var(info, r.into());
        self.tcx().mk_region(ty::ReCanonical(cvar))
    }

    /// Given a type variable `ty_var` of the given kind, first check
    /// if `ty_var` is bound to anything; if so, canonicalize
    /// *that*. Otherwise, create a new canonical variable for
    /// `ty_var`.
    fn canonicalize_ty_var(&mut self, ty_kind: CanonicalTyVarKind, ty_var: Ty<'tcx>) -> Ty<'tcx> {
        let infcx = self.infcx.expect("encountered ty-var without infcx");
        let bound_to = infcx.shallow_resolve(ty_var);
        if bound_to != ty_var {
            self.fold_ty(bound_to)
        } else {
            let info = CanonicalVarInfo {
                kind: CanonicalVarKind::Ty(ty_kind),
            };
            let cvar = self.canonical_var(info, ty_var.into());
            self.tcx().mk_infer(ty::InferTy::CanonicalTy(cvar))
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;

use parking_lot::Mutex;
use serenity;
use serenity::model::{Channel, CurrentUser, GuildChannel, GuildId, GuildStatus};
use serenity::prelude::*;
use slog;
use typemap::{Key, ShareMap};

use ::{Result};
use message::{Bus, BusSender, Message, MessageCreated, Payload};

pub struct Discord {
    data: Arc<Mutex<ShareMap>>,
}

struct User;

impl Key for User { type Value = CurrentUser; }

struct Guilds;

impl Key for Guilds { type Value = HashMap<GuildId, GuildStatus>; }

impl Discord {
    pub fn new<L>(logger: L, bus: Bus, token: &str) -> Result<Discord>
        where L: Into<Option<slog::Logger>>
    {
        let logger = logger.into().unwrap_or_else(|| slog::Logger::root(slog::Discard, o!()));
        let handler = Handler {
            sender: Arc::new(Mutex::new(bus.sender())),
            log: logger.new(o!()),
        };
        let mut client = serenity::Client::new(token, handler);
        let data = client.data.clone();

        thread::spawn(move || {
            client.start().unwrap();
        });
        spawn_actor(logger, data.clone(), bus);
        Ok(Discord {
            data,
        })
    }

    pub fn channels(&self) -> Result<Vec<GuildChannel>> {
        let lock = self.data.lock();
        channels(&lock)
    }
}

struct Handler {
    sender: Arc<Mutex<BusSender>>,
    log: slog::Logger,
}

impl EventHandler for Handler {
    fn on_ready(&self, ctx: Context, ready: serenity::model::Ready) {
        let mut lock = ctx.data.lock();
        debug!(self.log, "ready: {:?}", ready);
        lock.insert::<User>(ready.user);
        lock.insert::<Guilds>(ready.guilds.into_iter().map(|g| (g.id(), g)).collect());
        if let Ok(chan) = channels(&lock) {
            let s = self.sender.lock();
            let chan = chan.into_iter().map(|ch| ch.name).collect();
            s.try_send(Message::ChannelUpdated { channels: chan });
        }
    }

    fn on_guild_update(
        &self,
        ctx: Context,
        guild: Option<Arc<RwLock<serenity::model::Guild>>>,
        partial_guild: serenity::model::PartialGuild,
    ) {
        debug!(self.log, "guild_update: {:?}", partial_guild);
        let mut lock = ctx.data.lock();
        let g = if let Some(g) = guild {
            GuildStatus::OnlineGuild(g.read().expect("unexpected poisoned lock").clone())
        } else {
            GuildStatus::OnlinePartialGuild(partial_guild)
        };
        lock.get_mut::<Guilds>().map(|m| m.insert(g.id(), g));
        if let Ok(chan) = channels(&lock) {
            let s = self.sender.lock();
            let chan = chan.into_iter().map(|ch| ch.name).collect();
            s.try_send(Message::ChannelUpdated { channels: chan });
        }
    }

    fn on_message(&self, ctx: Context, msg: serenity::model::Message) {
        debug!(self.log, "{:?}", msg);
        if let Err(e) = on_message(&self.sender.lock(), ctx, msg) {
            error!(self.log, "Error occured: {}", e);
        }
    }
}

fn on_message(sender: &BusSender, ctx: serenity::client::Context, msg: serenity::model::Message) -> Result<()> {
    if let Some(Channel::Guild(ch)) = msg.channel_id.find() {
        let data = ctx.data.lock();
        let c = ch.read().expect("unexpected poisoned lock");
        if let Some(u) = data.get::<User>() {
            if u.id == msg.author.id {
                return Ok(());
            }
        }
        let nickname = c.guild_id.member(msg.author.id)?.nick.unwrap_or(msg.author.name);
        let mut m = Message::MessageCreated(MessageCreated {
            nickname,
            channel: format!("#{}", c.name),
            content: msg.content,
        });
        while let Err(e) = sender.try_send(m) {
            use std::sync::mpsc::TrySendError::*;
            m = match e {
                Full(p) => p,
                Disconnected(_) => { panic!("bus closed"); }
            };
            thread::yield_now();
        }
    }
    Ok(())
}

fn spawn_actor(
    logger: slog::Logger,
    data: Arc<Mutex<ShareMap>>,
    bus: Bus,
) {
    use message::Message::*;
    thread::spawn(move || {
        for Payload { message, .. } in bus {
            match message {
                MessageCreated(msg) => {
                    if !msg.channel.starts_with('#') {
                        continue;
                    }
                    let data = data.lock();
                    if let Some(channel) = find_channel(&data, &msg.channel[1..]) {
                        let m = format!("<{}> {}", msg.nickname, msg.content);
                        while let Err(e) = channel.send_message(|msg| msg.content(&m)) {
                            use serenity::Error::*;
                            match e {
                                Http(..) | Hyper(..) | WebSocket(..) => {
                                    thread::sleep(Duration::from_millis(100));
                                }
                                _ => {
                                    error!(logger, "failed to send a message: {}", e);
                                    break;
                                }
                            }
                        }
                    }
                }
                _ => { }
            }
        }
    });
}

fn find_channel(data: &ShareMap, channel: &str) -> Option<GuildChannel> {
    for ch in channels(data).unwrap_or(vec![]) {  // 땜빵
        if ch.name == channel {
            return Some(ch);
        }
    }
    None
}

fn channels(data: &ShareMap) -> Result<Vec<GuildChannel>> {
    if let Some(guilds) = data.get::<Guilds>() {
        let mut result = vec![];
        for g in guilds.values() {
            let channels = match *g {
                GuildStatus::Offline(ref g) => g.id.channels()?,
                GuildStatus::OnlineGuild(ref g) => g.channels()?,
                GuildStatus::OnlinePartialGuild(ref g) => g.channels()?,
            };
            result.extend(channels.into_iter().map(|(_, v)| v));
        }
        Ok(result)
    } else {
        Ok(vec![])
    }
}
<commit_msg>Use text channels only<commit_after>use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::thread;
use std::time::Duration;

use parking_lot::Mutex;
use serenity;
use serenity::model::{Channel, ChannelType, CurrentUser, GuildChannel, GuildId, GuildStatus};
use serenity::prelude::*;
use slog;
use typemap::{Key, ShareMap};

use ::{Result};
use message::{Bus, BusSender, Message, MessageCreated, Payload};

pub struct Discord {
    data: Arc<Mutex<ShareMap>>,
}

struct User;

impl Key for User { type Value = CurrentUser; }

struct Guilds;

impl Key for Guilds { type Value = HashMap<GuildId, GuildStatus>; }

impl Discord {
    pub fn new<L>(logger: L, bus: Bus, token: &str) -> Result<Discord>
        where L: Into<Option<slog::Logger>>
    {
        let logger = logger.into().unwrap_or_else(|| slog::Logger::root(slog::Discard, o!()));
        let handler = Handler {
            sender: Arc::new(Mutex::new(bus.sender())),
            log: logger.new(o!()),
        };
        let mut client = serenity::Client::new(token, handler);
        let data = client.data.clone();

        thread::spawn(move || {
            client.start().unwrap();
        });
        spawn_actor(logger, data.clone(), bus);
        Ok(Discord {
            data,
        })
    }

    pub fn channels(&self) -> Result<Vec<GuildChannel>> {
        let lock = self.data.lock();
        channels(&lock)
    }
}

struct Handler {
    sender: Arc<Mutex<BusSender>>,
    log: slog::Logger,
}

impl EventHandler for Handler {
    fn on_ready(&self, ctx: Context, ready: serenity::model::Ready) {
        let mut lock = ctx.data.lock();
        debug!(self.log, "ready: {:?}", ready);
        lock.insert::<User>(ready.user);
        lock.insert::<Guilds>(ready.guilds.into_iter().map(|g| (g.id(), g)).collect());
        if let Ok(chan) = channels(&lock) {
            let s = self.sender.lock();
            let chan = chan.into_iter().map(|ch| ch.name).collect();
            s.try_send(Message::ChannelUpdated { channels: chan });
        }
    }

    fn on_guild_update(
        &self,
        ctx: Context,
        guild: Option<Arc<RwLock<serenity::model::Guild>>>,
        partial_guild: serenity::model::PartialGuild,
    ) {
        debug!(self.log, "guild_update: {:?}", partial_guild);
        let mut lock = ctx.data.lock();
        let g = if let Some(g) = guild {
            GuildStatus::OnlineGuild(g.read().expect("unexpected poisoned lock").clone())
        } else {
            GuildStatus::OnlinePartialGuild(partial_guild)
        };
        lock.get_mut::<Guilds>().map(|m| m.insert(g.id(), g));
        if let Ok(chan) = channels(&lock) {
            let s = self.sender.lock();
            let chan = chan.into_iter().map(|ch| ch.name).collect();
            s.try_send(Message::ChannelUpdated { channels: chan });
        }
    }

    fn on_message(&self, ctx: Context, msg: serenity::model::Message) {
        debug!(self.log, "{:?}", msg);
        if let Err(e) = on_message(&self.sender.lock(), ctx, msg) {
            error!(self.log, "Error occured: {}", e);
        }
    }
}

fn on_message(sender: &BusSender, ctx: serenity::client::Context, msg: serenity::model::Message) -> Result<()> {
    if let Some(Channel::Guild(ch)) = msg.channel_id.find() {
        let data = ctx.data.lock();
        let c = ch.read().expect("unexpected poisoned lock");
        if let Some(u) = data.get::<User>() {
            if u.id == msg.author.id {
                return Ok(());
            }
        }
        let nickname = c.guild_id.member(msg.author.id)?.nick.unwrap_or(msg.author.name);
        let mut m = Message::MessageCreated(MessageCreated {
            nickname,
            channel: format!("#{}", c.name),
            content: msg.content,
        });
        while let Err(e) = sender.try_send(m) {
            use std::sync::mpsc::TrySendError::*;
            m = match e {
                Full(p) => p,
                Disconnected(_) => { panic!("bus closed"); }
            };
            thread::yield_now();
        }
    }
    Ok(())
}

fn spawn_actor(
    logger: slog::Logger,
    data: Arc<Mutex<ShareMap>>,
    bus: Bus,
) {
    use message::Message::*;
    thread::spawn(move || {
        for Payload { message, .. } in bus {
            match message {
                MessageCreated(msg) => {
                    if !msg.channel.starts_with('#') {
                        continue;
                    }
                    let data = data.lock();
                    if let Some(channel) = find_channel(&data, &msg.channel[1..]) {
                        let m = format!("<{}> {}", msg.nickname, msg.content);
                        while let Err(e) = channel.send_message(|msg| msg.content(&m)) {
                            use serenity::Error::*;
                            match e {
                                Http(..) | Hyper(..) | WebSocket(..) => {
                                    thread::sleep(Duration::from_millis(100));
                                }
                                _ => {
                                    error!(logger, "failed to send a message: {}", e);
                                    break;
                                }
                            }
                        }
                    }
                }
                _ => { }
            }
        }
    });
}

fn find_channel(data: &ShareMap, channel: &str) -> Option<GuildChannel> {
    for ch in channels(data).unwrap_or(vec![]) {  // 땜빵
        if ch.name == channel {
            return Some(ch);
        }
    }
    None
}

fn channels(data: &ShareMap) -> Result<Vec<GuildChannel>> {
    if let Some(guilds) = data.get::<Guilds>() {
        let mut result = vec![];
        for g in guilds.values() {
            let channels = match *g {
                GuildStatus::Offline(ref g) => g.id.channels()?,
                GuildStatus::OnlineGuild(ref g) => g.channels()?,
                GuildStatus::OnlinePartialGuild(ref g) => g.channels()?,
            };
            let channels = channels.into_iter()
                .map(|(_, v)| v)
                .filter(|ch| ch.kind == ChannelType::Text);
            result.extend(channels);
        }
        Ok(result)
    } else {
        Ok(vec![])
    }
}
<|endoftext|>


<commit_before>
//! DocBuilder

pub mod crte;

use std::io::prelude::*;
use std::io;
use std::fmt;
use std::env;
use std::path::PathBuf;
use std::fs;
use std::process::{Command, Output};
use std::collections;
use std::convert;

use toml;

/// Alright
pub struct DocBuilder {
    keep_build_directory: bool,
    destination: PathBuf,
    chroot_path: PathBuf,
    chroot_user: String,
    build_dir: PathBuf,
    crates_io_index_path: PathBuf,
    logs_path: PathBuf,
    skip_if_exist: bool,
    skip_if_log_exists: bool,
    debug: bool,
}

impl Default for DocBuilder {
    fn default() -> DocBuilder {

        let cwd = env::current_dir().unwrap();

        let mut destination = PathBuf::from(&cwd);
        destination.push("public_html/crates");

        let mut chroot_path = PathBuf::from(&cwd);
        chroot_path.push("chroot");

        let mut build_dir = PathBuf::from(&cwd);
        build_dir.push(&chroot_path);
        build_dir.push("home/onur");

        let mut crates_io_index_path = PathBuf::from(&cwd);
        crates_io_index_path.push("crates.io-index");

        let mut logs_path = PathBuf::from(&cwd);
        logs_path.push("logs");

        DocBuilder {
            destination: destination,
            chroot_path: chroot_path,
            build_dir: build_dir,
            crates_io_index_path: crates_io_index_path,
            logs_path: logs_path,

            chroot_user: "onur".to_string(),

            keep_build_directory: false,
            skip_if_exist: false,
            skip_if_log_exists: false,
            debug: false,
        }
    }
}

impl fmt::Debug for DocBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "DocBuilder {{ destination: {:?}, chroot_path: {:?}, chroot_user_home_dir: {:?}, \
               crates_io_index_path: {:?}, logs_path: {:?}, chroot_user: {:?}, \
                keep_build_directory: {:?}, skip_if_exist: {:?}, skip_if_log_exists: {:?}, debug: \
                {:?} }}",
                self.destination,
                self.chroot_path,
                self.build_dir,
                self.crates_io_index_path,
                self.logs_path,
                self.chroot_user,
                self.keep_build_directory,
                self.skip_if_exist,
                self.skip_if_log_exists,
                self.debug)
    }
}

impl DocBuilder {
    /// This functions reads files in crates.io-index and tries to build
    /// documentation for crates.
    pub fn build_doc_for_every_crate(&self) {
        self.build_doc_for_crate_path(&self.crates_io_index_path);
    }

    fn build_doc_for_crate_path(&self, path: &PathBuf) {
        for dir in path.read_dir().unwrap() {

            let path = dir.unwrap().path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                self.build_doc_for_crate_path(&path);
                continue
            }

            // FIXME: check errors here
            let crte = crte::Crate::from_cargo_index_file(path);
            self.build_doc_for_crate(&crte.unwrap());
        }
    }

    /// Builds documentation for crate
    ///
    /// This function will try to build documentation for every version of crate
    pub fn build_doc_for_crate(&self, crte: &crte::Crate) {
        for i in 0..crte.versions.len() {
            self.build_doc_for_crate_version(crte, i);
        }
    }

    fn open_log_for_crate(&self, crte: &crte::Crate, version_index: usize) -> Option<fs::File> {

        // Create a directory in logs folder
        let mut log_file_path = PathBuf::from(&self.logs_path);
        log_file_path.push(&crte.name);
        fs::create_dir(&log_file_path).unwrap();

        log_file_path.push(format!("{}-{}.log",
                                   crte.name, crte.versions[version_index]));

        // FIXME: We are getting panic if file is already exist
        match fs::File::create(log_file_path) {
            Ok(f) => Some(f),
            Err(e) => panic!(e)
        }
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf) -> Result<(), io::Error> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        // we are just returning on any error
        println!("CARGO TOML PATH {:?}", cargo_toml_path);

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| get_local_dependencies(dependencies_table))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir));

        Ok(())
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(crte::Crate, String)>,
                                 root_dir: &PathBuf) -> Result<(), String> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.1);

            // make sure path exists
            if !path.exists() {
                fs::create_dir_all(&path);
            }

            try!(self.download_latest_version_of_crate(&crte));
            try!(self.extract_crate(&crte, 0));

            let crte_download_dir = PathBuf::from(format!("{}/{}-{}",
                                                          self.build_dir.to_str().unwrap(),
                                                          crte.name, crte.versions[0]));

            if crte_download_dir.exists() {
                println!("CRATE DOWNLOAD DIR EXISTS YUPPI {}",
                         &crte_download_dir.to_str().unwrap());
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            copy_files(&crte_download_dir, &path);
        }

        Ok(())
    }

    /// Returns package folder inside build directory
    fn crate_root_dir(&self, crte: &crte::Crate, version_index: usize) -> PathBuf {
        let mut package_root = PathBuf::from(&self.build_dir);
        package_root.push(format!("{}-{}", &crte.name, &crte.versions[version_index]));
        package_root
    }

    /// Builds documentation for crate
    ///
    /// This operation involves following process:
    ///
    /// * Cleaning up build directory
    /// * Downloading crate
    /// * Extracting it into build directory (chroot dir home directory)
    /// * Building crate documentation with chroot
    /// * Checking build directory for if crate actually has any documentation
    /// * Copying crate documentation into destination path
    /// * Cleaning up build directory
    /// * Removing downloaded crate file
    pub fn build_doc_for_crate_version(&self, crte: &crte::Crate, version_index: usize) {

        let package_root = self.crate_root_dir(&crte, version_index);

        // TODO try to replace noob style logging
        let mut log_file = self.open_log_for_crate(crte, version_index).unwrap();

        println!("Building documentation for {}-{}", crte.name, crte.versions[version_index]);
        write!(&mut log_file,
               "Building documentation for {}-{}\n",
               crte.name, crte.versions[version_index]).unwrap();

        // Download crate
        write!(&mut log_file, "Downloading crate\n").unwrap();;
        if let Err(output) = self.download_crate(&crte, version_index) {
            write!(&mut log_file, "Failed to download crate: {}", output).unwrap();
            return;
        }

        // Extract crate
        write!(&mut log_file, "Extracting crate\n").unwrap();
        if let Err(output) = self.extract_crate(&crte, version_index) {
            write!(&mut log_file, "Failed to extract crate\n").unwrap();
            write!(&mut log_file, "{}\n", output).unwrap();
            return;
        }

        self.download_dependencies(&package_root);
    }

    /// Generates download url
    ///
    /// By default crates.io is using:
    /// https://crates.io/api/v1/crates/$crate/$version/download
    /// But I believe this url is increasing download count and this program is
    /// downloading alot during development. I am using redirected url.
    fn generate_download_url(&self, crte: &crte::Crate, version_index: usize) -> String {
        format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                crte.name,
                crte.name,
                crte.versions[version_index])
    }

    /// Downloads crate
    fn download_crate(&self, crte: &crte::Crate, version_index: usize) -> Result<String, String> {
        let url = self.generate_download_url(&crte, version_index);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download latest version of crate
    fn download_latest_version_of_crate(&self, crte: &crte::Crate) -> Result<String, String> {
        // TODO: it might be better to check crte.versions len here
        self.download_crate(&crte, 0)
    }

    /// Extracts crate into build directory
    fn extract_crate(&self, crte: &crte::Crate, version_index: usize) -> Result<String, String> {

        let crate_name = format!("{}-{}.crate", &crte.name, &crte.versions[version_index]);
        command_result(Command::new("tar")
                       .arg("-C")
                       .arg(&self.build_dir)
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

}

/// a simple function to capture command output
fn command_result(output: Output) -> Result<String, String> {
    let mut command_out = String::from_utf8_lossy(&output.stdout).into_owned();
    command_out.push_str(&String::from_utf8_lossy(&output.stderr).into_owned()[..]);
    match output.status.success() {
        true => Ok(command_out),
        false => Err(command_out)
    }
}

/// Get's local_dependencies from dependencies_table
fn get_local_dependencies(dependencies_table: &collections::BTreeMap<String, toml::Value>) ->
Option<Vec<(crte::Crate, String)>>  {

    let mut local_dependencies: Vec<(crte::Crate, String)> = Vec::new();

    for key in dependencies_table.keys() {

        dependencies_table.get(key)
            .and_then(|key_val| key_val.as_table())
            .map(|key_table| {
                key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                    key_table.get("version").and_then(|p| p.as_str())
                        .map(|version| {
                            let dep_crate = crte::Crate::new(key.clone(),
                            vec![version.to_string()]);
                            local_dependencies.push((dep_crate, path.to_string()));
                        });
                });
            });

    }
    Some(local_dependencies)
}

enum CopyFilesError {
    FailedToReadSourceDir(String),
}

// My first error implementation
impl convert::From for CopyFilesError {
    fn from(err: io::Error) -> CopyFilesError {
        match err {
            EntryMissing => "entry is missing",
            BadFileFormat => "a bad file format encountered",
            CouldNotOpenFile => "unable to open file",
            InternalError => "an internal error occurred",
        }
    }
}

fn copy_files(source: &PathBuf, destination: &PathBuf) -> Result<(), CopyFilesError> {

    for file in source.read_dir().unwrap() {

        // FIXME: unwrap'ing here is probably not a good idea
        let file = file.unwrap();
        let mut destination_full_path = PathBuf::from(&destination);
        destination_full_path.push(file.file_name());

        println!("SOURCE: {:#?}", file.path());
        println!("DESTIN: {:#?}", destination_full_path);

        if file.metadata().unwrap().is_dir() {
            try!(fs::create_dir(&destination_full_path));
            copy_files(&file.path(), &destination_full_path);
        } else {
            try!(fs::copy(&file.path(), &destination_full_path));
        }

    }

}
<commit_msg>Add copy_files function and my first error type<commit_after>
//! DocBuilder

pub mod crte;

use std::io::prelude::*;
use std::io;
use std::fmt;
use std::env;
use std::path::PathBuf;
use std::fs;
use std::process::{Command, Output};
use std::collections;
use std::error;

use toml;

/// Alright
pub struct DocBuilder {
    keep_build_directory: bool,
    destination: PathBuf,
    chroot_path: PathBuf,
    chroot_user: String,
    build_dir: PathBuf,
    crates_io_index_path: PathBuf,
    logs_path: PathBuf,
    skip_if_exist: bool,
    skip_if_log_exists: bool,
    debug: bool,
}

#[derive(Debug)]
enum DocBuilderError {
    LocalDependencyIoError(io::Error)
}

impl Default for DocBuilder {
    fn default() -> DocBuilder {

        let cwd = env::current_dir().unwrap();

        let mut destination = PathBuf::from(&cwd);
        destination.push("public_html/crates");

        let mut chroot_path = PathBuf::from(&cwd);
        chroot_path.push("chroot");

        let mut build_dir = PathBuf::from(&cwd);
        build_dir.push(&chroot_path);
        build_dir.push("home/onur");

        let mut crates_io_index_path = PathBuf::from(&cwd);
        crates_io_index_path.push("crates.io-index");

        let mut logs_path = PathBuf::from(&cwd);
        logs_path.push("logs");

        DocBuilder {
            destination: destination,
            chroot_path: chroot_path,
            build_dir: build_dir,
            crates_io_index_path: crates_io_index_path,
            logs_path: logs_path,

            chroot_user: "onur".to_string(),

            keep_build_directory: false,
            skip_if_exist: false,
            skip_if_log_exists: false,
            debug: false,
        }
    }
}

impl fmt::Debug for DocBuilder {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "DocBuilder {{ destination: {:?}, chroot_path: {:?}, chroot_user_home_dir: {:?}, \
               crates_io_index_path: {:?}, logs_path: {:?}, chroot_user: {:?}, \
                keep_build_directory: {:?}, skip_if_exist: {:?}, skip_if_log_exists: {:?}, debug: \
                {:?} }}",
                self.destination,
                self.chroot_path,
                self.build_dir,
                self.crates_io_index_path,
                self.logs_path,
                self.chroot_user,
                self.keep_build_directory,
                self.skip_if_exist,
                self.skip_if_log_exists,
                self.debug)
    }
}

impl DocBuilder {
    /// This functions reads files in crates.io-index and tries to build
    /// documentation for crates.
    pub fn build_doc_for_every_crate(&self) {
        self.build_doc_for_crate_path(&self.crates_io_index_path);
    }

    fn build_doc_for_crate_path(&self, path: &PathBuf) {
        for dir in path.read_dir().unwrap() {

            let path = dir.unwrap().path();

            // skip files under .git and config.json
            if path.to_str().unwrap().contains(".git") ||
                path.file_name().unwrap() == "config.json" {
                    continue;
                }

            if path.is_dir() {
                self.build_doc_for_crate_path(&path);
                continue
            }

            // FIXME: check errors here
            let crte = crte::Crate::from_cargo_index_file(path);
            self.build_doc_for_crate(&crte.unwrap());
        }
    }

    /// Builds documentation for crate
    ///
    /// This function will try to build documentation for every version of crate
    pub fn build_doc_for_crate(&self, crte: &crte::Crate) {
        for i in 0..crte.versions.len() {
            self.build_doc_for_crate_version(crte, i);
        }
    }

    fn open_log_for_crate(&self, crte: &crte::Crate, version_index: usize) -> Option<fs::File> {

        // Create a directory in logs folder
        let mut log_file_path = PathBuf::from(&self.logs_path);
        log_file_path.push(&crte.name);
        fs::create_dir(&log_file_path).unwrap();

        log_file_path.push(format!("{}-{}.log",
                                   crte.name, crte.versions[version_index]));

        // FIXME: We are getting panic if file is already exist
        match fs::File::create(log_file_path) {
            Ok(f) => Some(f),
            Err(e) => panic!(e)
        }
    }

    /// Download local dependencies from crate root and place them into right place
    ///
    /// Some packages have local dependencies defined in Cargo.toml
    ///
    /// This function is intentionall written verbose
    fn download_dependencies(&self, root_dir: &PathBuf) -> Result<(), io::Error> {

        let mut cargo_toml_path = PathBuf::from(&root_dir);
        cargo_toml_path.push("Cargo.toml");

        // we are just returning on any error
        println!("CARGO TOML PATH {:?}", cargo_toml_path);

        let mut cargo_toml_fh = try!(fs::File::open(cargo_toml_path));
        let mut cargo_toml_content = String::new();
        try!(cargo_toml_fh.read_to_string(&mut cargo_toml_content));

        toml::Parser::new(&cargo_toml_content[..]).parse().as_ref()
            .and_then(|cargo_toml| cargo_toml.get("dependencies"))
            .and_then(|dependencies| dependencies.as_table())
            .and_then(|dependencies_table| get_local_dependencies(dependencies_table))
            .map(|local_dependencies| self.handle_local_dependencies(local_dependencies, &root_dir));

        Ok(())
    }

    /// Handles local dependencies
    fn handle_local_dependencies(&self,
                                 local_dependencies: Vec<(crte::Crate, String)>,
                                 root_dir: &PathBuf) -> Result<(), String> {
        for local_dependency in local_dependencies {
            let crte = local_dependency.0;

            let mut path = PathBuf::from(&root_dir);
            path.push(local_dependency.1);

            // make sure path exists
            if !path.exists() {
                fs::create_dir_all(&path);
            }

            try!(self.download_latest_version_of_crate(&crte));
            try!(self.extract_crate(&crte, 0));

            let crte_download_dir = PathBuf::from(format!("{}/{}-{}",
                                                          self.build_dir.to_str().unwrap(),
                                                          crte.name, crte.versions[0]));

            if crte_download_dir.exists() {
                println!("CRATE DOWNLOAD DIR EXISTS YUPPI {}",
                         &crte_download_dir.to_str().unwrap());
            }

            // self.extract_crate will extract crate into build_dir
            // Copy files to proper location
            copy_files(&crte_download_dir, &path);
        }

        Ok(())
    }

    /// Returns package folder inside build directory
    fn crate_root_dir(&self, crte: &crte::Crate, version_index: usize) -> PathBuf {
        let mut package_root = PathBuf::from(&self.build_dir);
        package_root.push(format!("{}-{}", &crte.name, &crte.versions[version_index]));
        package_root
    }

    /// Builds documentation for crate
    ///
    /// This operation involves following process:
    ///
    /// * Cleaning up build directory
    /// * Downloading crate
    /// * Extracting it into build directory (chroot dir home directory)
    /// * Building crate documentation with chroot
    /// * Checking build directory for if crate actually has any documentation
    /// * Copying crate documentation into destination path
    /// * Cleaning up build directory
    /// * Removing downloaded crate file
    pub fn build_doc_for_crate_version(&self, crte: &crte::Crate, version_index: usize) {

        let package_root = self.crate_root_dir(&crte, version_index);

        // TODO try to replace noob style logging
        let mut log_file = self.open_log_for_crate(crte, version_index).unwrap();

        println!("Building documentation for {}-{}", crte.name, crte.versions[version_index]);
        write!(&mut log_file,
               "Building documentation for {}-{}\n",
               crte.name, crte.versions[version_index]).unwrap();

        // Download crate
        write!(&mut log_file, "Downloading crate\n").unwrap();;
        if let Err(output) = self.download_crate(&crte, version_index) {
            write!(&mut log_file, "Failed to download crate: {}", output).unwrap();
            return;
        }

        // Extract crate
        write!(&mut log_file, "Extracting crate\n").unwrap();
        if let Err(output) = self.extract_crate(&crte, version_index) {
            write!(&mut log_file, "Failed to extract crate\n").unwrap();
            write!(&mut log_file, "{}\n", output).unwrap();
            return;
        }

        self.download_dependencies(&package_root);
    }

    /// Generates download url
    ///
    /// By default crates.io is using:
    /// https://crates.io/api/v1/crates/$crate/$version/download
    /// But I believe this url is increasing download count and this program is
    /// downloading alot during development. I am using redirected url.
    fn generate_download_url(&self, crte: &crte::Crate, version_index: usize) -> String {
        format!("https://crates-io.s3-us-west-1.amazonaws.com/crates/{}/{}-{}.crate",
                crte.name,
                crte.name,
                crte.versions[version_index])
    }

    /// Downloads crate
    fn download_crate(&self, crte: &crte::Crate, version_index: usize) -> Result<String, String> {
        let url = self.generate_download_url(&crte, version_index);
        // Use wget for now
        command_result(Command::new("wget")
                       .arg("-c")
                       .arg("--content-disposition")
                       .arg(url)
                       .output()
                       .unwrap())
    }

    /// Download latest version of crate
    fn download_latest_version_of_crate(&self, crte: &crte::Crate) -> Result<String, String> {
        // TODO: it might be better to check crte.versions len here
        self.download_crate(&crte, 0)
    }

    /// Extracts crate into build directory
    fn extract_crate(&self, crte: &crte::Crate, version_index: usize) -> Result<String, String> {

        let crate_name = format!("{}-{}.crate", &crte.name, &crte.versions[version_index]);
        command_result(Command::new("tar")
                       .arg("-C")
                       .arg(&self.build_dir)
                       .arg("-xzvf")
                       .arg(crate_name)
                       .output()
                       .unwrap())
    }

}

/// a simple function to capture command output
fn command_result(output: Output) -> Result<String, String> {
    let mut command_out = String::from_utf8_lossy(&output.stdout).into_owned();
    command_out.push_str(&String::from_utf8_lossy(&output.stderr).into_owned()[..]);
    match output.status.success() {
        true => Ok(command_out),
        false => Err(command_out)
    }
}

/// Get's local_dependencies from dependencies_table
fn get_local_dependencies(dependencies_table: &collections::BTreeMap<String, toml::Value>) ->
Option<Vec<(crte::Crate, String)>>  {

    let mut local_dependencies: Vec<(crte::Crate, String)> = Vec::new();

    for key in dependencies_table.keys() {

        dependencies_table.get(key)
            .and_then(|key_val| key_val.as_table())
            .map(|key_table| {
                key_table.get("path").and_then(|p| p.as_str()).map(|path| {
                    key_table.get("version").and_then(|p| p.as_str())
                        .map(|version| {
                            let dep_crate = crte::Crate::new(key.clone(),
                            vec![version.to_string()]);
                            local_dependencies.push((dep_crate, path.to_string()));
                        });
                });
            });

    }
    Some(local_dependencies)
}

/// A simple function to copy files from source to destination
fn copy_files(source: &PathBuf, destination: &PathBuf) -> Result<(), DocBuilderError> {

    for file in try!(source.read_dir().map_err(DocBuilderError::LocalDependencyIoError)) {

        let file = try!(file.map_err(DocBuilderError::LocalDependencyIoError));
        let mut destination_full_path = PathBuf::from(&destination);
        destination_full_path.push(file.file_name());

        let metadata = try!(file.metadata().map_err(DocBuilderError::LocalDependencyIoError));

        if metadata.is_dir() {
            try!(fs::create_dir(&destination_full_path)
                    .map_err(DocBuilderError::LocalDependencyIoError));
            copy_files(&file.path(), &destination_full_path);
        } else {
            try!(fs::copy(&file.path(), &destination_full_path)
                    .map_err(DocBuilderError::LocalDependencyIoError));
        }

    }
    Ok(())
}
<|endoftext|>


<commit_before>//! Database operations

pub use self::add_package::add_package_into_database;
pub use self::add_package::add_build_into_database;
pub use self::file::add_path_into_database;

use postgres::{Connection, SslMode};
use postgres::error::{Error, ConnectError};
use std::env;

mod add_package;
mod file;

/// Connects to database
pub fn connect_db() -> Result<Connection, ConnectError> {
    // FIXME: unwrap might not be the best here
    let db_url = env::var("CRATESFYI_DATABASE_URL")
        .expect("CRATESFYI_DATABASE_URL environment variable is not exists");
    Connection::connect(&db_url[..], SslMode::None)
}

/// Creates database tables
pub fn create_tables(conn: &Connection) -> Result<(), Error> {
    let queries = [
        "CREATE TABLE crates ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255) UNIQUE NOT NULL, \
            latest_version_id INT DEFAULT 0, \
            versions JSON DEFAULT '[]', \
            downloads_total INT DEFAULT 0, \
            github_description VARCHAR(1024), \
            github_stars INT DEFAULT 0, \
            github_forks INT DEFAULT 0, \
            github_issues INT DEFAULT 0, \
            github_last_commit TIMESTAMP, \
            github_last_update TIMESTAMP \
        )",
        "CREATE TABLE releases ( \
            id SERIAL PRIMARY KEY, \
            crate_id INT NOT NULL REFERENCES crates(id), \
            version VARCHAR(100), \
            release_time TIMESTAMP, \
            dependencies JSON, \
            target_name VARCHAR(255), \
            yanked BOOL DEFAULT FALSE, \
            build_status BOOL DEFAULT FALSE, \
            rustdoc_status BOOL DEFAULT FALSE, \
            test_status BOOL DEFAULT FALSE, \
            license VARCHAR(100), \
            repository_url VARCHAR(255), \
            homepage_url VARCHAR(255), \
            description VARCHAR(1024), \
            description_long VARCHAR(51200), \
            readme VARCHAR(51200), \
            authors JSON, \
            keywords JSON, \
            have_examples BOOL DEFAULT FALSE, \
            downloads INT DEFAULT 0, \
            UNIQUE (crate_id, version) \
        )",
        "CREATE TABLE authors ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            email VARCHAR(255), \
            slug VARCHAR(255) UNIQUE NOT NULL \
        )",
        "CREATE TABLE author_rels ( \
            rid INT REFERENCES releases(id), \
            aid INT REFERENCES authors(id), \
            UNIQUE(rid, aid) \
        )",
        "CREATE TABLE keywords ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            slug VARCHAR(255) NOT NULL UNIQUE \
        )",
        "CREATE TABLE keyword_rels ( \
            rid INT REFERENCES releases(id), \
            kid INT REFERENCES keywords(id), \
            UNIQUE(rid, kid) \
        )",
        "CREATE TABLE owners ( \
            id SERIAL PRIMARY KEY, \
            login VARCHAR(255) NOT NULL UNIQUE, \
            avatar VARCHAR(255), \
            name VARCHAR(255), \
            email VARCHAR(255) \
        )",
        "CREATE TABLE owner_rels ( \
            cid INT REFERENCES releases(id), \
            oid INT REFERENCES owners(id), \
            UNIQUE(cid, oid) \
        )",
        "CREATE TABLE builds ( \
            id SERIAL, \
            rid INT NOT NULL REFERENCES releases(id), \
            rustc_version VARCHAR(100) NOT NULL, \
            cratesfyi_version VARCHAR(100) NOT NULL, \
            build_status BOOL NOT NULL, \
            build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            output TEXT \
        )",
        "CREATE TABLE queue ( \
            id SERIAL, \
            name VARCHAR(255), \
            version VARCHAR(100), \
            UNIQUE(name, version) \
        )",
        "CREATE TABLE files ( \
            path VARCHAR(255) NOT NULL PRIMARY KEY, \
            mime VARCHAR(100) NOT NULL, \
            date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            content BYTEA \
        )",
    ];

    for query in queries.into_iter() {
        try!(conn.execute(query, &[]));
    }

    Ok(())
}

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

    #[test]
    #[ignore]
    fn test_connect_db() {
        let conn = connect_db();
        assert!(conn.is_ok());
    }

    #[test]
    #[ignore]
    fn test_create_tables() {
        let _ = env_logger::init();
        let conn = connect_db();
        assert!(conn.is_ok());

        // FIXME: As expected this test always fails if database is already created
        let res = create_tables(&conn.unwrap());
        info!("RES: {:#?}", res);
        assert!(res.is_ok());
    }
}
<commit_msg>Increase path length<commit_after>//! Database operations

pub use self::add_package::add_package_into_database;
pub use self::add_package::add_build_into_database;
pub use self::file::add_path_into_database;

use postgres::{Connection, SslMode};
use postgres::error::{Error, ConnectError};
use std::env;

mod add_package;
mod file;

/// Connects to database
pub fn connect_db() -> Result<Connection, ConnectError> {
    // FIXME: unwrap might not be the best here
    let db_url = env::var("CRATESFYI_DATABASE_URL")
        .expect("CRATESFYI_DATABASE_URL environment variable is not exists");
    Connection::connect(&db_url[..], SslMode::None)
}

/// Creates database tables
pub fn create_tables(conn: &Connection) -> Result<(), Error> {
    let queries = [
        "CREATE TABLE crates ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255) UNIQUE NOT NULL, \
            latest_version_id INT DEFAULT 0, \
            versions JSON DEFAULT '[]', \
            downloads_total INT DEFAULT 0, \
            github_description VARCHAR(1024), \
            github_stars INT DEFAULT 0, \
            github_forks INT DEFAULT 0, \
            github_issues INT DEFAULT 0, \
            github_last_commit TIMESTAMP, \
            github_last_update TIMESTAMP \
        )",
        "CREATE TABLE releases ( \
            id SERIAL PRIMARY KEY, \
            crate_id INT NOT NULL REFERENCES crates(id), \
            version VARCHAR(100), \
            release_time TIMESTAMP, \
            dependencies JSON, \
            target_name VARCHAR(255), \
            yanked BOOL DEFAULT FALSE, \
            build_status BOOL DEFAULT FALSE, \
            rustdoc_status BOOL DEFAULT FALSE, \
            test_status BOOL DEFAULT FALSE, \
            license VARCHAR(100), \
            repository_url VARCHAR(255), \
            homepage_url VARCHAR(255), \
            description VARCHAR(1024), \
            description_long VARCHAR(51200), \
            readme VARCHAR(51200), \
            authors JSON, \
            keywords JSON, \
            have_examples BOOL DEFAULT FALSE, \
            downloads INT DEFAULT 0, \
            UNIQUE (crate_id, version) \
        )",
        "CREATE TABLE authors ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            email VARCHAR(255), \
            slug VARCHAR(255) UNIQUE NOT NULL \
        )",
        "CREATE TABLE author_rels ( \
            rid INT REFERENCES releases(id), \
            aid INT REFERENCES authors(id), \
            UNIQUE(rid, aid) \
        )",
        "CREATE TABLE keywords ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            slug VARCHAR(255) NOT NULL UNIQUE \
        )",
        "CREATE TABLE keyword_rels ( \
            rid INT REFERENCES releases(id), \
            kid INT REFERENCES keywords(id), \
            UNIQUE(rid, kid) \
        )",
        "CREATE TABLE owners ( \
            id SERIAL PRIMARY KEY, \
            login VARCHAR(255) NOT NULL UNIQUE, \
            avatar VARCHAR(255), \
            name VARCHAR(255), \
            email VARCHAR(255) \
        )",
        "CREATE TABLE owner_rels ( \
            cid INT REFERENCES releases(id), \
            oid INT REFERENCES owners(id), \
            UNIQUE(cid, oid) \
        )",
        "CREATE TABLE builds ( \
            id SERIAL, \
            rid INT NOT NULL REFERENCES releases(id), \
            rustc_version VARCHAR(100) NOT NULL, \
            cratesfyi_version VARCHAR(100) NOT NULL, \
            build_status BOOL NOT NULL, \
            build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            output TEXT \
        )",
        "CREATE TABLE queue ( \
            id SERIAL, \
            name VARCHAR(255), \
            version VARCHAR(100), \
            UNIQUE(name, version) \
        )",
        "CREATE TABLE files ( \
            path VARCHAR(4096) NOT NULL PRIMARY KEY, \
            mime VARCHAR(100) NOT NULL, \
            date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            content BYTEA \
        )",
    ];

    for query in queries.into_iter() {
        try!(conn.execute(query, &[]));
    }

    Ok(())
}

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

    #[test]
    #[ignore]
    fn test_connect_db() {
        let conn = connect_db();
        assert!(conn.is_ok());
    }

    #[test]
    #[ignore]
    fn test_create_tables() {
        let _ = env_logger::init();
        let conn = connect_db();
        assert!(conn.is_ok());

        // FIXME: As expected this test always fails if database is already created
        let res = create_tables(&conn.unwrap());
        info!("RES: {:#?}", res);
        assert!(res.is_ok());
    }
}
<|endoftext|>