<commit_before>use crate::{Assist, AssistId, AssistCtx};

use hir::Resolver;
use hir::db::HirDatabase;
use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
use ra_db::FilePosition;
use ra_fmt::{leading_indent, reindent};

use itertools::Itertools;

/// Given an `ast::ImplBlock`, resolves the target trait (the one being
/// implemented) to a `ast::TraitDef`.
pub(crate) fn resolve_target_trait_def(
    db: &impl HirDatabase,
    resolver: &Resolver,
    impl_block: &ast::ImplBlock,
) -> Option<TreeArc<ast::TraitDef>> {
    let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?;
    let hir_path = ast_path.path().and_then(hir::Path::from_ast)?;

    match resolver.resolve_path(db, &hir_path).take_types() {
        Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
        _ => None,
    }
}

pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
    let mut buf = String::new();

    for child in def.syntax().children() {
        if child.kind() == SyntaxKind::SEMI {
            buf.push_str(" { unimplemented!() }")
        } else {
            child.text().push_to(&mut buf);
        }
    }

    buf.trim_end().to_string()
}

pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let node = ctx.covering_node();
    let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
    let impl_item_list = impl_node.item_list()?;
    // Don't offer the assist when cursor is at the end, outside the block itself.
    if node.range().end() == impl_node.syntax().range().end() {
        return None;
    }

    let trait_def = {
        let db = ctx.db;
        // TODO: Can we get the position of cursor itself rather than supplied range?
        let range = ctx.frange;
        let position = FilePosition { file_id: range.file_id, offset: range.range.start() };
        let resolver = hir::source_binder::resolver_for_position(db, position);

        resolve_target_trait_def(db, &resolver, impl_node)?
    };

    let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
    let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };

    let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
    let impl_items = impl_item_list.impl_items();

    let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
    let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();

    let missing_fns: Vec<_> = trait_fns
        .into_iter()
        .filter(|t| def_name(t).is_some())
        .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
        .collect();
    if missing_fns.is_empty() {
        return None;
    }

    ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| {
        let indent = {
            // FIXME: Find a way to get the indent already used in the file.
            // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
            const DEFAULT_INDENT: &str = "    ";
            let first_item = impl_item_list.impl_items().next();
            let first_item_indent = first_item.and_then(|i| leading_indent(i.syntax()));
            let impl_block_indent = || leading_indent(impl_node.syntax()).unwrap_or_default();

            first_item_indent
                .map(ToOwned::to_owned)
                .unwrap_or_else(|| impl_block_indent().to_owned() + DEFAULT_INDENT)
        };

        let func_bodies = missing_fns.into_iter().map(build_func_body).join("\n");
        let func_bodies = String::from("\n") + &func_bodies;
        let func_bodies = reindent(&func_bodies, &indent) + "\n";

        let changed_range = {
            let last_whitespace = impl_item_list.syntax().children();
            let last_whitespace = last_whitespace.filter_map(ast::Whitespace::cast).last();
            let last_whitespace = last_whitespace.map(|w| w.syntax());

            let cursor_range = TextRange::from_to(node.range().end(), node.range().end());

            last_whitespace.map(|x| x.range()).unwrap_or(cursor_range)
        };

        let replaced_text_range = TextUnit::of_str(&func_bodies);

        edit.replace(changed_range, func_bodies);
        edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
    });

    ctx.build()
}

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

    #[test]
    fn test_add_missing_impl_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_copied_overriden_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) -> u32 { 42 }<|>
}",
        );
    }

    #[test]
    fn test_empty_impl_block() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {<|>}",
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {
    fn foo(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_cursor_after_empty_impl_block() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {}<|>",
        )
    }

    #[test]
    fn test_empty_trait() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo;
struct S;
impl Foo for S { <|> }",
        )
    }

    #[test]
    fn test_ignore_unnamed_trait_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S { <|> }",
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S {
    fn valid(some: u32) -> bool { false }<|>
}",
        )
    }
}
<commit_msg>Simplify trait resolution fragment<commit_after>use crate::{Assist, AssistId, AssistCtx};

use hir::Resolver;
use hir::db::HirDatabase;
use ra_syntax::{SmolStr, SyntaxKind, TextRange, TextUnit, TreeArc};
use ra_syntax::ast::{self, AstNode, FnDef, ImplItem, ImplItemKind, NameOwner};
use ra_db::FilePosition;
use ra_fmt::{leading_indent, reindent};

use itertools::Itertools;

/// Given an `ast::ImplBlock`, resolves the target trait (the one being
/// implemented) to a `ast::TraitDef`.
pub(crate) fn resolve_target_trait_def(
    db: &impl HirDatabase,
    resolver: &Resolver,
    impl_block: &ast::ImplBlock,
) -> Option<TreeArc<ast::TraitDef>> {
    let ast_path = impl_block.target_trait().map(AstNode::syntax).and_then(ast::PathType::cast)?;
    let hir_path = ast_path.path().and_then(hir::Path::from_ast)?;

    match resolver.resolve_path(db, &hir_path).take_types() {
        Some(hir::Resolution::Def(hir::ModuleDef::Trait(def))) => Some(def.source(db).1),
        _ => None,
    }
}

pub(crate) fn build_func_body(def: &ast::FnDef) -> String {
    let mut buf = String::new();

    for child in def.syntax().children() {
        if child.kind() == SyntaxKind::SEMI {
            buf.push_str(" { unimplemented!() }")
        } else {
            child.text().push_to(&mut buf);
        }
    }

    buf.trim_end().to_string()
}

pub(crate) fn add_missing_impl_members(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> {
    let node = ctx.covering_node();
    let impl_node = node.ancestors().find_map(ast::ImplBlock::cast)?;
    let impl_item_list = impl_node.item_list()?;
    // Don't offer the assist when cursor is at the end, outside the block itself.
    if node.range().end() == impl_node.syntax().range().end() {
        return None;
    }

    let trait_def = {
        let position = FilePosition { file_id: ctx.frange.file_id, offset: node.range().end() };
        let resolver = hir::source_binder::resolver_for_position(ctx.db, position);

        resolve_target_trait_def(ctx.db, &resolver, impl_node)?
    };

    let fn_def_opt = |kind| if let ImplItemKind::FnDef(def) = kind { Some(def) } else { None };
    let def_name = |def| -> Option<&SmolStr> { FnDef::name(def).map(ast::Name::text) };

    let trait_items = trait_def.syntax().descendants().find_map(ast::ItemList::cast)?.impl_items();
    let impl_items = impl_item_list.impl_items();

    let trait_fns = trait_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();
    let impl_fns = impl_items.map(ImplItem::kind).filter_map(fn_def_opt).collect::<Vec<_>>();

    let missing_fns: Vec<_> = trait_fns
        .into_iter()
        .filter(|t| def_name(t).is_some())
        .filter(|t| impl_fns.iter().all(|i| def_name(i) != def_name(t)))
        .collect();
    if missing_fns.is_empty() {
        return None;
    }

    ctx.add_action(AssistId("add_impl_missing_members"), "add missing impl members", |edit| {
        let indent = {
            // FIXME: Find a way to get the indent already used in the file.
            // Now, we copy the indent of first item or indent with 4 spaces relative to impl block
            const DEFAULT_INDENT: &str = "    ";
            let first_item = impl_item_list.impl_items().next();
            let first_item_indent = first_item.and_then(|i| leading_indent(i.syntax()));
            let impl_block_indent = || leading_indent(impl_node.syntax()).unwrap_or_default();

            first_item_indent
                .map(ToOwned::to_owned)
                .unwrap_or_else(|| impl_block_indent().to_owned() + DEFAULT_INDENT)
        };

        let func_bodies = missing_fns.into_iter().map(build_func_body).join("\n");
        let func_bodies = String::from("\n") + &func_bodies;
        let func_bodies = reindent(&func_bodies, &indent) + "\n";

        let changed_range = {
            let last_whitespace = impl_item_list.syntax().children();
            let last_whitespace = last_whitespace.filter_map(ast::Whitespace::cast).last();
            let last_whitespace = last_whitespace.map(|w| w.syntax());

            let cursor_range = TextRange::from_to(node.range().end(), node.range().end());

            last_whitespace.map(|x| x.range()).unwrap_or(cursor_range)
        };

        let replaced_text_range = TextUnit::of_str(&func_bodies);

        edit.replace(changed_range, func_bodies);
        edit.set_cursor(changed_range.start() + replaced_text_range - TextUnit::of_str("\n"));
    });

    ctx.build()
}

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

    #[test]
    fn test_add_missing_impl_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self);
    fn baz(&self);
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_copied_overriden_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    <|>
}",
            "
trait Foo {
    fn foo(&self);
    fn bar(&self) -> bool { true }
    fn baz(&self) -> u32 { 42 }
}

struct S;

impl Foo for S {
    fn bar(&self) {}
    fn foo(&self) { unimplemented!() }
    fn baz(&self) -> u32 { 42 }<|>
}",
        );
    }

    #[test]
    fn test_empty_impl_block() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {<|>}",
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {
    fn foo(&self) { unimplemented!() }<|>
}",
        );
    }

    #[test]
    fn test_cursor_after_empty_impl_block() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo { fn foo(&self); }
struct S;
impl Foo for S {}<|>",
        )
    }

    #[test]
    fn test_empty_trait() {
        check_assist_not_applicable(
            add_missing_impl_members,
            "
trait Foo;
struct S;
impl Foo for S { <|> }",
        )
    }

    #[test]
    fn test_ignore_unnamed_trait_members() {
        check_assist(
            add_missing_impl_members,
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S { <|> }",
            "
trait Foo {
    fn (arg: u32);
    fn valid(some: u32) -> bool { false }
}
struct S;
impl Foo for S {
    fn valid(some: u32) -> bool { false }<|>
}",
        )
    }
}
<|endoftext|>


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

//! This module provides linkage between libgraphviz traits and
//! `rustc::middle::typeck::infer::region_constraints`, generating a
//! rendering of the graph represented by the list of `Constraint`
//! instances (which make up the edges of the graph), as well as the
//! origin for each constraint (which are attached to the labels on
//! each edge).

/// For clarity, rename the graphviz crate locally to dot.
use graphviz as dot;

use hir::def_id::DefIndex;
use ty;
use middle::free_region::RegionRelations;
use middle::region;
use super::Constraint;
use infer::SubregionOrigin;
use infer::region_constraints::RegionConstraintData;
use util::nodemap::{FxHashMap, FxHashSet};

use std::borrow::Cow;
use std::collections::hash_map::Entry::Vacant;
use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};

fn print_help_message() {
    println!("\
-Z print-region-graph by default prints a region constraint graph for every \n\
function body, to the path `/tmp/constraints.nodeXXX.dot`, where the XXX is \n\
replaced with the node id of the function under analysis.                   \n\
                                                                            \n\
To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`,   \n\
where XXX is the node id desired.                                           \n\
                                                                            \n\
To generate output to some path other than the default                      \n\
`/tmp/constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`;  \n\
occurrences of the character `%` in the requested path will be replaced with\n\
the node id of the function under analysis.                                 \n\
                                                                            \n\
(Since you requested help via RUST_REGION_GRAPH=help, no region constraint  \n\
graphs will be printed.                                                     \n\
");
}

pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
    region_data: &RegionConstraintData<'tcx>,
    region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
{
    let tcx = region_rels.tcx;
    let context = region_rels.context;

    if !tcx.sess.opts.debugging_opts.print_region_graph {
        return;
    }

    let requested_node = env::var("RUST_REGION_GRAPH_NODE")
        .ok().and_then(|s| s.parse().map(DefIndex::from_raw_u32).ok());

    if requested_node.is_some() && requested_node != Some(context.index) {
        return;
    }

    let requested_output = env::var("RUST_REGION_GRAPH");
    debug!("requested_output: {:?} requested_node: {:?}",
           requested_output,
           requested_node);

    let output_path = {
        let output_template = match requested_output {
            Ok(ref s) if s == "help" => {
                static PRINTED_YET: AtomicBool = AtomicBool::new(false);
                if !PRINTED_YET.load(Ordering::SeqCst) {
                    print_help_message();
                    PRINTED_YET.store(true, Ordering::SeqCst);
                }
                return;
            }

            Ok(other_path) => other_path,
            Err(_) => "/tmp/constraints.node%.dot".to_string(),
        };

        if output_template.is_empty() {
            panic!("empty string provided as RUST_REGION_GRAPH");
        }

        if output_template.contains('%') {
            let mut new_str = String::new();
            for c in output_template.chars() {
                if c == '%' {
                    new_str.push_str(&context.index.as_raw_u32().to_string());
                } else {
                    new_str.push(c);
                }
            }
            new_str
        } else {
            output_template
        }
    };

    match dump_region_data_to(region_rels, &region_data.constraints, &output_path) {
        Ok(()) => {}
        Err(e) => {
            let msg = format!("io error dumping region constraints: {}", e);
            tcx.sess.err(&msg)
        }
    }
}

struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
    graph_name: String,
    region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
    map: &'a BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
    node_ids: FxHashMap<Node, usize>,
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
enum Node {
    RegionVid(ty::RegionVid),
    Region(ty::RegionKind),
}

#[derive(Clone, PartialEq, Eq, Debug, Copy)]
enum Edge<'tcx> {
    Constraint(Constraint<'tcx>),
    EnclScope(region::Scope, region::Scope),
}

impl<'a, 'gcx, 'tcx> ConstraintGraph<'a, 'gcx, 'tcx> {
    fn new(name: String,
           region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
           map: &'a ConstraintMap<'tcx>)
           -> ConstraintGraph<'a, 'gcx, 'tcx> {
        let mut i = 0;
        let mut node_ids = FxHashMap();
        {
            let mut add_node = |node| {
                if let Vacant(e) = node_ids.entry(node) {
                    e.insert(i);
                    i += 1;
                }
            };

            for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) {
                add_node(n1);
                add_node(n2);
            }

            region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
                add_node(Node::Region(ty::ReScope(sub)));
                add_node(Node::Region(ty::ReScope(sup)));
            });
        }

        ConstraintGraph {
            map,
            node_ids,
            region_rels,
            graph_name: name,
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn graph_id(&self) -> dot::Id {
        dot::Id::new(&*self.graph_name).unwrap()
    }
    fn node_id(&self, n: &Node) -> dot::Id {
        let node_id = match self.node_ids.get(n) {
            Some(node_id) => node_id,
            None => bug!("no node_id found for node: {:?}", n),
        };
        let name = || format!("node_{}", node_id);
        match dot::Id::new(name()) {
            Ok(id) => id,
            Err(_) => {
                bug!("failed to create graphviz node identified by {}", name());
            }
        }
    }
    fn node_label(&self, n: &Node) -> dot::LabelText {
        match *n {
            Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)),
            Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)),
        }
    }
    fn edge_label(&self, e: &Edge) -> dot::LabelText {
        match *e {
            Edge::Constraint(ref c) =>
                dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())),
            Edge::EnclScope(..) => dot::LabelText::label(format!("(enclosed)")),
        }
    }
}

fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
    match *c {
        Constraint::VarSubVar(rv_1, rv_2) =>
            (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
        Constraint::RegSubVar(r_1, rv_2) =>
            (Node::Region(*r_1), Node::RegionVid(rv_2)),
        Constraint::VarSubReg(rv_1, r_2) =>
            (Node::RegionVid(rv_1), Node::Region(*r_2)),
        Constraint::RegSubReg(r_1, r_2) =>
            (Node::Region(*r_1), Node::Region(*r_2)),
    }
}

fn edge_to_nodes(e: &Edge) -> (Node, Node) {
    match *e {
        Edge::Constraint(ref c) => constraint_to_nodes(c),
        Edge::EnclScope(sub, sup) => {
            (Node::Region(ty::ReScope(sub)),
             Node::Region(ty::ReScope(sup)))
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn nodes(&self) -> dot::Nodes<Node> {
        let mut set = FxHashSet();
        for node in self.node_ids.keys() {
            set.insert(*node);
        }
        debug!("constraint graph has {} nodes", set.len());
        set.into_iter().collect()
    }
    fn edges(&self) -> dot::Edges<Edge<'tcx>> {
        debug!("constraint graph has {} edges", self.map.len());
        let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
        self.region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
            v.push(Edge::EnclScope(sub, sup))
        });
        debug!("region graph has {} edges", v.len());
        Cow::Owned(v)
    }
    fn source(&self, edge: &Edge<'tcx>) -> Node {
        let (n1, _) = edge_to_nodes(edge);
        debug!("edge {:?} has source {:?}", edge, n1);
        n1
    }
    fn target(&self, edge: &Edge<'tcx>) -> Node {
        let (_, n2) = edge_to_nodes(edge);
        debug!("edge {:?} has target {:?}", edge, n2);
        n2
    }
}

pub type ConstraintMap<'tcx> = BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;

fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
                                       map: &ConstraintMap<'tcx>,
                                       path: &str)
                                       -> io::Result<()> {
    debug!("dump_region_data map (len: {}) path: {}",
           map.len(),
           path);
    let g = ConstraintGraph::new(format!("region_data"), region_rels, map);
    debug!("dump_region_data calling render");
    let mut v = Vec::new();
    dot::render(&g, &mut v).unwrap();
    File::create(path).and_then(|mut f| f.write_all(&v))
}
<commit_msg>rustc: Avoid /tmp/ in graphviz writing<commit_after>// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This module provides linkage between libgraphviz traits and
//! `rustc::middle::typeck::infer::region_constraints`, generating a
//! rendering of the graph represented by the list of `Constraint`
//! instances (which make up the edges of the graph), as well as the
//! origin for each constraint (which are attached to the labels on
//! each edge).

/// For clarity, rename the graphviz crate locally to dot.
use graphviz as dot;

use hir::def_id::DefIndex;
use ty;
use middle::free_region::RegionRelations;
use middle::region;
use super::Constraint;
use infer::SubregionOrigin;
use infer::region_constraints::RegionConstraintData;
use util::nodemap::{FxHashMap, FxHashSet};

use std::borrow::Cow;
use std::collections::hash_map::Entry::Vacant;
use std::collections::btree_map::BTreeMap;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::sync::atomic::{AtomicBool, Ordering};

fn print_help_message() {
    println!("\
-Z print-region-graph by default prints a region constraint graph for every \n\
function body, to the path `constraints.nodeXXX.dot`, where the XXX is \n\
replaced with the node id of the function under analysis.                   \n\
                                                                            \n\
To select one particular function body, set `RUST_REGION_GRAPH_NODE=XXX`,   \n\
where XXX is the node id desired.                                           \n\
                                                                            \n\
To generate output to some path other than the default                      \n\
`constraints.nodeXXX.dot`, set `RUST_REGION_GRAPH=/path/desired.dot`;  \n\
occurrences of the character `%` in the requested path will be replaced with\n\
the node id of the function under analysis.                                 \n\
                                                                            \n\
(Since you requested help via RUST_REGION_GRAPH=help, no region constraint  \n\
graphs will be printed.                                                     \n\
");
}

pub fn maybe_print_constraints_for<'a, 'gcx, 'tcx>(
    region_data: &RegionConstraintData<'tcx>,
    region_rels: &RegionRelations<'a, 'gcx, 'tcx>)
{
    let tcx = region_rels.tcx;
    let context = region_rels.context;

    if !tcx.sess.opts.debugging_opts.print_region_graph {
        return;
    }

    let requested_node = env::var("RUST_REGION_GRAPH_NODE")
        .ok().and_then(|s| s.parse().map(DefIndex::from_raw_u32).ok());

    if requested_node.is_some() && requested_node != Some(context.index) {
        return;
    }

    let requested_output = env::var("RUST_REGION_GRAPH");
    debug!("requested_output: {:?} requested_node: {:?}",
           requested_output,
           requested_node);

    let output_path = {
        let output_template = match requested_output {
            Ok(ref s) if s == "help" => {
                static PRINTED_YET: AtomicBool = AtomicBool::new(false);
                if !PRINTED_YET.load(Ordering::SeqCst) {
                    print_help_message();
                    PRINTED_YET.store(true, Ordering::SeqCst);
                }
                return;
            }

            Ok(other_path) => other_path,
            Err(_) => "constraints.node%.dot".to_string(),
        };

        if output_template.is_empty() {
            panic!("empty string provided as RUST_REGION_GRAPH");
        }

        if output_template.contains('%') {
            let mut new_str = String::new();
            for c in output_template.chars() {
                if c == '%' {
                    new_str.push_str(&context.index.as_raw_u32().to_string());
                } else {
                    new_str.push(c);
                }
            }
            new_str
        } else {
            output_template
        }
    };

    match dump_region_data_to(region_rels, &region_data.constraints, &output_path) {
        Ok(()) => {}
        Err(e) => {
            let msg = format!("io error dumping region constraints: {}", e);
            tcx.sess.err(&msg)
        }
    }
}

struct ConstraintGraph<'a, 'gcx: 'a+'tcx, 'tcx: 'a> {
    graph_name: String,
    region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
    map: &'a BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>,
    node_ids: FxHashMap<Node, usize>,
}

#[derive(Clone, Hash, PartialEq, Eq, Debug, Copy)]
enum Node {
    RegionVid(ty::RegionVid),
    Region(ty::RegionKind),
}

#[derive(Clone, PartialEq, Eq, Debug, Copy)]
enum Edge<'tcx> {
    Constraint(Constraint<'tcx>),
    EnclScope(region::Scope, region::Scope),
}

impl<'a, 'gcx, 'tcx> ConstraintGraph<'a, 'gcx, 'tcx> {
    fn new(name: String,
           region_rels: &'a RegionRelations<'a, 'gcx, 'tcx>,
           map: &'a ConstraintMap<'tcx>)
           -> ConstraintGraph<'a, 'gcx, 'tcx> {
        let mut i = 0;
        let mut node_ids = FxHashMap();
        {
            let mut add_node = |node| {
                if let Vacant(e) = node_ids.entry(node) {
                    e.insert(i);
                    i += 1;
                }
            };

            for (n1, n2) in map.keys().map(|c| constraint_to_nodes(c)) {
                add_node(n1);
                add_node(n2);
            }

            region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
                add_node(Node::Region(ty::ReScope(sub)));
                add_node(Node::Region(ty::ReScope(sup)));
            });
        }

        ConstraintGraph {
            map,
            node_ids,
            region_rels,
            graph_name: name,
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::Labeller<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn graph_id(&self) -> dot::Id {
        dot::Id::new(&*self.graph_name).unwrap()
    }
    fn node_id(&self, n: &Node) -> dot::Id {
        let node_id = match self.node_ids.get(n) {
            Some(node_id) => node_id,
            None => bug!("no node_id found for node: {:?}", n),
        };
        let name = || format!("node_{}", node_id);
        match dot::Id::new(name()) {
            Ok(id) => id,
            Err(_) => {
                bug!("failed to create graphviz node identified by {}", name());
            }
        }
    }
    fn node_label(&self, n: &Node) -> dot::LabelText {
        match *n {
            Node::RegionVid(n_vid) => dot::LabelText::label(format!("{:?}", n_vid)),
            Node::Region(n_rgn) => dot::LabelText::label(format!("{:?}", n_rgn)),
        }
    }
    fn edge_label(&self, e: &Edge) -> dot::LabelText {
        match *e {
            Edge::Constraint(ref c) =>
                dot::LabelText::label(format!("{:?}", self.map.get(c).unwrap())),
            Edge::EnclScope(..) => dot::LabelText::label(format!("(enclosed)")),
        }
    }
}

fn constraint_to_nodes(c: &Constraint) -> (Node, Node) {
    match *c {
        Constraint::VarSubVar(rv_1, rv_2) =>
            (Node::RegionVid(rv_1), Node::RegionVid(rv_2)),
        Constraint::RegSubVar(r_1, rv_2) =>
            (Node::Region(*r_1), Node::RegionVid(rv_2)),
        Constraint::VarSubReg(rv_1, r_2) =>
            (Node::RegionVid(rv_1), Node::Region(*r_2)),
        Constraint::RegSubReg(r_1, r_2) =>
            (Node::Region(*r_1), Node::Region(*r_2)),
    }
}

fn edge_to_nodes(e: &Edge) -> (Node, Node) {
    match *e {
        Edge::Constraint(ref c) => constraint_to_nodes(c),
        Edge::EnclScope(sub, sup) => {
            (Node::Region(ty::ReScope(sub)),
             Node::Region(ty::ReScope(sup)))
        }
    }
}

impl<'a, 'gcx, 'tcx> dot::GraphWalk<'a> for ConstraintGraph<'a, 'gcx, 'tcx> {
    type Node = Node;
    type Edge = Edge<'tcx>;
    fn nodes(&self) -> dot::Nodes<Node> {
        let mut set = FxHashSet();
        for node in self.node_ids.keys() {
            set.insert(*node);
        }
        debug!("constraint graph has {} nodes", set.len());
        set.into_iter().collect()
    }
    fn edges(&self) -> dot::Edges<Edge<'tcx>> {
        debug!("constraint graph has {} edges", self.map.len());
        let mut v: Vec<_> = self.map.keys().map(|e| Edge::Constraint(*e)).collect();
        self.region_rels.region_scope_tree.each_encl_scope(|sub, sup| {
            v.push(Edge::EnclScope(sub, sup))
        });
        debug!("region graph has {} edges", v.len());
        Cow::Owned(v)
    }
    fn source(&self, edge: &Edge<'tcx>) -> Node {
        let (n1, _) = edge_to_nodes(edge);
        debug!("edge {:?} has source {:?}", edge, n1);
        n1
    }
    fn target(&self, edge: &Edge<'tcx>) -> Node {
        let (_, n2) = edge_to_nodes(edge);
        debug!("edge {:?} has target {:?}", edge, n2);
        n2
    }
}

pub type ConstraintMap<'tcx> = BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>;

fn dump_region_data_to<'a, 'gcx, 'tcx>(region_rels: &RegionRelations<'a, 'gcx, 'tcx>,
                                       map: &ConstraintMap<'tcx>,
                                       path: &str)
                                       -> io::Result<()> {
    debug!("dump_region_data map (len: {}) path: {}",
           map.len(),
           path);
    let g = ConstraintGraph::new(format!("region_data"), region_rels, map);
    debug!("dump_region_data calling render");
    let mut v = Vec::new();
    dot::render(&g, &mut v).unwrap();
    File::create(path).and_then(|mut f| f.write_all(&v))
}
<|endoftext|>


<commit_before>use super::{Inner, Runtime};

use reactor::Reactor;

use std::io;
use std::sync::Mutex;
use std::time::Duration;

use num_cpus;
use tokio_reactor;
use tokio_threadpool::Builder as ThreadPoolBuilder;
use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer};
use tokio_trace_core as trace;

/// Builds Tokio Runtime with custom configuration values.
///
/// Methods can be chained in order to set the configuration values. The
/// Runtime is constructed by calling [`build`].
///
/// New instances of `Builder` are obtained via [`Builder::new`].
///
/// See function level documentation for details on the various configuration
/// settings.
///
/// [`build`]: #method.build
/// [`Builder::new`]: #method.new
///
/// # Examples
///
/// ```
/// extern crate tokio;
/// extern crate tokio_timer;
///
/// use std::time::Duration;
///
/// use tokio::runtime::Builder;
/// use tokio_timer::clock::Clock;
///
/// fn main() {
///     // build Runtime
///     let mut runtime = Builder::new()
///         .blocking_threads(4)
///         .clock(Clock::system())
///         .core_threads(4)
///         .keep_alive(Some(Duration::from_secs(60)))
///         .name_prefix("my-custom-name-")
///         .stack_size(3 * 1024 * 1024)
///         .build()
///         .unwrap();
///
///     // use runtime ...
/// }
/// ```
#[derive(Debug)]
pub struct Builder {
    /// Thread pool specific builder
    threadpool_builder: ThreadPoolBuilder,

    /// The number of worker threads
    core_threads: usize,

    /// The clock to use
    clock: Clock,
}

impl Builder {
    /// Returns a new runtime builder initialized with default configuration
    /// values.
    ///
    /// Configuration methods can be chained on the return value.
    pub fn new() -> Builder {
        let core_threads = num_cpus::get().max(1);

        let mut threadpool_builder = ThreadPoolBuilder::new();
        threadpool_builder.name_prefix("tokio-runtime-worker-");
        threadpool_builder.pool_size(core_threads);

        Builder {
            threadpool_builder,
            core_threads,
            clock: Clock::new(),
        }
    }

    /// Set the `Clock` instance that will be used by the runtime.
    pub fn clock(&mut self, clock: Clock) -> &mut Self {
        self.clock = clock;
        self
    }

    /// Set builder to set up the thread pool instance.
    #[deprecated(
        since = "0.1.9",
        note = "use the `core_threads`, `blocking_threads`, `name_prefix`, \
                `keep_alive`, and `stack_size` functions on `runtime::Builder`, \
                instead")]
    #[doc(hidden)]
    pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
        self.threadpool_builder = val;
        self
    }

    /// Set the maximum number of worker threads for the `Runtime`'s thread pool.
    ///
    /// This must be a number between 1 and 32,768 though it is advised to keep
    /// this value on the smaller side.
    ///
    /// The default value is the number of cores available to the system.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .core_threads(4)
    ///     .build()
    ///     .unwrap();
    /// # }
    /// ```
    pub fn core_threads(&mut self, val: usize) -> &mut Self {
        self.core_threads = val;
        self.threadpool_builder.pool_size(val);
        self
    }

    /// Set the maximum number of concurrent blocking sections in the `Runtime`'s
    /// thread pool.
    ///
    /// When the maximum concurrent `blocking` calls is reached, any further
    /// calls to `blocking` will return `NotReady` and the task is notified once
    /// previously in-flight calls to `blocking` return.
    ///
    /// This must be a number between 1 and 32,768 though it is advised to keep
    /// this value on the smaller side.
    ///
    /// The default value is 100.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .blocking_threads(200)
    ///     .build();
    /// # }
    /// ```
    pub fn blocking_threads(&mut self, val: usize) -> &mut Self {
        self.threadpool_builder.max_blocking(val);
        self
    }

    /// Set the worker thread keep alive duration for threads in the `Runtime`'s
    /// thread pool.
    ///
    /// If set, a worker thread will wait for up to the specified duration for
    /// work, at which point the thread will shutdown. When work becomes
    /// available, a new thread will eventually be spawned to replace the one
    /// that shut down.
    ///
    /// When the value is `None`, the thread will wait for work forever.
    ///
    /// The default value is `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    /// use std::time::Duration;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .keep_alive(Some(Duration::from_secs(30)))
    ///     .build();
    /// # }
    /// ```
    pub fn keep_alive(&mut self, val: Option<Duration>) -> &mut Self {
        self.threadpool_builder.keep_alive(val);
        self
    }

    /// Set name prefix of threads spawned by the `Runtime`'s thread pool.
    ///
    /// Thread name prefix is used for generating thread names. For example, if
    /// prefix is `my-pool-`, then threads in the pool will get names like
    /// `my-pool-1` etc.
    ///
    /// The default prefix is "tokio-runtime-worker-".
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .name_prefix("my-pool-")
    ///     .build();
    /// # }
    /// ```
    pub fn name_prefix<S: Into<String>>(&mut self, val: S) -> &mut Self {
        self.threadpool_builder.name_prefix(val);
        self
    }

    /// Set the stack size (in bytes) for worker threads.
    ///
    /// The actual stack size may be greater than this value if the platform
    /// specifies minimal stack size.
    ///
    /// The default stack size for spawned threads is 2 MiB, though this
    /// particular stack size is subject to change in the future.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .stack_size(32 * 1024)
    ///     .build();
    /// # }
    /// ```
    pub fn stack_size(&mut self, val: usize) -> &mut Self {
        self.threadpool_builder.stack_size(val);
        self
    }

    /// Execute function `f` after each thread is started but before it starts
    /// doing work.
    ///
    /// This is intended for bookkeeping and monitoring use cases.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let thread_pool = runtime::Builder::new()
    ///     .after_start(|| {
    ///         println!("thread started");
    ///     })
    ///     .build();
    /// # }
    /// ```
    pub fn after_start<F>(&mut self, f: F) -> &mut Self
        where F: Fn() + Send + Sync + 'static
    {
        self.threadpool_builder.after_start(f);
        self
    }

    /// Execute function `f` before each thread stops.
    ///
    /// This is intended for bookkeeping and monitoring use cases.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let thread_pool = runtime::Builder::new()
    ///     .before_stop(|| {
    ///         println!("thread stopping");
    ///     })
    ///     .build();
    /// # }
    /// ```
    pub fn before_stop<F>(&mut self, f: F) -> &mut Self
        where F: Fn() + Send + Sync + 'static
    {
        self.threadpool_builder.before_stop(f);
        self
    }

    /// Create the configured `Runtime`.
    ///
    /// The returned `ThreadPool` instance is ready to spawn tasks.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # use tokio::runtime::Builder;
    /// # pub fn main() {
    /// let runtime = Builder::new().build().unwrap();
    /// // ... call runtime.run(...)
    /// # let _ = runtime;
    /// # }
    /// ```
    pub fn build(&mut self) -> io::Result<Runtime> {
        // TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
        self.threadpool_builder.pool_size(self.core_threads);

        let mut reactor_handles = Vec::new();
        let mut timer_handles = Vec::new();
        let mut timers = Vec::new();

        for _ in 0..self.core_threads {
            // Create a new reactor.
            let reactor = Reactor::new()?;
            reactor_handles.push(reactor.handle());

            // Create a new timer.
            let timer = Timer::new_with_now(reactor, self.clock.clone());
            timer_handles.push(timer.handle());
            timers.push(Mutex::new(Some(timer)));
        }

        // Get a handle to the clock for the runtime.
        let clock = self.clock.clone();

        // Get the current trace dispatcher.
        // TODO(eliza): when `tokio-trace-core` is stable enough to take a
        // public API dependency, we should allow users to set a custom
        // subscriber for the runtime.
        let dispatch = trace::dispatcher::get_default(trace::Dispatch::clone);

        let pool = self
            .threadpool_builder
            .around_worker(move |w, enter| {
                let index = w.id().to_usize();

                tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
                    clock::with_default(&clock, enter, |enter| {
                        timer::with_default(&timer_handles[index], enter, |_| {
                            trace::dispatcher::with_default(&dispatch, || {
                                w.run();
                            })
                        });
                    })
                });
            })
            .custom_park(move |worker_id| {
                let index = worker_id.to_usize();

                timers[index]
                    .lock()
                    .unwrap()
                    .take()
                    .unwrap()
            })
            .build();

        // To support deprecated `reactor()` function
        let reactor = Reactor::new()?;
        let reactor_handle = reactor.handle();

        Ok(Runtime {
            inner: Some(Inner {
                reactor_handle,
                reactor: Mutex::new(Some(reactor)),
                pool,
            }),
        })
    }
}
<commit_msg>rt: forward panic_handler to tokio::runtime::Builder (#1055)<commit_after>use super::{Inner, Runtime};

use reactor::Reactor;

use std::io;
use std::sync::Mutex;
use std::time::Duration;
use std::any::Any;

use num_cpus;
use tokio_reactor;
use tokio_threadpool::Builder as ThreadPoolBuilder;
use tokio_timer::clock::{self, Clock};
use tokio_timer::timer::{self, Timer};
use tokio_trace_core as trace;

/// Builds Tokio Runtime with custom configuration values.
///
/// Methods can be chained in order to set the configuration values. The
/// Runtime is constructed by calling [`build`].
///
/// New instances of `Builder` are obtained via [`Builder::new`].
///
/// See function level documentation for details on the various configuration
/// settings.
///
/// [`build`]: #method.build
/// [`Builder::new`]: #method.new
///
/// # Examples
///
/// ```
/// extern crate tokio;
/// extern crate tokio_timer;
///
/// use std::time::Duration;
///
/// use tokio::runtime::Builder;
/// use tokio_timer::clock::Clock;
///
/// fn main() {
///     // build Runtime
///     let mut runtime = Builder::new()
///         .blocking_threads(4)
///         .clock(Clock::system())
///         .core_threads(4)
///         .keep_alive(Some(Duration::from_secs(60)))
///         .name_prefix("my-custom-name-")
///         .stack_size(3 * 1024 * 1024)
///         .build()
///         .unwrap();
///
///     // use runtime ...
/// }
/// ```
#[derive(Debug)]
pub struct Builder {
    /// Thread pool specific builder
    threadpool_builder: ThreadPoolBuilder,

    /// The number of worker threads
    core_threads: usize,

    /// The clock to use
    clock: Clock,
}

impl Builder {
    /// Returns a new runtime builder initialized with default configuration
    /// values.
    ///
    /// Configuration methods can be chained on the return value.
    pub fn new() -> Builder {
        let core_threads = num_cpus::get().max(1);

        let mut threadpool_builder = ThreadPoolBuilder::new();
        threadpool_builder.name_prefix("tokio-runtime-worker-");
        threadpool_builder.pool_size(core_threads);

        Builder {
            threadpool_builder,
            core_threads,
            clock: Clock::new(),
        }
    }

    /// Set the `Clock` instance that will be used by the runtime.
    pub fn clock(&mut self, clock: Clock) -> &mut Self {
        self.clock = clock;
        self
    }

    /// Set builder to set up the thread pool instance.
    #[deprecated(
        since = "0.1.9",
        note = "use the `core_threads`, `blocking_threads`, `name_prefix`, \
                `keep_alive`, and `stack_size` functions on `runtime::Builder`, \
                instead")]
    #[doc(hidden)]
    pub fn threadpool_builder(&mut self, val: ThreadPoolBuilder) -> &mut Self {
        self.threadpool_builder = val;
        self
    }

    /// Sets a callback to handle panics in futures.
    ///
    /// The callback is triggered when a panic during a future bubbles up to
    /// Tokio. By default Tokio catches these panics, and they will be ignored.
    /// The parameter passed to this callback is the same error value returned
    /// from `std::panic::catch_unwind()`. To abort the process on panics, use
    /// `std::panic::resume_unwind()` in this callback as shown below.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .panic_handler(|err| std::panic::resume_unwind(err))
    ///     .build()
    ///     .unwrap();
    /// # }
    /// ```
    pub fn panic_handler<F>(&mut self, f: F) -> &mut Self
    where
        F: Fn(Box<Any + Send>) + Send + Sync + 'static,
    {
        self.threadpool_builder.panic_handler(f);
        self
    }

    /// Set the maximum number of worker threads for the `Runtime`'s thread pool.
    ///
    /// This must be a number between 1 and 32,768 though it is advised to keep
    /// this value on the smaller side.
    ///
    /// The default value is the number of cores available to the system.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .core_threads(4)
    ///     .build()
    ///     .unwrap();
    /// # }
    /// ```
    pub fn core_threads(&mut self, val: usize) -> &mut Self {
        self.core_threads = val;
        self.threadpool_builder.pool_size(val);
        self
    }

    /// Set the maximum number of concurrent blocking sections in the `Runtime`'s
    /// thread pool.
    ///
    /// When the maximum concurrent `blocking` calls is reached, any further
    /// calls to `blocking` will return `NotReady` and the task is notified once
    /// previously in-flight calls to `blocking` return.
    ///
    /// This must be a number between 1 and 32,768 though it is advised to keep
    /// this value on the smaller side.
    ///
    /// The default value is 100.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .blocking_threads(200)
    ///     .build();
    /// # }
    /// ```
    pub fn blocking_threads(&mut self, val: usize) -> &mut Self {
        self.threadpool_builder.max_blocking(val);
        self
    }

    /// Set the worker thread keep alive duration for threads in the `Runtime`'s
    /// thread pool.
    ///
    /// If set, a worker thread will wait for up to the specified duration for
    /// work, at which point the thread will shutdown. When work becomes
    /// available, a new thread will eventually be spawned to replace the one
    /// that shut down.
    ///
    /// When the value is `None`, the thread will wait for work forever.
    ///
    /// The default value is `None`.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    /// use std::time::Duration;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .keep_alive(Some(Duration::from_secs(30)))
    ///     .build();
    /// # }
    /// ```
    pub fn keep_alive(&mut self, val: Option<Duration>) -> &mut Self {
        self.threadpool_builder.keep_alive(val);
        self
    }

    /// Set name prefix of threads spawned by the `Runtime`'s thread pool.
    ///
    /// Thread name prefix is used for generating thread names. For example, if
    /// prefix is `my-pool-`, then threads in the pool will get names like
    /// `my-pool-1` etc.
    ///
    /// The default prefix is "tokio-runtime-worker-".
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .name_prefix("my-pool-")
    ///     .build();
    /// # }
    /// ```
    pub fn name_prefix<S: Into<String>>(&mut self, val: S) -> &mut Self {
        self.threadpool_builder.name_prefix(val);
        self
    }

    /// Set the stack size (in bytes) for worker threads.
    ///
    /// The actual stack size may be greater than this value if the platform
    /// specifies minimal stack size.
    ///
    /// The default stack size for spawned threads is 2 MiB, though this
    /// particular stack size is subject to change in the future.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let mut rt = runtime::Builder::new()
    ///     .stack_size(32 * 1024)
    ///     .build();
    /// # }
    /// ```
    pub fn stack_size(&mut self, val: usize) -> &mut Self {
        self.threadpool_builder.stack_size(val);
        self
    }

    /// Execute function `f` after each thread is started but before it starts
    /// doing work.
    ///
    /// This is intended for bookkeeping and monitoring use cases.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let thread_pool = runtime::Builder::new()
    ///     .after_start(|| {
    ///         println!("thread started");
    ///     })
    ///     .build();
    /// # }
    /// ```
    pub fn after_start<F>(&mut self, f: F) -> &mut Self
        where F: Fn() + Send + Sync + 'static
    {
        self.threadpool_builder.after_start(f);
        self
    }

    /// Execute function `f` before each thread stops.
    ///
    /// This is intended for bookkeeping and monitoring use cases.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # extern crate futures;
    /// # use tokio::runtime;
    ///
    /// # pub fn main() {
    /// let thread_pool = runtime::Builder::new()
    ///     .before_stop(|| {
    ///         println!("thread stopping");
    ///     })
    ///     .build();
    /// # }
    /// ```
    pub fn before_stop<F>(&mut self, f: F) -> &mut Self
        where F: Fn() + Send + Sync + 'static
    {
        self.threadpool_builder.before_stop(f);
        self
    }

    /// Create the configured `Runtime`.
    ///
    /// The returned `ThreadPool` instance is ready to spawn tasks.
    ///
    /// # Examples
    ///
    /// ```
    /// # extern crate tokio;
    /// # use tokio::runtime::Builder;
    /// # pub fn main() {
    /// let runtime = Builder::new().build().unwrap();
    /// // ... call runtime.run(...)
    /// # let _ = runtime;
    /// # }
    /// ```
    pub fn build(&mut self) -> io::Result<Runtime> {
        // TODO(stjepang): Once we remove the `threadpool_builder` method, remove this line too.
        self.threadpool_builder.pool_size(self.core_threads);

        let mut reactor_handles = Vec::new();
        let mut timer_handles = Vec::new();
        let mut timers = Vec::new();

        for _ in 0..self.core_threads {
            // Create a new reactor.
            let reactor = Reactor::new()?;
            reactor_handles.push(reactor.handle());

            // Create a new timer.
            let timer = Timer::new_with_now(reactor, self.clock.clone());
            timer_handles.push(timer.handle());
            timers.push(Mutex::new(Some(timer)));
        }

        // Get a handle to the clock for the runtime.
        let clock = self.clock.clone();

        // Get the current trace dispatcher.
        // TODO(eliza): when `tokio-trace-core` is stable enough to take a
        // public API dependency, we should allow users to set a custom
        // subscriber for the runtime.
        let dispatch = trace::dispatcher::get_default(trace::Dispatch::clone);

        let pool = self
            .threadpool_builder
            .around_worker(move |w, enter| {
                let index = w.id().to_usize();

                tokio_reactor::with_default(&reactor_handles[index], enter, |enter| {
                    clock::with_default(&clock, enter, |enter| {
                        timer::with_default(&timer_handles[index], enter, |_| {
                            trace::dispatcher::with_default(&dispatch, || {
                                w.run();
                            })
                        });
                    })
                });
            })
            .custom_park(move |worker_id| {
                let index = worker_id.to_usize();

                timers[index]
                    .lock()
                    .unwrap()
                    .take()
                    .unwrap()
            })
            .build();

        // To support deprecated `reactor()` function
        let reactor = Reactor::new()?;
        let reactor_handle = reactor.handle();

        Ok(Runtime {
            inner: Some(Inner {
                reactor_handle,
                reactor: Mutex::new(Some(reactor)),
                pool,
            }),
        })
    }
}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the list directory action.
//!
//! A list directory action stats all files in the provided directory.

use crate::session::{self, Session};

use std::fs::{Metadata};
use std::path::{PathBuf, Path};

/// A request type for the list directory action.
pub struct Request {
    path: PathBuf,
}

/// A response type for the list directory action.
pub struct Response {
    path: PathBuf,
    metadata: Metadata,
}

/// An error type for failures that can occur during the list directory action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to list a directory.
    ListDir(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            ListDir(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            ListDir(ref error) => {
                write!(fmt, "unable to list directory: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let entries = crate::fs::list_dir(&request.path)
        .map_err(Error::ListDir)?;

    for entry in entries {
        session.reply(Response {
            path: entry.path,
            metadata: entry.metadata,
        })?;
    }

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::ListDirRequest;

    fn from_proto(proto: Self::Proto) -> Result<Request, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::SystemTime;
    use tempfile::tempdir;

    #[test]
    fn test_empty_dir() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path()),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 0);
    }

    #[test]
    fn test_nonexistent_path() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path().join("nonexistent_subdir")),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_lexicographical_order() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("юникод")).unwrap();
        std::fs::File::create(dir_path.join("unicode")).unwrap();
        std::fs::File::create(dir_path.join("file")).unwrap();
        std::fs::File::create(dir_path.join("afile")).unwrap();
        std::fs::File::create(dir_path.join("Datei")).unwrap();
        std::fs::File::create(dir_path.join("snake_case")).unwrap();
        std::fs::File::create(dir_path.join("CamelCase")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 7);
        assert_eq!(&replies[0].path,
                   &dir_path.join("CamelCase"));
        assert_eq!(&replies[1].path,
                   &dir_path.join("Datei"));
        assert_eq!(&replies[2].path,
                   &dir_path.join("afile"));
        assert_eq!(&replies[3].path,
                   &dir_path.join("file"));
        assert_eq!(&replies[4].path,
                   &dir_path.join("snake_case"));
        assert_eq!(&replies[5].path,
                   &dir_path.join("unicode"));
        assert_eq!(&replies[6].path,
                   &dir_path.join("юникод"));
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_dir_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let inner_dir_path = &dir_path.join("dir");
        std::fs::create_dir(&inner_dir_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        handle(&mut session, request).unwrap();
        assert_eq!(session.reply_count(), 1);
        let inner_dir = &session.reply::<Response>(0);
        assert_eq!(&inner_dir.path, inner_dir_path);
        assert_eq!(inner_dir.metadata.uid(), users::get_current_uid());
        assert_eq!(inner_dir.metadata.gid(), users::get_current_gid());
        assert_eq!(inner_dir.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(inner_dir.metadata.mode() & 0o40000, 0o40000);
        assert_eq!(inner_dir.metadata.nlink(), 2);

        assert!(inner_dir.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.modified().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_symlink_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let sl_path = dir_path.join("symlink");
        std::os::unix::fs::symlink(&file_path, &sl_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);
        let symlink = replies[1];
        assert_eq!(&symlink.path, &sl_path);
        assert_eq!(symlink.metadata.mode() & 0o120000, 0o120000);
        assert_eq!(symlink.metadata.nlink(), 1);
        assert!(symlink.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.modified().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_file_response_linux() {
        use std::os::unix::fs::MetadataExt as _;
        use std::os::unix::fs::PermissionsExt;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        std::fs::set_permissions(&file_path,
                                 PermissionsExt::from_mode(0o664)).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert_eq!(file.metadata.mode(), 0o100664);
        assert_eq!(file.metadata.uid(), users::get_current_uid());
        assert_eq!(file.metadata.gid(), users::get_current_gid());
        assert_eq!(file.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(file.metadata.nlink(), 1);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    fn test_file_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_st_flags_linux() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
    }

    #[test]
    fn test_unicode_paths() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("❤ℝℝG❤")).unwrap();
        std::fs::File::create(dir_path.join("файл")).unwrap();
        std::fs::File::create(dir_path.join("ファイル")).unwrap();
        std::fs::File::create(dir_path.join("αρχείο")).unwrap();
        std::fs::File::create(dir_path.join("फ़ाइल")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 5);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(1);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(2);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(3);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(4);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }
}
<commit_msg>Improve docs for the list directory action.<commit_after>// Copyright 2020 Google LLC
//
// Use of this source code is governed by an MIT-style license that can be found
// in the LICENSE file or at https://opensource.org/licenses/MIT.

//! A handler and associated types for the list directory action.
//!
//! A list directory action collects basic metadata for all files in the
//! provided directory.
//!
//! Note that this includes only entries directly under the specified directory.
//! For more sophisticated recursive traversals, please look into the [timeline]
//! action.
//!
//! [timeline]: ../timeline/index.html

use crate::session::{self, Session};

use std::fs::{Metadata};
use std::path::{PathBuf, Path};

/// A request type for the list directory action.
pub struct Request {
    /// A path to the directory ought to be listed.
    path: PathBuf,
}

/// A response type for the list directory action.
pub struct Response {
    /// A full path to a particular file within the listed directory.
    path: PathBuf,
    /// Metadata about a particular file within the listed directory.
    metadata: Metadata,
}

/// An error type for failures that can occur during the list directory action.
#[derive(Debug)]
enum Error {
    /// A failure occurred during the attempt to list a directory.
    ListDir(std::io::Error),
}

impl std::error::Error for Error {

    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        use Error::*;

        match *self {
            ListDir(ref error) => Some(error),
        }
    }
}

impl std::fmt::Display for Error {

    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        use Error::*;

        match *self {
            ListDir(ref error) => {
                write!(fmt, "unable to list directory: {}", error)
            }
        }
    }
}

impl From<Error> for session::Error {

    fn from(error: Error) -> session::Error {
        session::Error::action(error)
    }
}

/// Handles requests for the list directory action.
pub fn handle<S>(session: &mut S, request: Request) -> session::Result<()>
where
    S: Session,
{
    let entries = crate::fs::list_dir(&request.path)
        .map_err(Error::ListDir)?;

    for entry in entries {
        session.reply(Response {
            path: entry.path,
            metadata: entry.metadata,
        })?;
    }

    Ok(())
}

impl super::Request for Request {

    type Proto = rrg_proto::ListDirRequest;

    fn from_proto(proto: Self::Proto) -> Result<Request, session::ParseError> {
        use std::convert::TryInto as _;

        let path = proto.pathspec
            .ok_or(session::MissingFieldError::new("path spec"))?
            .try_into().map_err(session::ParseError::malformed)?;

        Ok(Request {
            path: path,
        })
    }
}

impl super::Response for Response {

    const RDF_NAME: Option<&'static str> = Some("StatEntry");

    type Proto = rrg_proto::StatEntry;

    fn into_proto(self) -> Self::Proto {
        use rrg_proto::convert::IntoLossy as _;

        rrg_proto::StatEntry {
            pathspec: Some(self.path.into()),
            ..self.metadata.into_lossy()
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::time::SystemTime;
    use tempfile::tempdir;

    #[test]
    fn test_empty_dir() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path()),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 0);
    }

    #[test]
    fn test_nonexistent_path() {
        let dir = tempdir().unwrap();
        let request = super::Request {
            path: PathBuf::from(dir.path().join("nonexistent_subdir")),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_err());
    }

    #[test]
    fn test_lexicographical_order() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("юникод")).unwrap();
        std::fs::File::create(dir_path.join("unicode")).unwrap();
        std::fs::File::create(dir_path.join("file")).unwrap();
        std::fs::File::create(dir_path.join("afile")).unwrap();
        std::fs::File::create(dir_path.join("Datei")).unwrap();
        std::fs::File::create(dir_path.join("snake_case")).unwrap();
        std::fs::File::create(dir_path.join("CamelCase")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 7);
        assert_eq!(&replies[0].path,
                   &dir_path.join("CamelCase"));
        assert_eq!(&replies[1].path,
                   &dir_path.join("Datei"));
        assert_eq!(&replies[2].path,
                   &dir_path.join("afile"));
        assert_eq!(&replies[3].path,
                   &dir_path.join("file"));
        assert_eq!(&replies[4].path,
                   &dir_path.join("snake_case"));
        assert_eq!(&replies[5].path,
                   &dir_path.join("unicode"));
        assert_eq!(&replies[6].path,
                   &dir_path.join("юникод"));
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_dir_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let inner_dir_path = &dir_path.join("dir");
        std::fs::create_dir(&inner_dir_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        handle(&mut session, request).unwrap();
        assert_eq!(session.reply_count(), 1);
        let inner_dir = &session.reply::<Response>(0);
        assert_eq!(&inner_dir.path, inner_dir_path);
        assert_eq!(inner_dir.metadata.uid(), users::get_current_uid());
        assert_eq!(inner_dir.metadata.gid(), users::get_current_gid());
        assert_eq!(inner_dir.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(inner_dir.metadata.mode() & 0o40000, 0o40000);
        assert_eq!(inner_dir.metadata.nlink(), 2);

        assert!(inner_dir.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.modified().unwrap() <= SystemTime::now());
        assert!(inner_dir.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_symlink_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let sl_path = dir_path.join("symlink");
        std::os::unix::fs::symlink(&file_path, &sl_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());

        let mut replies = session.replies().collect::<Vec<&Response>>();
        replies.sort_by_key(|reply| reply.path.clone());

        assert_eq!(replies.len(), 2);
        let symlink = replies[1];
        assert_eq!(&symlink.path, &sl_path);
        assert_eq!(symlink.metadata.mode() & 0o120000, 0o120000);
        assert_eq!(symlink.metadata.nlink(), 1);
        assert!(symlink.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.modified().unwrap() <= SystemTime::now());
        assert!(symlink.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_file_response_linux() {
        use std::os::unix::fs::MetadataExt as _;
        use std::os::unix::fs::PermissionsExt;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        std::fs::set_permissions(&file_path,
                                 PermissionsExt::from_mode(0o664)).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert_eq!(file.metadata.mode(), 0o100664);
        assert_eq!(file.metadata.uid(), users::get_current_uid());
        assert_eq!(file.metadata.gid(), users::get_current_gid());
        assert_eq!(file.metadata.dev(),
                   dir_path.metadata().unwrap().dev());
        assert_eq!(file.metadata.nlink(), 1);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    fn test_file_response() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }

    #[test]
    #[cfg(target_os = "linux")]
    fn test_st_flags_linux() {
        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        let file_path = dir_path.join("file");
        std::fs::File::create(&file_path).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 1);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.path, file_path);
    }

    #[test]
    fn test_unicode_paths() {
        use std::os::unix::fs::MetadataExt as _;

        let dir = tempdir().unwrap();
        let dir_path = dir.path();
        std::fs::File::create(dir_path.join("❤ℝℝG❤")).unwrap();
        std::fs::File::create(dir_path.join("файл")).unwrap();
        std::fs::File::create(dir_path.join("ファイル")).unwrap();
        std::fs::File::create(dir_path.join("αρχείο")).unwrap();
        std::fs::File::create(dir_path.join("फ़ाइल")).unwrap();
        let request = super::Request {
            path: PathBuf::from(&dir_path),
        };
        let mut session = session::test::Fake::new();
        assert!(handle(&mut session, request).is_ok());
        assert_eq!(session.reply_count(), 5);
        let file = &session.reply::<Response>(0);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(1);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(2);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(3);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
        let file = &session.reply::<Response>(4);
        assert_eq!(file.metadata.size(), 0);
        assert!(file.metadata.accessed().unwrap() <= SystemTime::now());
        assert!(file.metadata.modified().unwrap() <= SystemTime::now());
        assert!(file.metadata.created().unwrap() <= SystemTime::now());
    }
}
<|endoftext|>