<commit_before>use std::marker::PhantomData;

use ra_db::{LocationIntener, FileId};
use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, ast};
use ra_arena::{Arena, RawId, impl_arena_id};

use crate::{
    HirDatabase, Def, Crate,
    Module, Trait, Type, Static, Const,
};

#[derive(Debug, Default)]
pub struct HirInterner {
    defs: LocationIntener<DefLoc, DefId>,
    macros: LocationIntener<MacroCallLoc, MacroCallId>,
    pub(crate) fns: LocationIntener<ItemLoc<ast::FnDef>, FunctionId>,
    pub(crate) structs: LocationIntener<ItemLoc<ast::StructDef>, StructId>,
    pub(crate) enums: LocationIntener<ItemLoc<ast::EnumDef>, EnumId>,
    pub(crate) enum_variants: LocationIntener<ItemLoc<ast::EnumVariant>, EnumVariantId>,
}

impl HirInterner {
    pub fn len(&self) -> usize {
        self.defs.len() + self.macros.len()
    }
}

/// hir makes heavy use of ids: integer (u32) handlers to various things. You
/// can think of id as a pointer (but without a lifetime) or a file descriptor
/// (but for hir objects).
///
/// This module defines a bunch of ids we are using. The most important ones are
/// probably `HirFileId` and `DefId`.

/// Input to the analyzer is a set of files, where each file is indentified by
/// `FileId` and contains source code. However, another source of source code in
/// Rust are macros: each macro can be thought of as producing a "temporary
/// file". To assign an id to such a file, we use the id of the macro call that
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
/// written by user), or a `MacroCallId` (source code produced by macro).
///
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
/// the call plus the offset of the macro call in the file. Note that this is a
/// recursive definition! However, the size_of of `HirFileId` is finite
/// (because everything bottoms out at the real `FileId`) and small
/// (`MacroCallId` uses the location interner).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HirFileId(HirFileIdRepr);

impl HirFileId {
    /// For macro-expansion files, returns the file original source file the
    /// expansionoriginated from.
    pub fn original_file(self, db: &impl HirDatabase) -> FileId {
        match self.0 {
            HirFileIdRepr::File(file_id) => file_id,
            HirFileIdRepr::Macro(macro_call_id) => {
                let loc = macro_call_id.loc(db);
                loc.source_item_id.file_id.original_file(db)
            }
        }
    }

    pub(crate) fn as_original_file(self) -> FileId {
        match self.0 {
            HirFileIdRepr::File(file_id) => file_id,
            HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
        }
    }

    pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> {
        match self.0 {
            HirFileIdRepr::Macro(it) => Some(it),
            _ => None,
        }
    }

    pub(crate) fn hir_source_file(
        db: &impl HirDatabase,
        file_id: HirFileId,
    ) -> TreeArc<SourceFile> {
        match file_id.0 {
            HirFileIdRepr::File(file_id) => db.source_file(file_id),
            HirFileIdRepr::Macro(m) => {
                if let Some(exp) = db.expand_macro_invocation(m) {
                    return exp.file();
                }
                // returning an empty string looks fishy...
                SourceFile::parse("")
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HirFileIdRepr {
    File(FileId),
    Macro(MacroCallId),
}

impl From<FileId> for HirFileId {
    fn from(file_id: FileId) -> HirFileId {
        HirFileId(HirFileIdRepr::File(file_id))
    }
}

impl From<MacroCallId> for HirFileId {
    fn from(macro_call_id: MacroCallId) -> HirFileId {
        HirFileId(HirFileIdRepr::Macro(macro_call_id))
    }
}

/// `MacroCallId` identifies a particular macro invocation, like
/// `println!("Hello, {}", world)`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroCallId(RawId);
impl_arena_id!(MacroCallId);

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MacroCallLoc {
    pub(crate) module: Module,
    pub(crate) source_item_id: SourceItemId,
}

impl MacroCallId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> MacroCallLoc {
        db.as_ref().macros.id2loc(self)
    }
}

impl MacroCallLoc {
    #[allow(unused)]
    pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId {
        db.as_ref().macros.loc2id(&self)
    }
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ItemLoc<N: AstNode> {
    pub(crate) module: Module,
    raw: SourceItemId,
    _ty: PhantomData<N>,
}

impl<N: AstNode> ItemLoc<N> {
    pub(crate) fn from_ast(
        db: &impl HirDatabase,
        module: Module,
        file_id: HirFileId,
        ast: &N,
    ) -> ItemLoc<N> {
        let items = db.file_items(file_id);
        let raw = SourceItemId {
            file_id,
            item_id: Some(items.id_of(file_id, ast.syntax())),
        };
        ItemLoc {
            module,
            raw,
            _ty: PhantomData,
        }
    }

    pub(crate) fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<N>) {
        let syntax = db.file_item(self.raw);
        let ast = N::cast(&syntax)
            .unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", self.raw))
            .to_owned();
        (self.raw.file_id, ast)
    }
}

impl<N: AstNode> Clone for ItemLoc<N> {
    fn clone(&self) -> ItemLoc<N> {
        ItemLoc {
            module: self.module,
            raw: self.raw,
            _ty: PhantomData,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FunctionId(RawId);
impl_arena_id!(FunctionId);

impl FunctionId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::FnDef> {
        db.as_ref().fns.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructId(RawId);
impl_arena_id!(StructId);

impl StructId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::StructDef> {
        db.as_ref().structs.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumId(RawId);
impl_arena_id!(EnumId);

impl EnumId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::EnumDef> {
        db.as_ref().enums.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumVariantId(RawId);
impl_arena_id!(EnumVariantId);

impl EnumVariantId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::EnumVariant> {
        db.as_ref().enum_variants.id2loc(self)
    }
}

/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
/// in a specific module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(RawId);
impl_arena_id!(DefId);

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DefLoc {
    pub(crate) kind: DefKind,
    pub(crate) module: Module,
    pub(crate) source_item_id: SourceItemId,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum DefKind {
    Const,
    Static,
    Trait,
    Type,
    Item,
    // /// The constructor of a struct. E.g. if we have `struct Foo(usize)`, the
    // /// name `Foo` needs to resolve to different types depending on whether we
    // /// are in the types or values namespace: As a type, `Foo` of course refers
    // /// to the struct `Foo`; as a value, `Foo` is a callable type with signature
    // /// `(usize) -> Foo`. The cleanest approach to handle this seems to be to
    // /// have different defs in the two namespaces.
    // ///
    // /// rustc does the same; note that it even creates a struct constructor if
    // /// the struct isn't a tuple struct (see `CtorKind::Fictive` in rustc).
    // StructCtor,
}

impl DefId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> DefLoc {
        db.as_ref().defs.id2loc(self)
    }

    pub fn resolve(self, db: &impl HirDatabase) -> Def {
        let loc = self.loc(db);
        match loc.kind {
            DefKind::Const => {
                let def = Const::new(self);
                Def::Const(def)
            }
            DefKind::Static => {
                let def = Static::new(self);
                Def::Static(def)
            }
            DefKind::Trait => {
                let def = Trait::new(self);
                Def::Trait(def)
            }
            DefKind::Type => {
                let def = Type::new(self);
                Def::Type(def)
            }
            DefKind::Item => Def::Item,
        }
    }

    pub(crate) fn source(self, db: &impl HirDatabase) -> (HirFileId, TreeArc<SyntaxNode>) {
        let loc = self.loc(db);
        let syntax = db.file_item(loc.source_item_id);
        (loc.source_item_id.file_id, syntax)
    }

    /// For a module, returns that module; for any other def, returns the containing module.
    pub fn module(self, db: &impl HirDatabase) -> Module {
        self.loc(db).module
    }

    /// Returns the containing crate.
    pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
        self.module(db).krate(db)
    }
}

impl DefLoc {
    pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> DefId {
        db.as_ref().defs.loc2id(&self)
    }
}

/// Identifier of item within a specific file. This is stable over reparses, so
/// it's OK to use it as a salsa key/value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SourceFileItemId(RawId);
impl_arena_id!(SourceFileItemId);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceItemId {
    pub(crate) file_id: HirFileId,
    /// None for the whole file.
    pub(crate) item_id: Option<SourceFileItemId>,
}

/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back.
#[derive(Debug, PartialEq, Eq)]
pub struct SourceFileItems {
    file_id: HirFileId,
    arena: Arena<SourceFileItemId, TreeArc<SyntaxNode>>,
}

impl SourceFileItems {
    pub(crate) fn new(file_id: HirFileId, source_file: &SourceFile) -> SourceFileItems {
        let mut res = SourceFileItems {
            file_id,
            arena: Arena::default(),
        };
        res.init(source_file);
        res
    }

    fn init(&mut self, source_file: &SourceFile) {
        // By walking the tree in bread-first order we make sure that parents
        // get lower ids then children. That is, addding a new child does not
        // change parent's id. This means that, say, adding a new function to a
        // trait does not chage ids of top-level items, which helps caching.
        bfs(source_file.syntax(), |it| {
            if let Some(enum_variant) = ast::EnumVariant::cast(it) {
                self.alloc(enum_variant.syntax().to_owned());
            } else if let Some(module_item) = ast::ModuleItem::cast(it) {
                self.alloc(module_item.syntax().to_owned());
            } else if let Some(macro_call) = ast::MacroCall::cast(it) {
                self.alloc(macro_call.syntax().to_owned());
            }
        })
    }

    fn alloc(&mut self, item: TreeArc<SyntaxNode>) -> SourceFileItemId {
        self.arena.alloc(item)
    }
    pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId {
        assert_eq!(
            self.file_id, file_id,
            "SourceFileItems: wrong file, expected {:?}, got {:?}",
            self.file_id, file_id
        );
        self.id_of_unchecked(item)
    }
    pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId {
        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| *i == item) {
            return id;
        }
        // This should not happen. Let's try to give a sensible diagnostics.
        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
            // FIXME(#288): whyyy are we getting here?
            log::error!(
                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
                item,
                i
            );
            return id;
        }
        panic!(
            "Can't find {:?} in SourceFileItems:\n{:?}",
            item,
            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
        );
    }
    pub fn id_of_source_file(&self) -> SourceFileItemId {
        let (id, _syntax) = self.arena.iter().next().unwrap();
        id
    }
}

impl std::ops::Index<SourceFileItemId> for SourceFileItems {
    type Output = SyntaxNode;
    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
        &self.arena[idx]
    }
}

/// Walks the subtree in bfs order, calling `f` for each node.
fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) {
    let mut curr_layer = vec![node];
    let mut next_layer = vec![];
    while !curr_layer.is_empty() {
        curr_layer.drain(..).for_each(|node| {
            next_layer.extend(node.children());
            f(node);
        });
        std::mem::swap(&mut curr_layer, &mut next_layer);
    }
}
<commit_msg>remove dead code<commit_after>use std::marker::PhantomData;

use ra_db::{LocationIntener, FileId};
use ra_syntax::{TreeArc, SyntaxNode, SourceFile, AstNode, ast};
use ra_arena::{Arena, RawId, impl_arena_id};

use crate::{
    HirDatabase, Def,
    Module, Trait, Type, Static, Const,
};

#[derive(Debug, Default)]
pub struct HirInterner {
    defs: LocationIntener<DefLoc, DefId>,
    macros: LocationIntener<MacroCallLoc, MacroCallId>,
    pub(crate) fns: LocationIntener<ItemLoc<ast::FnDef>, FunctionId>,
    pub(crate) structs: LocationIntener<ItemLoc<ast::StructDef>, StructId>,
    pub(crate) enums: LocationIntener<ItemLoc<ast::EnumDef>, EnumId>,
    pub(crate) enum_variants: LocationIntener<ItemLoc<ast::EnumVariant>, EnumVariantId>,
}

impl HirInterner {
    pub fn len(&self) -> usize {
        self.defs.len() + self.macros.len()
    }
}

/// hir makes heavy use of ids: integer (u32) handlers to various things. You
/// can think of id as a pointer (but without a lifetime) or a file descriptor
/// (but for hir objects).
///
/// This module defines a bunch of ids we are using. The most important ones are
/// probably `HirFileId` and `DefId`.

/// Input to the analyzer is a set of files, where each file is indentified by
/// `FileId` and contains source code. However, another source of source code in
/// Rust are macros: each macro can be thought of as producing a "temporary
/// file". To assign an id to such a file, we use the id of the macro call that
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
/// written by user), or a `MacroCallId` (source code produced by macro).
///
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file containin
/// the call plus the offset of the macro call in the file. Note that this is a
/// recursive definition! However, the size_of of `HirFileId` is finite
/// (because everything bottoms out at the real `FileId`) and small
/// (`MacroCallId` uses the location interner).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct HirFileId(HirFileIdRepr);

impl HirFileId {
    /// For macro-expansion files, returns the file original source file the
    /// expansionoriginated from.
    pub fn original_file(self, db: &impl HirDatabase) -> FileId {
        match self.0 {
            HirFileIdRepr::File(file_id) => file_id,
            HirFileIdRepr::Macro(macro_call_id) => {
                let loc = macro_call_id.loc(db);
                loc.source_item_id.file_id.original_file(db)
            }
        }
    }

    pub(crate) fn as_original_file(self) -> FileId {
        match self.0 {
            HirFileIdRepr::File(file_id) => file_id,
            HirFileIdRepr::Macro(_r) => panic!("macro generated file: {:?}", self),
        }
    }

    pub(crate) fn as_macro_call_id(self) -> Option<MacroCallId> {
        match self.0 {
            HirFileIdRepr::Macro(it) => Some(it),
            _ => None,
        }
    }

    pub(crate) fn hir_source_file(
        db: &impl HirDatabase,
        file_id: HirFileId,
    ) -> TreeArc<SourceFile> {
        match file_id.0 {
            HirFileIdRepr::File(file_id) => db.source_file(file_id),
            HirFileIdRepr::Macro(m) => {
                if let Some(exp) = db.expand_macro_invocation(m) {
                    return exp.file();
                }
                // returning an empty string looks fishy...
                SourceFile::parse("")
            }
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
enum HirFileIdRepr {
    File(FileId),
    Macro(MacroCallId),
}

impl From<FileId> for HirFileId {
    fn from(file_id: FileId) -> HirFileId {
        HirFileId(HirFileIdRepr::File(file_id))
    }
}

impl From<MacroCallId> for HirFileId {
    fn from(macro_call_id: MacroCallId) -> HirFileId {
        HirFileId(HirFileIdRepr::Macro(macro_call_id))
    }
}

/// `MacroCallId` identifies a particular macro invocation, like
/// `println!("Hello, {}", world)`.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct MacroCallId(RawId);
impl_arena_id!(MacroCallId);

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct MacroCallLoc {
    pub(crate) module: Module,
    pub(crate) source_item_id: SourceItemId,
}

impl MacroCallId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> MacroCallLoc {
        db.as_ref().macros.id2loc(self)
    }
}

impl MacroCallLoc {
    #[allow(unused)]
    pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> MacroCallId {
        db.as_ref().macros.loc2id(&self)
    }
}

#[derive(Debug, PartialEq, Eq, Hash)]
pub struct ItemLoc<N: AstNode> {
    pub(crate) module: Module,
    raw: SourceItemId,
    _ty: PhantomData<N>,
}

impl<N: AstNode> ItemLoc<N> {
    pub(crate) fn from_ast(
        db: &impl HirDatabase,
        module: Module,
        file_id: HirFileId,
        ast: &N,
    ) -> ItemLoc<N> {
        let items = db.file_items(file_id);
        let raw = SourceItemId {
            file_id,
            item_id: Some(items.id_of(file_id, ast.syntax())),
        };
        ItemLoc {
            module,
            raw,
            _ty: PhantomData,
        }
    }

    pub(crate) fn source(&self, db: &impl HirDatabase) -> (HirFileId, TreeArc<N>) {
        let syntax = db.file_item(self.raw);
        let ast = N::cast(&syntax)
            .unwrap_or_else(|| panic!("invalid ItemLoc: {:?}", self.raw))
            .to_owned();
        (self.raw.file_id, ast)
    }
}

impl<N: AstNode> Clone for ItemLoc<N> {
    fn clone(&self) -> ItemLoc<N> {
        ItemLoc {
            module: self.module,
            raw: self.raw,
            _ty: PhantomData,
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FunctionId(RawId);
impl_arena_id!(FunctionId);

impl FunctionId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::FnDef> {
        db.as_ref().fns.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct StructId(RawId);
impl_arena_id!(StructId);

impl StructId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::StructDef> {
        db.as_ref().structs.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumId(RawId);
impl_arena_id!(EnumId);

impl EnumId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::EnumDef> {
        db.as_ref().enums.id2loc(self)
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct EnumVariantId(RawId);
impl_arena_id!(EnumVariantId);

impl EnumVariantId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> ItemLoc<ast::EnumVariant> {
        db.as_ref().enum_variants.id2loc(self)
    }
}

/// Def's are a core concept of hir. A `Def` is an Item (function, module, etc)
/// in a specific module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DefId(RawId);
impl_arena_id!(DefId);

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct DefLoc {
    pub(crate) kind: DefKind,
    pub(crate) module: Module,
    pub(crate) source_item_id: SourceItemId,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub(crate) enum DefKind {
    Const,
    Static,
    Trait,
    Type,
    Item,
    // /// The constructor of a struct. E.g. if we have `struct Foo(usize)`, the
    // /// name `Foo` needs to resolve to different types depending on whether we
    // /// are in the types or values namespace: As a type, `Foo` of course refers
    // /// to the struct `Foo`; as a value, `Foo` is a callable type with signature
    // /// `(usize) -> Foo`. The cleanest approach to handle this seems to be to
    // /// have different defs in the two namespaces.
    // ///
    // /// rustc does the same; note that it even creates a struct constructor if
    // /// the struct isn't a tuple struct (see `CtorKind::Fictive` in rustc).
    // StructCtor,
}

impl DefId {
    pub(crate) fn loc(self, db: &impl AsRef<HirInterner>) -> DefLoc {
        db.as_ref().defs.id2loc(self)
    }

    pub fn resolve(self, db: &impl HirDatabase) -> Def {
        let loc = self.loc(db);
        match loc.kind {
            DefKind::Const => {
                let def = Const::new(self);
                Def::Const(def)
            }
            DefKind::Static => {
                let def = Static::new(self);
                Def::Static(def)
            }
            DefKind::Trait => {
                let def = Trait::new(self);
                Def::Trait(def)
            }
            DefKind::Type => {
                let def = Type::new(self);
                Def::Type(def)
            }
            DefKind::Item => Def::Item,
        }
    }

    pub(crate) fn source(self, db: &impl HirDatabase) -> (HirFileId, TreeArc<SyntaxNode>) {
        let loc = self.loc(db);
        let syntax = db.file_item(loc.source_item_id);
        (loc.source_item_id.file_id, syntax)
    }
}

impl DefLoc {
    pub(crate) fn id(&self, db: &impl AsRef<HirInterner>) -> DefId {
        db.as_ref().defs.loc2id(&self)
    }
}

/// Identifier of item within a specific file. This is stable over reparses, so
/// it's OK to use it as a salsa key/value.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SourceFileItemId(RawId);
impl_arena_id!(SourceFileItemId);

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceItemId {
    pub(crate) file_id: HirFileId,
    /// None for the whole file.
    pub(crate) item_id: Option<SourceFileItemId>,
}

/// Maps items' `SyntaxNode`s to `SourceFileItemId`s and back.
#[derive(Debug, PartialEq, Eq)]
pub struct SourceFileItems {
    file_id: HirFileId,
    arena: Arena<SourceFileItemId, TreeArc<SyntaxNode>>,
}

impl SourceFileItems {
    pub(crate) fn new(file_id: HirFileId, source_file: &SourceFile) -> SourceFileItems {
        let mut res = SourceFileItems {
            file_id,
            arena: Arena::default(),
        };
        res.init(source_file);
        res
    }

    fn init(&mut self, source_file: &SourceFile) {
        // By walking the tree in bread-first order we make sure that parents
        // get lower ids then children. That is, addding a new child does not
        // change parent's id. This means that, say, adding a new function to a
        // trait does not chage ids of top-level items, which helps caching.
        bfs(source_file.syntax(), |it| {
            if let Some(enum_variant) = ast::EnumVariant::cast(it) {
                self.alloc(enum_variant.syntax().to_owned());
            } else if let Some(module_item) = ast::ModuleItem::cast(it) {
                self.alloc(module_item.syntax().to_owned());
            } else if let Some(macro_call) = ast::MacroCall::cast(it) {
                self.alloc(macro_call.syntax().to_owned());
            }
        })
    }

    fn alloc(&mut self, item: TreeArc<SyntaxNode>) -> SourceFileItemId {
        self.arena.alloc(item)
    }
    pub(crate) fn id_of(&self, file_id: HirFileId, item: &SyntaxNode) -> SourceFileItemId {
        assert_eq!(
            self.file_id, file_id,
            "SourceFileItems: wrong file, expected {:?}, got {:?}",
            self.file_id, file_id
        );
        self.id_of_unchecked(item)
    }
    pub(crate) fn id_of_unchecked(&self, item: &SyntaxNode) -> SourceFileItemId {
        if let Some((id, _)) = self.arena.iter().find(|(_id, i)| *i == item) {
            return id;
        }
        // This should not happen. Let's try to give a sensible diagnostics.
        if let Some((id, i)) = self.arena.iter().find(|(_id, i)| i.range() == item.range()) {
            // FIXME(#288): whyyy are we getting here?
            log::error!(
                "unequal syntax nodes with the same range:\n{:?}\n{:?}",
                item,
                i
            );
            return id;
        }
        panic!(
            "Can't find {:?} in SourceFileItems:\n{:?}",
            item,
            self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
        );
    }
    pub fn id_of_source_file(&self) -> SourceFileItemId {
        let (id, _syntax) = self.arena.iter().next().unwrap();
        id
    }
}

impl std::ops::Index<SourceFileItemId> for SourceFileItems {
    type Output = SyntaxNode;
    fn index(&self, idx: SourceFileItemId) -> &SyntaxNode {
        &self.arena[idx]
    }
}

/// Walks the subtree in bfs order, calling `f` for each node.
fn bfs(node: &SyntaxNode, mut f: impl FnMut(&SyntaxNode)) {
    let mut curr_layer = vec![node];
    let mut next_layer = vec![];
    while !curr_layer.is_empty() {
        curr_layer.drain(..).for_each(|node| {
            next_layer.extend(node.children());
            f(node);
        });
        std::mem::swap(&mut curr_layer, &mut next_layer);
    }
}
<|endoftext|>


<commit_before>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

#[macro_export]
macro_rules! cubeb_log_internal {
    ($level: expr, $msg: expr) => {
        #[allow(unused_unsafe)]
        unsafe {
            if $level <= $crate::ffi::g_cubeb_log_level.into() {
                cubeb_log_internal!(__INTERNAL__ $msg);
            }
        }
    };
    ($level: expr, $fmt: expr, $($arg: expr),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            if $level <= $crate::ffi::g_cubeb_log_level.into() {
                cubeb_log_internal!(__INTERNAL__ format!($fmt, $($arg),*));
            }
        }
    };
    (__INTERNAL__ $msg: expr) => {
        if let Some(log_callback) = $crate::ffi::g_cubeb_log_callback {
            let filename = std::path::Path::new(file!()).file_name().unwrap().to_str().unwrap();
            let cstr = ::std::ffi::CString::new(format!("{}:{}: {}\n", filename, line!(), $msg)).unwrap();
            log_callback(cstr.as_ptr());
        }
    }
}

#[macro_export]
macro_rules! cubeb_logv {
    ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Verbose, $msg));
    ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Verbose, $fmt, $($arg),*));
}

#[macro_export]
macro_rules! cubeb_log {
    ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Normal, $msg));
    ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Normal, $fmt, $($arg),*));
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_normal_logging() {
        cubeb_log!("This is log at normal level");
        cubeb_log!("{} Formatted log", 1);
        cubeb_log!("{} Formatted {} log {}", 1, 2, 3);
    }

    #[test]
    fn test_verbose_logging() {
        cubeb_logv!("This is a log at verbose level");
        cubeb_logv!("{} Formatted log", 1);
        cubeb_logv!("{} Formatted {} log {}", 1, 2, 3);
    }
}
<commit_msg>Do not allocate buffer when logging messages<commit_after>// Copyright © 2017-2018 Mozilla Foundation
//
// This program is made available under an ISC-style license.  See the
// accompanying file LICENSE for details.

#[macro_export]
macro_rules! cubeb_log_internal {
    ($level: expr, $msg: expr) => {
        #[allow(unused_unsafe)]
        unsafe {
            if $level <= $crate::ffi::g_cubeb_log_level.into() {
                cubeb_log_internal!(__INTERNAL__ $msg);
            }
        }
    };
    ($level: expr, $fmt: expr, $($arg: expr),+) => {
        #[allow(unused_unsafe)]
        unsafe {
            if $level <= $crate::ffi::g_cubeb_log_level.into() {
                cubeb_log_internal!(__INTERNAL__ format!($fmt, $($arg),*));
            }
        }
    };
    (__INTERNAL__ $msg: expr) => {
        if let Some(log_callback) = $crate::ffi::g_cubeb_log_callback {
            use std::io::Write;

            let mut buf = [0 as u8; 1024];
            let filename = std::path::Path::new(file!())
                .file_name()
                .unwrap()
                .to_str()
                .unwrap();
            // +2 for ':', +1 for ' ', and +1 for converting line value to number of digits
            let len = filename.len() + ((line!() as f32).log10() as usize) + $msg.len() + 4;
            assert!(len < buf.len(), "log is too long!");
            write!(&mut buf[..], "{}:{}: {}", filename, line!(), $msg).unwrap();
            buf[len] = 0;
            let cstr = unsafe { std::ffi::CStr::from_bytes_with_nul_unchecked(&buf[..len + 1]) };
            log_callback(cstr.as_ptr());
        }
    }
}

#[macro_export]
macro_rules! cubeb_logv {
    ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Verbose, $msg));
    ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Verbose, $fmt, $($arg),*));
}

#[macro_export]
macro_rules! cubeb_log {
    ($msg: expr) => (cubeb_log_internal!($crate::LogLevel::Normal, $msg));
    ($fmt: expr, $($arg: expr),+) => (cubeb_log_internal!($crate::LogLevel::Normal, $fmt, $($arg),*));
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_normal_logging() {
        cubeb_log!("This is log at normal level");
        cubeb_log!("{} Formatted log", 1);
        cubeb_log!("{} Formatted {} log {}", 1, 2, 3);
    }

    #[test]
    fn test_verbose_logging() {
        cubeb_logv!("This is a log at verbose level");
        cubeb_logv!("{} Formatted log", 1);
        cubeb_logv!("{} Formatted {} log {}", 1, 2, 3);
    }
}
<|endoftext|>


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

//! A generic, safe mechanism by which DOM objects can be pinned and transferred
//! between tasks (or intra-task for asynchronous events). Akin to Gecko's
//! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures
//! that the actual SpiderMonkey GC integration occurs on the script task via
//! message passing. Ownership of a `Trusted<T>` object means the DOM object of
//! type T to which it points remains alive. Any other behaviour is undefined.
//! To guarantee the lifetime of a DOM object when performing asynchronous operations,
//! obtain a `Trusted<T>` from that object and pass it along with each operation.
//! A usable pointer to the original DOM object can be obtained on the script task
//! from a `Trusted<T>` via the `to_temporary` method.
//!
//! The implementation of Trusted<T> is as follows:
//! A hashtable resides in the script task, keyed on the pointer to the Rust DOM object.
//! The values in this hashtable are atomic reference counts. When a Trusted<T> object is
//! created or cloned, this count is increased. When a Trusted<T> is dropped, the count
//! decreases. If the count hits zero, a message is dispatched to the script task to remove
//! the entry from the hashmap if the count is still zero. The JS reflector for the DOM object
//! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry
//! is removed.

use dom::bindings::js::{Temporary, JSRef, Unrooted};
use dom::bindings::utils::{Reflector, Reflectable};
use script_task::{ScriptMsg, ScriptChan};

use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext};

use libc;
use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::collections::hash_map::Entry::{Vacant, Occupied};
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> = Rc::new(RefCell::new(None)));

/// A pointer to a Rust DOM object that needs to be destroyed.
pub struct TrustedReference(*const libc::c_void);
unsafe impl Send for TrustedReference {}

/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among tasks for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
/// `Trusted<T>` instance.
pub struct Trusted<T> {
    /// A pointer to the Rust DOM object of type T, but void to allow
    /// sending `Trusted<T>` between tasks, regardless of T's sendability.
    ptr: *const libc::c_void,
    refcount: Arc<Mutex<usize>>,
    script_chan: Box<ScriptChan + Send>,
    owner_thread: *const libc::c_void,
    phantom: PhantomData<T>,
}

unsafe impl<T: Reflectable> Send for Trusted<T> {}

impl<T: Reflectable> Trusted<T> {
    /// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will
    /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's
    /// lifetime.
    pub fn new(cx: *mut JSContext, ptr: JSRef<T>, script_chan: Box<ScriptChan + Send>) -> Trusted<T> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let refcount = live_references.addref(cx, &*ptr as *const T);
            Trusted {
                ptr: &*ptr as *const T as *const libc::c_void,
                refcount: refcount,
                script_chan: script_chan.clone(),
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
                phantom: PhantomData,
            }
        })
    }

    /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on
    /// a different thread than the original value from which this `Trusted<T>` was
    /// obtained.
    pub fn to_temporary(&self) -> Temporary<T> {
        assert!(LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
        }));
        unsafe {
            Temporary::from_unrooted(Unrooted::from_raw(self.ptr as *const T))
        }
    }
}

impl<T: Reflectable> Clone for Trusted<T> {
    fn clone(&self) -> Trusted<T> {
        {
            let mut refcount = self.refcount.lock().unwrap();
            *refcount += 1;
        }

        Trusted {
            ptr: self.ptr,
            refcount: self.refcount.clone(),
            script_chan: self.script_chan.clone(),
            owner_thread: self.owner_thread,
            phantom: PhantomData,
        }
    }
}

#[unsafe_destructor]
impl<T: Reflectable> Drop for Trusted<T> {
    fn drop(&mut self) {
        let mut refcount = self.refcount.lock().unwrap();
        assert!(*refcount > 0);
        *refcount -= 1;
        if *refcount == 0 {
            // It's possible this send will fail if the script task
            // has already exited. There's not much we can do at this
            // point though.
            let msg = ScriptMsg::RefcountCleanup(TrustedReference(self.ptr));
            let _ = self.script_chan.send(msg);
        }
    }
}

/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
pub struct LiveDOMReferences {
    // keyed on pointer to Rust DOM object
    table: RefCell<HashMap<*const libc::c_void, Arc<Mutex<usize>>>>
}

impl LiveDOMReferences {
    /// Set up the task-local data required for storing the outstanding DOM references.
    pub fn initialize() {
        LIVE_REFERENCES.with(|ref r| {
            *r.borrow_mut() = Some(LiveDOMReferences {
                table: RefCell::new(HashMap::new()),
            })
        });
    }

    fn addref<T: Reflectable>(&self, cx: *mut JSContext, ptr: *const T) -> Arc<Mutex<usize>> {
        let mut table = self.table.borrow_mut();
        match table.entry(ptr as *const libc::c_void) {
            Occupied(mut entry) => {
                let refcount = entry.get_mut();
                *refcount.lock().unwrap() += 1;
                refcount.clone()
            }
            Vacant(entry) => {
                unsafe {
                    let rootable = (*ptr).reflector().rootable();
                    JS_AddObjectRoot(cx, rootable);
                }
                let refcount = Arc::new(Mutex::new(1));
                entry.insert(refcount.clone());
                refcount
            }
        }
    }

    /// Unpin the given DOM object if its refcount is 0.
    pub fn cleanup(cx: *mut JSContext, raw_reflectable: TrustedReference) {
        let TrustedReference(raw_reflectable) = raw_reflectable;
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let reflectable = raw_reflectable as *const Reflector;
            let mut table = live_references.table.borrow_mut();
            match table.entry(raw_reflectable) {
                Occupied(entry) => {
                    if *entry.get().lock().unwrap() != 0 {
                        // there could have been a new reference taken since
                        // this message was dispatched.
                        return;
                    }

                    unsafe {
                        JS_RemoveObjectRoot(cx, (*reflectable).rootable());
                    }
                    let _ = entry.remove();
                }
                Vacant(_) => {
                    // there could be a cleanup message dispatched, then a new
                    // pinned reference obtained and released before the message
                    // is processed, at which point there would be no matching
                    // hashtable entry.
                    info!("attempt to cleanup an unrecognized reflector");
                }
            }
        })
    }
}
<commit_msg>Add a Reflectable bound to Trusted's type parameter.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

//! A generic, safe mechanism by which DOM objects can be pinned and transferred
//! between tasks (or intra-task for asynchronous events). Akin to Gecko's
//! nsMainThreadPtrHandle, this uses thread-safe reference counting and ensures
//! that the actual SpiderMonkey GC integration occurs on the script task via
//! message passing. Ownership of a `Trusted<T>` object means the DOM object of
//! type T to which it points remains alive. Any other behaviour is undefined.
//! To guarantee the lifetime of a DOM object when performing asynchronous operations,
//! obtain a `Trusted<T>` from that object and pass it along with each operation.
//! A usable pointer to the original DOM object can be obtained on the script task
//! from a `Trusted<T>` via the `to_temporary` method.
//!
//! The implementation of Trusted<T> is as follows:
//! A hashtable resides in the script task, keyed on the pointer to the Rust DOM object.
//! The values in this hashtable are atomic reference counts. When a Trusted<T> object is
//! created or cloned, this count is increased. When a Trusted<T> is dropped, the count
//! decreases. If the count hits zero, a message is dispatched to the script task to remove
//! the entry from the hashmap if the count is still zero. The JS reflector for the DOM object
//! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry
//! is removed.

use dom::bindings::js::{Temporary, JSRef, Unrooted};
use dom::bindings::utils::{Reflector, Reflectable};
use script_task::{ScriptMsg, ScriptChan};

use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext};

use libc;
use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::collections::hash_map::Entry::{Vacant, Occupied};
use std::marker::PhantomData;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> = Rc::new(RefCell::new(None)));

/// A pointer to a Rust DOM object that needs to be destroyed.
pub struct TrustedReference(*const libc::c_void);
unsafe impl Send for TrustedReference {}

/// A safe wrapper around a raw pointer to a DOM object that can be
/// shared among tasks for use in asynchronous operations. The underlying
/// DOM object is guaranteed to live at least as long as the last outstanding
/// `Trusted<T>` instance.
pub struct Trusted<T: Reflectable> {
    /// A pointer to the Rust DOM object of type T, but void to allow
    /// sending `Trusted<T>` between tasks, regardless of T's sendability.
    ptr: *const libc::c_void,
    refcount: Arc<Mutex<usize>>,
    script_chan: Box<ScriptChan + Send>,
    owner_thread: *const libc::c_void,
    phantom: PhantomData<T>,
}

unsafe impl<T: Reflectable> Send for Trusted<T> {}

impl<T: Reflectable> Trusted<T> {
    /// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will
    /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's
    /// lifetime.
    pub fn new(cx: *mut JSContext, ptr: JSRef<T>, script_chan: Box<ScriptChan + Send>) -> Trusted<T> {
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let refcount = live_references.addref(cx, &*ptr as *const T);
            Trusted {
                ptr: &*ptr as *const T as *const libc::c_void,
                refcount: refcount,
                script_chan: script_chan.clone(),
                owner_thread: (&*live_references) as *const _ as *const libc::c_void,
                phantom: PhantomData,
            }
        })
    }

    /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on
    /// a different thread than the original value from which this `Trusted<T>` was
    /// obtained.
    pub fn to_temporary(&self) -> Temporary<T> {
        assert!(LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            self.owner_thread == (&*live_references) as *const _ as *const libc::c_void
        }));
        unsafe {
            Temporary::from_unrooted(Unrooted::from_raw(self.ptr as *const T))
        }
    }
}

impl<T: Reflectable> Clone for Trusted<T> {
    fn clone(&self) -> Trusted<T> {
        {
            let mut refcount = self.refcount.lock().unwrap();
            *refcount += 1;
        }

        Trusted {
            ptr: self.ptr,
            refcount: self.refcount.clone(),
            script_chan: self.script_chan.clone(),
            owner_thread: self.owner_thread,
            phantom: PhantomData,
        }
    }
}

#[unsafe_destructor]
impl<T: Reflectable> Drop for Trusted<T> {
    fn drop(&mut self) {
        let mut refcount = self.refcount.lock().unwrap();
        assert!(*refcount > 0);
        *refcount -= 1;
        if *refcount == 0 {
            // It's possible this send will fail if the script task
            // has already exited. There's not much we can do at this
            // point though.
            let msg = ScriptMsg::RefcountCleanup(TrustedReference(self.ptr));
            let _ = self.script_chan.send(msg);
        }
    }
}

/// The set of live, pinned DOM objects that are currently prevented
/// from being garbage collected due to outstanding references.
pub struct LiveDOMReferences {
    // keyed on pointer to Rust DOM object
    table: RefCell<HashMap<*const libc::c_void, Arc<Mutex<usize>>>>
}

impl LiveDOMReferences {
    /// Set up the task-local data required for storing the outstanding DOM references.
    pub fn initialize() {
        LIVE_REFERENCES.with(|ref r| {
            *r.borrow_mut() = Some(LiveDOMReferences {
                table: RefCell::new(HashMap::new()),
            })
        });
    }

    fn addref<T: Reflectable>(&self, cx: *mut JSContext, ptr: *const T) -> Arc<Mutex<usize>> {
        let mut table = self.table.borrow_mut();
        match table.entry(ptr as *const libc::c_void) {
            Occupied(mut entry) => {
                let refcount = entry.get_mut();
                *refcount.lock().unwrap() += 1;
                refcount.clone()
            }
            Vacant(entry) => {
                unsafe {
                    let rootable = (*ptr).reflector().rootable();
                    JS_AddObjectRoot(cx, rootable);
                }
                let refcount = Arc::new(Mutex::new(1));
                entry.insert(refcount.clone());
                refcount
            }
        }
    }

    /// Unpin the given DOM object if its refcount is 0.
    pub fn cleanup(cx: *mut JSContext, raw_reflectable: TrustedReference) {
        let TrustedReference(raw_reflectable) = raw_reflectable;
        LIVE_REFERENCES.with(|ref r| {
            let r = r.borrow();
            let live_references = r.as_ref().unwrap();
            let reflectable = raw_reflectable as *const Reflector;
            let mut table = live_references.table.borrow_mut();
            match table.entry(raw_reflectable) {
                Occupied(entry) => {
                    if *entry.get().lock().unwrap() != 0 {
                        // there could have been a new reference taken since
                        // this message was dispatched.
                        return;
                    }

                    unsafe {
                        JS_RemoveObjectRoot(cx, (*reflectable).rootable());
                    }
                    let _ = entry.remove();
                }
                Vacant(_) => {
                    // there could be a cleanup message dispatched, then a new
                    // pinned reference obtained and released before the message
                    // is processed, at which point there would be no matching
                    // hashtable entry.
                    info!("attempt to cleanup an unrecognized reflector");
                }
            }
        })
    }
}
<|endoftext|>


<commit_before>//! Request logging middleware
use std::env;
use std::fmt;
use std::fmt::{Display, Formatter};

use libc;
use time;
use regex::Regex;

use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middlewares::{Middleware, Started, Finished};

/// `Middleware` for logging request and response info to the terminal.
///
/// ## Usage
///
/// Create `Logger` middlewares with the specified `format`.
/// Default `Logger` could be created with `default` method, it uses the default format:
///
/// ```ignore
///  %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
/// ```
/// ```rust
/// extern crate actix_web;
/// use actix_web::Application;
/// use actix_web::middlewares::Logger;
///
/// fn main() {
///     let app = Application::default("/")
///         .middleware(Logger::default())
///         .middleware(Logger::new("%a %{User-Agent}i"))
///         .finish();
/// }
/// ```
///
/// ## Format
///
/// `%%`  The percent sign
///
/// `%a`  Remote IP-address (IP-address of proxy if using reverse proxy)
///
/// `%t`  Time when the request was started to process
///
/// `%P`  The process ID of the child that serviced the request
///
/// `%r`  First line of request
///
/// `%s`  Response status code
///
/// `%b`  Size of response in bytes, including HTTP headers
///
/// `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
///
/// `%D`  Time taken to serve the request, in milliseconds
///
/// `%{FOO}i`  request.headers['FOO']
///
/// `%{FOO}o`  response.headers['FOO']
///
/// `%{FOO}e`  os.environ['FOO']
///
pub struct Logger {
    format: Format,
}

impl Logger {
    /// Create `Logger` middleware with the specified `format`.
    pub fn new(format: &str) -> Logger {
        Logger { format: Format::new(format) }
    }
}

impl Default for Logger {
    /// Create `Logger` middleware with format:
    ///
    /// ```ignore
    /// %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
    /// ```
    fn default() -> Logger {
        Logger { format: Format::default() }
    }
}

struct StartTime(time::Tm);

impl Logger {

    fn log(&self, req: &mut HttpRequest, resp: &HttpResponse) {
        let entry_time = req.extensions().get::<StartTime>().unwrap().0;

        let render = |fmt: &mut Formatter| {
            for unit in &self.format.0 {
                unit.render(fmt, req, resp, entry_time)?;
            }
            Ok(())
        };
        info!("{}", FormatDisplay(&render));
    }
}

impl Middleware for Logger {

    fn start(&self, req: &mut HttpRequest) -> Started {
        req.extensions().insert(StartTime(time::now()));
        Started::Done
    }

    fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished {
        self.log(req, resp);
        Finished::Done
    }
}

/// A formatting style for the `Logger`, consisting of multiple
/// `FormatText`s concatenated into one line.
#[derive(Clone)]
#[doc(hidden)]
struct Format(Vec<FormatText>);

impl Default for Format {
    /// Return the default formatting style for the `Logger`:
    fn default() -> Format {
        Format::new(r#"%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T"#)
    }
}

impl Format {
    /// Create a `Format` from a format string.
    ///
    /// Returns `None` if the format string syntax is incorrect.
    pub fn new(s: &str) -> Format {
        trace!("Access log format: {}", s);
        let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbTD]?)").unwrap();

        let mut idx = 0;
        let mut results = Vec::new();
        for cap in fmt.captures_iter(s) {
            let m = cap.get(0).unwrap();
            let pos = m.start();
            if idx != pos {
                results.push(FormatText::Str(s[idx..pos].to_owned()));
            }
            idx = m.end();

            if let Some(key) = cap.get(2) {
                results.push(
                    match cap.get(3).unwrap().as_str() {
                        "i" => FormatText::RequestHeader(key.as_str().to_owned()),
                        "o" => FormatText::ResponseHeader(key.as_str().to_owned()),
                        "e" => FormatText::EnvironHeader(key.as_str().to_owned()),
                        _ => unreachable!(),
                    })
            } else {
                let m = cap.get(1).unwrap();
                results.push(
                    match m.as_str() {
                        "%" => FormatText::Percent,
                        "a" => FormatText::RemoteAddr,
                        "t" => FormatText::RequestTime,
                        "P" => FormatText::Pid,
                        "r" => FormatText::RequestLine,
                        "s" => FormatText::ResponseStatus,
                        "b" => FormatText::ResponseSize,
                        "T" => FormatText::Time,
                        "D" => FormatText::TimeMillis,
                        _ => FormatText::Str(m.as_str().to_owned()),
                    }
                );
            }
        }
        if idx != s.len() {
            results.push(FormatText::Str(s[idx..].to_owned()));
        }

        Format(results)
    }
}

/// A string of text to be logged. This is either one of the data
/// fields supported by the `Logger`, or a custom `String`.
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum FormatText {
    Str(String),
    Pid,
    Percent,
    RequestLine,
    RequestTime,
    ResponseStatus,
    ResponseSize,
    Time,
    TimeMillis,
    RemoteAddr,
    RequestHeader(String),
    ResponseHeader(String),
    EnvironHeader(String),
}

impl FormatText {

    fn render(&self, fmt: &mut Formatter,
              req: &HttpRequest,
              resp: &HttpResponse,
              entry_time: time::Tm) -> Result<(), fmt::Error>
    {
        match *self {
            FormatText::Str(ref string) => fmt.write_str(string),
            FormatText::Percent => "%".fmt(fmt),
            FormatText::RequestLine => {
                if req.query_string().is_empty() {
                    fmt.write_fmt(format_args!(
                        "{} {} {:?}",
                        req.method(), req.path(), req.version()))
                } else {
                    fmt.write_fmt(format_args!(
                        "{} {}?{} {:?}",
                        req.method(), req.path(), req.query_string(), req.version()))
                }
            },
            FormatText::ResponseStatus => resp.status().as_u16().fmt(fmt),
            FormatText::ResponseSize => resp.response_size().fmt(fmt),
            FormatText::Pid => unsafe{libc::getpid().fmt(fmt)},
            FormatText::Time => {
                let response_time = time::now() - entry_time;
                let response_time = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time))
            },
            FormatText::TimeMillis => {
                let response_time = time::now() - entry_time;
                let response_time_ms = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time_ms))
            },
            FormatText::RemoteAddr => {
                if let Some(addr) = req.remote() {
                    addr.fmt(fmt)
                } else {
                    "-".fmt(fmt)
                }
            }
            FormatText::RequestTime => {
                entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]")
                    .unwrap()
                    .fmt(fmt)
            }
            FormatText::RequestHeader(ref name) => {
                let s = if let Some(val) = req.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::ResponseHeader(ref name) => {
                let s = if let Some(val) = resp.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::EnvironHeader(ref name) => {
                if let Ok(val) = env::var(name) {
                    fmt.write_fmt(format_args!("{}", val))
                } else {
                    "-".fmt(fmt)
                }
            }
        }
    }
}

pub(crate) struct FormatDisplay<'a>(
    &'a Fn(&mut Formatter) -> Result<(), fmt::Error>);

impl<'a> fmt::Display for FormatDisplay<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        (self.0)(fmt)
    }
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    use Body;
    use super::*;
    use time;
    use http::{Method, Version, StatusCode};
    use http::header::{self, HeaderMap};

    #[test]
    fn test_logger() {
        let _ = env_logger::init();
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let mut req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .header("X-Test", "ttt")
            .force_close().body(Body::Empty).unwrap();

        match logger.start(&mut req) {
            Started::Done => (),
            _ => panic!(),
        }
        match logger.finish(&mut req, &resp) {
            Finished::Done => (),
            _ => panic!(),
        }
    }

    #[test]
    fn test_default_format() {
        let format = Format::default();

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET / HTTP/1.1"));
        assert!(s.contains("200 0"));
        assert!(s.contains("ACTIX-WEB"));

        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), "test".to_owned());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET /?test HTTP/1.1"));
    }
}
<commit_msg>better logger format test<commit_after>//! Request logging middleware
use std::env;
use std::fmt;
use std::fmt::{Display, Formatter};

use libc;
use time;
use regex::Regex;

use httprequest::HttpRequest;
use httpresponse::HttpResponse;
use middlewares::{Middleware, Started, Finished};

/// `Middleware` for logging request and response info to the terminal.
///
/// ## Usage
///
/// Create `Logger` middlewares with the specified `format`.
/// Default `Logger` could be created with `default` method, it uses the default format:
///
/// ```ignore
///  %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
/// ```
/// ```rust
/// extern crate actix_web;
/// use actix_web::Application;
/// use actix_web::middlewares::Logger;
///
/// fn main() {
///     let app = Application::default("/")
///         .middleware(Logger::default())
///         .middleware(Logger::new("%a %{User-Agent}i"))
///         .finish();
/// }
/// ```
///
/// ## Format
///
/// `%%`  The percent sign
///
/// `%a`  Remote IP-address (IP-address of proxy if using reverse proxy)
///
/// `%t`  Time when the request was started to process
///
/// `%P`  The process ID of the child that serviced the request
///
/// `%r`  First line of request
///
/// `%s`  Response status code
///
/// `%b`  Size of response in bytes, including HTTP headers
///
/// `%T` Time taken to serve the request, in seconds with floating fraction in .06f format
///
/// `%D`  Time taken to serve the request, in milliseconds
///
/// `%{FOO}i`  request.headers['FOO']
///
/// `%{FOO}o`  response.headers['FOO']
///
/// `%{FOO}e`  os.environ['FOO']
///
pub struct Logger {
    format: Format,
}

impl Logger {
    /// Create `Logger` middleware with the specified `format`.
    pub fn new(format: &str) -> Logger {
        Logger { format: Format::new(format) }
    }
}

impl Default for Logger {
    /// Create `Logger` middleware with format:
    ///
    /// ```ignore
    /// %a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T
    /// ```
    fn default() -> Logger {
        Logger { format: Format::default() }
    }
}

struct StartTime(time::Tm);

impl Logger {

    fn log(&self, req: &mut HttpRequest, resp: &HttpResponse) {
        let entry_time = req.extensions().get::<StartTime>().unwrap().0;

        let render = |fmt: &mut Formatter| {
            for unit in &self.format.0 {
                unit.render(fmt, req, resp, entry_time)?;
            }
            Ok(())
        };
        info!("{}", FormatDisplay(&render));
    }
}

impl Middleware for Logger {

    fn start(&self, req: &mut HttpRequest) -> Started {
        req.extensions().insert(StartTime(time::now()));
        Started::Done
    }

    fn finish(&self, req: &mut HttpRequest, resp: &HttpResponse) -> Finished {
        self.log(req, resp);
        Finished::Done
    }
}

/// A formatting style for the `Logger`, consisting of multiple
/// `FormatText`s concatenated into one line.
#[derive(Clone)]
#[doc(hidden)]
struct Format(Vec<FormatText>);

impl Default for Format {
    /// Return the default formatting style for the `Logger`:
    fn default() -> Format {
        Format::new(r#"%a %t "%r" %s %b "%{Referrer}i" "%{User-Agent}i" %T"#)
    }
}

impl Format {
    /// Create a `Format` from a format string.
    ///
    /// Returns `None` if the format string syntax is incorrect.
    pub fn new(s: &str) -> Format {
        trace!("Access log format: {}", s);
        let fmt = Regex::new(r"%(\{([A-Za-z0-9\-_]+)\}([ioe])|[atPrsbTD]?)").unwrap();

        let mut idx = 0;
        let mut results = Vec::new();
        for cap in fmt.captures_iter(s) {
            let m = cap.get(0).unwrap();
            let pos = m.start();
            if idx != pos {
                results.push(FormatText::Str(s[idx..pos].to_owned()));
            }
            idx = m.end();

            if let Some(key) = cap.get(2) {
                results.push(
                    match cap.get(3).unwrap().as_str() {
                        "i" => FormatText::RequestHeader(key.as_str().to_owned()),
                        "o" => FormatText::ResponseHeader(key.as_str().to_owned()),
                        "e" => FormatText::EnvironHeader(key.as_str().to_owned()),
                        _ => unreachable!(),
                    })
            } else {
                let m = cap.get(1).unwrap();
                results.push(
                    match m.as_str() {
                        "%" => FormatText::Percent,
                        "a" => FormatText::RemoteAddr,
                        "t" => FormatText::RequestTime,
                        "P" => FormatText::Pid,
                        "r" => FormatText::RequestLine,
                        "s" => FormatText::ResponseStatus,
                        "b" => FormatText::ResponseSize,
                        "T" => FormatText::Time,
                        "D" => FormatText::TimeMillis,
                        _ => FormatText::Str(m.as_str().to_owned()),
                    }
                );
            }
        }
        if idx != s.len() {
            results.push(FormatText::Str(s[idx..].to_owned()));
        }

        Format(results)
    }
}

/// A string of text to be logged. This is either one of the data
/// fields supported by the `Logger`, or a custom `String`.
#[doc(hidden)]
#[derive(Debug, Clone)]
pub enum FormatText {
    Str(String),
    Pid,
    Percent,
    RequestLine,
    RequestTime,
    ResponseStatus,
    ResponseSize,
    Time,
    TimeMillis,
    RemoteAddr,
    RequestHeader(String),
    ResponseHeader(String),
    EnvironHeader(String),
}

impl FormatText {

    fn render(&self, fmt: &mut Formatter,
              req: &HttpRequest,
              resp: &HttpResponse,
              entry_time: time::Tm) -> Result<(), fmt::Error>
    {
        match *self {
            FormatText::Str(ref string) => fmt.write_str(string),
            FormatText::Percent => "%".fmt(fmt),
            FormatText::RequestLine => {
                if req.query_string().is_empty() {
                    fmt.write_fmt(format_args!(
                        "{} {} {:?}",
                        req.method(), req.path(), req.version()))
                } else {
                    fmt.write_fmt(format_args!(
                        "{} {}?{} {:?}",
                        req.method(), req.path(), req.query_string(), req.version()))
                }
            },
            FormatText::ResponseStatus => resp.status().as_u16().fmt(fmt),
            FormatText::ResponseSize => resp.response_size().fmt(fmt),
            FormatText::Pid => unsafe{libc::getpid().fmt(fmt)},
            FormatText::Time => {
                let response_time = time::now() - entry_time;
                let response_time = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time))
            },
            FormatText::TimeMillis => {
                let response_time = time::now() - entry_time;
                let response_time_ms = (response_time.num_seconds() * 1000) as f64 +
                    (response_time.num_nanoseconds().unwrap_or(0) as f64) / 1000000.0;

                fmt.write_fmt(format_args!("{:.6}", response_time_ms))
            },
            FormatText::RemoteAddr => {
                if let Some(addr) = req.remote() {
                    addr.fmt(fmt)
                } else {
                    "-".fmt(fmt)
                }
            }
            FormatText::RequestTime => {
                entry_time.strftime("[%d/%b/%Y:%H:%M:%S %z]")
                    .unwrap()
                    .fmt(fmt)
            }
            FormatText::RequestHeader(ref name) => {
                let s = if let Some(val) = req.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::ResponseHeader(ref name) => {
                let s = if let Some(val) = resp.headers().get(name) {
                    if let Ok(s) = val.to_str() { s } else { "-" }
                } else {
                    "-"
                };
                fmt.write_fmt(format_args!("{}", s))
            }
            FormatText::EnvironHeader(ref name) => {
                if let Ok(val) = env::var(name) {
                    fmt.write_fmt(format_args!("{}", val))
                } else {
                    "-".fmt(fmt)
                }
            }
        }
    }
}

pub(crate) struct FormatDisplay<'a>(
    &'a Fn(&mut Formatter) -> Result<(), fmt::Error>);

impl<'a> fmt::Display for FormatDisplay<'a> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), fmt::Error> {
        (self.0)(fmt)
    }
}

#[cfg(test)]
mod tests {
    use Body;
    use super::*;
    use time;
    use http::{Method, Version, StatusCode};
    use http::header::{self, HeaderMap};

    #[test]
    fn test_logger() {
        let logger = Logger::new("%% %{User-Agent}i %{X-Test}o %{HOME}e %D test");

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let mut req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .header("X-Test", "ttt")
            .force_close().body(Body::Empty).unwrap();

        match logger.start(&mut req) {
            Started::Done => (),
            _ => panic!(),
        }
        match logger.finish(&mut req, &resp) {
            Finished::Done => (),
            _ => panic!(),
        }
        let entry_time = time::now();
        let render = |fmt: &mut Formatter| {
            for unit in logger.format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("ACTIX-WEB ttt"));
    }

    #[test]
    fn test_default_format() {
        let format = Format::default();

        let mut headers = HeaderMap::new();
        headers.insert(header::USER_AGENT, header::HeaderValue::from_static("ACTIX-WEB"));
        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, headers, String::new());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET / HTTP/1.1"));
        assert!(s.contains("200 0"));
        assert!(s.contains("ACTIX-WEB"));

        let req = HttpRequest::new(
            Method::GET, "/".to_owned(), Version::HTTP_11, HeaderMap::new(), "test".to_owned());
        let resp = HttpResponse::builder(StatusCode::OK)
            .force_close().body(Body::Empty).unwrap();
        let entry_time = time::now();

        let render = |fmt: &mut Formatter| {
            for unit in format.0.iter() {
                unit.render(fmt, &req, &resp, entry_time)?;
            }
            Ok(())
        };
        let s = format!("{}", FormatDisplay(&render));
        assert!(s.contains("GET /?test HTTP/1.1"));
    }
}
<|endoftext|>


<commit_before>// This file is part of the uutils coreutils package.
//
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
// (c) Jian Zeng <anonymousknight96@gmail.com>
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

use std::io::{stdout, Read, Write};

use uucore::display::Quotable;
use uucore::encoding::{wrap_print, Data, Format};
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::{format_usage, InvalidEncodingHandling};

use std::fs::File;
use std::io::{BufReader, Stdin};
use std::path::Path;

use clap::{crate_version, Arg, Command};

pub static BASE_CMD_PARSE_ERROR: i32 = 1;

// Config.
pub struct Config {
    pub decode: bool,
    pub ignore_garbage: bool,
    pub wrap_cols: Option<usize>,
    pub to_read: Option<String>,
}

pub mod options {
    pub static DECODE: &str = "decode";
    pub static WRAP: &str = "wrap";
    pub static IGNORE_GARBAGE: &str = "ignore-garbage";
    pub static FILE: &str = "file";
}

impl Config {
    pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
        let file: Option<String> = match options.values_of(options::FILE) {
            Some(mut values) => {
                let name = values.next().unwrap();
                if let Some(extra_op) = values.next() {
                    return Err(UUsageError::new(
                        BASE_CMD_PARSE_ERROR,
                        format!("extra operand {}", extra_op.quote(),),
                    ));
                }

                if name == "-" {
                    None
                } else {
                    if !Path::exists(Path::new(name)) {
                        return Err(USimpleError::new(
                            BASE_CMD_PARSE_ERROR,
                            format!("{}: No such file or directory", name.maybe_quote()),
                        ));
                    }
                    Some(name.to_owned())
                }
            }
            None => None,
        };

        let cols = options
            .value_of(options::WRAP)
            .map(|num| {
                num.parse::<usize>().map_err(|_| {
                    USimpleError::new(
                        BASE_CMD_PARSE_ERROR,
                        format!("invalid wrap size: {}", num.quote()),
                    )
                })
            })
            .transpose()?;

        Ok(Self {
            decode: options.is_present(options::DECODE),
            ignore_garbage: options.is_present(options::IGNORE_GARBAGE),
            wrap_cols: cols,
            to_read: file,
        })
    }
}

pub fn parse_base_cmd_args(args: impl uucore::Args, about: &str, usage: &str) -> UResult<Config> {
    let command = base_app(about, usage);
    let arg_list = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();
    Config::from(&command.get_matches_from(arg_list))
}

pub fn base_app<'a>(about: &'a str, usage: &'a str) -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(about)
        .override_usage(format_usage(usage))
        .infer_long_args(true)
        // Format arguments.
        .arg(
            Arg::new(options::DECODE)
                .short('d')
                .long(options::DECODE)
                .help("decode data"),
        )
        .arg(
            Arg::new(options::IGNORE_GARBAGE)
                .short('i')
                .long(options::IGNORE_GARBAGE)
                .help("when decoding, ignore non-alphabetic characters"),
        )
        .arg(
            Arg::new(options::WRAP)
                .short('w')
                .long(options::WRAP)
                .takes_value(true)
                .help(
                    "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
                ),
        )
        // "multiple" arguments are used to check whether there is more than one
        // file passed in.
        .arg(Arg::new(options::FILE).index(1).multiple_occurrences(true))
}

pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult<Box<dyn Read + 'a>> {
    match &config.to_read {
        Some(name) => {
            let file_buf =
                File::open(Path::new(name)).map_err_context(|| name.maybe_quote().to_string())?;
            Ok(Box::new(BufReader::new(file_buf))) // as Box<dyn Read>
        }
        None => {
            Ok(Box::new(stdin_ref.lock())) // as Box<dyn Read>
        }
    }
}

pub fn handle_input<R: Read>(
    input: &mut R,
    format: Format,
    line_wrap: Option<usize>,
    ignore_garbage: bool,
    decode: bool,
) -> UResult<()> {
    let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
    if let Some(wrap) = line_wrap {
        data = data.line_wrap(wrap);
    }

    if !decode {
        match data.encode() {
            Ok(s) => {
                wrap_print(&data, &s);
                Ok(())
            }
            Err(_) => Err(USimpleError::new(
                1,
                "error: invalid input (length must be multiple of 4 characters)",
            )),
        }
    } else {
        match data.decode() {
            Ok(s) => {
                // Silent the warning as we want to the error message
                #[allow(clippy::question_mark)]
                if stdout().write_all(&s).is_err() {
                    // on windows console, writing invalid utf8 returns an error
                    return Err(USimpleError::new(1, "error: cannot write non-utf8 data"));
                }
                Ok(())
            }
            Err(_) => Err(USimpleError::new(1, "error: invalid input")),
        }
    }
}
<commit_msg>base: set exit code to 1 for clap errors<commit_after>// This file is part of the uutils coreutils package.
//
// (c) Jordy Dickinson <jordy.dickinson@gmail.com>
// (c) Jian Zeng <anonymousknight96@gmail.com>
// (c) Alex Lyon <arcterus@mail.com>
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.

use std::io::{stdout, Read, Write};

use uucore::display::Quotable;
use uucore::encoding::{wrap_print, Data, Format};
use uucore::error::{FromIo, UResult, USimpleError, UUsageError};
use uucore::{format_usage, InvalidEncodingHandling};

use std::fs::File;
use std::io::{BufReader, Stdin};
use std::path::Path;

use clap::{crate_version, Arg, Command};

pub static BASE_CMD_PARSE_ERROR: i32 = 1;

// Config.
pub struct Config {
    pub decode: bool,
    pub ignore_garbage: bool,
    pub wrap_cols: Option<usize>,
    pub to_read: Option<String>,
}

pub mod options {
    pub static DECODE: &str = "decode";
    pub static WRAP: &str = "wrap";
    pub static IGNORE_GARBAGE: &str = "ignore-garbage";
    pub static FILE: &str = "file";
}

impl Config {
    pub fn from(options: &clap::ArgMatches) -> UResult<Self> {
        let file: Option<String> = match options.values_of(options::FILE) {
            Some(mut values) => {
                let name = values.next().unwrap();
                if let Some(extra_op) = values.next() {
                    return Err(UUsageError::new(
                        BASE_CMD_PARSE_ERROR,
                        format!("extra operand {}", extra_op.quote(),),
                    ));
                }

                if name == "-" {
                    None
                } else {
                    if !Path::exists(Path::new(name)) {
                        return Err(USimpleError::new(
                            BASE_CMD_PARSE_ERROR,
                            format!("{}: No such file or directory", name.maybe_quote()),
                        ));
                    }
                    Some(name.to_owned())
                }
            }
            None => None,
        };

        let cols = options
            .value_of(options::WRAP)
            .map(|num| {
                num.parse::<usize>().map_err(|_| {
                    USimpleError::new(
                        BASE_CMD_PARSE_ERROR,
                        format!("invalid wrap size: {}", num.quote()),
                    )
                })
            })
            .transpose()?;

        Ok(Self {
            decode: options.is_present(options::DECODE),
            ignore_garbage: options.is_present(options::IGNORE_GARBAGE),
            wrap_cols: cols,
            to_read: file,
        })
    }
}

pub fn parse_base_cmd_args(args: impl uucore::Args, about: &str, usage: &str) -> UResult<Config> {
    let command = base_app(about, usage);
    let arg_list = args
        .collect_str(InvalidEncodingHandling::ConvertLossy)
        .accept_any();
    Config::from(&command.try_get_matches_from(arg_list)?)
}

pub fn base_app<'a>(about: &'a str, usage: &'a str) -> Command<'a> {
    Command::new(uucore::util_name())
        .version(crate_version!())
        .about(about)
        .override_usage(format_usage(usage))
        .infer_long_args(true)
        // Format arguments.
        .arg(
            Arg::new(options::DECODE)
                .short('d')
                .long(options::DECODE)
                .help("decode data"),
        )
        .arg(
            Arg::new(options::IGNORE_GARBAGE)
                .short('i')
                .long(options::IGNORE_GARBAGE)
                .help("when decoding, ignore non-alphabetic characters"),
        )
        .arg(
            Arg::new(options::WRAP)
                .short('w')
                .long(options::WRAP)
                .takes_value(true)
                .help(
                    "wrap encoded lines after COLS character (default 76, 0 to disable wrapping)",
                ),
        )
        // "multiple" arguments are used to check whether there is more than one
        // file passed in.
        .arg(Arg::new(options::FILE).index(1).multiple_occurrences(true))
}

pub fn get_input<'a>(config: &Config, stdin_ref: &'a Stdin) -> UResult<Box<dyn Read + 'a>> {
    match &config.to_read {
        Some(name) => {
            let file_buf =
                File::open(Path::new(name)).map_err_context(|| name.maybe_quote().to_string())?;
            Ok(Box::new(BufReader::new(file_buf))) // as Box<dyn Read>
        }
        None => {
            Ok(Box::new(stdin_ref.lock())) // as Box<dyn Read>
        }
    }
}

pub fn handle_input<R: Read>(
    input: &mut R,
    format: Format,
    line_wrap: Option<usize>,
    ignore_garbage: bool,
    decode: bool,
) -> UResult<()> {
    let mut data = Data::new(input, format).ignore_garbage(ignore_garbage);
    if let Some(wrap) = line_wrap {
        data = data.line_wrap(wrap);
    }

    if !decode {
        match data.encode() {
            Ok(s) => {
                wrap_print(&data, &s);
                Ok(())
            }
            Err(_) => Err(USimpleError::new(
                1,
                "error: invalid input (length must be multiple of 4 characters)",
            )),
        }
    } else {
        match data.decode() {
            Ok(s) => {
                // Silent the warning as we want to the error message
                #[allow(clippy::question_mark)]
                if stdout().write_all(&s).is_err() {
                    // on windows console, writing invalid utf8 returns an error
                    return Err(USimpleError::new(1, "error: cannot write non-utf8 data"));
                }
                Ok(())
            }
            Err(_) => Err(USimpleError::new(1, "error: invalid input")),
        }
    }
}
<|endoftext|>


<commit_before>//! Asynchronous DNS resolver

use std::io::{self, ErrorKind};
use std::mem;
use std::net::SocketAddr;
use std::sync::Arc;

use futures::Future;
use tokio::runtime::current_thread::Runtime;
use trust_dns_resolver::AsyncResolver;

use config::Config;

// Taken from
// bluejekyll/trust-dns/resolver/examples/global_resolver.rs
lazy_static! {
    // First we need to setup the global Resolver
    static ref GLOBAL_DNS_RESOLVER: AsyncResolver = init_resolver(false);
    static ref GLOBAL_GOOGLE_DNS_RESOLVER: AsyncResolver = init_resolver(true);
}

fn init_resolver(force_google: bool) -> AsyncResolver {
    use std::sync::{Arc, Condvar, Mutex};
    use std::thread;

    // We'll be using this condvar to get the Resolver from the thread...
    let pair = Arc::new((Mutex::new(None::<AsyncResolver>), Condvar::new()));
    let pair2 = pair.clone();

    // Spawn the runtime to a new thread...
    //
    // This thread will manage the actual resolution runtime
    thread::spawn(move || {
                      // A runtime for this new thread
                      let mut runtime = Runtime::new().expect("failed to launch Runtime");

                      // our platform independent future, result, see next blocks
                      let (resolver, bg) = {
                          // To make this independent, if targeting macOS, BSD, Linux, or Windows, we can use the system's configuration:
                          #[cfg(any(unix, windows))]
                          {
                              if force_google {
                                  use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
                                  // Get a new resolver with the google nameservers as the upstream recursive resolvers
                                  AsyncResolver::new(ResolverConfig::google(), ResolverOpts::default())
                              } else {
                                  use trust_dns_resolver::system_conf::read_system_conf;
                                  // use the system resolver configuration
                                  let (config, opts) = read_system_conf().expect("Failed to read global dns sysconf");
                                  AsyncResolver::new(config, opts)
                              }
                          }

                          // For other operating systems, we can use one of the preconfigured definitions
                          #[cfg(not(any(unix, windows)))]
                          {
                              // Directly reference the config types
                              use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};

                              // Get a new resolver with the google nameservers as the upstream recursive resolvers
                              AsyncResolver::new(ResolverConfig::google(), ResolverOpts::default())
                          }
                      };

                      let &(ref lock, ref cvar) = &*pair2;
                      let mut started = lock.lock().unwrap();
                      *started = Some(resolver);
                      cvar.notify_one();
                      drop(started);

                      runtime.block_on(bg).expect("Failed to create DNS resolver");
                  });

    // Wait for the thread to start up.
    let &(ref lock, ref cvar) = &*pair;
    let mut resolver = lock.lock().unwrap();
    while resolver.is_none() {
        resolver = cvar.wait(resolver).unwrap();
    }

    // take the started resolver
    let resolver = mem::replace(&mut *resolver, None);

    // set the global resolver
    resolver.expect("resolver should not be none")
}

fn inner_resolve(config: Arc<Config>,
                 addr: &str,
                 port: u16,
                 check_forbidden: bool,
                 force_google: bool)
                 -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    let owned_addr = addr.to_owned();
    let owned_addr2 = owned_addr.clone();

    let resolver = if force_google {
        &*GLOBAL_GOOGLE_DNS_RESOLVER
    } else {
        &*GLOBAL_DNS_RESOLVER
    };

    resolver.lookup_ip(addr)
            .map_err(move |err| {
                         error!("Failed to resolve {}, err: {}", owned_addr2, err);
                         io::Error::new(io::ErrorKind::Other, "dns resolve error")
                     })
            .and_then(move |lookup_result| {
                          let mut vaddr = Vec::new();
                          for ip in lookup_result.iter() {
                              if check_forbidden {
                                  let forbidden_ip = &config.forbidden_ip;
                                  if forbidden_ip.contains(&ip) {
                                      debug!("Resolved {} => {}, which is skipped by forbidden_ip", owned_addr, ip);
                                      continue;
                                  }

                                  vaddr.push(SocketAddr::new(ip, port));
                              }
                          }

                          if vaddr.is_empty() {
                              let err = io::Error::new(ErrorKind::Other,
                                                       format!("resolved {} to empty address, all IPs are filtered",
                                                               owned_addr));
                              Err(err)
                          } else {
                              debug!("Resolved {} => {:?}", owned_addr, vaddr);
                              Ok(vaddr)
                          }
                      })
}

pub fn resolve(config: Arc<Config>,
               addr: &str,
               port: u16,
               check_forbidden: bool)
               -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    inner_resolve(config, addr, port, check_forbidden, false)
}

pub fn resolve_by_google(config: Arc<Config>,
                         addr: &str,
                         port: u16,
                         check_forbidden: bool)
                         -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    inner_resolve(config, addr, port, check_forbidden, true)
}
<commit_msg>:bug: fix dns resolve error (#122)<commit_after>//! Asynchronous DNS resolver

use std::io::{self, ErrorKind};
use std::mem;
use std::net::SocketAddr;
use std::sync::Arc;

use futures::Future;
use tokio::runtime::current_thread::Runtime;
use trust_dns_resolver::AsyncResolver;

use config::Config;

// Taken from
// bluejekyll/trust-dns/resolver/examples/global_resolver.rs
lazy_static! {
    // First we need to setup the global Resolver
    static ref GLOBAL_DNS_RESOLVER: AsyncResolver = init_resolver(false);
    static ref GLOBAL_GOOGLE_DNS_RESOLVER: AsyncResolver = init_resolver(true);
}

fn init_resolver(force_google: bool) -> AsyncResolver {
    use std::sync::{Arc, Condvar, Mutex};
    use std::thread;

    // We'll be using this condvar to get the Resolver from the thread...
    let pair = Arc::new((Mutex::new(None::<AsyncResolver>), Condvar::new()));
    let pair2 = pair.clone();

    // Spawn the runtime to a new thread...
    //
    // This thread will manage the actual resolution runtime
    thread::spawn(move || {
                      // A runtime for this new thread
                      let mut runtime = Runtime::new().expect("failed to launch Runtime");

                      // our platform independent future, result, see next blocks
                      let (resolver, bg) = {
                          // To make this independent, if targeting macOS, BSD, Linux, or Windows, we can use the system's configuration:
                          #[cfg(any(unix, windows))]
                          {
                              if force_google {
                                  use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};
                                  // Get a new resolver with the google nameservers as the upstream recursive resolvers
                                  AsyncResolver::new(ResolverConfig::google(), ResolverOpts::default())
                              } else {
                                  use trust_dns_resolver::system_conf::read_system_conf;
                                  // use the system resolver configuration
                                  let (config, opts) = read_system_conf().expect("Failed to read global dns sysconf");
                                  AsyncResolver::new(config, opts)
                              }
                          }

                          // For other operating systems, we can use one of the preconfigured definitions
                          #[cfg(not(any(unix, windows)))]
                          {
                              // Directly reference the config types
                              use trust_dns_resolver::config::{ResolverConfig, ResolverOpts};

                              // Get a new resolver with the google nameservers as the upstream recursive resolvers
                              AsyncResolver::new(ResolverConfig::google(), ResolverOpts::default())
                          }
                      };

                      let &(ref lock, ref cvar) = &*pair2;
                      let mut started = lock.lock().unwrap();
                      *started = Some(resolver);
                      cvar.notify_one();
                      drop(started);

                      runtime.block_on(bg).expect("Failed to create DNS resolver");
                  });

    // Wait for the thread to start up.
    let &(ref lock, ref cvar) = &*pair;
    let mut resolver = lock.lock().unwrap();
    while resolver.is_none() {
        resolver = cvar.wait(resolver).unwrap();
    }

    // take the started resolver
    let resolver = mem::replace(&mut *resolver, None);

    // set the global resolver
    resolver.expect("resolver should not be none")
}

fn inner_resolve(config: Arc<Config>,
                 addr: &str,
                 port: u16,
                 check_forbidden: bool,
                 force_google: bool)
                 -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    let owned_addr = addr.to_owned();
    let owned_addr2 = owned_addr.clone();

    let resolver = if force_google {
        &*GLOBAL_GOOGLE_DNS_RESOLVER
    } else {
        &*GLOBAL_DNS_RESOLVER
    };

    resolver.lookup_ip(addr)
            .map_err(move |err| {
                         error!("Failed to resolve {}, err: {}", owned_addr2, err);
                         io::Error::new(io::ErrorKind::Other, "dns resolve error")
                     })
            .and_then(move |lookup_result| {
                          let mut vaddr = Vec::new();
                          for ip in lookup_result.iter() {
                              if check_forbidden {
                                  let forbidden_ip = &config.forbidden_ip;
                                  if forbidden_ip.contains(&ip) {
                                      debug!("Resolved {} => {}, which is skipped by forbidden_ip", owned_addr, ip);
                                      continue;
                                  }
                              }
                              vaddr.push(SocketAddr::new(ip, port));
                          }

                          if vaddr.is_empty() {
                              let err = io::Error::new(ErrorKind::Other,
                                                       format!("resolved {} to empty address, all IPs are filtered",
                                                               owned_addr));
                              Err(err)
                          } else {
                              debug!("Resolved {} => {:?}", owned_addr, vaddr);
                              Ok(vaddr)
                          }
                      })
}

pub fn resolve(config: Arc<Config>,
               addr: &str,
               port: u16,
               check_forbidden: bool)
               -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    inner_resolve(config, addr, port, check_forbidden, false)
}

pub fn resolve_by_google(config: Arc<Config>,
                         addr: &str,
                         port: u16,
                         check_forbidden: bool)
                         -> impl Future<Item = Vec<SocketAddr>, Error = io::Error> + Send {
    inner_resolve(config, addr, port, check_forbidden, true)
}
<|endoftext|>


<commit_before>extern crate chrono;
#[macro_use]
extern crate clap;
extern crate hostname;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate target_info;
extern crate xdg;

mod command_line_arguments;
mod configuration;
mod context;
mod datetime;
mod execution_timer;
mod program_info;
mod system_info;

use xdg::BaseDirectories;

use context::Context;
use execution_timer::ExecutionTimer;

// This produces various constants about the build environment which can be referred to using ::PKG_... syntax.
include!(concat!(env!("OUT_DIR"), "/built.rs"));

fn main() {
    std::env::set_var("IN_QORK", "1");
    let context = Context::new();
    configure_logging(context.xdg());
    let _timer = ExecutionTimer::with_start_message("main.main");
    log_build_info();
    context.log_created_message();
    load_user_configuration(&context);
}

fn configure_logging(xdg: &BaseDirectories) {
    let path = xdg.place_config_file("logging.toml");

    match path {
        Ok(p) => {
            if p.exists() {
                log4rs::init_file(&p, Default::default()).unwrap();
                info!("Logging initialized using file at {:?}", &p);
            }
        },
        Err(_) => {
            // Do nothing, not sure there is anything we can do.
        }
    }
}

fn log_build_info() {
    info!(r#"BuildInfo {{ PKG_VERSION: "{}", PROFILE: "{}", DEBUG: "{}", OPT_LEVEL: "{}", RUSTC: "{}", RUSTC_VERSION: "{}", FEATURES_STR: "{}", BUILD_TIME_UTC: "{}", arch: "{}", endian: "{}", env: "{}", family: "{}", os: "{}" }}"#,
        PKG_VERSION, PROFILE, DEBUG, OPT_LEVEL, RUSTC, RUSTC_VERSION, FEATURES_STR, BUILT_TIME_UTC,
        CFG_TARGET_ARCH, CFG_ENDIAN, CFG_ENV, CFG_FAMILY, CFG_OS
        );
}

fn load_user_configuration(context: &Context) {
    let _timer = ExecutionTimer::with_start_message("main.load_user_configuration");

    if !context.program_info().parsed_args().load_config() {
        info!("Loading of user configuration is disabled.");
        return
    }

    let xdg = context.xdg();
    let dir = xdg.get_config_home();
    if !dir.exists() {
        warn!("The config_directory does not exist, no config will be loaded, config_directory={:?}", dir);
        return
    }

    if !dir.is_dir() {
        warn!("The config_directory is a file, not a directory, no config will be loaded, config_directory={:?}", dir);
        return
    }

    info!("Loading user configuration from {:?}", dir);

    let path = xdg.place_config_file("config.toml");
    match path {
        Ok(p) => {
            if p.exists() {

            } else {
                info!("The config file {:?} does not exist", p);
            }
        }
        Err(e) => {
            warn!("Error while asking for config.toml: {}", e);
            return;
        }
    }
}
<commit_msg>Refactor config loading code.<commit_after>extern crate chrono;
#[macro_use]
extern crate clap;
extern crate hostname;
#[macro_use]
extern crate log;
extern crate log4rs;
#[macro_use]
extern crate target_info;
extern crate xdg;

mod command_line_arguments;
mod configuration;
mod context;
mod datetime;
mod execution_timer;
mod program_info;
mod system_info;

use std::fs;
use xdg::BaseDirectories;

use context::Context;
use execution_timer::ExecutionTimer;

// This produces various constants about the build environment which can be referred to using ::PKG_... syntax.
include!(concat!(env!("OUT_DIR"), "/built.rs"));

fn main() {
    std::env::set_var("IN_QORK", "1");
    let context = Context::new();
    configure_logging(context.xdg());
    let _timer = ExecutionTimer::with_start_message("main.main");
    log_build_info();
    context.log_created_message();
    load_user_configuration(&context);
}

fn configure_logging(xdg: &BaseDirectories) {
    let path = xdg.place_config_file("logging.toml");

    match path {
        Ok(p) => {
            if p.exists() {
                log4rs::init_file(&p, Default::default()).unwrap();
                info!("Logging initialized using file at {:?}", &p);
            }
        },
        Err(_) => {
            // Do nothing, not sure there is anything we can do.
        }
    }
}

fn log_build_info() {
    info!(r#"BuildInfo {{ PKG_VERSION: "{}", PROFILE: "{}", DEBUG: "{}", OPT_LEVEL: "{}", RUSTC: "{}", RUSTC_VERSION: "{}", FEATURES_STR: "{}", BUILD_TIME_UTC: "{}", arch: "{}", endian: "{}", env: "{}", family: "{}", os: "{}" }}"#,
        PKG_VERSION, PROFILE, DEBUG, OPT_LEVEL, RUSTC, RUSTC_VERSION, FEATURES_STR, BUILT_TIME_UTC,
        CFG_TARGET_ARCH, CFG_ENDIAN, CFG_ENV, CFG_FAMILY, CFG_OS
        );
}

fn load_user_configuration(context: &Context) {
    let _timer = ExecutionTimer::with_start_message("main.load_user_configuration");

    if !context.program_info().parsed_args().load_config() {
        info!("Loading of user configuration is disabled.");
        return
    }

    let xdg = context.xdg();
    let dir = xdg.get_config_home();
    if !dir.exists() {
        warn!("The config_directory does not exist, no config will be loaded, config_directory={:?}", dir);
        return
    }

    if !dir.is_dir() {
        warn!("The config_directory is a file, not a directory, no config will be loaded, config_directory={:?}", dir);
        return
    }

    info!("Loading user configuration from {:?}", dir);

    let path = xdg.place_config_file("config.toml");
    if path.is_err() {
        warn!("Could not locate config.toml file");
        return;
    }
    let path = path.unwrap();
    if !path.exists() {
        debug!("The file {:?} does not exist. No user config will be loaded.", path);
        return;
    }
    if !path.is_file() {
        warn!("The user configuration file {:?} appears to be a directory.", path);
        return;
    }

    // Ok, the file exists and can be loaded.
}
<|endoftext|>