<commit_before>

use visit::IntoNeighbors;
use visit::{VisitMap, Visitable};

/// Strictly monotonically increasing event time for a depth first search.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default)]
pub struct Time(pub u64);

/// A depth first search (DFS) visitor event.
#[derive(Copy, Clone, Debug)]
pub enum DfsEvent<N> {
    Discover(N, Time),
    TreeEdge(N, N),
    BackEdge(N, N),
    /// A cross or forward edge.
    ///
    /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
    /// then it is a forward edge, else a cross edge.
    CrossForwardEdge(N, N),
    Finish(N, Time),
}

macro_rules! control {
    ($e:expr) => {
        match $e {
            x => if x.should_break() {
                return x;
            } else {
            }
        }
    }
}

/// Control flow for visitor callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
    Continue,
    Break(B),
}

impl<B> Control<B> {
    /// Get the value in `Control::Break(_)`, if present.
    pub fn break_value(self) -> Option<B> {
        match self {
            Control::Continue => None,
            Control::Break(b) => Some(b),
        }
    }
}

/// The default is `Continue`.
impl<B> Default for Control<B> {
    fn default() -> Self { Control::Continue }
}

/// Control flow for visitor callbacks.
///
/// The empty return value `()` will continue until the visit is done.
pub trait ControlFlow : Default {
    #[inline]
    fn should_break(&self) -> bool { false }
}

impl ControlFlow for () { }

impl<B> ControlFlow for Control<B> {
    fn should_break(&self) -> bool {
        if let Control::Break(_) = *self { true } else { false }
    }
}

/// A recursive depth first search.
///
/// Starting points are the nodes in the iterator `starts` (specify just one
/// start vertex *x* by using `Some(x)`).
///
/// The visit emits discovery and finish events for each reachable vertex,
/// and edge classification of each reachable edge. `visitor` is called for each
/// event, see [`DfsEvent`][de] for possible values.
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
/// break the visit early, and the last control value is returned by the
/// function.
///
/// [de]: enum.DfsEvent.html
///
/// # Example
///
/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
/// the goal vertex.
///
/// ```
/// use petgraph::prelude::*;
/// use petgraph::graph::node_index as n;
/// use petgraph::visit::depth_first_search;
/// use petgraph::visit::{DfsEvent, Control};
///
/// let gr: Graph<(), ()> = Graph::from_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 3),
///     (2, 3), (2, 4),
///     (4, 0), (4, 5),
/// ]);
///
/// // record each predecessor, mapping node → node
/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
/// let start = n(0);
/// let goal = n(5);
/// let ret = depth_first_search(&gr, Some(start), |event| {
///     if let DfsEvent::TreeEdge(u, v) = event {
///         predecessor[v.index()] = u;
///         if v == goal {
///             return Control::Break(v);
///         }
///     }
///     Control::Continue
/// });
///
/// let mut next = goal;
/// let mut path = vec![next];
/// while next != start {
///     let pred = predecessor[next.index()];
///     path.push(pred);
///     next = pred;
/// }
/// path.reverse();
/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
/// ```
pub fn depth_first_search<G, F, C, I>(graph: G, starts: I, mut visitor: F) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          I: IntoIterator<Item=G::NodeId>,
          C: ControlFlow,
{
    let mut time = Time(0);
    let mut discovered = graph.visit_map();
    let mut finished = graph.visit_map();

    for start in starts {
        control!(dfs_visitor(graph, start, &mut visitor, &mut discovered, &mut
                             finished, &mut time));
    }
    C::default()
}

fn dfs_visitor<G, F, C>(graph: G, u: G::NodeId, visitor: &mut F,
                     discovered: &mut G::Map, finished: &mut G::Map,
                     time: &mut Time) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    if !discovered.visit(u) {
        return C::default();
    }
    control!(visitor(DfsEvent::Discover(u, time_post_inc(time))));
    for v in graph.neighbors(u) {
        if !discovered.is_visited(&v) {
            control!(visitor(DfsEvent::TreeEdge(u, v)));
            control!(dfs_visitor(graph, v, visitor, discovered, finished, time));
        } else if !finished.is_visited(&v) {
            control!(visitor(DfsEvent::BackEdge(u, v)));
        } else {
            control!(visitor(DfsEvent::CrossForwardEdge(u, v)));
        }
    }
    let first_finish = finished.visit(u);
    debug_assert!(first_finish);
    control!(visitor(DfsEvent::Finish(u, time_post_inc(time))));
    C::default()
}

fn time_post_inc(x: &mut Time) -> Time {
    let v = *x;
    x.0 += 1;
    v
}
<commit_msg>visit: More doc<commit_after>

use visit::IntoNeighbors;
use visit::{VisitMap, Visitable};

/// Strictly monotonically increasing event time for a depth first search.
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default)]
pub struct Time(pub u64);

/// A depth first search (DFS) visitor event.
#[derive(Copy, Clone, Debug)]
pub enum DfsEvent<N> {
    Discover(N, Time),
    /// An edge of the tree formed by the traversal.
    TreeEdge(N, N),
    /// An edge to an already visited node.
    BackEdge(N, N),
    /// A cross or forward edge.
    ///
    /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
    /// then it is a forward edge, else a cross edge.
    CrossForwardEdge(N, N),
    Finish(N, Time),
}

macro_rules! control {
    ($e:expr) => {
        match $e {
            x => if x.should_break() {
                return x;
            } else {
            }
        }
    }
}

/// Control flow for visitor callbacks.
#[derive(Copy, Clone, Debug)]
pub enum Control<B> {
    Continue,
    Break(B),
}

impl<B> Control<B> {
    /// Get the value in `Control::Break(_)`, if present.
    pub fn break_value(self) -> Option<B> {
        match self {
            Control::Continue => None,
            Control::Break(b) => Some(b),
        }
    }
}

/// The default is `Continue`.
impl<B> Default for Control<B> {
    fn default() -> Self { Control::Continue }
}

/// Control flow for visitor callbacks.
///
/// The empty return value `()` will continue until the visit is done.
pub trait ControlFlow : Default {
    #[inline]
    fn should_break(&self) -> bool { false }
}

impl ControlFlow for () { }

impl<B> ControlFlow for Control<B> {
    fn should_break(&self) -> bool {
        if let Control::Break(_) = *self { true } else { false }
    }
}

/// A recursive depth first search.
///
/// Starting points are the nodes in the iterator `starts` (specify just one
/// start vertex *x* by using `Some(x)`).
///
/// The visit emits discovery and finish events for each reachable vertex,
/// and edge classification of each reachable edge. `visitor` is called for each
/// event, see [`DfsEvent`][de] for possible values.
///
/// If the return value of the visitor is simply `()`, the visit runs until it
/// is finished. If the return value is a `Control<B>`, it can be used to
/// break the visit early, and the last control value is returned by the
/// function.
///
/// [de]: enum.DfsEvent.html
///
/// # Example
///
/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
/// the goal vertex.
///
/// ```
/// use petgraph::prelude::*;
/// use petgraph::graph::node_index as n;
/// use petgraph::visit::depth_first_search;
/// use petgraph::visit::{DfsEvent, Control};
///
/// let gr: Graph<(), ()> = Graph::from_edges(&[
///     (0, 1), (0, 2), (0, 3),
///     (1, 3),
///     (2, 3), (2, 4),
///     (4, 0), (4, 5),
/// ]);
///
/// // record each predecessor, mapping node → node
/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
/// let start = n(0);
/// let goal = n(5);
/// let ret = depth_first_search(&gr, Some(start), |event| {
///     if let DfsEvent::TreeEdge(u, v) = event {
///         predecessor[v.index()] = u;
///         if v == goal {
///             return Control::Break(v);
///         }
///     }
///     Control::Continue
/// });
///
/// let mut next = goal;
/// let mut path = vec![next];
/// while next != start {
///     let pred = predecessor[next.index()];
///     path.push(pred);
///     next = pred;
/// }
/// path.reverse();
/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
/// ```
pub fn depth_first_search<G, F, C, I>(graph: G, starts: I, mut visitor: F) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          I: IntoIterator<Item=G::NodeId>,
          C: ControlFlow,
{
    let mut time = Time(0);
    let mut discovered = graph.visit_map();
    let mut finished = graph.visit_map();

    for start in starts {
        control!(dfs_visitor(graph, start, &mut visitor, &mut discovered, &mut
                             finished, &mut time));
    }
    C::default()
}

fn dfs_visitor<G, F, C>(graph: G, u: G::NodeId, visitor: &mut F,
                     discovered: &mut G::Map, finished: &mut G::Map,
                     time: &mut Time) -> C
    where G: IntoNeighbors + Visitable,
          F: FnMut(DfsEvent<G::NodeId>) -> C,
          C: ControlFlow,
{
    if !discovered.visit(u) {
        return C::default();
    }
    control!(visitor(DfsEvent::Discover(u, time_post_inc(time))));
    for v in graph.neighbors(u) {
        if !discovered.is_visited(&v) {
            control!(visitor(DfsEvent::TreeEdge(u, v)));
            control!(dfs_visitor(graph, v, visitor, discovered, finished, time));
        } else if !finished.is_visited(&v) {
            control!(visitor(DfsEvent::BackEdge(u, v)));
        } else {
            control!(visitor(DfsEvent::CrossForwardEdge(u, v)));
        }
    }
    let first_finish = finished.visit(u);
    debug_assert!(first_finish);
    control!(visitor(DfsEvent::Finish(u, time_post_inc(time))));
    C::default()
}

fn time_post_inc(x: &mut Time) -> Time {
    let v = *x;
    x.0 += 1;
    v
}
<|endoftext|>


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

use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    parse::Parser,
    parse_quote,
    punctuated::Punctuated,
    token::{Comma, Unsafe},
    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
};

use super::{
    unqualify::{unqualify_params, unqualify_ret_type},
    RsCodegenResult, Use,
};
use crate::{
    conversion::{
        analysis::fun::{
            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy, UnsafetyNeeded,
        },
        api::ImplBlockDetails,
    },
    types::{Namespace, QualifiedName},
};
use crate::{
    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
    types::make_ident,
};

impl UnsafetyNeeded {
    fn bridge_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::None => None,
            _ => Some(parse_quote! { unsafe }),
        }
    }

    fn wrapper_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
            _ => None,
        }
    }
}

pub(super) fn gen_function(
    ns: &Namespace,
    fun: FuncToConvert,
    analysis: FnAnalysis,
    cpp_call_name: String,
) -> RsCodegenResult {
    if !analysis.generate_code {
        return RsCodegenResult::default();
    }
    let cxxbridge_name = analysis.cxxbridge_name;
    let rust_name = analysis.rust_name;
    let ret_type = analysis.ret_type;
    let param_details = analysis.param_details;
    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
    let params = analysis.params;
    let vis = analysis.vis;
    let kind = analysis.kind;
    let doc_attr = fun.doc_attr;

    let mut cpp_name_attr = Vec::new();
    let mut impl_entry = None;
    let rust_name_attr: Vec<_> = match &analysis.rust_rename_strategy {
        RustRenameStrategy::RenameUsingRustAttr => Attribute::parse_outer
            .parse2(quote!(
                #[rust_name = #rust_name]
            ))
            .unwrap(),
        _ => Vec::new(),
    };
    let mut materialization = match kind {
        FnKind::Method(..) => None,
        FnKind::Function => match analysis.rust_rename_strategy {
            RustRenameStrategy::RenameInOutputMod(alias) => {
                Some(Use::UsedFromCxxBridgeWithAlias(alias))
            }
            _ => Some(Use::UsedFromCxxBridge),
        },
    };
    let any_param_needs_rust_conversion = param_details
        .iter()
        .any(|pd| pd.conversion.rust_work_needed());
    let rust_wrapper_needed = any_param_needs_rust_conversion
        || (cxxbridge_name != rust_name && matches!(kind, FnKind::Method(..)));
    let wrapper_unsafety = analysis.requires_unsafe.wrapper_token();
    if rust_wrapper_needed {
        match kind {
            FnKind::Method(ref type_name, MethodKind::Constructor) => {
                // Constructor.
                impl_entry = Some(generate_constructor_impl(
                    &param_details,
                    type_name,
                    &cxxbridge_name,
                    &rust_name,
                    &wrapper_unsafety,
                    &doc_attr,
                ));
            }
            FnKind::Method(ref type_name, ref method_kind) => {
                // Method, or static method.
                impl_entry = Some(generate_method_impl(
                    &param_details,
                    matches!(
                        method_kind,
                        MethodKind::MakeUnique | MethodKind::Constructor
                    ),
                    type_name,
                    &cxxbridge_name,
                    &rust_name,
                    &ret_type,
                    &wrapper_unsafety,
                    &doc_attr,
                ));
            }
            _ => {
                // Generate plain old function
                materialization = Some(Use::Custom(generate_function_impl(
                    &param_details,
                    &rust_name,
                    &ret_type,
                    &wrapper_unsafety,
                    &doc_attr,
                )));
            }
        }
    }
    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
        cpp_name_attr = Attribute::parse_outer
            .parse2(quote!(
                #[cxx_name = #cpp_call_name]
            ))
            .unwrap();
    }
    // In very rare occasions, we might need to give an explicit lifetime.
    let (lifetime_tokens, params, ret_type) =
        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type);

    // Finally - namespace support. All the Types in everything
    // above this point are fully qualified. We need to unqualify them.
    // We need to do that _after_ the above wrapper_function_needed
    // work, because it relies upon spotting fully qualified names like
    // std::unique_ptr. However, after it's done its job, all such
    // well-known types should be unqualified already (e.g. just UniquePtr)
    // and the following code will act to unqualify only those types
    // which the user has declared.
    let params = unqualify_params(params);
    let ret_type = unqualify_ret_type(ret_type.into_owned());
    // And we need to make an attribute for the namespace that the function
    // itself is in.
    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
        Vec::new()
    } else {
        let namespace_string = ns.to_string();
        Attribute::parse_outer
            .parse2(quote!(
                #[namespace = #namespace_string]
            ))
            .unwrap()
    };
    // At last, actually generate the cxx::bridge entry.
    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
        #(#namespace_attr)*
        #(#rust_name_attr)*
        #(#cpp_name_attr)*
        #doc_attr
        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
    ));
    RsCodegenResult {
        extern_c_mod_items: vec![extern_c_mod_item],
        bridge_items: Vec::new(),
        global_items: Vec::new(),
        bindgen_mod_items: Vec::new(),
        impl_entry,
        materializations: materialization.into_iter().collect(),
        extern_rust_mod_items: Vec::new(),
    }
}

fn generate_arg_lists(
    param_details: &[ArgumentAnalysis],
    avoid_self: bool,
) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>) {
    let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
    let mut arg_list = Vec::new();

    for pd in param_details {
        let type_name = pd.conversion.rust_wrapper_unconverted_type();
        let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
            parse_quote!(self)
        } else {
            pd.name.clone()
        };
        wrapper_params.push(parse_quote!(
            #wrapper_arg_name: #type_name
        ));
        arg_list.push(pd.conversion.rust_conversion(wrapper_arg_name));
    }
    (wrapper_params, arg_list)
}

/// Generate an 'impl Type { methods-go-here }' item
#[allow(clippy::too_many_arguments)] // it's true, but probably best for now
fn generate_method_impl(
    param_details: &[ArgumentAnalysis],
    avoid_self: bool,
    impl_block_type_name: &QualifiedName,
    cxxbridge_name: &Ident,
    rust_name: &str,
    ret_type: &ReturnType,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<ImplBlockDetails> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, avoid_self);
    let (lifetime_tokens, wrapper_params, ret_type) =
        add_explicit_lifetime_if_necessary(param_details, wrapper_params, ret_type);
    let rust_name = make_ident(&rust_name);
    Box::new(ImplBlockDetails {
        item: ImplItem::Method(parse_quote! {
            #doc_attr
            pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                cxxbridge::#cxxbridge_name ( #(#arg_list),* )
            }
        }),
        ty: impl_block_type_name.get_final_ident(),
    })
}

/// Generate a 'impl Type { methods-go-here }' item which is a constructor
/// for use with moveit traits.
fn generate_constructor_impl(
    param_details: &[ArgumentAnalysis],
    impl_block_type_name: &QualifiedName,
    cxxbridge_name: &Ident,
    rust_name: &str,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<ImplBlockDetails> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, true);
    let wrapper_params: Punctuated<FnArg, Comma> = wrapper_params.into_iter().skip(1).collect();
    let ptr_arg_name = &arg_list[0];
    let rust_name = make_ident(&rust_name);
    Box::new(ImplBlockDetails {
        item: ImplItem::Method(parse_quote! {
            #doc_attr
            pub #unsafety fn #rust_name ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> {
                unsafe {
                    autocxx::moveit::new::by_raw(|#ptr_arg_name| {
                        let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
                        cxxbridge::#cxxbridge_name(#(#arg_list),* )
                    })
                }
            }
        }),
        ty: impl_block_type_name.get_final_ident(),
    })
}

/// Generate a function call wrapper
fn generate_function_impl(
    param_details: &[ArgumentAnalysis],
    rust_name: &str,
    ret_type: &ReturnType,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<Item> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, false);
    let rust_name = make_ident(&rust_name);
    Box::new(Item::Fn(parse_quote! {
        #doc_attr
        pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type {
            cxxbridge::#rust_name ( #(#arg_list),* )
        }
    }))
}
<commit_msg>Add 'move' keyword<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use proc_macro2::TokenStream;
use quote::quote;
use syn::{
    parse::Parser,
    parse_quote,
    punctuated::Punctuated,
    token::{Comma, Unsafe},
    Attribute, FnArg, ForeignItem, Ident, ImplItem, Item, ReturnType,
};

use super::{
    unqualify::{unqualify_params, unqualify_ret_type},
    RsCodegenResult, Use,
};
use crate::{
    conversion::{
        analysis::fun::{
            ArgumentAnalysis, FnAnalysis, FnKind, MethodKind, RustRenameStrategy, UnsafetyNeeded,
        },
        api::ImplBlockDetails,
    },
    types::{Namespace, QualifiedName},
};
use crate::{
    conversion::{api::FuncToConvert, codegen_rs::lifetime::add_explicit_lifetime_if_necessary},
    types::make_ident,
};

impl UnsafetyNeeded {
    fn bridge_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::None => None,
            _ => Some(parse_quote! { unsafe }),
        }
    }

    fn wrapper_token(&self) -> Option<Unsafe> {
        match self {
            UnsafetyNeeded::Always => Some(parse_quote! { unsafe }),
            _ => None,
        }
    }
}

pub(super) fn gen_function(
    ns: &Namespace,
    fun: FuncToConvert,
    analysis: FnAnalysis,
    cpp_call_name: String,
) -> RsCodegenResult {
    if !analysis.generate_code {
        return RsCodegenResult::default();
    }
    let cxxbridge_name = analysis.cxxbridge_name;
    let rust_name = analysis.rust_name;
    let ret_type = analysis.ret_type;
    let param_details = analysis.param_details;
    let wrapper_function_needed = analysis.cpp_wrapper.is_some();
    let params = analysis.params;
    let vis = analysis.vis;
    let kind = analysis.kind;
    let doc_attr = fun.doc_attr;

    let mut cpp_name_attr = Vec::new();
    let mut impl_entry = None;
    let rust_name_attr: Vec<_> = match &analysis.rust_rename_strategy {
        RustRenameStrategy::RenameUsingRustAttr => Attribute::parse_outer
            .parse2(quote!(
                #[rust_name = #rust_name]
            ))
            .unwrap(),
        _ => Vec::new(),
    };
    let mut materialization = match kind {
        FnKind::Method(..) => None,
        FnKind::Function => match analysis.rust_rename_strategy {
            RustRenameStrategy::RenameInOutputMod(alias) => {
                Some(Use::UsedFromCxxBridgeWithAlias(alias))
            }
            _ => Some(Use::UsedFromCxxBridge),
        },
    };
    let any_param_needs_rust_conversion = param_details
        .iter()
        .any(|pd| pd.conversion.rust_work_needed());
    let rust_wrapper_needed = any_param_needs_rust_conversion
        || (cxxbridge_name != rust_name && matches!(kind, FnKind::Method(..)));
    let wrapper_unsafety = analysis.requires_unsafe.wrapper_token();
    if rust_wrapper_needed {
        match kind {
            FnKind::Method(ref type_name, MethodKind::Constructor) => {
                // Constructor.
                impl_entry = Some(generate_constructor_impl(
                    &param_details,
                    type_name,
                    &cxxbridge_name,
                    &rust_name,
                    &wrapper_unsafety,
                    &doc_attr,
                ));
            }
            FnKind::Method(ref type_name, ref method_kind) => {
                // Method, or static method.
                impl_entry = Some(generate_method_impl(
                    &param_details,
                    matches!(
                        method_kind,
                        MethodKind::MakeUnique | MethodKind::Constructor
                    ),
                    type_name,
                    &cxxbridge_name,
                    &rust_name,
                    &ret_type,
                    &wrapper_unsafety,
                    &doc_attr,
                ));
            }
            _ => {
                // Generate plain old function
                materialization = Some(Use::Custom(generate_function_impl(
                    &param_details,
                    &rust_name,
                    &ret_type,
                    &wrapper_unsafety,
                    &doc_attr,
                )));
            }
        }
    }
    if cxxbridge_name != cpp_call_name && !wrapper_function_needed {
        cpp_name_attr = Attribute::parse_outer
            .parse2(quote!(
                #[cxx_name = #cpp_call_name]
            ))
            .unwrap();
    }
    // In very rare occasions, we might need to give an explicit lifetime.
    let (lifetime_tokens, params, ret_type) =
        add_explicit_lifetime_if_necessary(&param_details, params, &ret_type);

    // Finally - namespace support. All the Types in everything
    // above this point are fully qualified. We need to unqualify them.
    // We need to do that _after_ the above wrapper_function_needed
    // work, because it relies upon spotting fully qualified names like
    // std::unique_ptr. However, after it's done its job, all such
    // well-known types should be unqualified already (e.g. just UniquePtr)
    // and the following code will act to unqualify only those types
    // which the user has declared.
    let params = unqualify_params(params);
    let ret_type = unqualify_ret_type(ret_type.into_owned());
    // And we need to make an attribute for the namespace that the function
    // itself is in.
    let namespace_attr = if ns.is_empty() || wrapper_function_needed {
        Vec::new()
    } else {
        let namespace_string = ns.to_string();
        Attribute::parse_outer
            .parse2(quote!(
                #[namespace = #namespace_string]
            ))
            .unwrap()
    };
    // At last, actually generate the cxx::bridge entry.
    let bridge_unsafety = analysis.requires_unsafe.bridge_token();
    let extern_c_mod_item = ForeignItem::Fn(parse_quote!(
        #(#namespace_attr)*
        #(#rust_name_attr)*
        #(#cpp_name_attr)*
        #doc_attr
        #vis #bridge_unsafety fn #cxxbridge_name #lifetime_tokens ( #params ) #ret_type;
    ));
    RsCodegenResult {
        extern_c_mod_items: vec![extern_c_mod_item],
        bridge_items: Vec::new(),
        global_items: Vec::new(),
        bindgen_mod_items: Vec::new(),
        impl_entry,
        materializations: materialization.into_iter().collect(),
        extern_rust_mod_items: Vec::new(),
    }
}

fn generate_arg_lists(
    param_details: &[ArgumentAnalysis],
    avoid_self: bool,
) -> (Punctuated<FnArg, Comma>, Vec<TokenStream>) {
    let mut wrapper_params: Punctuated<FnArg, Comma> = Punctuated::new();
    let mut arg_list = Vec::new();

    for pd in param_details {
        let type_name = pd.conversion.rust_wrapper_unconverted_type();
        let wrapper_arg_name = if pd.self_type.is_some() && !avoid_self {
            parse_quote!(self)
        } else {
            pd.name.clone()
        };
        wrapper_params.push(parse_quote!(
            #wrapper_arg_name: #type_name
        ));
        arg_list.push(pd.conversion.rust_conversion(wrapper_arg_name));
    }
    (wrapper_params, arg_list)
}

/// Generate an 'impl Type { methods-go-here }' item
#[allow(clippy::too_many_arguments)] // it's true, but probably best for now
fn generate_method_impl(
    param_details: &[ArgumentAnalysis],
    avoid_self: bool,
    impl_block_type_name: &QualifiedName,
    cxxbridge_name: &Ident,
    rust_name: &str,
    ret_type: &ReturnType,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<ImplBlockDetails> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, avoid_self);
    let (lifetime_tokens, wrapper_params, ret_type) =
        add_explicit_lifetime_if_necessary(param_details, wrapper_params, ret_type);
    let rust_name = make_ident(&rust_name);
    Box::new(ImplBlockDetails {
        item: ImplItem::Method(parse_quote! {
            #doc_attr
            pub #unsafety fn #rust_name #lifetime_tokens ( #wrapper_params ) #ret_type {
                cxxbridge::#cxxbridge_name ( #(#arg_list),* )
            }
        }),
        ty: impl_block_type_name.get_final_ident(),
    })
}

/// Generate a 'impl Type { methods-go-here }' item which is a constructor
/// for use with moveit traits.
fn generate_constructor_impl(
    param_details: &[ArgumentAnalysis],
    impl_block_type_name: &QualifiedName,
    cxxbridge_name: &Ident,
    rust_name: &str,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<ImplBlockDetails> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, true);
    let wrapper_params: Punctuated<FnArg, Comma> = wrapper_params.into_iter().skip(1).collect();
    let ptr_arg_name = &arg_list[0];
    let rust_name = make_ident(&rust_name);
    Box::new(ImplBlockDetails {
        item: ImplItem::Method(parse_quote! {
            #doc_attr
            pub #unsafety fn #rust_name ( #wrapper_params ) -> impl autocxx::moveit::new::New<Output=Self> {
                unsafe {
                    autocxx::moveit::new::by_raw(move |#ptr_arg_name| {
                        let #ptr_arg_name = #ptr_arg_name.get_unchecked_mut().as_mut_ptr();
                        cxxbridge::#cxxbridge_name(#(#arg_list),* )
                    })
                }
            }
        }),
        ty: impl_block_type_name.get_final_ident(),
    })
}

/// Generate a function call wrapper
fn generate_function_impl(
    param_details: &[ArgumentAnalysis],
    rust_name: &str,
    ret_type: &ReturnType,
    unsafety: &Option<Unsafe>,
    doc_attr: &Option<Attribute>,
) -> Box<Item> {
    let (wrapper_params, arg_list) = generate_arg_lists(param_details, false);
    let rust_name = make_ident(&rust_name);
    Box::new(Item::Fn(parse_quote! {
        #doc_attr
        pub #unsafety fn #rust_name ( #wrapper_params ) #ret_type {
            cxxbridge::#rust_name ( #(#arg_list),* )
        }
    }))
}
<|endoftext|>


<commit_before><commit_msg>Instead of separately tracking the number of inputs and outputs, just read the values from the actual vectors containing information about those inputs and outputs.<commit_after><|endoftext|>


<commit_before>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::disk::Disk;
use sys::processor::*;

use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::mem::{size_of, zeroed};

use DiskExt;
use Pid;
use ProcessExt;
use RefreshKind;
use SystemExt;

use windows::network::{self, NetworkData};
use windows::process::{
    compute_cpu_usage, get_handle, get_parent_process_id, update_proc_info, Process,
};
use windows::processor::CounterValue;
use windows::tools::*;

use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::minwinbase::STILL_ACTIVE;
use winapi::um::pdh::PdhEnumObjectItemsW;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::psapi::K32EnumProcesses;
use winapi::um::sysinfoapi::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
use winapi::um::winnt::HANDLE;

use rayon::prelude::*;

const PROCESS_LEN: usize = 10192;

/// Struct containing system's information.
pub struct System {
    process_list: HashMap<usize, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
    query: Option<Query>,
    network: NetworkData,
    uptime: u64,
}

impl System {
    fn clear_procs(&mut self) {
        if self.processors.len() > 0 {
            let mut to_delete = Vec::new();

            for (pid, proc_) in self.process_list.iter_mut() {
                if !is_proc_running(get_handle(proc_)) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, self.processors.len() as u64 - 1);
                }
            }
            for pid in to_delete {
                self.process_list.remove(&pid);
            }
        }
    }
}

struct Wrap(Process);

unsafe impl Send for Wrap {}

struct WrapSystem<'a>(UnsafeCell<&'a mut System>);

impl<'a> WrapSystem<'a> {
    fn get(&self) -> &'a mut System {
        unsafe { *(self.0.get()) }
    }
}

unsafe impl<'a> Send for WrapSystem<'a> {}
unsafe impl<'a> Sync for WrapSystem<'a> {}

impl SystemExt for System {
    #[allow(non_snake_case)]
    fn new_with_specifics(refreshes: RefreshKind) -> System {
        let mut s = System {
            process_list: HashMap::new(),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: init_processors(),
            temperatures: component::get_components(),
            disks: Vec::new(),
            query: Query::new(),
            network: network::new(),
            uptime: get_uptime(),
        };
        // TODO: in case a translation fails, it might be nice to log it somewhere...
        if let Some(ref mut query) = s.query {
            let x = unsafe { load_symbols() };
            if let Some(processor_trans) = get_translation(&"Processor".to_owned(), &x) {
                let idle_time_trans = get_translation(&"% Idle Time".to_owned(), &x);
                let proc_time_trans = get_translation(&"% Processor Time".to_owned(), &x);
                if let Some(ref proc_time_trans) = proc_time_trans {
                    add_counter(
                        format!("\\{}(_Total)\\{}", processor_trans, proc_time_trans),
                        query,
                        get_key_used(&mut s.processors[0]),
                        "tot_0".to_owned(),
                        CounterValue::Float(0.),
                    );
                }
                if let Some(ref idle_time_trans) = idle_time_trans {
                    add_counter(
                        format!("\\{}(_Total)\\{}", processor_trans, idle_time_trans),
                        query,
                        get_key_idle(&mut s.processors[0]),
                        "tot_1".to_owned(),
                        CounterValue::Float(0.),
                    );
                }
                for (pos, proc_) in s.processors.iter_mut().skip(1).enumerate() {
                    if let Some(ref proc_time_trans) = proc_time_trans {
                        add_counter(
                            format!("\\{}({})\\{}", processor_trans, pos, proc_time_trans),
                            query,
                            get_key_used(proc_),
                            format!("{}_0", pos),
                            CounterValue::Float(0.),
                        );
                    }
                    if let Some(ref idle_time_trans) = idle_time_trans {
                        add_counter(
                            format!("\\{}({})\\{}", processor_trans, pos, idle_time_trans),
                            query,
                            get_key_idle(proc_),
                            format!("{}_1", pos),
                            CounterValue::Float(0.),
                        );
                    }
                }
            }

            if let Some(network_trans) = get_translation(&"Network Interface".to_owned(), &x) {
                let network_in_trans = get_translation(&"Bytes Received/Sec".to_owned(), &x);
                let network_out_trans = get_translation(&"Bytes Sent/sec".to_owned(), &x);

                const PERF_DETAIL_WIZARD: DWORD = 400;
                const PDH_MORE_DATA: DWORD = 0x800007D2;

                let mut network_trans_utf16: Vec<u16> = network_trans.encode_utf16().collect();
                network_trans_utf16.push(0);
                let mut dwCounterListSize: DWORD = 0;
                let mut dwInstanceListSize: DWORD = 0;
                let status = unsafe {
                    PdhEnumObjectItemsW(
                        ::std::ptr::null(),
                        ::std::ptr::null(),
                        network_trans_utf16.as_ptr(),
                        ::std::ptr::null_mut(),
                        &mut dwCounterListSize,
                        ::std::ptr::null_mut(),
                        &mut dwInstanceListSize,
                        PERF_DETAIL_WIZARD,
                        0,
                    )
                };
                if status != PDH_MORE_DATA as i32 {
                    eprintln!("PdhEnumObjectItems invalid status: {:x}", status);
                } else {
                    let mut pwsCounterListBuffer: Vec<u16> =
                        Vec::with_capacity(dwCounterListSize as usize);
                    let mut pwsInstanceListBuffer: Vec<u16> =
                        Vec::with_capacity(dwInstanceListSize as usize);
                    unsafe {
                        pwsCounterListBuffer.set_len(dwCounterListSize as usize);
                        pwsInstanceListBuffer.set_len(dwInstanceListSize as usize);
                    }
                    let status = unsafe {
                        PdhEnumObjectItemsW(
                            ::std::ptr::null(),
                            ::std::ptr::null(),
                            network_trans_utf16.as_ptr(),
                            pwsCounterListBuffer.as_mut_ptr(),
                            &mut dwCounterListSize,
                            pwsInstanceListBuffer.as_mut_ptr(),
                            &mut dwInstanceListSize,
                            PERF_DETAIL_WIZARD,
                            0,
                        )
                    };
                    if status != ERROR_SUCCESS as i32 {
                        eprintln!("PdhEnumObjectItems invalid status: {:x}", status);
                    } else {
                        for (pos, x) in pwsInstanceListBuffer
                            .split(|x| *x == 0)
                            .filter(|x| x.len() > 0)
                            .enumerate()
                        {
                            let net_interface = String::from_utf16(x).expect("invalid utf16");
                            if let Some(ref network_in_trans) = network_in_trans {
                                let mut key_in = None;
                                add_counter(
                                    format!(
                                        "\\{}({})\\{}",
                                        network_trans, net_interface, network_in_trans
                                    ),
                                    query,
                                    &mut key_in,
                                    format!("net{}_in", pos),
                                    CounterValue::Integer(0),
                                );
                                if key_in.is_some() {
                                    network::get_keys_in(&mut s.network).push(key_in.unwrap());
                                }
                            }
                            if let Some(ref network_out_trans) = network_out_trans {
                                let mut key_out = None;
                                add_counter(
                                    format!(
                                        "\\{}({})\\{}",
                                        network_trans, net_interface, network_out_trans
                                    ),
                                    query,
                                    &mut key_out,
                                    format!("net{}_out", pos),
                                    CounterValue::Integer(0),
                                );
                                if key_out.is_some() {
                                    network::get_keys_out(&mut s.network).push(key_out.unwrap());
                                }
                            }
                        }
                    }
                }
            }
            query.start();
        }
        s.refresh_specifics(refreshes);
        s
    }

    fn refresh_system(&mut self) {
        unsafe {
            let mut mem_info: MEMORYSTATUSEX = zeroed();
            mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
            GlobalMemoryStatusEx(&mut mem_info);
            self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64);
            self.mem_free = auto_cast!(mem_info.ullAvailPhys, u64);
            //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64);
            //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64);
        }
        self.uptime = get_uptime();
        if let Some(ref mut query) = self.query {
            for p in self.processors.iter_mut() {
                let mut idle_time = None;
                if let &mut Some(ref key_idle) = get_key_idle(p) {
                    idle_time = Some(query.get(&key_idle.unique_id).expect("key disappeared"));
                }
                if let Some(idle_time) = idle_time {
                    set_cpu_usage(p, 1. - idle_time);
                }
            }
        }
    }

    fn refresh_network(&mut self) {
        network::refresh(&mut self.network, &self.query);
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        if refresh_existing_process(self, pid, true) == false {
            self.process_list.remove(&pid);
            false
        } else {
            true
        }
    }

    fn refresh_processes(&mut self) {
        // I think that 10192 as length will be enough to get all processes at once...
        let mut process_ids: Vec<DWORD> = Vec::with_capacity(PROCESS_LEN);
        let mut cb_needed = 0;

        unsafe {
            process_ids.set_len(PROCESS_LEN);
        }
        let size = ::std::mem::size_of::<DWORD>() * process_ids.len();
        unsafe {
            if K32EnumProcesses(process_ids.as_mut_ptr(), size as DWORD, &mut cb_needed) == 0 {
                return;
            }
        }
        let nb_processes = cb_needed / ::std::mem::size_of::<DWORD>() as DWORD;
        unsafe {
            process_ids.set_len(nb_processes as usize);
        }

        {
            let this = WrapSystem(UnsafeCell::new(self));

            process_ids
                .par_iter()
                .filter_map(|pid| {
                    let pid = *pid as usize;
                    if !refresh_existing_process(this.get(), pid, false) {
                        let ppid = unsafe { get_parent_process_id(pid) };
                        let mut p = Process::new(pid, ppid, 0);
                        update_proc_info(&mut p);
                        Some(Wrap(p))
                    } else {
                        None
                    }
                })
                .collect::<Vec<_>>()
        }
        .into_iter()
        .for_each(|p| {
            self.process_list.insert(p.0.pid(), p.0);
        });
        self.clear_procs();
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = unsafe { get_disks() };
    }

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&(pid as usize))
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total >> 10
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free >> 10
    }

    fn get_used_memory(&self) -> u64 {
        (self.mem_total - self.mem_free) >> 10
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total >> 10
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free >> 10
    }

    fn get_used_swap(&self) -> u64 {
        (self.swap_total - self.swap_free) >> 10
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }

    fn get_network(&self) -> &NetworkData {
        &self.network
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }
}

fn is_proc_running(handle: HANDLE) -> bool {
    let mut exit_code = 0;
    let ret = unsafe { GetExitCodeProcess(handle, &mut exit_code) };
    !(ret == FALSE || exit_code != STILL_ACTIVE)
}

fn refresh_existing_process(s: &mut System, pid: Pid, compute_cpu: bool) -> bool {
    if let Some(ref mut entry) = s.process_list.get_mut(&(pid as usize)) {
        if !is_proc_running(get_handle(entry)) {
            return false;
        }
        update_proc_info(entry);
        if compute_cpu {
            compute_cpu_usage(entry, s.processors.len() as u64 - 1);
        }
        true
    } else {
        false
    }
}
<commit_msg>Add new refresh methods for windows<commit_after>//
// Sysinfo
//
// Copyright (c) 2018 Guillaume Gomez
//

use sys::component::{self, Component};
use sys::disk::Disk;
use sys::processor::*;

use std::cell::UnsafeCell;
use std::collections::HashMap;
use std::mem::{size_of, zeroed};

use DiskExt;
use Pid;
use ProcessExt;
use RefreshKind;
use SystemExt;

use windows::network::{self, NetworkData};
use windows::process::{
    compute_cpu_usage, get_handle, get_parent_process_id, update_proc_info, Process,
};
use windows::processor::CounterValue;
use windows::tools::*;

use winapi::shared::minwindef::{DWORD, FALSE};
use winapi::shared::winerror::ERROR_SUCCESS;
use winapi::um::minwinbase::STILL_ACTIVE;
use winapi::um::pdh::PdhEnumObjectItemsW;
use winapi::um::processthreadsapi::GetExitCodeProcess;
use winapi::um::psapi::K32EnumProcesses;
use winapi::um::sysinfoapi::{GlobalMemoryStatusEx, MEMORYSTATUSEX};
use winapi::um::winnt::HANDLE;

use rayon::prelude::*;

const PROCESS_LEN: usize = 10192;

/// Struct containing system's information.
pub struct System {
    process_list: HashMap<usize, Process>,
    mem_total: u64,
    mem_free: u64,
    swap_total: u64,
    swap_free: u64,
    processors: Vec<Processor>,
    temperatures: Vec<Component>,
    disks: Vec<Disk>,
    query: Option<Query>,
    network: NetworkData,
    uptime: u64,
}

impl System {
    fn clear_procs(&mut self) {
        if self.processors.len() > 0 {
            let mut to_delete = Vec::new();

            for (pid, proc_) in self.process_list.iter_mut() {
                if !is_proc_running(get_handle(proc_)) {
                    to_delete.push(*pid);
                } else {
                    compute_cpu_usage(proc_, self.processors.len() as u64 - 1);
                }
            }
            for pid in to_delete {
                self.process_list.remove(&pid);
            }
        }
    }
}

struct Wrap(Process);

unsafe impl Send for Wrap {}

struct WrapSystem<'a>(UnsafeCell<&'a mut System>);

impl<'a> WrapSystem<'a> {
    fn get(&self) -> &'a mut System {
        unsafe { *(self.0.get()) }
    }
}

unsafe impl<'a> Send for WrapSystem<'a> {}
unsafe impl<'a> Sync for WrapSystem<'a> {}

impl SystemExt for System {
    #[allow(non_snake_case)]
    fn new_with_specifics(refreshes: RefreshKind) -> System {
        let mut s = System {
            process_list: HashMap::new(),
            mem_total: 0,
            mem_free: 0,
            swap_total: 0,
            swap_free: 0,
            processors: init_processors(),
            temperatures: component::get_components(),
            disks: Vec::new(),
            query: Query::new(),
            network: network::new(),
            uptime: get_uptime(),
        };
        // TODO: in case a translation fails, it might be nice to log it somewhere...
        if let Some(ref mut query) = s.query {
            let x = unsafe { load_symbols() };
            if let Some(processor_trans) = get_translation(&"Processor".to_owned(), &x) {
                let idle_time_trans = get_translation(&"% Idle Time".to_owned(), &x);
                let proc_time_trans = get_translation(&"% Processor Time".to_owned(), &x);
                if let Some(ref proc_time_trans) = proc_time_trans {
                    add_counter(
                        format!("\\{}(_Total)\\{}", processor_trans, proc_time_trans),
                        query,
                        get_key_used(&mut s.processors[0]),
                        "tot_0".to_owned(),
                        CounterValue::Float(0.),
                    );
                }
                if let Some(ref idle_time_trans) = idle_time_trans {
                    add_counter(
                        format!("\\{}(_Total)\\{}", processor_trans, idle_time_trans),
                        query,
                        get_key_idle(&mut s.processors[0]),
                        "tot_1".to_owned(),
                        CounterValue::Float(0.),
                    );
                }
                for (pos, proc_) in s.processors.iter_mut().skip(1).enumerate() {
                    if let Some(ref proc_time_trans) = proc_time_trans {
                        add_counter(
                            format!("\\{}({})\\{}", processor_trans, pos, proc_time_trans),
                            query,
                            get_key_used(proc_),
                            format!("{}_0", pos),
                            CounterValue::Float(0.),
                        );
                    }
                    if let Some(ref idle_time_trans) = idle_time_trans {
                        add_counter(
                            format!("\\{}({})\\{}", processor_trans, pos, idle_time_trans),
                            query,
                            get_key_idle(proc_),
                            format!("{}_1", pos),
                            CounterValue::Float(0.),
                        );
                    }
                }
            }

            if let Some(network_trans) = get_translation(&"Network Interface".to_owned(), &x) {
                let network_in_trans = get_translation(&"Bytes Received/Sec".to_owned(), &x);
                let network_out_trans = get_translation(&"Bytes Sent/sec".to_owned(), &x);

                const PERF_DETAIL_WIZARD: DWORD = 400;
                const PDH_MORE_DATA: DWORD = 0x800007D2;

                let mut network_trans_utf16: Vec<u16> = network_trans.encode_utf16().collect();
                network_trans_utf16.push(0);
                let mut dwCounterListSize: DWORD = 0;
                let mut dwInstanceListSize: DWORD = 0;
                let status = unsafe {
                    PdhEnumObjectItemsW(
                        ::std::ptr::null(),
                        ::std::ptr::null(),
                        network_trans_utf16.as_ptr(),
                        ::std::ptr::null_mut(),
                        &mut dwCounterListSize,
                        ::std::ptr::null_mut(),
                        &mut dwInstanceListSize,
                        PERF_DETAIL_WIZARD,
                        0,
                    )
                };
                if status != PDH_MORE_DATA as i32 {
                    eprintln!("PdhEnumObjectItems invalid status: {:x}", status);
                } else {
                    let mut pwsCounterListBuffer: Vec<u16> =
                        Vec::with_capacity(dwCounterListSize as usize);
                    let mut pwsInstanceListBuffer: Vec<u16> =
                        Vec::with_capacity(dwInstanceListSize as usize);
                    unsafe {
                        pwsCounterListBuffer.set_len(dwCounterListSize as usize);
                        pwsInstanceListBuffer.set_len(dwInstanceListSize as usize);
                    }
                    let status = unsafe {
                        PdhEnumObjectItemsW(
                            ::std::ptr::null(),
                            ::std::ptr::null(),
                            network_trans_utf16.as_ptr(),
                            pwsCounterListBuffer.as_mut_ptr(),
                            &mut dwCounterListSize,
                            pwsInstanceListBuffer.as_mut_ptr(),
                            &mut dwInstanceListSize,
                            PERF_DETAIL_WIZARD,
                            0,
                        )
                    };
                    if status != ERROR_SUCCESS as i32 {
                        eprintln!("PdhEnumObjectItems invalid status: {:x}", status);
                    } else {
                        for (pos, x) in pwsInstanceListBuffer
                            .split(|x| *x == 0)
                            .filter(|x| x.len() > 0)
                            .enumerate()
                        {
                            let net_interface = String::from_utf16(x).expect("invalid utf16");
                            if let Some(ref network_in_trans) = network_in_trans {
                                let mut key_in = None;
                                add_counter(
                                    format!(
                                        "\\{}({})\\{}",
                                        network_trans, net_interface, network_in_trans
                                    ),
                                    query,
                                    &mut key_in,
                                    format!("net{}_in", pos),
                                    CounterValue::Integer(0),
                                );
                                if key_in.is_some() {
                                    network::get_keys_in(&mut s.network).push(key_in.unwrap());
                                }
                            }
                            if let Some(ref network_out_trans) = network_out_trans {
                                let mut key_out = None;
                                add_counter(
                                    format!(
                                        "\\{}({})\\{}",
                                        network_trans, net_interface, network_out_trans
                                    ),
                                    query,
                                    &mut key_out,
                                    format!("net{}_out", pos),
                                    CounterValue::Integer(0),
                                );
                                if key_out.is_some() {
                                    network::get_keys_out(&mut s.network).push(key_out.unwrap());
                                }
                            }
                        }
                    }
                }
            }
            query.start();
        }
        s.refresh_specifics(refreshes);
        s
    }

    fn refresh_cpu(&mut self) {
        self.uptime = get_uptime();
        if let Some(ref mut query) = self.query {
            for p in self.processors.iter_mut() {
                let mut idle_time = None;
                if let &mut Some(ref key_idle) = get_key_idle(p) {
                    idle_time = Some(query.get(&key_idle.unique_id).expect("key disappeared"));
                }
                if let Some(idle_time) = idle_time {
                    set_cpu_usage(p, 1. - idle_time);
                }
            }
        }
    }

    fn refresh_memory(&mut self) {
        self.uptime = get_uptime();
        unsafe {
            let mut mem_info: MEMORYSTATUSEX = zeroed();
            mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
            GlobalMemoryStatusEx(&mut mem_info);
            self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64);
            self.mem_free = auto_cast!(mem_info.ullAvailPhys, u64);
            //self.swap_total = auto_cast!(mem_info.ullTotalPageFile - mem_info.ullTotalPhys, u64);
            //self.swap_free = auto_cast!(mem_info.ullAvailPageFile, u64);
        }
    }

    fn refresh_temperatures(&mut self) {
        // does nothing for the moment...
    }

    fn refresh_network(&mut self) {
        network::refresh(&mut self.network, &self.query);
    }

    fn refresh_process(&mut self, pid: Pid) -> bool {
        if refresh_existing_process(self, pid, true) == false {
            self.process_list.remove(&pid);
            false
        } else {
            true
        }
    }

    fn refresh_processes(&mut self) {
        // I think that 10192 as length will be enough to get all processes at once...
        let mut process_ids: Vec<DWORD> = Vec::with_capacity(PROCESS_LEN);
        let mut cb_needed = 0;

        unsafe {
            process_ids.set_len(PROCESS_LEN);
        }
        let size = ::std::mem::size_of::<DWORD>() * process_ids.len();
        unsafe {
            if K32EnumProcesses(process_ids.as_mut_ptr(), size as DWORD, &mut cb_needed) == 0 {
                return;
            }
        }
        let nb_processes = cb_needed / ::std::mem::size_of::<DWORD>() as DWORD;
        unsafe {
            process_ids.set_len(nb_processes as usize);
        }

        {
            let this = WrapSystem(UnsafeCell::new(self));

            process_ids
                .par_iter()
                .filter_map(|pid| {
                    let pid = *pid as usize;
                    if !refresh_existing_process(this.get(), pid, false) {
                        let ppid = unsafe { get_parent_process_id(pid) };
                        let mut p = Process::new(pid, ppid, 0);
                        update_proc_info(&mut p);
                        Some(Wrap(p))
                    } else {
                        None
                    }
                })
                .collect::<Vec<_>>()
        }
        .into_iter()
        .for_each(|p| {
            self.process_list.insert(p.0.pid(), p.0);
        });
        self.clear_procs();
    }

    fn refresh_disks(&mut self) {
        for disk in &mut self.disks {
            disk.update();
        }
    }

    fn refresh_disk_list(&mut self) {
        self.disks = unsafe { get_disks() };
    }

    fn get_process_list(&self) -> &HashMap<Pid, Process> {
        &self.process_list
    }

    fn get_process(&self, pid: Pid) -> Option<&Process> {
        self.process_list.get(&(pid as usize))
    }

    fn get_processor_list(&self) -> &[Processor] {
        &self.processors[..]
    }

    fn get_total_memory(&self) -> u64 {
        self.mem_total >> 10
    }

    fn get_free_memory(&self) -> u64 {
        self.mem_free >> 10
    }

    fn get_used_memory(&self) -> u64 {
        (self.mem_total - self.mem_free) >> 10
    }

    fn get_total_swap(&self) -> u64 {
        self.swap_total >> 10
    }

    fn get_free_swap(&self) -> u64 {
        self.swap_free >> 10
    }

    fn get_used_swap(&self) -> u64 {
        (self.swap_total - self.swap_free) >> 10
    }

    fn get_components_list(&self) -> &[Component] {
        &self.temperatures[..]
    }

    fn get_disks(&self) -> &[Disk] {
        &self.disks[..]
    }

    fn get_network(&self) -> &NetworkData {
        &self.network
    }

    fn get_uptime(&self) -> u64 {
        self.uptime
    }
}

fn is_proc_running(handle: HANDLE) -> bool {
    let mut exit_code = 0;
    let ret = unsafe { GetExitCodeProcess(handle, &mut exit_code) };
    !(ret == FALSE || exit_code != STILL_ACTIVE)
}

fn refresh_existing_process(s: &mut System, pid: Pid, compute_cpu: bool) -> bool {
    if let Some(ref mut entry) = s.process_list.get_mut(&(pid as usize)) {
        if !is_proc_running(get_handle(entry)) {
            return false;
        }
        update_proc_info(entry);
        if compute_cpu {
            compute_cpu_usage(entry, s.processors.len() as u64 - 1);
        }
        true
    } else {
        false
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use backend::extract;
use operation::selection::select_package;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    let mut base_pathbuf = base_path.to_path_buf();
    let tarball_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), "")));
    let gz_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + &package.name + "/"));
    let install_path = base_pathbuf.append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &package.name + "/"));

    try!(download::download_file_while(
            &package.url,
            &tarball_path,
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );

    try!(extract::extract_tar(&tarball_path, &gz_path));
    try!(extract::extract_gz(&gz_path, &install_path));

    Ok(())
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            return install_package(&result, base_path);
        }
    }
    Err("Specified package not found".to_string())
}<commit_msg>Install dependencies if there are any<commit_after>use std::error::Error;
use std::fs;
use std::io::stdin;
use std::path::Path;

use backend::download;
use backend::extract;
use operation::selection::select_package;
use package::find;
use package::pkg::Package;
use paths::{ self, PathAppend };

fn install_package(package: &Package, base_path: &Path) -> Result<(), String> {
    let mut base_pathbuf = base_path.to_path_buf();
    let tarball_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + get_option!(package.url.split('/').last(), "")));
    let gz_path = base_pathbuf.append(&(paths::DOWNLOAD_DIR.to_string() + &package.name + "/"));
    let install_path = base_pathbuf.append(&(paths::PACKAGE_INSTALL_DIR.to_string() + &package.name + "/"));

    try!(download::download_file_while(
            &package.url,
            &tarball_path,
            |all, current|{
                println!("Downloaded {} bytes, out of {} bytes.", current, all);
            }
        )
    );

    try!(extract::extract_tar(&tarball_path, &gz_path));
    try!(extract::extract_gz(&gz_path, &install_path));

    Ok(())
}

pub fn install(input: &str, base_path: &Path) -> Result<(), String> {
    let package_list_dir = get!(fs::read_dir(base_path.to_path_buf().append(paths::PACKAGE_LIST_DIR)), "");
    for list_path in package_list_dir {
        let result = find::select_package(input, list_path.unwrap().path().as_path(), select_package);
        if result.is_ok() {
            let result = result.unwrap();
            for dep in &result.dependencies {
                try!(install_package(&dep, base_path));
            }
            try!(install_package(&result, base_path));
        }
    }
    Err("Specified package not found".to_string())
}<|endoftext|>


<commit_before>use parser::{Pattern, TypeDefinition, Expression};

use super::{OperationsResult, type_definition, expression};
use super::scope::ScopeStack;

pub fn into_operations(
    scope: &mut ScopeStack,
    pattern: Pattern,
    type_def: TypeDefinition,
    expr: Option<Expression>,
) -> OperationsResult {
    let type_id = type_definition::resolve_type_id(scope, type_def)?;

    let name = match pattern {
        Pattern::Identifier(name) => name,
    };

    expr.map_or(Ok(Vec::new()), |e| {
        let mem = scope.declare(name, type_id);
        expression::into_operations(scope, e, type_id, mem)
    })
}

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

    use parser::Identifier;
    use operations::item_type::ItemType;

    #[test]
    fn declaration_only() {
        // When only doing a declaration, no operations should be generated
        // since there is no expression to actually evaluate
        let mut scope = ScopeStack::new();
        scope.declare_type(Identifier::from("u8"), ItemType::Primitive(1));

        let ops = into_operations(
            &mut scope,
            Pattern::Identifier(Identifier::from("foo")),
            TypeDefinition::Name {name: Identifier::from("u8")},
            None
        ).unwrap();
        assert_eq!(ops.len(), 0);
    }
}
<commit_msg>Making sure to always declare and allocate in declaration<commit_after>use parser::{Pattern, TypeDefinition, Expression};

use super::{Operation, OperationsResult, type_definition, expression};
use super::scope::ScopeStack;

pub fn into_operations(
    scope: &mut ScopeStack,
    pattern: Pattern,
    type_def: TypeDefinition,
    expr: Option<Expression>,
) -> OperationsResult {
    let type_id = type_definition::resolve_type_id(scope, type_def)?;

    let name = match pattern {
        Pattern::Identifier(name) => name,
    };

    // Need to always declare the variable in the scope
    let mem = scope.declare(name, type_id);
    let mut ops = vec![Operation::Allocate(mem)];

    if let Some(expr) = expr {
        ops.extend(expression::into_operations(scope, expr, type_id, mem)?)
    }

    Ok(ops)
}

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

    use parser::Identifier;
    use operations::item_type::ItemType;

    #[test]
    fn declaration_only() {
        // When only doing a declaration, no operations should be generated
        // since there is no expression to actually evaluate
        let mut scope = ScopeStack::new();
        scope.declare_type(Identifier::from("u8"), ItemType::Primitive(1));

        let ops = into_operations(
            &mut scope,
            Pattern::Identifier(Identifier::from("foo")),
            TypeDefinition::Name {name: Identifier::from("u8")},
            None
        ).unwrap();

        assert!(!scope.lookup(&Identifier::from("foo")).is_empty(), "No value was declared");
        assert_eq!(ops.len(), 1);
    }
}
<|endoftext|>


<commit_before>use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::prelude::*;
use std::path::{Path, PathBuf};

use toml;

use core::{SourceId, Source, Package, Registry, Dependency, PackageIdSpec};
use core::PackageId;
use ops::{self, CompileFilter};
use sources::{GitSource, PathSource, RegistrySource};
use util::{CargoResult, ChainError, Config, human, internal};

#[derive(RustcDecodable, RustcEncodable)]
enum CrateListing {
    V1(CrateListingV1),
}

#[derive(RustcDecodable, RustcEncodable)]
struct CrateListingV1 {
    v1: BTreeMap<PackageId, BTreeSet<String>>,
}

struct Transaction {
    bins: Vec<PathBuf>,
}

impl Drop for Transaction {
    fn drop(&mut self) {
        for bin in self.bins.iter() {
            let _ = fs::remove_file(bin);
        }
    }
}

pub fn install(root: Option<&str>,
               krate: Option<&str>,
               source_id: &SourceId,
               vers: Option<&str>,
               opts: &ops::CompileOptions) -> CargoResult<()> {
    let config = opts.config;
    let root = try!(resolve_root(root, config));
    let (pkg, source) = if source_id.is_git() {
        try!(select_pkg(GitSource::new(source_id, config), source_id,
                        krate, vers, &mut |git| git.read_packages()))
    } else if source_id.is_path() {
        let path = source_id.url().to_file_path().ok()
                            .expect("path sources must have a valid path");
        try!(select_pkg(PathSource::new(&path, source_id, config),
                        source_id, krate, vers,
                        &mut |path| path.read_packages()))
    } else {
        try!(select_pkg(RegistrySource::new(source_id, config),
                        source_id, krate, vers,
                        &mut |_| Err(human("must specify a crate to install from \
                                            crates.io, or use --path or --git to \
                                            specify alternate source"))))
    };

    let mut list = try!(read_crate_list(&root));
    let dst = root.join("bin");
    try!(check_overwrites(&dst, &pkg, &opts.filter, &list));

    let target_dir = config.cwd().join("target-install");
    config.set_target_dir(&target_dir);
    let compile = try!(ops::compile_pkg(&pkg, Some(source), opts).chain_error(|| {
        human(format!("failed to compile `{}`, intermediate artifacts can be \
                       found at `{}`", pkg, target_dir.display()))
    }));

    let mut t = Transaction { bins: Vec::new() };
    try!(fs::create_dir_all(&dst));
    for bin in compile.binaries.iter() {
        let dst = dst.join(bin.file_name().unwrap());
        try!(config.shell().status("Installing", dst.display()));
        try!(fs::copy(&bin, &dst).chain_error(|| {
            human(format!("failed to copy `{}` to `{}`", bin.display(),
                          dst.display()))
        }));
        t.bins.push(dst);
    }
    try!(fs::remove_dir_all(&target_dir));

    list.v1.entry(pkg.package_id().clone()).or_insert_with(|| {
        BTreeSet::new()
    }).extend(t.bins.iter().map(|t| {
        t.file_name().unwrap().to_string_lossy().into_owned()
    }));
    try!(write_crate_list(&root, list));

    t.bins.truncate(0);

    // Print a warning that if this directory isn't in PATH that they won't be
    // able to run these commands.
    let path = env::var_os("PATH").unwrap_or(OsString::new());
    for path in env::split_paths(&path) {
        if path == dst {
            return Ok(())
        }
    }

    try!(config.shell().warn(&format!("be sure to add `{}` to your PATH to be \
                                       able to run the installed binaries",
                                      dst.display())));
    Ok(())
}

fn select_pkg<'a, T>(mut source: T,
                     source_id: &SourceId,
                     name: Option<&str>,
                     vers: Option<&str>,
                     list_all: &mut FnMut(&mut T) -> CargoResult<Vec<Package>>)
                     -> CargoResult<(Package, Box<Source + 'a>)>
    where T: Source + 'a
{
    try!(source.update());
    match name {
        Some(name) => {
            let dep = try!(Dependency::parse(name, vers, source_id));
            let deps = try!(source.query(&dep));
            match deps.iter().map(|p| p.package_id()).max() {
                Some(pkgid) => {
                    try!(source.download(&[pkgid.clone()]));
                    Ok((try!(source.get(&[pkgid.clone()])).remove(0),
                        Box::new(source)))
                }
                None => {
                    let vers_info = vers.map(|v| format!(" with version `{}`", v))
                                        .unwrap_or(String::new());
                    Err(human(format!("could not find `{}` in `{}`{}", name,
                                      source_id, vers_info)))
                }
            }
        }
        None => {
            let candidates = try!(list_all(&mut source));
            let binaries = candidates.iter().filter(|cand| {
                cand.targets().iter().filter(|t| t.is_bin()).count() > 0
            });
            let examples = candidates.iter().filter(|cand| {
                cand.targets().iter().filter(|t| t.is_example()).count() > 0
            });
            let pkg = match try!(one(binaries, |v| multi_err("binaries", v))) {
                Some(p) => p,
                None => {
                    match try!(one(examples, |v| multi_err("examples", v))) {
                        Some(p) => p,
                        None => bail!("no packages found with binaries or \
                                       examples"),
                    }
                }
            };
            return Ok((pkg.clone(), Box::new(source)));

            #[allow(deprecated)] // connect => join in 1.3
            fn multi_err(kind: &str, mut pkgs: Vec<&Package>) -> String {
                pkgs.sort_by(|a, b| a.name().cmp(b.name()));
                format!("multiple packages with {} found: {}", kind,
                        pkgs.iter().map(|p| p.name()).collect::<Vec<_>>()
                            .connect(", "))
            }
        }
    }
}

fn one<I, F>(mut i: I, f: F) -> CargoResult<Option<I::Item>>
    where I: Iterator,
          F: FnOnce(Vec<I::Item>) -> String
{
    match (i.next(), i.next()) {
        (Some(i1), Some(i2)) => {
            let mut v = vec![i1, i2];
            v.extend(i);
            Err(human(f(v)))
        }
        (Some(i), None) => Ok(Some(i)),
        (None, _) => Ok(None)
    }
}

fn check_overwrites(dst: &Path,
                    pkg: &Package,
                    filter: &ops::CompileFilter,
                    prev: &CrateListingV1) -> CargoResult<()> {
    let check = |name| {
        let name = format!("{}{}", name, env::consts::EXE_SUFFIX);
        if fs::metadata(dst.join(&name)).is_err() {
            return Ok(())
        }
        let mut msg = format!("binary `{}` already exists in destination", name);
        if let Some((p, _)) = prev.v1.iter().find(|&(_, v)| v.contains(&name)) {
            msg.push_str(&format!(" as part of `{}`", p));
        }
        Err(human(msg))
    };
    match *filter {
        CompileFilter::Everything => {
            // If explicit --bin or --example flags were passed then those'll
            // get checked during cargo_compile, we only care about the "build
            // everything" case here
            if pkg.targets().iter().filter(|t| t.is_bin()).next().is_none() {
                bail!("specified package has no binaries")
            }

            for target in pkg.targets().iter().filter(|t| t.is_bin()) {
                try!(check(target.name()));
            }
        }
        CompileFilter::Only { bins, examples, .. } => {
            for bin in bins.iter().chain(examples) {
                try!(check(bin));
            }
        }
    }
    Ok(())
}

fn read_crate_list(path: &Path) -> CargoResult<CrateListingV1> {
    let metadata = path.join(".crates.toml");
    let mut f = match File::open(&metadata) {
        Ok(f) => f,
        Err(..) => return Ok(CrateListingV1 { v1: BTreeMap::new() }),
    };
    (|| -> CargoResult<_> {
        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));
        let listing = try!(toml::decode_str(&contents).chain_error(|| {
            internal("invalid TOML found for metadata")
        }));
        match listing {
            CrateListing::V1(v1) => Ok(v1),
        }
    }).chain_error(|| {
        human(format!("failed to parse crate metadata at `{}`",
                      metadata.display()))
    })
}

fn write_crate_list(path: &Path, listing: CrateListingV1) -> CargoResult<()> {
    let metadata = path.join(".crates.toml");
    (|| -> CargoResult<_> {
        let mut f = try!(File::create(&metadata));
        let data = toml::encode_str::<CrateListing>(&CrateListing::V1(listing));
        try!(f.write_all(data.as_bytes()));
        Ok(())
    }).chain_error(|| {
        human(format!("failed to write crate metadata at `{}`",
                      metadata.display()))
    })
}

pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
    let dst = try!(resolve_root(dst, config));
    let list = try!(read_crate_list(&dst));
    let mut shell = config.shell();
    let out = shell.out();
    for (k, v) in list.v1.iter() {
        try!(writeln!(out, "{}:", k));
        for bin in v {
            try!(writeln!(out, "    {}", bin));
        }
    }
    Ok(())
}

pub fn uninstall(root: Option<&str>,
                 spec: &str,
                 bins: &[String],
                 config: &Config) -> CargoResult<()> {
    let root = try!(resolve_root(root, config));
    let mut metadata = try!(read_crate_list(&root));
    let mut to_remove = Vec::new();
    {
        let result = try!(PackageIdSpec::query_str(spec, metadata.v1.keys()))
                                        .clone();
        let mut installed = match metadata.v1.entry(result.clone()) {
            Entry::Occupied(e) => e,
            Entry::Vacant(..) => panic!("entry not found: {}", result),
        };
        let dst = root.join("bin");
        for bin in installed.get() {
            let bin = dst.join(bin);
            if fs::metadata(&bin).is_err() {
                bail!("corrupt metadata, `{}` does not exist when it should",
                      bin.display())
            }
        }

        let bins = bins.iter().map(|s| {
            if s.ends_with(env::consts::EXE_SUFFIX) {
                s.to_string()
            } else {
                format!("{}{}", s, env::consts::EXE_SUFFIX)
            }
        }).collect::<Vec<_>>();

        for bin in bins.iter() {
            if !installed.get().contains(bin) {
                bail!("binary `{}` not installed as part of `{}`", bin, result)
            }
        }

        if bins.len() == 0 {
            to_remove.extend(installed.get().iter().map(|b| dst.join(b)));
            installed.get_mut().clear();
        } else {
            for bin in bins.iter() {
                to_remove.push(dst.join(bin));
                installed.get_mut().remove(bin);
            }
        }
        if installed.get().len() == 0 {
            installed.remove();
        }
    }
    try!(write_crate_list(&root, metadata));
    for bin in to_remove {
        try!(config.shell().status("Removing", bin.display()));
        try!(fs::remove_file(bin));
    }

    Ok(())
}

fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult<PathBuf> {
    let config_root = try!(config.get_string("install.root"));
    Ok(flag.map(PathBuf::from).or_else(|| {
        env::var_os("CARGO_INSTALL_ROOT").map(PathBuf::from)
    }).or_else(|| {
        config_root.clone().map(|(v, _)| PathBuf::from(v))
    }).unwrap_or_else(|| {
        config.home().to_owned()
    }))
}
<commit_msg>Auto merge of #2228 - tbu-:pr_only_missing, r=alexcrichton<commit_after>use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::env;
use std::ffi::OsString;
use std::fs::{self, File};
use std::io::prelude::*;
use std::io;
use std::path::{Path, PathBuf};

use toml;

use core::{SourceId, Source, Package, Registry, Dependency, PackageIdSpec};
use core::PackageId;
use ops::{self, CompileFilter};
use sources::{GitSource, PathSource, RegistrySource};
use util::{CargoResult, ChainError, Config, human, internal};

#[derive(RustcDecodable, RustcEncodable)]
enum CrateListing {
    V1(CrateListingV1),
}

#[derive(RustcDecodable, RustcEncodable)]
struct CrateListingV1 {
    v1: BTreeMap<PackageId, BTreeSet<String>>,
}

struct Transaction {
    bins: Vec<PathBuf>,
}

impl Drop for Transaction {
    fn drop(&mut self) {
        for bin in self.bins.iter() {
            let _ = fs::remove_file(bin);
        }
    }
}

pub fn install(root: Option<&str>,
               krate: Option<&str>,
               source_id: &SourceId,
               vers: Option<&str>,
               opts: &ops::CompileOptions) -> CargoResult<()> {
    let config = opts.config;
    let root = try!(resolve_root(root, config));
    let (pkg, source) = if source_id.is_git() {
        try!(select_pkg(GitSource::new(source_id, config), source_id,
                        krate, vers, &mut |git| git.read_packages()))
    } else if source_id.is_path() {
        let path = source_id.url().to_file_path().ok()
                            .expect("path sources must have a valid path");
        try!(select_pkg(PathSource::new(&path, source_id, config),
                        source_id, krate, vers,
                        &mut |path| path.read_packages()))
    } else {
        try!(select_pkg(RegistrySource::new(source_id, config),
                        source_id, krate, vers,
                        &mut |_| Err(human("must specify a crate to install from \
                                            crates.io, or use --path or --git to \
                                            specify alternate source"))))
    };

    let mut list = try!(read_crate_list(&root));
    let dst = root.join("bin");
    try!(check_overwrites(&dst, &pkg, &opts.filter, &list));

    let target_dir = config.cwd().join("target-install");
    config.set_target_dir(&target_dir);
    let compile = try!(ops::compile_pkg(&pkg, Some(source), opts).chain_error(|| {
        human(format!("failed to compile `{}`, intermediate artifacts can be \
                       found at `{}`", pkg, target_dir.display()))
    }));

    let mut t = Transaction { bins: Vec::new() };
    try!(fs::create_dir_all(&dst));
    for bin in compile.binaries.iter() {
        let dst = dst.join(bin.file_name().unwrap());
        try!(config.shell().status("Installing", dst.display()));
        try!(fs::copy(&bin, &dst).chain_error(|| {
            human(format!("failed to copy `{}` to `{}`", bin.display(),
                          dst.display()))
        }));
        t.bins.push(dst);
    }
    try!(fs::remove_dir_all(&target_dir));

    list.v1.entry(pkg.package_id().clone()).or_insert_with(|| {
        BTreeSet::new()
    }).extend(t.bins.iter().map(|t| {
        t.file_name().unwrap().to_string_lossy().into_owned()
    }));
    try!(write_crate_list(&root, list));

    t.bins.truncate(0);

    // Print a warning that if this directory isn't in PATH that they won't be
    // able to run these commands.
    let path = env::var_os("PATH").unwrap_or(OsString::new());
    for path in env::split_paths(&path) {
        if path == dst {
            return Ok(())
        }
    }

    try!(config.shell().warn(&format!("be sure to add `{}` to your PATH to be \
                                       able to run the installed binaries",
                                      dst.display())));
    Ok(())
}

fn select_pkg<'a, T>(mut source: T,
                     source_id: &SourceId,
                     name: Option<&str>,
                     vers: Option<&str>,
                     list_all: &mut FnMut(&mut T) -> CargoResult<Vec<Package>>)
                     -> CargoResult<(Package, Box<Source + 'a>)>
    where T: Source + 'a
{
    try!(source.update());
    match name {
        Some(name) => {
            let dep = try!(Dependency::parse(name, vers, source_id));
            let deps = try!(source.query(&dep));
            match deps.iter().map(|p| p.package_id()).max() {
                Some(pkgid) => {
                    try!(source.download(&[pkgid.clone()]));
                    Ok((try!(source.get(&[pkgid.clone()])).remove(0),
                        Box::new(source)))
                }
                None => {
                    let vers_info = vers.map(|v| format!(" with version `{}`", v))
                                        .unwrap_or(String::new());
                    Err(human(format!("could not find `{}` in `{}`{}", name,
                                      source_id, vers_info)))
                }
            }
        }
        None => {
            let candidates = try!(list_all(&mut source));
            let binaries = candidates.iter().filter(|cand| {
                cand.targets().iter().filter(|t| t.is_bin()).count() > 0
            });
            let examples = candidates.iter().filter(|cand| {
                cand.targets().iter().filter(|t| t.is_example()).count() > 0
            });
            let pkg = match try!(one(binaries, |v| multi_err("binaries", v))) {
                Some(p) => p,
                None => {
                    match try!(one(examples, |v| multi_err("examples", v))) {
                        Some(p) => p,
                        None => bail!("no packages found with binaries or \
                                       examples"),
                    }
                }
            };
            return Ok((pkg.clone(), Box::new(source)));

            #[allow(deprecated)] // connect => join in 1.3
            fn multi_err(kind: &str, mut pkgs: Vec<&Package>) -> String {
                pkgs.sort_by(|a, b| a.name().cmp(b.name()));
                format!("multiple packages with {} found: {}", kind,
                        pkgs.iter().map(|p| p.name()).collect::<Vec<_>>()
                            .connect(", "))
            }
        }
    }
}

fn one<I, F>(mut i: I, f: F) -> CargoResult<Option<I::Item>>
    where I: Iterator,
          F: FnOnce(Vec<I::Item>) -> String
{
    match (i.next(), i.next()) {
        (Some(i1), Some(i2)) => {
            let mut v = vec![i1, i2];
            v.extend(i);
            Err(human(f(v)))
        }
        (Some(i), None) => Ok(Some(i)),
        (None, _) => Ok(None)
    }
}

fn check_overwrites(dst: &Path,
                    pkg: &Package,
                    filter: &ops::CompileFilter,
                    prev: &CrateListingV1) -> CargoResult<()> {
    let check = |name| {
        let name = format!("{}{}", name, env::consts::EXE_SUFFIX);
        if fs::metadata(dst.join(&name)).is_err() {
            return Ok(())
        }
        let mut msg = format!("binary `{}` already exists in destination", name);
        if let Some((p, _)) = prev.v1.iter().find(|&(_, v)| v.contains(&name)) {
            msg.push_str(&format!(" as part of `{}`", p));
        }
        Err(human(msg))
    };
    match *filter {
        CompileFilter::Everything => {
            // If explicit --bin or --example flags were passed then those'll
            // get checked during cargo_compile, we only care about the "build
            // everything" case here
            if pkg.targets().iter().filter(|t| t.is_bin()).next().is_none() {
                bail!("specified package has no binaries")
            }

            for target in pkg.targets().iter().filter(|t| t.is_bin()) {
                try!(check(target.name()));
            }
        }
        CompileFilter::Only { bins, examples, .. } => {
            for bin in bins.iter().chain(examples) {
                try!(check(bin));
            }
        }
    }
    Ok(())
}

fn read_crate_list(path: &Path) -> CargoResult<CrateListingV1> {
    let metadata = path.join(".crates.toml");
    let mut f = match File::open(&metadata) {
        Ok(f) => f,
        Err(e) => {
            if e.kind() == io::ErrorKind::NotFound {
                return Ok(CrateListingV1 { v1: BTreeMap::new() });
            }
            return Err(e).chain_error(|| {
                human(format!("failed to open crate metadata at `{}`",
                              metadata.display()))
            });
        }
    };
    (|| -> CargoResult<_> {
        let mut contents = String::new();
        try!(f.read_to_string(&mut contents));
        let listing = try!(toml::decode_str(&contents).chain_error(|| {
            internal("invalid TOML found for metadata")
        }));
        match listing {
            CrateListing::V1(v1) => Ok(v1),
        }
    }).chain_error(|| {
        human(format!("failed to parse crate metadata at `{}`",
                      metadata.display()))
    })
}

fn write_crate_list(path: &Path, listing: CrateListingV1) -> CargoResult<()> {
    let metadata = path.join(".crates.toml");
    (|| -> CargoResult<_> {
        let mut f = try!(File::create(&metadata));
        let data = toml::encode_str::<CrateListing>(&CrateListing::V1(listing));
        try!(f.write_all(data.as_bytes()));
        Ok(())
    }).chain_error(|| {
        human(format!("failed to write crate metadata at `{}`",
                      metadata.display()))
    })
}

pub fn install_list(dst: Option<&str>, config: &Config) -> CargoResult<()> {
    let dst = try!(resolve_root(dst, config));
    let list = try!(read_crate_list(&dst));
    let mut shell = config.shell();
    let out = shell.out();
    for (k, v) in list.v1.iter() {
        try!(writeln!(out, "{}:", k));
        for bin in v {
            try!(writeln!(out, "    {}", bin));
        }
    }
    Ok(())
}

pub fn uninstall(root: Option<&str>,
                 spec: &str,
                 bins: &[String],
                 config: &Config) -> CargoResult<()> {
    let root = try!(resolve_root(root, config));
    let mut metadata = try!(read_crate_list(&root));
    let mut to_remove = Vec::new();
    {
        let result = try!(PackageIdSpec::query_str(spec, metadata.v1.keys()))
                                        .clone();
        let mut installed = match metadata.v1.entry(result.clone()) {
            Entry::Occupied(e) => e,
            Entry::Vacant(..) => panic!("entry not found: {}", result),
        };
        let dst = root.join("bin");
        for bin in installed.get() {
            let bin = dst.join(bin);
            if fs::metadata(&bin).is_err() {
                bail!("corrupt metadata, `{}` does not exist when it should",
                      bin.display())
            }
        }

        let bins = bins.iter().map(|s| {
            if s.ends_with(env::consts::EXE_SUFFIX) {
                s.to_string()
            } else {
                format!("{}{}", s, env::consts::EXE_SUFFIX)
            }
        }).collect::<Vec<_>>();

        for bin in bins.iter() {
            if !installed.get().contains(bin) {
                bail!("binary `{}` not installed as part of `{}`", bin, result)
            }
        }

        if bins.len() == 0 {
            to_remove.extend(installed.get().iter().map(|b| dst.join(b)));
            installed.get_mut().clear();
        } else {
            for bin in bins.iter() {
                to_remove.push(dst.join(bin));
                installed.get_mut().remove(bin);
            }
        }
        if installed.get().len() == 0 {
            installed.remove();
        }
    }
    try!(write_crate_list(&root, metadata));
    for bin in to_remove {
        try!(config.shell().status("Removing", bin.display()));
        try!(fs::remove_file(bin));
    }

    Ok(())
}

fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult<PathBuf> {
    let config_root = try!(config.get_string("install.root"));
    Ok(flag.map(PathBuf::from).or_else(|| {
        env::var_os("CARGO_INSTALL_ROOT").map(PathBuf::from)
    }).or_else(|| {
        config_root.clone().map(|(v, _)| PathBuf::from(v))
    }).unwrap_or_else(|| {
        config.home().to_owned()
    }))
}
<|endoftext|>


<commit_before><commit_msg>Add ID reporting in imag-mail<commit_after><|endoftext|>