<commit_before>use crate::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use crate::hir;

use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::MacroKind;

use std::fmt::Debug;

/// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum CtorOf {
    /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit struct.
    Struct,
    /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit variant.
    Variant,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum CtorKind {
    /// Constructor function automatically created by a tuple struct/variant.
    Fn,
    /// Constructor constant automatically created by a unit struct/variant.
    Const,
    /// Unusable name in value namespace created by a struct variant.
    Fictive,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum NonMacroAttrKind {
    /// Single-segment attribute defined by the language (`#[inline]`)
    Builtin,
    /// Multi-segment custom attribute living in a "tool module" (`#[rustfmt::skip]`).
    Tool,
    /// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`).
    DeriveHelper,
    /// Single-segment custom attribute registered with `#[register_attr]`.
    Registered,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum DefKind {
    // Type namespace
    Mod,
    /// Refers to the struct itself, `DefKind::Ctor` refers to its constructor if it exists.
    Struct,
    Union,
    Enum,
    /// Refers to the variant itself, `DefKind::Ctor` refers to its constructor if it exists.
    Variant,
    Trait,
    /// `type Foo = Bar;`
    TyAlias,
    ForeignTy,
    TraitAlias,
    AssocTy,
    TyParam,

    // Value namespace
    Fn,
    Const,
    ConstParam,
    Static,
    /// Refers to the struct or enum variant's constructor.
    Ctor(CtorOf, CtorKind),
    AssocFn,
    AssocConst,

    // Macro namespace
    Macro(MacroKind),

    // Not namespaced (or they are, but we don't treat them so)
    ExternCrate,
    Use,
    ForeignMod,
    AnonConst,
    OpaqueTy,
    Field,
    LifetimeParam,
    GlobalAsm,
    Impl,
    Closure,
    Generator,
}

impl DefKind {
    pub fn descr(self, def_id: DefId) -> &'static str {
        match self {
            DefKind::Fn => "function",
            DefKind::Mod if def_id.index == CRATE_DEF_INDEX && def_id.krate != LOCAL_CRATE => {
                "crate"
            }
            DefKind::Mod => "module",
            DefKind::Static => "static",
            DefKind::Enum => "enum",
            DefKind::Variant => "variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Fn) => "tuple variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Const) => "unit variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Fictive) => "struct variant",
            DefKind::Struct => "struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) => "tuple struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Const) => "unit struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Fictive) => {
                panic!("impossible struct constructor")
            }
            DefKind::OpaqueTy => "opaque type",
            DefKind::TyAlias => "type alias",
            DefKind::TraitAlias => "trait alias",
            DefKind::AssocTy => "associated type",
            DefKind::Union => "union",
            DefKind::Trait => "trait",
            DefKind::ForeignTy => "foreign type",
            DefKind::AssocFn => "associated function",
            DefKind::Const => "constant",
            DefKind::AssocConst => "associated constant",
            DefKind::TyParam => "type parameter",
            DefKind::ConstParam => "const parameter",
            DefKind::Macro(macro_kind) => macro_kind.descr(),
            DefKind::LifetimeParam => "lifetime parameter",
            DefKind::Use => "import",
            DefKind::ForeignMod => "foreign module",
            DefKind::AnonConst => "constant expression",
            DefKind::Field => "field",
            DefKind::Impl => "implementation",
            DefKind::Closure => "closure",
            DefKind::Generator => "generator",
            DefKind::ExternCrate => "extern crate",
            DefKind::GlobalAsm => "global assembly block",
        }
    }

    /// Gets an English article for the definition.
    pub fn article(&self) -> &'static str {
        match *self {
            DefKind::AssocTy
            | DefKind::AssocConst
            | DefKind::AssocFn
            | DefKind::Enum
            | DefKind::OpaqueTy
            | DefKind::Impl
            | DefKind::Use
            | DefKind::ExternCrate => "an",
            DefKind::Macro(macro_kind) => macro_kind.article(),
            _ => "a",
        }
    }

    pub fn ns(&self) -> Option<Namespace> {
        match self {
            DefKind::Mod
            | DefKind::Struct
            | DefKind::Union
            | DefKind::Enum
            | DefKind::Variant
            | DefKind::Trait
            | DefKind::OpaqueTy
            | DefKind::TyAlias
            | DefKind::ForeignTy
            | DefKind::TraitAlias
            | DefKind::AssocTy
            | DefKind::TyParam => Some(Namespace::TypeNS),

            DefKind::Fn
            | DefKind::Const
            | DefKind::ConstParam
            | DefKind::Static
            | DefKind::Ctor(..)
            | DefKind::AssocFn
            | DefKind::AssocConst => Some(Namespace::ValueNS),

            DefKind::Macro(..) => Some(Namespace::MacroNS),

            // Not namespaced.
            DefKind::AnonConst
            | DefKind::Field
            | DefKind::LifetimeParam
            | DefKind::ExternCrate
            | DefKind::Closure
            | DefKind::Generator
            | DefKind::Use
            | DefKind::ForeignMod
            | DefKind::GlobalAsm
            | DefKind::Impl => None,
        }
    }
}

/// The resolution of a path or export.
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum Res<Id = hir::HirId> {
    Def(DefKind, DefId),

    // Type namespace
    PrimTy(hir::PrimTy),
    SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
    ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]`

    // Value namespace
    SelfCtor(DefId /* impl */), // `DefId` refers to the impl
    Local(Id),

    // Macro namespace
    NonMacroAttr(NonMacroAttrKind), // e.g., `#[inline]` or `#[rustfmt::skip]`

    // All namespaces
    Err,
}

/// The result of resolving a path before lowering to HIR,
/// with "module" segments resolved and associated item
/// segments deferred to type checking.
/// `base_res` is the resolution of the resolved part of the
/// path, `unresolved_segments` is the number of unresolved
/// segments.
///
/// ```text
/// module::Type::AssocX::AssocY::MethodOrAssocType
/// ^~~~~~~~~~~~  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// base_res      unresolved_segments = 3
///
/// <T as Trait>::AssocX::AssocY::MethodOrAssocType
///       ^~~~~~~~~~~~~~  ^~~~~~~~~~~~~~~~~~~~~~~~~
///       base_res        unresolved_segments = 2
/// ```
#[derive(Copy, Clone, Debug)]
pub struct PartialRes {
    base_res: Res<NodeId>,
    unresolved_segments: usize,
}

impl PartialRes {
    #[inline]
    pub fn new(base_res: Res<NodeId>) -> Self {
        PartialRes { base_res, unresolved_segments: 0 }
    }

    #[inline]
    pub fn with_unresolved_segments(base_res: Res<NodeId>, mut unresolved_segments: usize) -> Self {
        if base_res == Res::Err {
            unresolved_segments = 0
        }
        PartialRes { base_res, unresolved_segments }
    }

    #[inline]
    pub fn base_res(&self) -> Res<NodeId> {
        self.base_res
    }

    #[inline]
    pub fn unresolved_segments(&self) -> usize {
        self.unresolved_segments
    }
}

/// Different kinds of symbols don't influence each other.
///
/// Therefore, they have a separate universe (namespace).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Namespace {
    TypeNS,
    ValueNS,
    MacroNS,
}

impl Namespace {
    pub fn descr(self) -> &'static str {
        match self {
            Self::TypeNS => "type",
            Self::ValueNS => "value",
            Self::MacroNS => "macro",
        }
    }
}

/// Just a helper ‒ separate structure for each namespace.
#[derive(Copy, Clone, Default, Debug)]
pub struct PerNS<T> {
    pub value_ns: T,
    pub type_ns: T,
    pub macro_ns: T,
}

impl<T> PerNS<T> {
    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
        PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
    }
}

impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
    type Output = T;

    fn index(&self, ns: Namespace) -> &T {
        match ns {
            Namespace::ValueNS => &self.value_ns,
            Namespace::TypeNS => &self.type_ns,
            Namespace::MacroNS => &self.macro_ns,
        }
    }
}

impl<T> ::std::ops::IndexMut<Namespace> for PerNS<T> {
    fn index_mut(&mut self, ns: Namespace) -> &mut T {
        match ns {
            Namespace::ValueNS => &mut self.value_ns,
            Namespace::TypeNS => &mut self.type_ns,
            Namespace::MacroNS => &mut self.macro_ns,
        }
    }
}

impl<T> PerNS<Option<T>> {
    /// Returns `true` if all the items in this collection are `None`.
    pub fn is_empty(&self) -> bool {
        self.type_ns.is_none() && self.value_ns.is_none() && self.macro_ns.is_none()
    }

    /// Returns an iterator over the items which are `Some`.
    pub fn present_items(self) -> impl Iterator<Item = T> {
        use std::iter::once;

        once(self.type_ns).chain(once(self.value_ns)).chain(once(self.macro_ns)).filter_map(|it| it)
    }
}

impl CtorKind {
    pub fn from_ast(vdata: &ast::VariantData) -> CtorKind {
        match *vdata {
            ast::VariantData::Tuple(..) => CtorKind::Fn,
            ast::VariantData::Unit(..) => CtorKind::Const,
            ast::VariantData::Struct(..) => CtorKind::Fictive,
        }
    }

    pub fn from_hir(vdata: &hir::VariantData<'_>) -> CtorKind {
        match *vdata {
            hir::VariantData::Tuple(..) => CtorKind::Fn,
            hir::VariantData::Unit(..) => CtorKind::Const,
            hir::VariantData::Struct(..) => CtorKind::Fictive,
        }
    }
}

impl NonMacroAttrKind {
    pub fn descr(self) -> &'static str {
        match self {
            NonMacroAttrKind::Builtin => "built-in attribute",
            NonMacroAttrKind::Tool => "tool attribute",
            NonMacroAttrKind::DeriveHelper => "derive helper attribute",
            NonMacroAttrKind::Registered => "explicitly registered attribute",
        }
    }

    pub fn article(self) -> &'static str {
        match self {
            NonMacroAttrKind::Registered => "an",
            _ => "a",
        }
    }

    /// Users of some attributes cannot mark them as used, so they are considered always used.
    pub fn is_used(self) -> bool {
        match self {
            NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper => true,
            NonMacroAttrKind::Builtin | NonMacroAttrKind::Registered => false,
        }
    }
}

impl<Id> Res<Id> {
    /// Return the `DefId` of this `Def` if it has an ID, else panic.
    pub fn def_id(&self) -> DefId
    where
        Id: Debug,
    {
        self.opt_def_id()
            .unwrap_or_else(|| panic!("attempted .def_id() on invalid res: {:?}", self))
    }

    /// Return `Some(..)` with the `DefId` of this `Res` if it has a ID, else `None`.
    pub fn opt_def_id(&self) -> Option<DefId> {
        match *self {
            Res::Def(_, id) => Some(id),

            Res::Local(..)
            | Res::PrimTy(..)
            | Res::SelfTy(..)
            | Res::SelfCtor(..)
            | Res::ToolMod
            | Res::NonMacroAttr(..)
            | Res::Err => None,
        }
    }

    /// Return the `DefId` of this `Res` if it represents a module.
    pub fn mod_def_id(&self) -> Option<DefId> {
        match *self {
            Res::Def(DefKind::Mod, id) => Some(id),
            _ => None,
        }
    }

    /// A human readable name for the res kind ("function", "module", etc.).
    pub fn descr(&self) -> &'static str {
        match *self {
            Res::Def(kind, def_id) => kind.descr(def_id),
            Res::SelfCtor(..) => "self constructor",
            Res::PrimTy(..) => "builtin type",
            Res::Local(..) => "local variable",
            Res::SelfTy(..) => "self type",
            Res::ToolMod => "tool module",
            Res::NonMacroAttr(attr_kind) => attr_kind.descr(),
            Res::Err => "unresolved item",
        }
    }

    /// Gets an English article for the `Res`.
    pub fn article(&self) -> &'static str {
        match *self {
            Res::Def(kind, _) => kind.article(),
            Res::NonMacroAttr(kind) => kind.article(),
            Res::Err => "an",
            _ => "a",
        }
    }

    pub fn map_id<R>(self, mut map: impl FnMut(Id) -> R) -> Res<R> {
        match self {
            Res::Def(kind, id) => Res::Def(kind, id),
            Res::SelfCtor(id) => Res::SelfCtor(id),
            Res::PrimTy(id) => Res::PrimTy(id),
            Res::Local(id) => Res::Local(map(id)),
            Res::SelfTy(a, b) => Res::SelfTy(a, b),
            Res::ToolMod => Res::ToolMod,
            Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
            Res::Err => Res::Err,
        }
    }

    pub fn macro_kind(self) -> Option<MacroKind> {
        match self {
            Res::Def(DefKind::Macro(kind), _) => Some(kind),
            Res::NonMacroAttr(..) => Some(MacroKind::Attr),
            _ => None,
        }
    }

    /// Returns `None` if this is `Res::Err`
    pub fn ns(&self) -> Option<Namespace> {
        match self {
            Res::Def(kind, ..) => kind.ns(),
            Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS),
            Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
            Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
            Res::Err => None,
        }
    }

    /// Always returns `true` if `self` is `Res::Err`
    pub fn matches_ns(&self, ns: Namespace) -> bool {
        self.ns().map(|actual_ns| actual_ns == ns).unwrap_or(true)
    }
}
<commit_msg>Use map_or(true) instead of unwrap_or(true)<commit_after>use crate::def_id::{DefId, CRATE_DEF_INDEX, LOCAL_CRATE};
use crate::hir;

use rustc_ast as ast;
use rustc_ast::NodeId;
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::MacroKind;

use std::fmt::Debug;

/// Encodes if a `DefKind::Ctor` is the constructor of an enum variant or a struct.
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum CtorOf {
    /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit struct.
    Struct,
    /// This `DefKind::Ctor` is a synthesized constructor of a tuple or unit variant.
    Variant,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum CtorKind {
    /// Constructor function automatically created by a tuple struct/variant.
    Fn,
    /// Constructor constant automatically created by a unit struct/variant.
    Const,
    /// Unusable name in value namespace created by a struct variant.
    Fictive,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum NonMacroAttrKind {
    /// Single-segment attribute defined by the language (`#[inline]`)
    Builtin,
    /// Multi-segment custom attribute living in a "tool module" (`#[rustfmt::skip]`).
    Tool,
    /// Single-segment custom attribute registered by a derive macro (`#[serde(default)]`).
    DeriveHelper,
    /// Single-segment custom attribute registered with `#[register_attr]`.
    Registered,
}

#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum DefKind {
    // Type namespace
    Mod,
    /// Refers to the struct itself, `DefKind::Ctor` refers to its constructor if it exists.
    Struct,
    Union,
    Enum,
    /// Refers to the variant itself, `DefKind::Ctor` refers to its constructor if it exists.
    Variant,
    Trait,
    /// `type Foo = Bar;`
    TyAlias,
    ForeignTy,
    TraitAlias,
    AssocTy,
    TyParam,

    // Value namespace
    Fn,
    Const,
    ConstParam,
    Static,
    /// Refers to the struct or enum variant's constructor.
    Ctor(CtorOf, CtorKind),
    AssocFn,
    AssocConst,

    // Macro namespace
    Macro(MacroKind),

    // Not namespaced (or they are, but we don't treat them so)
    ExternCrate,
    Use,
    ForeignMod,
    AnonConst,
    OpaqueTy,
    Field,
    LifetimeParam,
    GlobalAsm,
    Impl,
    Closure,
    Generator,
}

impl DefKind {
    pub fn descr(self, def_id: DefId) -> &'static str {
        match self {
            DefKind::Fn => "function",
            DefKind::Mod if def_id.index == CRATE_DEF_INDEX && def_id.krate != LOCAL_CRATE => {
                "crate"
            }
            DefKind::Mod => "module",
            DefKind::Static => "static",
            DefKind::Enum => "enum",
            DefKind::Variant => "variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Fn) => "tuple variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Const) => "unit variant",
            DefKind::Ctor(CtorOf::Variant, CtorKind::Fictive) => "struct variant",
            DefKind::Struct => "struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Fn) => "tuple struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Const) => "unit struct",
            DefKind::Ctor(CtorOf::Struct, CtorKind::Fictive) => {
                panic!("impossible struct constructor")
            }
            DefKind::OpaqueTy => "opaque type",
            DefKind::TyAlias => "type alias",
            DefKind::TraitAlias => "trait alias",
            DefKind::AssocTy => "associated type",
            DefKind::Union => "union",
            DefKind::Trait => "trait",
            DefKind::ForeignTy => "foreign type",
            DefKind::AssocFn => "associated function",
            DefKind::Const => "constant",
            DefKind::AssocConst => "associated constant",
            DefKind::TyParam => "type parameter",
            DefKind::ConstParam => "const parameter",
            DefKind::Macro(macro_kind) => macro_kind.descr(),
            DefKind::LifetimeParam => "lifetime parameter",
            DefKind::Use => "import",
            DefKind::ForeignMod => "foreign module",
            DefKind::AnonConst => "constant expression",
            DefKind::Field => "field",
            DefKind::Impl => "implementation",
            DefKind::Closure => "closure",
            DefKind::Generator => "generator",
            DefKind::ExternCrate => "extern crate",
            DefKind::GlobalAsm => "global assembly block",
        }
    }

    /// Gets an English article for the definition.
    pub fn article(&self) -> &'static str {
        match *self {
            DefKind::AssocTy
            | DefKind::AssocConst
            | DefKind::AssocFn
            | DefKind::Enum
            | DefKind::OpaqueTy
            | DefKind::Impl
            | DefKind::Use
            | DefKind::ExternCrate => "an",
            DefKind::Macro(macro_kind) => macro_kind.article(),
            _ => "a",
        }
    }

    pub fn ns(&self) -> Option<Namespace> {
        match self {
            DefKind::Mod
            | DefKind::Struct
            | DefKind::Union
            | DefKind::Enum
            | DefKind::Variant
            | DefKind::Trait
            | DefKind::OpaqueTy
            | DefKind::TyAlias
            | DefKind::ForeignTy
            | DefKind::TraitAlias
            | DefKind::AssocTy
            | DefKind::TyParam => Some(Namespace::TypeNS),

            DefKind::Fn
            | DefKind::Const
            | DefKind::ConstParam
            | DefKind::Static
            | DefKind::Ctor(..)
            | DefKind::AssocFn
            | DefKind::AssocConst => Some(Namespace::ValueNS),

            DefKind::Macro(..) => Some(Namespace::MacroNS),

            // Not namespaced.
            DefKind::AnonConst
            | DefKind::Field
            | DefKind::LifetimeParam
            | DefKind::ExternCrate
            | DefKind::Closure
            | DefKind::Generator
            | DefKind::Use
            | DefKind::ForeignMod
            | DefKind::GlobalAsm
            | DefKind::Impl => None,
        }
    }
}

/// The resolution of a path or export.
#[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
pub enum Res<Id = hir::HirId> {
    Def(DefKind, DefId),

    // Type namespace
    PrimTy(hir::PrimTy),
    SelfTy(Option<DefId> /* trait */, Option<DefId> /* impl */),
    ToolMod, // e.g., `rustfmt` in `#[rustfmt::skip]`

    // Value namespace
    SelfCtor(DefId /* impl */), // `DefId` refers to the impl
    Local(Id),

    // Macro namespace
    NonMacroAttr(NonMacroAttrKind), // e.g., `#[inline]` or `#[rustfmt::skip]`

    // All namespaces
    Err,
}

/// The result of resolving a path before lowering to HIR,
/// with "module" segments resolved and associated item
/// segments deferred to type checking.
/// `base_res` is the resolution of the resolved part of the
/// path, `unresolved_segments` is the number of unresolved
/// segments.
///
/// ```text
/// module::Type::AssocX::AssocY::MethodOrAssocType
/// ^~~~~~~~~~~~  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/// base_res      unresolved_segments = 3
///
/// <T as Trait>::AssocX::AssocY::MethodOrAssocType
///       ^~~~~~~~~~~~~~  ^~~~~~~~~~~~~~~~~~~~~~~~~
///       base_res        unresolved_segments = 2
/// ```
#[derive(Copy, Clone, Debug)]
pub struct PartialRes {
    base_res: Res<NodeId>,
    unresolved_segments: usize,
}

impl PartialRes {
    #[inline]
    pub fn new(base_res: Res<NodeId>) -> Self {
        PartialRes { base_res, unresolved_segments: 0 }
    }

    #[inline]
    pub fn with_unresolved_segments(base_res: Res<NodeId>, mut unresolved_segments: usize) -> Self {
        if base_res == Res::Err {
            unresolved_segments = 0
        }
        PartialRes { base_res, unresolved_segments }
    }

    #[inline]
    pub fn base_res(&self) -> Res<NodeId> {
        self.base_res
    }

    #[inline]
    pub fn unresolved_segments(&self) -> usize {
        self.unresolved_segments
    }
}

/// Different kinds of symbols don't influence each other.
///
/// Therefore, they have a separate universe (namespace).
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Namespace {
    TypeNS,
    ValueNS,
    MacroNS,
}

impl Namespace {
    pub fn descr(self) -> &'static str {
        match self {
            Self::TypeNS => "type",
            Self::ValueNS => "value",
            Self::MacroNS => "macro",
        }
    }
}

/// Just a helper ‒ separate structure for each namespace.
#[derive(Copy, Clone, Default, Debug)]
pub struct PerNS<T> {
    pub value_ns: T,
    pub type_ns: T,
    pub macro_ns: T,
}

impl<T> PerNS<T> {
    pub fn map<U, F: FnMut(T) -> U>(self, mut f: F) -> PerNS<U> {
        PerNS { value_ns: f(self.value_ns), type_ns: f(self.type_ns), macro_ns: f(self.macro_ns) }
    }
}

impl<T> ::std::ops::Index<Namespace> for PerNS<T> {
    type Output = T;

    fn index(&self, ns: Namespace) -> &T {
        match ns {
            Namespace::ValueNS => &self.value_ns,
            Namespace::TypeNS => &self.type_ns,
            Namespace::MacroNS => &self.macro_ns,
        }
    }
}

impl<T> ::std::ops::IndexMut<Namespace> for PerNS<T> {
    fn index_mut(&mut self, ns: Namespace) -> &mut T {
        match ns {
            Namespace::ValueNS => &mut self.value_ns,
            Namespace::TypeNS => &mut self.type_ns,
            Namespace::MacroNS => &mut self.macro_ns,
        }
    }
}

impl<T> PerNS<Option<T>> {
    /// Returns `true` if all the items in this collection are `None`.
    pub fn is_empty(&self) -> bool {
        self.type_ns.is_none() && self.value_ns.is_none() && self.macro_ns.is_none()
    }

    /// Returns an iterator over the items which are `Some`.
    pub fn present_items(self) -> impl Iterator<Item = T> {
        use std::iter::once;

        once(self.type_ns).chain(once(self.value_ns)).chain(once(self.macro_ns)).filter_map(|it| it)
    }
}

impl CtorKind {
    pub fn from_ast(vdata: &ast::VariantData) -> CtorKind {
        match *vdata {
            ast::VariantData::Tuple(..) => CtorKind::Fn,
            ast::VariantData::Unit(..) => CtorKind::Const,
            ast::VariantData::Struct(..) => CtorKind::Fictive,
        }
    }

    pub fn from_hir(vdata: &hir::VariantData<'_>) -> CtorKind {
        match *vdata {
            hir::VariantData::Tuple(..) => CtorKind::Fn,
            hir::VariantData::Unit(..) => CtorKind::Const,
            hir::VariantData::Struct(..) => CtorKind::Fictive,
        }
    }
}

impl NonMacroAttrKind {
    pub fn descr(self) -> &'static str {
        match self {
            NonMacroAttrKind::Builtin => "built-in attribute",
            NonMacroAttrKind::Tool => "tool attribute",
            NonMacroAttrKind::DeriveHelper => "derive helper attribute",
            NonMacroAttrKind::Registered => "explicitly registered attribute",
        }
    }

    pub fn article(self) -> &'static str {
        match self {
            NonMacroAttrKind::Registered => "an",
            _ => "a",
        }
    }

    /// Users of some attributes cannot mark them as used, so they are considered always used.
    pub fn is_used(self) -> bool {
        match self {
            NonMacroAttrKind::Tool | NonMacroAttrKind::DeriveHelper => true,
            NonMacroAttrKind::Builtin | NonMacroAttrKind::Registered => false,
        }
    }
}

impl<Id> Res<Id> {
    /// Return the `DefId` of this `Def` if it has an ID, else panic.
    pub fn def_id(&self) -> DefId
    where
        Id: Debug,
    {
        self.opt_def_id()
            .unwrap_or_else(|| panic!("attempted .def_id() on invalid res: {:?}", self))
    }

    /// Return `Some(..)` with the `DefId` of this `Res` if it has a ID, else `None`.
    pub fn opt_def_id(&self) -> Option<DefId> {
        match *self {
            Res::Def(_, id) => Some(id),

            Res::Local(..)
            | Res::PrimTy(..)
            | Res::SelfTy(..)
            | Res::SelfCtor(..)
            | Res::ToolMod
            | Res::NonMacroAttr(..)
            | Res::Err => None,
        }
    }

    /// Return the `DefId` of this `Res` if it represents a module.
    pub fn mod_def_id(&self) -> Option<DefId> {
        match *self {
            Res::Def(DefKind::Mod, id) => Some(id),
            _ => None,
        }
    }

    /// A human readable name for the res kind ("function", "module", etc.).
    pub fn descr(&self) -> &'static str {
        match *self {
            Res::Def(kind, def_id) => kind.descr(def_id),
            Res::SelfCtor(..) => "self constructor",
            Res::PrimTy(..) => "builtin type",
            Res::Local(..) => "local variable",
            Res::SelfTy(..) => "self type",
            Res::ToolMod => "tool module",
            Res::NonMacroAttr(attr_kind) => attr_kind.descr(),
            Res::Err => "unresolved item",
        }
    }

    /// Gets an English article for the `Res`.
    pub fn article(&self) -> &'static str {
        match *self {
            Res::Def(kind, _) => kind.article(),
            Res::NonMacroAttr(kind) => kind.article(),
            Res::Err => "an",
            _ => "a",
        }
    }

    pub fn map_id<R>(self, mut map: impl FnMut(Id) -> R) -> Res<R> {
        match self {
            Res::Def(kind, id) => Res::Def(kind, id),
            Res::SelfCtor(id) => Res::SelfCtor(id),
            Res::PrimTy(id) => Res::PrimTy(id),
            Res::Local(id) => Res::Local(map(id)),
            Res::SelfTy(a, b) => Res::SelfTy(a, b),
            Res::ToolMod => Res::ToolMod,
            Res::NonMacroAttr(attr_kind) => Res::NonMacroAttr(attr_kind),
            Res::Err => Res::Err,
        }
    }

    pub fn macro_kind(self) -> Option<MacroKind> {
        match self {
            Res::Def(DefKind::Macro(kind), _) => Some(kind),
            Res::NonMacroAttr(..) => Some(MacroKind::Attr),
            _ => None,
        }
    }

    /// Returns `None` if this is `Res::Err`
    pub fn ns(&self) -> Option<Namespace> {
        match self {
            Res::Def(kind, ..) => kind.ns(),
            Res::PrimTy(..) | Res::SelfTy(..) | Res::ToolMod => Some(Namespace::TypeNS),
            Res::SelfCtor(..) | Res::Local(..) => Some(Namespace::ValueNS),
            Res::NonMacroAttr(..) => Some(Namespace::MacroNS),
            Res::Err => None,
        }
    }

    /// Always returns `true` if `self` is `Res::Err`
    pub fn matches_ns(&self, ns: Namespace) -> bool {
        self.ns().map_or(true, |actual_ns| actual_ns == ns)
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Mozilla Foundation
//
// 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.

use client::{connect_to_server,connect_with_retry,ServerConnection};
use cmdline::Command;
use compiler::run_compiler;
use protobuf::RepeatedField;
use protocol::{
    CacheStats,
    ClientRequest,
    Compile,
    CompileStarted,
    GetStats,
    Shutdown,
    UnhandledCompile,
};
use server;
use std::env;
use std::io::{self,Error,ErrorKind};
use std::process;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

/// The default sccache server port.
pub const DEFAULT_PORT : u16 = 4225;

/// Possible responses from the server for a `Compile` request.
enum CompileResponse {
    /// The compilation was started.
    CompileStarted(CompileStarted),
    /// The server could not handle this compilation request.
    UnhandledCompile(UnhandledCompile),
}

/// Pipe `cmd`'s stdio to `/dev/null`, unless a specific env var is set.
fn maybe_redirect_stdio(cmd : &mut process::Command) {
    if !match env::var("SCCACHE_NO_DAEMON") {
            Ok(val) => val == "1",
            Err(_) => false,
    } {
        cmd.stdin(process::Stdio::null())
            .stdout(process::Stdio::null())
            .stderr(process::Stdio::null());
    }
}

/// Re-execute the current executable as a background server.
fn run_server_process() -> io::Result<()> {
    env::current_exe().and_then(|exe_path| {
        let mut cmd = process::Command::new(exe_path);
        maybe_redirect_stdio(&mut cmd);
        cmd.env("SCCACHE_START_SERVER", "1")
            .spawn()
    }).and(Ok(()))
}

/// Convert `res` into a process exit code.
///
/// If `res` is `Ok`, return `0`, else call `else_func` and then
/// return 1.
fn result_exit_code<T : FnOnce(io::Error)>(res : io::Result<()>,
                                           else_func : T) -> i32 {
    res.and(Ok(0)).unwrap_or_else(|e| {
        else_func(e);
        1
    })
}

/// Attempt to connect to an sccache server listening on `port`, or start one if no server is running.
fn connect_or_start_server(port : u16) -> io::Result<ServerConnection> {
    connect_to_server(port).or_else(|e| {
        //FIXME: this can sometimes hit a connection timed out?
        if e.kind() == io::ErrorKind::ConnectionRefused {
            // If the connection was refused we probably need to start
            // the server.
            run_server_process().and_then(|()| connect_with_retry(port))
        } else {
            debug!("Error: {}", e);
            Err(e)
        }
    })
}

/// Send a `GetStats` request to the server, and return the `CacheStats` request if successful.
pub fn request_stats(mut conn : ServerConnection) -> io::Result<CacheStats> {
    debug!("request_stats");
    let mut req = ClientRequest::new();
    req.set_get_stats(GetStats::new());
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_stats() {
        Ok(response.take_stats())
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected server response!"))
    }
}

/// Send a `Shutdown` request to the server, and return the `CacheStats` contained within the response if successful.
pub fn request_shutdown(mut conn : ServerConnection) -> io::Result<CacheStats> {
    debug!("request_shutdown");
    let mut req = ClientRequest::new();
    req.set_shutdown(Shutdown::new());
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_shutting_down() {
        Ok(response.take_shutting_down().take_stats())
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected server response!"))
    }
}

/// Format `size_in_bytes` as a size in sensible units.
///
/// e.g. format_size(3 * 1024 * 1024 * 1024) == "3 GB"
fn format_size(size_in_bytes : u64) -> String {
    let mut size = size_in_bytes;
    let mut remainder = 0;
    for suffix in ["bytes", "kB", "MB", "GB", "TB"].iter() {
        if size < 1024 {
            let frac = if remainder > 0 {
                let rem = (100.0 * remainder as f32 / 1024.0).trunc() as i32;
                format!(".{}", if rem % 10 == 0 { rem / 10 } else { rem })
            } else {
                "".to_owned()
            };
            return format!("{}{} {}", size, frac, suffix);
        }
        remainder = size % 1024;
        size = size / 1024;
    }
    //TODO: handle this more gracefully
    return format!("{} {}", size, "PB");
}

/// Print `stats` to stdout.
fn print_stats(stats : CacheStats) -> io::Result<()> {
    for stat in stats.get_stats().iter() {
        //TODO: properly align output
        print!("{}\t\t", stat.get_name());
        if stat.has_count() {
            print!("{}", stat.get_count());
        } else if stat.has_str() {
            print!("{}", stat.get_str());
        } else if stat.has_size() {
            print!("{}", format_size(stat.get_size()));
        }
        print!("\n");
    }
    Ok(())
}

/// Send a `Compile` request to the server, and return the server response if successful.
fn request_compile(conn : &mut ServerConnection, cmdline : &Vec<String>, cwd : &str) -> io::Result<CompileResponse> {
    let mut req = ClientRequest::new();
    let mut compile = Compile::new();
    compile.set_cwd(cwd.to_owned());
    compile.set_command(RepeatedField::from_vec(cmdline.clone()));
    req.set_compile(compile);
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_compile_started() {
        Ok(CompileResponse::CompileStarted(response.take_compile_started()))
    } else if response.has_unhandled_compile() {
        Ok(CompileResponse::UnhandledCompile(response.take_unhandled_compile()))
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected response from server"))
    }
}

/// Return the signal that caused a process to exit from `status`.
#[cfg(unix)]
#[allow(dead_code)]
fn status_signal(status : process::ExitStatus) -> Option<i32> {
    status.signal()
}

/// Not implemented for non-Unix.
#[cfg(not(unix))]
#[allow(dead_code)]
fn status_signal(status : process::ExitStatus) -> Option<i32> {
    None
}

/// Handle `response`, the response from sending a `Compile` request to the server. Return the compiler exit status.
///
/// If the server returned `CompileStarted`, wait for a `CompileFinished` and
/// print the results.
///
/// If the server returned `UnhandledCompile`, run the compilation command
/// locally and return the result.
fn handle_compile_response(_conn : &mut ServerConnection, response : CompileResponse, cmdline : &Vec<String>, cwd : &str) -> io::Result<i32> {
    match response {
        CompileResponse::CompileStarted(_) => {
            debug!("Server sent CompileStarted");
            //TODO: wait for CompileFinished!
            Ok(0)
        }
        CompileResponse::UnhandledCompile(_) => {
            debug!("Server sent UnhandledCompile");
            run_compiler(cmdline, cwd)
                .and_then(|status| {
                    Ok(status.code()
                       .unwrap_or_else(|| {
                           /* TODO: this breaks type inference, figure out why
                           status_signal(status)
                           .and_then(|sig : i32| {
                           println!("Compile terminated by signal {}", sig);
                           None
                       });
                            */
                           // Arbitrary.
                           2
                       }))
                })
        }
    }
}

pub fn run_command(cmd : Command) -> i32 {
    match cmd {
        // Actual usage gets printed in `cmdline::parse`.
        Command::Usage => 1,
        Command::ShowStats => {
            result_exit_code(connect_or_start_server(DEFAULT_PORT).and_then(request_stats).and_then(print_stats),
                             |e| {
                                 println!("Couldn't get stats from server: {}", e);
                             })
        },
        Command::InternalStartServer => {
            // Can't report failure here, we're already daemonized.
            result_exit_code(server::start_server(DEFAULT_PORT), |_| {})
        },
        Command::StartServer => {
            println!("Starting sccache server...");
            result_exit_code(run_server_process(), |e| {
                println!("failed to spawn server: {}", e);
            })
        },
        Command::StopServer => {
            println!("Stopping sccache server...");
            result_exit_code(connect_to_server(DEFAULT_PORT).and_then(request_shutdown).and_then(print_stats),
                             |_e| {
                                 //TODO: check if this was connection refused,
                                 // print error if not.
                                 println!("Couldn't connect to server");
                             })

        },
        Command::Compile { cmdline, cwd } => {
            connect_or_start_server(DEFAULT_PORT)
                .and_then(|mut conn| request_compile(&mut conn, &cmdline, &cwd)
                          .and_then(|res| handle_compile_response(&mut conn, res, &cmdline, &cwd)))
                .unwrap_or_else(|e| {
                    println!("Failed to execute compile: {}", e);
                    1
                })
        },
    }
}

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

    #[test]
    fn test_format_size() {
        assert_eq!("10 bytes", format_size(10));
        assert_eq!("1023 bytes", format_size(1023));
        assert_eq!("1 kB", format_size(1024));
        assert_eq!("1.5 kB", format_size(1024 + 512));
        assert_eq!("1023.99 kB", format_size(1024 * 1024 - 1));
        assert_eq!("1 MB", format_size(1024 * 1024));
        assert_eq!("1 GB", format_size(1024 * 1024 * 1024));
        assert_eq!("1.25 GB", format_size(1024 * 1024 * (1024 + 256)));
        assert_eq!("3 GB", format_size(1024 * 1024 * 1024 * 3));
        assert_eq!("1 TB", format_size(1024 * 1024 * 1024 * 1024));
        assert_eq!("1 PB", format_size(1024 * 1024 * 1024 * 1024 * 1024));
    }
}
<commit_msg>Put request_compile+handle_compile_response into a do_compile method<commit_after>// Copyright 2016 Mozilla Foundation
//
// 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.

use client::{connect_to_server,connect_with_retry,ServerConnection};
use cmdline::Command;
use compiler::run_compiler;
use protobuf::RepeatedField;
use protocol::{
    CacheStats,
    ClientRequest,
    Compile,
    CompileStarted,
    GetStats,
    Shutdown,
    UnhandledCompile,
};
use server;
use std::env;
use std::io::{self,Error,ErrorKind};
use std::process;
#[cfg(unix)]
use std::os::unix::process::ExitStatusExt;

/// The default sccache server port.
pub const DEFAULT_PORT : u16 = 4225;

/// Possible responses from the server for a `Compile` request.
enum CompileResponse {
    /// The compilation was started.
    CompileStarted(CompileStarted),
    /// The server could not handle this compilation request.
    UnhandledCompile(UnhandledCompile),
}

/// Pipe `cmd`'s stdio to `/dev/null`, unless a specific env var is set.
fn maybe_redirect_stdio(cmd : &mut process::Command) {
    if !match env::var("SCCACHE_NO_DAEMON") {
            Ok(val) => val == "1",
            Err(_) => false,
    } {
        cmd.stdin(process::Stdio::null())
            .stdout(process::Stdio::null())
            .stderr(process::Stdio::null());
    }
}

/// Re-execute the current executable as a background server.
fn run_server_process() -> io::Result<()> {
    env::current_exe().and_then(|exe_path| {
        let mut cmd = process::Command::new(exe_path);
        maybe_redirect_stdio(&mut cmd);
        cmd.env("SCCACHE_START_SERVER", "1")
            .spawn()
    }).and(Ok(()))
}

/// Convert `res` into a process exit code.
///
/// If `res` is `Ok`, return `0`, else call `else_func` and then
/// return 1.
fn result_exit_code<T : FnOnce(io::Error)>(res : io::Result<()>,
                                           else_func : T) -> i32 {
    res.and(Ok(0)).unwrap_or_else(|e| {
        else_func(e);
        1
    })
}

/// Attempt to connect to an sccache server listening on `port`, or start one if no server is running.
fn connect_or_start_server(port : u16) -> io::Result<ServerConnection> {
    connect_to_server(port).or_else(|e| {
        //FIXME: this can sometimes hit a connection timed out?
        if e.kind() == io::ErrorKind::ConnectionRefused {
            // If the connection was refused we probably need to start
            // the server.
            run_server_process().and_then(|()| connect_with_retry(port))
        } else {
            debug!("Error: {}", e);
            Err(e)
        }
    })
}

/// Send a `GetStats` request to the server, and return the `CacheStats` request if successful.
pub fn request_stats(mut conn : ServerConnection) -> io::Result<CacheStats> {
    debug!("request_stats");
    let mut req = ClientRequest::new();
    req.set_get_stats(GetStats::new());
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_stats() {
        Ok(response.take_stats())
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected server response!"))
    }
}

/// Send a `Shutdown` request to the server, and return the `CacheStats` contained within the response if successful.
pub fn request_shutdown(mut conn : ServerConnection) -> io::Result<CacheStats> {
    debug!("request_shutdown");
    let mut req = ClientRequest::new();
    req.set_shutdown(Shutdown::new());
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_shutting_down() {
        Ok(response.take_shutting_down().take_stats())
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected server response!"))
    }
}

/// Format `size_in_bytes` as a size in sensible units.
///
/// e.g. format_size(3 * 1024 * 1024 * 1024) == "3 GB"
fn format_size(size_in_bytes : u64) -> String {
    let mut size = size_in_bytes;
    let mut remainder = 0;
    for suffix in ["bytes", "kB", "MB", "GB", "TB"].iter() {
        if size < 1024 {
            let frac = if remainder > 0 {
                let rem = (100.0 * remainder as f32 / 1024.0).trunc() as i32;
                format!(".{}", if rem % 10 == 0 { rem / 10 } else { rem })
            } else {
                "".to_owned()
            };
            return format!("{}{} {}", size, frac, suffix);
        }
        remainder = size % 1024;
        size = size / 1024;
    }
    //TODO: handle this more gracefully
    return format!("{} {}", size, "PB");
}

/// Print `stats` to stdout.
fn print_stats(stats : CacheStats) -> io::Result<()> {
    for stat in stats.get_stats().iter() {
        //TODO: properly align output
        print!("{}\t\t", stat.get_name());
        if stat.has_count() {
            print!("{}", stat.get_count());
        } else if stat.has_str() {
            print!("{}", stat.get_str());
        } else if stat.has_size() {
            print!("{}", format_size(stat.get_size()));
        }
        print!("\n");
    }
    Ok(())
}

/// Send a `Compile` request to the server, and return the server response if successful.
fn request_compile(conn : &mut ServerConnection, cmdline : &Vec<String>, cwd : &str) -> io::Result<CompileResponse> {
    let mut req = ClientRequest::new();
    let mut compile = Compile::new();
    compile.set_cwd(cwd.to_owned());
    compile.set_command(RepeatedField::from_vec(cmdline.clone()));
    req.set_compile(compile);
    //TODO: better error mapping
    let mut response = try!(conn.request(req).or(Err(Error::new(ErrorKind::Other, "Failed to send data to or receive data from server"))));
    if response.has_compile_started() {
        Ok(CompileResponse::CompileStarted(response.take_compile_started()))
    } else if response.has_unhandled_compile() {
        Ok(CompileResponse::UnhandledCompile(response.take_unhandled_compile()))
    } else {
        Err(Error::new(ErrorKind::Other, "Unexpected response from server"))
    }
}

/// Return the signal that caused a process to exit from `status`.
#[cfg(unix)]
#[allow(dead_code)]
fn status_signal(status : process::ExitStatus) -> Option<i32> {
    status.signal()
}

/// Not implemented for non-Unix.
#[cfg(not(unix))]
#[allow(dead_code)]
fn status_signal(status : process::ExitStatus) -> Option<i32> {
    None
}

/// Handle `response`, the response from sending a `Compile` request to the server. Return the compiler exit status.
///
/// If the server returned `CompileStarted`, wait for a `CompileFinished` and
/// print the results.
///
/// If the server returned `UnhandledCompile`, run the compilation command
/// locally and return the result.
fn handle_compile_response(_conn : &mut ServerConnection, response : CompileResponse, cmdline : &Vec<String>, cwd : &str) -> io::Result<i32> {
    match response {
        CompileResponse::CompileStarted(_) => {
            debug!("Server sent CompileStarted");
            //TODO: wait for CompileFinished!
            Ok(0)
        }
        CompileResponse::UnhandledCompile(_) => {
            debug!("Server sent UnhandledCompile");
            run_compiler(cmdline, cwd)
                .and_then(|status| {
                    Ok(status.code()
                       .unwrap_or_else(|| {
                           /* TODO: this breaks type inference, figure out why
                           status_signal(status)
                           .and_then(|sig : i32| {
                           println!("Compile terminated by signal {}", sig);
                           None
                       });
                            */
                           // Arbitrary.
                           2
                       }))
                })
        }
    }
}

/// Send a `Compile` request to the sccache server `conn`, and handle the response.
///
/// See `request_compile` and `handle_compile_response`.
pub fn do_compile(mut conn : ServerConnection, cmdline : Vec<String>, cwd : String) -> io::Result<i32> {
    request_compile(&mut conn, &cmdline, &cwd)
        .and_then(|res| handle_compile_response(&mut conn, res, &cmdline, &cwd))
}

/// Run `cmd` and return the process exit status.
pub fn run_command(cmd : Command) -> i32 {
    match cmd {
        // Actual usage gets printed in `cmdline::parse`.
        Command::Usage => 1,
        Command::ShowStats => {
            result_exit_code(connect_or_start_server(DEFAULT_PORT).and_then(request_stats).and_then(print_stats),
                             |e| {
                                 println!("Couldn't get stats from server: {}", e);
                             })
        },
        Command::InternalStartServer => {
            // Can't report failure here, we're already daemonized.
            result_exit_code(server::start_server(DEFAULT_PORT), |_| {})
        },
        Command::StartServer => {
            println!("Starting sccache server...");
            result_exit_code(run_server_process(), |e| {
                println!("failed to spawn server: {}", e);
            })
        },
        Command::StopServer => {
            println!("Stopping sccache server...");
            result_exit_code(connect_to_server(DEFAULT_PORT).and_then(request_shutdown).and_then(print_stats),
                             |_e| {
                                 //TODO: check if this was connection refused,
                                 // print error if not.
                                 println!("Couldn't connect to server");
                             })

        },
        Command::Compile { cmdline, cwd } => {
            connect_or_start_server(DEFAULT_PORT)
                .and_then(|conn| do_compile(conn, cmdline, cwd))
                .unwrap_or_else(|e| {
                    println!("Failed to execute compile: {}", e);
                    1
                })
        },
    }
}

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

    #[test]
    fn test_format_size() {
        assert_eq!("10 bytes", format_size(10));
        assert_eq!("1023 bytes", format_size(1023));
        assert_eq!("1 kB", format_size(1024));
        assert_eq!("1.5 kB", format_size(1024 + 512));
        assert_eq!("1023.99 kB", format_size(1024 * 1024 - 1));
        assert_eq!("1 MB", format_size(1024 * 1024));
        assert_eq!("1 GB", format_size(1024 * 1024 * 1024));
        assert_eq!("1.25 GB", format_size(1024 * 1024 * (1024 + 256)));
        assert_eq!("3 GB", format_size(1024 * 1024 * 1024 * 3));
        assert_eq!("1 TB", format_size(1024 * 1024 * 1024 * 1024));
        assert_eq!("1 PB", format_size(1024 * 1024 * 1024 * 1024 * 1024));
    }
}
<|endoftext|>


<commit_before>use std::string::String as StdString;

use types;

use serde_json::value::{ToJson, Value};

use proto::{
    Term_TermType as TermType,
};

use types::Command as Cmd;

include!(concat!(env!("OUT_DIR"), "/serde_types.rs"));

#[allow(non_upper_case_globals)]
pub const r: Client = Client;

impl RootCommand for Client {}

pub trait RootCommand {
    fn table<T>(&self, arg: T) -> Command<types::Table, TableOpts> where
        T: Into<types::String>
        {
            r.db("test").table(arg)
        }
}

#[derive(Debug, Clone)]
pub struct Command<T, O>(T, Option<O>);

pub struct Client;

impl Cmd {
    pub fn make<A, T, O, PT, PO>(typ: TermType, arg: Option<A>, opts: Option<O>, cmd: Option<&Command<PT, PO>>) -> Command<T, O>
        where
        A: types::DataType,
        T: types::DataType,
        O: ToJson + Clone,
        PT: types::DataType,
        PO: ToJson + Clone
        {
            let (prev_cmd, prev_opts) = match cmd {
                Some(cmd) => (Some(cmd.0.clone().into()), cmd.1.clone()),
                None => (None, None),
            };
            let mut dt = Cmd::new(typ, prev_cmd);
            if let Some(arg) = arg {
                dt = dt.with_args(arg.into());
            }
            if let Some(opt) = prev_opts {
                let obj = types::Object::from(opt);
                dt = dt.with_opts(obj);
            }
            Command(dt.into(), opts)
        }
}

macro_rules! Root {
    () => {None as Option<&Command<types::Null, ()>>}
}

macro_rules! NoArg {
    () => {None as Option<types::Null>}
}

#[allow(dead_code)]
impl Client {
    pub fn db<T>(&self, arg: T) -> Command<types::Db, ()> where
        T: Into<types::String>
        {
            Cmd::make(TermType::DB, Some(arg.into()), None, Root!())
        }

    pub fn uuid(&self) -> Command<types::String, ()>
    {
        Cmd::make(TermType::UUID, NoArg!(), None, Root!())
    }
}

#[allow(dead_code)]
impl<O> Command<types::Db, O>
where O: ToJson + Clone
{
    pub fn table<T>(&self, arg: T) -> Command<types::Table, TableOpts> where
        T: Into<types::String>
        {
            Cmd::make(TermType::TABLE, Some(arg.into()), Some(TableOpts::default()), Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::Table, O>
where O: ToJson + Clone
{
    pub fn get<T>(&self, arg: T) -> Command<types::ObjectSelection, ()> where
        T: Into<types::PrimaryKey>,
    {
        Cmd::make(TermType::GET, Some(arg.into()), None, Some(self))
    }

    pub fn changes(&self) -> Command<types::Stream, ChangesOpts<bool>> where
        ChangesOpts<bool>: Default + ToJson + Clone
        {
            let opts: ChangesOpts<bool> = Default::default();
            Cmd::make(TermType::CHANGES, NoArg!(), Some(opts), Some(self))
        }

    pub fn get_all<T>(&self, arg: T) -> Command<types::StreamSelection, GetAllOpts> where
        T: Into<types::SecondaryKey>,
        GetAllOpts: Default + ToJson + Clone
        {
            Cmd::make(TermType::GET_ALL, Some(arg.into()), Some(GetAllOpts::default()), Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::Stream, O>
where O: ToJson + Clone
{
    pub fn changes(&self) -> Command<types::Stream, ChangesOpts<bool>> where
        ChangesOpts<bool>: Default + ToJson + Clone
        {
            let opts: ChangesOpts<bool> = Default::default();
            Cmd::make(TermType::CHANGES, NoArg!(), Some(opts), Some(self))
        }

    pub fn map<T>(&self, arg: T) -> Command<types::Stream, ()> where
        T: Into<types::Stream>,
        {
            Cmd::make(TermType::MAP, Some(arg.into()), None, Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::StreamSelection, O>
where O: ToJson + Clone
{
    pub fn changes(&self) -> Command<types::Stream, ChangesOpts<bool>> where
        ChangesOpts<bool>: Default + ToJson + Clone
        {
            let opts: ChangesOpts<bool> = Default::default();
            Cmd::make(TermType::CHANGES, NoArg!(), Some(opts), Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::Object, O>
where O: ToJson + Clone
{
    pub fn get_field<T, V>(&self, arg: T) -> Command<V, ()>
        where T: Into<types::String>, V: types::DataType
        {
            Cmd::make(TermType::GET_FIELD, Some(arg.into()), None, Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::ObjectSelection, O>
where O: ToJson + Clone
{
    pub fn changes(&self) -> Command<types::Stream, ChangesOpts<bool>> where
        ChangesOpts<bool>: Default + ToJson + Clone
        {
            let opts: ChangesOpts<bool> = Default::default();
            Cmd::make(TermType::CHANGES, NoArg!(), Some(opts), Some(self))
        }
}

#[allow(dead_code)]
impl<O> Command<types::Array, O>
where O: ToJson + Clone
{
    pub fn get_field<T>(&self, arg: T) -> Command<types::Array, ()>
        where T: Into<types::String>
        {
            Cmd::make(TermType::GET_FIELD, Some(arg.into()), None, Some(self))
        }

    pub fn map<T>(&self, arg: T) -> Command<types::Array, ()> where
        T: Into<types::Array>,
        {
            Cmd::make(TermType::MAP, Some(arg.into()), None, Some(self))
        }
}

#[allow(dead_code)]
impl<T> Command<T, TableOpts>
{
    pub fn read_mode(mut self, arg: ReadMode) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.read_mode = arg;
        }
        self
    }

    pub fn identifier_format(mut self, arg: IdentifierFormat) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.identifier_format = arg;
        }
        self
    }
}

pub trait SquashArg where Self: ToJson + Clone {}
impl SquashArg for bool {}
impl SquashArg for f32 {}

#[allow(dead_code)]
impl<T, A> Command<T, ChangesOpts<A>>
where A: SquashArg, ChangesOpts<A>: Default + ToJson + Clone
{
    pub fn squash<B>(self, arg: B) -> Command<T, ChangesOpts<B>>
        where B: SquashArg, ChangesOpts<B>: Default + ToJson + Clone
        {
            let o = self.1.unwrap_or(Default::default());
            let opts = ChangesOpts {
                squash: arg,
                changefeed_queue_size: o.changefeed_queue_size,
                include_initial: o.include_initial,
                include_states: o.include_states,
                include_offsets: o.include_offsets,
                include_types: o.include_types,
            };
            Command(self.0, Some(opts))
        }

    pub fn changefeed_queue_size(mut self, arg: u64) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.changefeed_queue_size = arg;
        }
        self
    }

    pub fn include_initial(mut self, arg: bool) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.include_initial = arg;
        }
        self
    }

    pub fn include_states(mut self, arg: bool) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.include_states = arg;
        }
        self
    }

    pub fn include_offsets(mut self, arg: bool) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.include_offsets = arg;
        }
        self
    }

    pub fn include_types(mut self, arg: bool) -> Self {
        if let Some(ref mut opts) = self.1 {
            opts.include_types = arg;
        }
        self
    }
}

#[allow(dead_code)]
impl<T> Command<T, GetAllOpts>
{
    pub fn index(mut self, arg: &str) -> Self {
        match self.1 {
            Some(ref mut opts) => {
                opts.index = arg.to_string();
            },
            None => {
                let opts = GetAllOpts {
                    index: arg.to_string(),
                };
                self.1 = Some(opts);
            }
        }
        self
    }
}
<commit_msg>Extract db and table commands<commit_after><|endoftext|>


<commit_before>// Copyright 2013 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.

//! Operations on ASCII strings and characters.

use to_str::{ToStr,ToStrConsume};
use str;
use str::StrSlice;
use cast;
use iterator::{Iterator, IteratorUtil};
use vec::{CopyableVector, ImmutableVector, OwnedVector};
use to_bytes::IterBytes;
use option::{Some, None};

/// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero.
#[deriving(Clone, Eq)]
pub struct Ascii { priv chr: u8 }

impl Ascii {
    /// Converts a ascii character into a `u8`.
    #[inline]
    pub fn to_byte(self) -> u8 {
        self.chr
    }

    /// Converts a ascii character into a `char`.
    #[inline]
    pub fn to_char(self) -> char {
        self.chr as char
    }

    /// Convert to lowercase.
    #[inline]
    pub fn to_lower(self) -> Ascii {
        if self.chr >= 65 && self.chr <= 90 {
            Ascii{chr: self.chr | 0x20 }
        } else {
            self
        }
    }

    /// Convert to uppercase.
    #[inline]
    pub fn to_upper(self) -> Ascii {
        if self.chr >= 97 && self.chr <= 122 {
            Ascii{chr: self.chr & !0x20 }
        } else {
            self
        }
    }

    /// Compares two ascii characters of equality, ignoring case.
    #[inline]
    pub fn eq_ignore_case(self, other: Ascii) -> bool {
        self.to_lower().chr == other.to_lower().chr
    }
}

impl ToStr for Ascii {
    #[inline]
    fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
}

/// Trait for converting into an ascii type.
pub trait AsciiCast<T> {
    /// Convert to an ascii type
    fn to_ascii(&self) -> T;

    /// Convert to an ascii type, not doing any range asserts
    unsafe fn to_ascii_nocheck(&self) -> T;

    /// Check if convertible to ascii
    fn is_ascii(&self) -> bool;
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
    #[inline]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
        cast::transmute(*self)
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        foreach b in self.iter() {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
    #[inline]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
        let (p,len): (*u8, uint) = cast::transmute(*self);
        cast::transmute((p, len - 1))
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        self.byte_iter().all(|b| b.is_ascii())
    }
}

impl AsciiCast<Ascii> for u8 {
    #[inline]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self }
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        *self & 128 == 0u8
    }
}

impl AsciiCast<Ascii> for char {
    #[inline]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self as u8 }
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        *self - ('\x7F' & *self) == '\x00'
    }
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast {
    /// Take ownership and cast to an ascii vector without trailing zero element.
    fn into_ascii(self) -> ~[Ascii];

    /// Take ownership and cast to an ascii vector without trailing zero element.
    /// Does not perform validation checks.
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii];
}

impl OwnedAsciiCast for ~[u8] {
    #[inline]
    fn into_ascii(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.into_ascii_nocheck()}
    }

    #[inline]
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
        cast::transmute(self)
    }
}

impl OwnedAsciiCast for ~str {
    #[inline]
    fn into_ascii(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.into_ascii_nocheck()}
    }

    #[inline]
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
        let mut r: ~[Ascii] = cast::transmute(self);
        r.pop();
        r
    }
}

/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
pub trait AsciiStr {
    /// Convert to a string.
    fn to_str_ascii(&self) -> ~str;

    /// Convert to vector representing a lower cased ascii string.
    fn to_lower(&self) -> ~[Ascii];

    /// Convert to vector representing a upper cased ascii string.
    fn to_upper(&self) -> ~[Ascii];

    /// Compares two Ascii strings ignoring case
    fn eq_ignore_case(self, other: &[Ascii]) -> bool;
}

impl<'self> AsciiStr for &'self [Ascii] {
    #[inline]
    fn to_str_ascii(&self) -> ~str {
        let mut cpy = self.to_owned();
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }

    #[inline]
    fn to_lower(&self) -> ~[Ascii] {
        self.map(|a| a.to_lower())
    }

    #[inline]
    fn to_upper(&self) -> ~[Ascii] {
        self.map(|a| a.to_upper())
    }

    #[inline]
    fn eq_ignore_case(self, other: &[Ascii]) -> bool {
        do self.iter().zip(other.iter()).all |(&a, &b)| { a.eq_ignore_case(b) }
    }
}

impl ToStrConsume for ~[Ascii] {
    #[inline]
    fn into_str(self) -> ~str {
        let mut cpy = self;
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }
}

impl IterBytes for Ascii {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool {
        f([self.to_byte()])
    }
}

/// Trait to convert to a owned byte array by consuming self
pub trait ToBytesConsume {
    /// Converts to a owned byte array by consuming self
    fn into_bytes(self) -> ~[u8];
}

impl ToBytesConsume for ~[Ascii] {
    fn into_bytes(self) -> ~[u8] {
        unsafe {cast::transmute(self)}
    }
}

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

    macro_rules! v2ascii (
        ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
        (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
    )

    #[test]
    fn test_ascii() {
        assert_eq!(65u8.to_ascii().to_byte(), 65u8);
        assert_eq!(65u8.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_byte(), 65u8);

        assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
        assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
        assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
        assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');

        assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
        assert_eq!('['.to_ascii().to_lower().to_char(), '[');
        assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
        assert_eq!('{'.to_ascii().to_upper().to_char(), '{');

        assert!("banana".iter().all(|c| c.is_ascii()));
        assert!(!"ประเทศไทย中华Việt Nam".iter().all(|c| c.is_ascii()));
    }

    #[test]
    fn test_ascii_vec() {
        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
        assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
        // FIXME: #5475 borrowchk error, owned vectors do not live long enough
        // if chained-from directly
        let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
        let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));

        assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
        assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");

        assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~"");
        assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca");
        assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;");

        assert!("aBcDeF&?#".to_ascii().eq_ignore_case("AbCdEf&?#".to_ascii()));

        assert!("".is_ascii());
        assert!("a".is_ascii());
        assert!(!"\u2009".is_ascii());

    }

    #[test]
    fn test_owned_ascii_vec() {
        assert_eq!((~"( ;").into_ascii(), v2ascii!(~[40, 32, 59]));
        assert_eq!((~[40u8, 32u8, 59u8]).into_ascii(), v2ascii!(~[40, 32, 59]));
    }

    #[test]
    fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }

    #[test]
    fn test_ascii_into_str() {
        assert_eq!(v2ascii!(~[40, 32, 59]).into_str(), ~"( ;");
    }

    #[test]
    fn test_ascii_to_bytes() {
        assert_eq!(v2ascii!(~[40, 32, 59]).to_bytes(false), ~[40u8, 32u8, 59u8]);
        assert_eq!(v2ascii!(~[40, 32, 59]).into_bytes(), ~[40u8, 32u8, 59u8]);
    }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
}
<commit_msg>remove obsolete `foreach` keyword<commit_after>// Copyright 2013 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.

//! Operations on ASCII strings and characters.

use to_str::{ToStr,ToStrConsume};
use str;
use str::StrSlice;
use cast;
use iterator::{Iterator, IteratorUtil};
use vec::{CopyableVector, ImmutableVector, OwnedVector};
use to_bytes::IterBytes;
use option::{Some, None};

/// Datatype to hold one ascii character. It wraps a `u8`, with the highest bit always zero.
#[deriving(Clone, Eq)]
pub struct Ascii { priv chr: u8 }

impl Ascii {
    /// Converts a ascii character into a `u8`.
    #[inline]
    pub fn to_byte(self) -> u8 {
        self.chr
    }

    /// Converts a ascii character into a `char`.
    #[inline]
    pub fn to_char(self) -> char {
        self.chr as char
    }

    /// Convert to lowercase.
    #[inline]
    pub fn to_lower(self) -> Ascii {
        if self.chr >= 65 && self.chr <= 90 {
            Ascii{chr: self.chr | 0x20 }
        } else {
            self
        }
    }

    /// Convert to uppercase.
    #[inline]
    pub fn to_upper(self) -> Ascii {
        if self.chr >= 97 && self.chr <= 122 {
            Ascii{chr: self.chr & !0x20 }
        } else {
            self
        }
    }

    /// Compares two ascii characters of equality, ignoring case.
    #[inline]
    pub fn eq_ignore_case(self, other: Ascii) -> bool {
        self.to_lower().chr == other.to_lower().chr
    }
}

impl ToStr for Ascii {
    #[inline]
    fn to_str(&self) -> ~str { str::from_bytes(['\'' as u8, self.chr, '\'' as u8]) }
}

/// Trait for converting into an ascii type.
pub trait AsciiCast<T> {
    /// Convert to an ascii type
    fn to_ascii(&self) -> T;

    /// Convert to an ascii type, not doing any range asserts
    unsafe fn to_ascii_nocheck(&self) -> T;

    /// Check if convertible to ascii
    fn is_ascii(&self) -> bool;
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self [u8] {
    #[inline]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
        cast::transmute(*self)
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        for b in self.iter() {
            if !b.is_ascii() { return false; }
        }
        true
    }
}

impl<'self> AsciiCast<&'self[Ascii]> for &'self str {
    #[inline]
    fn to_ascii(&self) -> &'self[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> &'self[Ascii] {
        let (p,len): (*u8, uint) = cast::transmute(*self);
        cast::transmute((p, len - 1))
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        self.byte_iter().all(|b| b.is_ascii())
    }
}

impl AsciiCast<Ascii> for u8 {
    #[inline]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self }
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        *self & 128 == 0u8
    }
}

impl AsciiCast<Ascii> for char {
    #[inline]
    fn to_ascii(&self) -> Ascii {
        assert!(self.is_ascii());
        unsafe {self.to_ascii_nocheck()}
    }

    #[inline]
    unsafe fn to_ascii_nocheck(&self) -> Ascii {
        Ascii{ chr: *self as u8 }
    }

    #[inline]
    fn is_ascii(&self) -> bool {
        *self - ('\x7F' & *self) == '\x00'
    }
}

/// Trait for copyless casting to an ascii vector.
pub trait OwnedAsciiCast {
    /// Take ownership and cast to an ascii vector without trailing zero element.
    fn into_ascii(self) -> ~[Ascii];

    /// Take ownership and cast to an ascii vector without trailing zero element.
    /// Does not perform validation checks.
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii];
}

impl OwnedAsciiCast for ~[u8] {
    #[inline]
    fn into_ascii(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.into_ascii_nocheck()}
    }

    #[inline]
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
        cast::transmute(self)
    }
}

impl OwnedAsciiCast for ~str {
    #[inline]
    fn into_ascii(self) -> ~[Ascii] {
        assert!(self.is_ascii());
        unsafe {self.into_ascii_nocheck()}
    }

    #[inline]
    unsafe fn into_ascii_nocheck(self) -> ~[Ascii] {
        let mut r: ~[Ascii] = cast::transmute(self);
        r.pop();
        r
    }
}

/// Trait for converting an ascii type to a string. Needed to convert `&[Ascii]` to `~str`
pub trait AsciiStr {
    /// Convert to a string.
    fn to_str_ascii(&self) -> ~str;

    /// Convert to vector representing a lower cased ascii string.
    fn to_lower(&self) -> ~[Ascii];

    /// Convert to vector representing a upper cased ascii string.
    fn to_upper(&self) -> ~[Ascii];

    /// Compares two Ascii strings ignoring case
    fn eq_ignore_case(self, other: &[Ascii]) -> bool;
}

impl<'self> AsciiStr for &'self [Ascii] {
    #[inline]
    fn to_str_ascii(&self) -> ~str {
        let mut cpy = self.to_owned();
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }

    #[inline]
    fn to_lower(&self) -> ~[Ascii] {
        self.map(|a| a.to_lower())
    }

    #[inline]
    fn to_upper(&self) -> ~[Ascii] {
        self.map(|a| a.to_upper())
    }

    #[inline]
    fn eq_ignore_case(self, other: &[Ascii]) -> bool {
        do self.iter().zip(other.iter()).all |(&a, &b)| { a.eq_ignore_case(b) }
    }
}

impl ToStrConsume for ~[Ascii] {
    #[inline]
    fn into_str(self) -> ~str {
        let mut cpy = self;
        cpy.push(0u8.to_ascii());
        unsafe {cast::transmute(cpy)}
    }
}

impl IterBytes for Ascii {
    #[inline]
    fn iter_bytes(&self, _lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool {
        f([self.to_byte()])
    }
}

/// Trait to convert to a owned byte array by consuming self
pub trait ToBytesConsume {
    /// Converts to a owned byte array by consuming self
    fn into_bytes(self) -> ~[u8];
}

impl ToBytesConsume for ~[Ascii] {
    fn into_bytes(self) -> ~[u8] {
        unsafe {cast::transmute(self)}
    }
}

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

    macro_rules! v2ascii (
        ( [$($e:expr),*]) => ( [$(Ascii{chr:$e}),*]);
        (~[$($e:expr),*]) => (~[$(Ascii{chr:$e}),*]);
    )

    #[test]
    fn test_ascii() {
        assert_eq!(65u8.to_ascii().to_byte(), 65u8);
        assert_eq!(65u8.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_char(), 'A');
        assert_eq!('A'.to_ascii().to_byte(), 65u8);

        assert_eq!('A'.to_ascii().to_lower().to_char(), 'a');
        assert_eq!('Z'.to_ascii().to_lower().to_char(), 'z');
        assert_eq!('a'.to_ascii().to_upper().to_char(), 'A');
        assert_eq!('z'.to_ascii().to_upper().to_char(), 'Z');

        assert_eq!('@'.to_ascii().to_lower().to_char(), '@');
        assert_eq!('['.to_ascii().to_lower().to_char(), '[');
        assert_eq!('`'.to_ascii().to_upper().to_char(), '`');
        assert_eq!('{'.to_ascii().to_upper().to_char(), '{');

        assert!("banana".iter().all(|c| c.is_ascii()));
        assert!(!"ประเทศไทย中华Việt Nam".iter().all(|c| c.is_ascii()));
    }

    #[test]
    fn test_ascii_vec() {
        assert_eq!((&[40u8, 32u8, 59u8]).to_ascii(), v2ascii!([40, 32, 59]));
        assert_eq!("( ;".to_ascii(),                 v2ascii!([40, 32, 59]));
        // FIXME: #5475 borrowchk error, owned vectors do not live long enough
        // if chained-from directly
        let v = ~[40u8, 32u8, 59u8]; assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));
        let v = ~"( ;";              assert_eq!(v.to_ascii(), v2ascii!([40, 32, 59]));

        assert_eq!("abCDef&?#".to_ascii().to_lower().to_str_ascii(), ~"abcdef&?#");
        assert_eq!("abCDef&?#".to_ascii().to_upper().to_str_ascii(), ~"ABCDEF&?#");

        assert_eq!("".to_ascii().to_lower().to_str_ascii(), ~"");
        assert_eq!("YMCA".to_ascii().to_lower().to_str_ascii(), ~"ymca");
        assert_eq!("abcDEFxyz:.;".to_ascii().to_upper().to_str_ascii(), ~"ABCDEFXYZ:.;");

        assert!("aBcDeF&?#".to_ascii().eq_ignore_case("AbCdEf&?#".to_ascii()));

        assert!("".is_ascii());
        assert!("a".is_ascii());
        assert!(!"\u2009".is_ascii());

    }

    #[test]
    fn test_owned_ascii_vec() {
        assert_eq!((~"( ;").into_ascii(), v2ascii!(~[40, 32, 59]));
        assert_eq!((~[40u8, 32u8, 59u8]).into_ascii(), v2ascii!(~[40, 32, 59]));
    }

    #[test]
    fn test_ascii_to_str() { assert_eq!(v2ascii!([40, 32, 59]).to_str_ascii(), ~"( ;"); }

    #[test]
    fn test_ascii_into_str() {
        assert_eq!(v2ascii!(~[40, 32, 59]).into_str(), ~"( ;");
    }

    #[test]
    fn test_ascii_to_bytes() {
        assert_eq!(v2ascii!(~[40, 32, 59]).to_bytes(false), ~[40u8, 32u8, 59u8]);
        assert_eq!(v2ascii!(~[40, 32, 59]).into_bytes(), ~[40u8, 32u8, 59u8]);
    }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_u8_slice()  { (&[127u8, 128u8, 255u8]).to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_vec_fail_str_slice() { "zoä华".to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_u8_slice() { 255u8.to_ascii(); }

    #[test] #[should_fail]
    fn test_ascii_fail_char_slice() { 'λ'.to_ascii(); }
}
<|endoftext|>


<commit_before>//! Compiles JMESPath expressions.

extern crate rustc_serialize;

use std::io::Cursor;
use std::collections::BTreeMap;
use self::rustc_serialize::json::Json;

use ast::{Ast, Comparator, KeyValuePair};
use vm::Opcode;

pub fn compile_opcodes(ast: &Ast) -> Vec<Opcode> {
    let mut opcodes = compile_with_offset(&ast, 0);
    opcodes.push(Opcode::Halt);
    opcodes
}

fn compile_with_offset(ast: &Ast, offset: usize) -> Vec<Opcode> {
    let mut opcodes: Vec<Opcode> = Vec::new();
    match *ast {
        Ast::CurrentNode => opcodes.push(Opcode::Load(0)),
        Ast::Identifier(ref j) => opcodes.push(Opcode::Field(j.clone())),
        Ast::Index(i) => {
            if i < 0 {
                opcodes.push(Opcode::NegativeIndex((i.abs() - 1) as usize));
            } else {
                opcodes.push(Opcode::Index(i as usize));
            }
        },
        Ast::Or(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes.push(Opcode::Truthy);
            let next_offset = opcodes.len() + 1;
            let right = compile_with_offset(&*rhs, next_offset);
            opcodes.push(Opcode::Brt(next_offset + right.len()));
            opcodes = merge_opcodes(opcodes, right);
        },
        Ast::Subexpr(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*rhs, offset));
        },
        Ast::Comparison(ref cmp, ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*rhs, offset));
            opcodes.push(match cmp {
                &Comparator::Lt => Opcode::Lt,
                &Comparator::Lte => Opcode::Lte,
                &Comparator::Gt => Opcode::Gt,
                &Comparator::Gte => Opcode::Gte,
                &Comparator::Eq => Opcode::Eq,
                &Comparator::Ne => Opcode::Ne,
            });
        },
        Ast::Condition(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes.push(Opcode::Push(Json::Boolean(true)));
            opcodes.push(Opcode::Eq);
            let next_offset = opcodes.len() + 1;
            let right = compile_with_offset(&*rhs, next_offset);
            opcodes.push(Opcode::Brf(next_offset + right.len() + 1));
            opcodes = merge_opcodes(opcodes, right);
            let next_offset = opcodes.len() + 2;
            opcodes.push(Opcode::Br(next_offset));
            opcodes.push(Opcode::Push(Json::Null));
        },
        Ast::Literal(ref json) => opcodes.push(Opcode::Push(json.clone())),
        _ => panic!("not implemented yet!")
    };
    opcodes
}

fn merge_opcodes(mut a: Vec<Opcode>, b: Vec<Opcode>) -> Vec<Opcode> {
    for opcode in b {
        a.push(opcode);
    }
    a
}

#[cfg(test)]
mod test {
    extern crate rustc_serialize;
    use self::rustc_serialize::json::Json;
    use super::*;
    use ast::{Ast, Comparator};
    use vm::Opcode;

    #[test] fn assembles_identifiers() {
        let ast = Ast::Identifier("foo".to_owned());
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Field("foo".to_owned()), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_positive_index() {
        let ast = Ast::Index(1);
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Index(1), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_negative_index() {
        let ast = Ast::Index(-2);
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::NegativeIndex(1), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_or_expression() {
        let ast = Ast::Or(
            Box::new(Ast::Identifier("foo".to_owned())),
            Box::new(Ast::Identifier("bar".to_owned())));
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Field("foo".to_owned()),
                        Opcode::Truthy,
                        Opcode::Brt(4),
                        Opcode::Field("bar".to_owned()),
                        Opcode::Halt],
                   opcodes);
    }

    #[test] fn assembles_current_node() {
        let ast = Ast::CurrentNode;
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Load(0), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_eq_comparison() {
        let tests = vec![(Comparator::Lt, Opcode::Lt),
                         (Comparator::Lte, Opcode::Lte),
                         (Comparator::Gt, Opcode::Gt),
                         (Comparator::Gte, Opcode::Gte),
                         (Comparator::Eq, Opcode::Eq),
                         (Comparator::Ne, Opcode::Ne)];
        for (cmp, op) in tests {
            let ast = Ast::Comparison(
                cmp,
                Box::new(Ast::Identifier("foo".to_string())),
                Box::new(Ast::Identifier("bar".to_string())));
            let opcodes = compile_opcodes(&ast);
            assert_eq!(vec![Opcode::Field("foo".to_string()),
                            Opcode::Field("bar".to_string()),
                            op,
                            Opcode::Halt], opcodes);
        }
    }

    #[test] fn assembles_conditions() {
        let ast = Ast::Condition(
            Box::new(Ast::Literal(Json::Boolean(true))),
            Box::new(Ast::Identifier("bar".to_owned())));
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Push(Json::Boolean(true)),
                        Opcode::Push(Json::Boolean(true)),
                        Opcode::Eq,
                        Opcode::Brf(6),
                        Opcode::Field("bar".to_owned()),
                        Opcode::Br(7),
                        Opcode::Push(Json::Null),
                        Opcode::Halt],
                   opcodes);
    }
}
<commit_msg>Starting a constant pool<commit_after>//! Compiles JMESPath expressions.

extern crate rustc_serialize;

use std::ops::Index;
use std::io::Cursor;
use std::collections::BTreeMap;
use self::rustc_serialize::json::Json;

use ast::{Ast, Comparator, KeyValuePair};
use vm::Opcode;

#[derive(Clone, PartialEq, Debug)]
pub struct ConstantPool<'a> {
    constants: Vec<&'a Json>
}

impl<'a> ConstantPool<'a> {
    pub fn new() -> ConstantPool<'a> {
        ConstantPool {
            constants: vec!()
        }
    }

    /// Get a constant from the pool and return the index.
    ///
    /// This will insert a new constant if it is not in the pool.
    pub fn get(&mut self, json: &'a Json) -> usize {
        for (k, v) in self.constants.iter().enumerate() {
            if *v == json {
                return k;
            }
        }
        self.constants.push(json);
        self.constants.len() - 1
    }
}

/// Retrieve a constant from the pool by index
impl<'c> Index<usize> for ConstantPool<'c> {
    type Output = Json;

    fn index<'d>(&'d self, index: usize) -> &'d Json {
        self.constants[index]
    }
}

pub fn compile_opcodes(ast: &Ast) -> Vec<Opcode> {
    let mut opcodes = compile_with_offset(&ast, 0);
    opcodes.push(Opcode::Halt);
    opcodes
}

fn compile_with_offset(ast: &Ast, offset: usize) -> Vec<Opcode> {
    let mut opcodes: Vec<Opcode> = Vec::new();
    match *ast {
        Ast::CurrentNode => opcodes.push(Opcode::Load(0)),
        Ast::Identifier(ref j) => opcodes.push(Opcode::Field(j.clone())),
        Ast::Index(i) => {
            if i < 0 {
                opcodes.push(Opcode::NegativeIndex((i.abs() - 1) as usize));
            } else {
                opcodes.push(Opcode::Index(i as usize));
            }
        },
        Ast::Or(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes.push(Opcode::Truthy);
            let next_offset = opcodes.len() + 1;
            let right = compile_with_offset(&*rhs, next_offset);
            opcodes.push(Opcode::Brt(next_offset + right.len()));
            opcodes = merge_opcodes(opcodes, right);
        },
        Ast::Subexpr(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*rhs, offset));
        },
        Ast::Comparison(ref cmp, ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*rhs, offset));
            opcodes.push(match cmp {
                &Comparator::Lt => Opcode::Lt,
                &Comparator::Lte => Opcode::Lte,
                &Comparator::Gt => Opcode::Gt,
                &Comparator::Gte => Opcode::Gte,
                &Comparator::Eq => Opcode::Eq,
                &Comparator::Ne => Opcode::Ne,
            });
        },
        Ast::Condition(ref lhs, ref rhs) => {
            opcodes = merge_opcodes(opcodes, compile_with_offset(&*lhs, offset));
            opcodes.push(Opcode::Push(Json::Boolean(true)));
            opcodes.push(Opcode::Eq);
            let next_offset = opcodes.len() + 1;
            let right = compile_with_offset(&*rhs, next_offset);
            opcodes.push(Opcode::Brf(next_offset + right.len() + 1));
            opcodes = merge_opcodes(opcodes, right);
            let next_offset = opcodes.len() + 2;
            opcodes.push(Opcode::Br(next_offset));
            opcodes.push(Opcode::Push(Json::Null));
        },
        Ast::Literal(ref json) => opcodes.push(Opcode::Push(json.clone())),
        _ => panic!("not implemented yet!")
    };
    opcodes
}

fn merge_opcodes(mut a: Vec<Opcode>, b: Vec<Opcode>) -> Vec<Opcode> {
    for opcode in b {
        a.push(opcode);
    }
    a
}

#[cfg(test)]
mod test {
    extern crate rustc_serialize;
    use self::rustc_serialize::json::Json;
    use super::*;
    use ast::{Ast, Comparator};
    use vm::Opcode;

    #[test] fn add_to_constant_pool() {
        let b1 = Json::Boolean(true);
        let b2 = Json::Boolean(false);
        let mut pool = ConstantPool::new();
        assert_eq!(0, pool.get(&b1));
        assert_eq!(0, pool.get(&b1));
        assert_eq!(1, pool.get(&b2));
        assert_eq!(1, pool.get(&b2));
        assert_eq!(b1, pool[0]);
    }

    #[test] fn assembles_identifiers() {
        let ast = Ast::Identifier("foo".to_owned());
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Field("foo".to_owned()), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_positive_index() {
        let ast = Ast::Index(1);
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Index(1), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_negative_index() {
        let ast = Ast::Index(-2);
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::NegativeIndex(1), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_or_expression() {
        let ast = Ast::Or(
            Box::new(Ast::Identifier("foo".to_owned())),
            Box::new(Ast::Identifier("bar".to_owned())));
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Field("foo".to_owned()),
                        Opcode::Truthy,
                        Opcode::Brt(4),
                        Opcode::Field("bar".to_owned()),
                        Opcode::Halt],
                   opcodes);
    }

    #[test] fn assembles_current_node() {
        let ast = Ast::CurrentNode;
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Load(0), Opcode::Halt], opcodes);
    }

    #[test] fn assembles_eq_comparison() {
        let tests = vec![(Comparator::Lt, Opcode::Lt),
                         (Comparator::Lte, Opcode::Lte),
                         (Comparator::Gt, Opcode::Gt),
                         (Comparator::Gte, Opcode::Gte),
                         (Comparator::Eq, Opcode::Eq),
                         (Comparator::Ne, Opcode::Ne)];
        for (cmp, op) in tests {
            let ast = Ast::Comparison(
                cmp,
                Box::new(Ast::Identifier("foo".to_string())),
                Box::new(Ast::Identifier("bar".to_string())));
            let opcodes = compile_opcodes(&ast);
            assert_eq!(vec![Opcode::Field("foo".to_string()),
                            Opcode::Field("bar".to_string()),
                            op,
                            Opcode::Halt], opcodes);
        }
    }

    #[test] fn assembles_conditions() {
        let ast = Ast::Condition(
            Box::new(Ast::Literal(Json::Boolean(true))),
            Box::new(Ast::Identifier("bar".to_owned())));
        let opcodes = compile_opcodes(&ast);
        assert_eq!(vec![Opcode::Push(Json::Boolean(true)),
                        Opcode::Push(Json::Boolean(true)),
                        Opcode::Eq,
                        Opcode::Brf(6),
                        Opcode::Field("bar".to_owned()),
                        Opcode::Br(7),
                        Opcode::Push(Json::Null),
                        Opcode::Halt],
                   opcodes);
    }
}
<|endoftext|>