<commit_before>use std::collections::HashMap;
use std::num::NonZeroU64;
use std::rc::Rc;

// "ensure" seems to require "bail" be in scope (macro hygiene issue?).
#[allow(unused_imports)]
use failure::{bail, ensure};
use log::debug;

use crate::core::interning::InternedString;
use crate::core::{Dependency, PackageId, SourceId, Summary};
use crate::util::CargoResult;
use crate::util::Graph;

use super::dep_cache::RegistryQueryer;
use super::types::{ConflictMap, FeaturesSet, ResolveOpts};

pub use super::encode::Metadata;
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
pub use super::resolve::Resolve;

// A `Context` is basically a bunch of local resolution information which is
// kept around for all `BacktrackFrame` instances. As a result, this runs the
// risk of being cloned *a lot* so we want to make this as cheap to clone as
// possible.
#[derive(Clone)]
pub struct Context {
    pub activations: Activations,
    /// list the features that are activated for each package
    pub resolve_features: im_rc::HashMap<PackageId, FeaturesSet>,
    /// get the package that will be linking to a native library by its links attribute
    pub links: im_rc::HashMap<InternedString, PackageId>,
    /// for each package the list of names it can see,
    /// then for each name the exact version that name represents and weather the name is public.
    pub public_dependency:
        Option<im_rc::HashMap<PackageId, im_rc::HashMap<InternedString, (PackageId, bool)>>>,

    /// a way to look up for a package in activations what packages required it
    /// and all of the exact deps that it fulfilled.
    pub parents: Graph<PackageId, Rc<Vec<Dependency>>>,
}

/// When backtracking it can be useful to know how far back to go.
/// The `ContextAge` of a `Context` is a monotonically increasing counter of the number
/// of decisions made to get to this state.
/// Several structures store the `ContextAge` when it was added,
/// to be used in `find_candidate` for backtracking.
pub type ContextAge = usize;

/// Find the activated version of a crate based on the name, source, and semver compatibility.
/// By storing this in a hash map we ensure that there is only one
/// semver compatible version of each crate.
/// This all so stores the `ContextAge`.
pub type Activations =
    im_rc::HashMap<(InternedString, SourceId, SemverCompatibility), (Summary, ContextAge)>;

/// A type that represents when cargo treats two Versions as compatible.
/// Versions `a` and `b` are compatible if their left-most nonzero digit is the
/// same.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum SemverCompatibility {
    Major(NonZeroU64),
    Minor(NonZeroU64),
    Patch(u64),
}

impl From<&semver::Version> for SemverCompatibility {
    fn from(ver: &semver::Version) -> Self {
        if let Some(m) = NonZeroU64::new(ver.major) {
            return SemverCompatibility::Major(m);
        }
        if let Some(m) = NonZeroU64::new(ver.minor) {
            return SemverCompatibility::Minor(m);
        }
        SemverCompatibility::Patch(ver.patch)
    }
}

impl PackageId {
    pub fn as_activations_key(self) -> (InternedString, SourceId, SemverCompatibility) {
        (self.name(), self.source_id(), self.version().into())
    }
}

impl Context {
    pub fn new(check_public_visible_dependencies: bool) -> Context {
        Context {
            resolve_features: im_rc::HashMap::new(),
            links: im_rc::HashMap::new(),
            public_dependency: if check_public_visible_dependencies {
                Some(im_rc::HashMap::new())
            } else {
                None
            },
            parents: Graph::new(),
            activations: im_rc::HashMap::new(),
        }
    }

    /// Activate this summary by inserting it into our list of known activations.
    ///
    /// The `parent` passed in here is the parent summary/dependency edge which
    /// cased `summary` to get activated. This may not be present for the root
    /// crate, for example.
    ///
    /// Returns `true` if this summary with the given features is already activated.
    pub fn flag_activated(
        &mut self,
        summary: &Summary,
        opts: &ResolveOpts,
        parent: Option<(&Summary, &Dependency)>,
    ) -> CargoResult<bool> {
        let id = summary.package_id();
        let age: ContextAge = self.age();
        match self.activations.entry(id.as_activations_key()) {
            im_rc::hashmap::Entry::Occupied(o) => {
                debug_assert_eq!(
                    &o.get().0,
                    summary,
                    "cargo does not allow two semver compatible versions"
                );
            }
            im_rc::hashmap::Entry::Vacant(v) => {
                if let Some(link) = summary.links() {
                    ensure!(
                        self.links.insert(link, id).is_none(),
                        "Attempting to resolve a dependency with more then one crate with the \
                         links={}.\nThis will not build as is. Consider rebuilding the .lock file.",
                        &*link
                    );
                }
                v.insert((summary.clone(), age));

                // If we've got a parent dependency which activated us, *and*
                // the dependency has a different source id listed than the
                // `summary` itself, then things get interesting. This basically
                // means that a `[patch]` was used to augment `dep.source_id()`
                // with `summary`.
                //
                // In this scenario we want to consider the activation key, as
                // viewed from the perspective of `dep.source_id()`, as being
                // fulfilled. This means that we need to add a second entry in
                // the activations map for the source that was patched, in
                // addition to the source of the actual `summary` itself.
                //
                // Without this it would be possible to have both 1.0.0 and
                // 1.1.0 "from crates.io" in a dependency graph if one of those
                // versions came from a `[patch]` source.
                if let Some((_, dep)) = parent {
                    if dep.source_id() != id.source_id() {
                        let key = (id.name(), dep.source_id(), id.version().into());
                        let prev = self.activations.insert(key, (summary.clone(), age));
                        assert!(prev.is_none());
                    }
                }

                return Ok(false);
            }
        }
        debug!("checking if {} is already activated", summary.package_id());
        if opts.all_features {
            return Ok(false);
        }

        let has_default_feature = summary.features().contains_key("default");
        Ok(match self.resolve_features.get(&id) {
            Some(prev) => {
                opts.features.is_subset(prev)
                    && (!opts.uses_default_features
                        || prev.contains("default")
                        || !has_default_feature)
            }
            None => {
                opts.features.is_empty() && (!opts.uses_default_features || !has_default_feature)
            }
        })
    }

    /// Returns the `ContextAge` of this `Context`.
    /// For now we use (len of activations) as the age.
    /// See the `ContextAge` docs for more details.
    pub fn age(&self) -> ContextAge {
        self.activations.len()
    }

    /// If the package is active returns the `ContextAge` when it was added
    pub fn is_active(&self, id: PackageId) -> Option<ContextAge> {
        self.activations
            .get(&id.as_activations_key())
            .and_then(|(s, l)| if s.package_id() == id { Some(*l) } else { None })
    }

    /// Checks whether all of `parent` and the keys of `conflicting activations`
    /// are still active.
    /// If so returns the `ContextAge` when the newest one was added.
    pub fn is_conflicting(
        &self,
        parent: Option<PackageId>,
        conflicting_activations: &ConflictMap,
    ) -> Option<usize> {
        let mut max = 0;
        for &id in conflicting_activations.keys().chain(parent.as_ref()) {
            if let Some(age) = self.is_active(id) {
                max = std::cmp::max(max, age);
            } else {
                return None;
            }
        }
        Some(max)
    }

    pub fn resolve_replacements(
        &self,
        registry: &RegistryQueryer<'_>,
    ) -> HashMap<PackageId, PackageId> {
        self.activations
            .values()
            .filter_map(|(s, _)| registry.used_replacement_for(s.package_id()))
            .collect()
    }

    pub fn graph(&self) -> Graph<PackageId, Vec<Dependency>> {
        let mut graph: Graph<PackageId, Vec<Dependency>> = Graph::new();
        self.activations
            .values()
            .for_each(|(r, _)| graph.add(r.package_id()));
        for i in self.parents.iter() {
            graph.add(*i);
            for (o, e) in self.parents.edges(i) {
                let old_link = graph.link(*o, *i);
                assert!(old_link.is_empty());
                *old_link = e.to_vec();
            }
        }
        graph
    }
}
<commit_msg>Modify Context::flag_activated to return ActivateResult<commit_after>use std::collections::HashMap;
use std::num::NonZeroU64;
use std::rc::Rc;

use failure::format_err;
use log::debug;

use crate::core::interning::InternedString;
use crate::core::{Dependency, PackageId, SourceId, Summary};
use crate::util::Graph;

use super::dep_cache::RegistryQueryer;
use super::errors::ActivateResult;
use super::types::{ConflictMap, FeaturesSet, ResolveOpts};

pub use super::encode::Metadata;
pub use super::encode::{EncodableDependency, EncodablePackageId, EncodableResolve};
pub use super::resolve::Resolve;

// A `Context` is basically a bunch of local resolution information which is
// kept around for all `BacktrackFrame` instances. As a result, this runs the
// risk of being cloned *a lot* so we want to make this as cheap to clone as
// possible.
#[derive(Clone)]
pub struct Context {
    pub activations: Activations,
    /// list the features that are activated for each package
    pub resolve_features: im_rc::HashMap<PackageId, FeaturesSet>,
    /// get the package that will be linking to a native library by its links attribute
    pub links: im_rc::HashMap<InternedString, PackageId>,
    /// for each package the list of names it can see,
    /// then for each name the exact version that name represents and weather the name is public.
    pub public_dependency:
        Option<im_rc::HashMap<PackageId, im_rc::HashMap<InternedString, (PackageId, bool)>>>,

    /// a way to look up for a package in activations what packages required it
    /// and all of the exact deps that it fulfilled.
    pub parents: Graph<PackageId, Rc<Vec<Dependency>>>,
}

/// When backtracking it can be useful to know how far back to go.
/// The `ContextAge` of a `Context` is a monotonically increasing counter of the number
/// of decisions made to get to this state.
/// Several structures store the `ContextAge` when it was added,
/// to be used in `find_candidate` for backtracking.
pub type ContextAge = usize;

/// Find the activated version of a crate based on the name, source, and semver compatibility.
/// By storing this in a hash map we ensure that there is only one
/// semver compatible version of each crate.
/// This all so stores the `ContextAge`.
pub type Activations =
    im_rc::HashMap<(InternedString, SourceId, SemverCompatibility), (Summary, ContextAge)>;

/// A type that represents when cargo treats two Versions as compatible.
/// Versions `a` and `b` are compatible if their left-most nonzero digit is the
/// same.
#[derive(Clone, Copy, Eq, PartialEq, Hash, Debug)]
pub enum SemverCompatibility {
    Major(NonZeroU64),
    Minor(NonZeroU64),
    Patch(u64),
}

impl From<&semver::Version> for SemverCompatibility {
    fn from(ver: &semver::Version) -> Self {
        if let Some(m) = NonZeroU64::new(ver.major) {
            return SemverCompatibility::Major(m);
        }
        if let Some(m) = NonZeroU64::new(ver.minor) {
            return SemverCompatibility::Minor(m);
        }
        SemverCompatibility::Patch(ver.patch)
    }
}

impl PackageId {
    pub fn as_activations_key(self) -> (InternedString, SourceId, SemverCompatibility) {
        (self.name(), self.source_id(), self.version().into())
    }
}

impl Context {
    pub fn new(check_public_visible_dependencies: bool) -> Context {
        Context {
            resolve_features: im_rc::HashMap::new(),
            links: im_rc::HashMap::new(),
            public_dependency: if check_public_visible_dependencies {
                Some(im_rc::HashMap::new())
            } else {
                None
            },
            parents: Graph::new(),
            activations: im_rc::HashMap::new(),
        }
    }

    /// Activate this summary by inserting it into our list of known activations.
    ///
    /// The `parent` passed in here is the parent summary/dependency edge which
    /// cased `summary` to get activated. This may not be present for the root
    /// crate, for example.
    ///
    /// Returns `true` if this summary with the given features is already activated.
    pub fn flag_activated(
        &mut self,
        summary: &Summary,
        opts: &ResolveOpts,
        parent: Option<(&Summary, &Dependency)>,
    ) -> ActivateResult<bool> {
        let id = summary.package_id();
        let age: ContextAge = self.age();
        match self.activations.entry(id.as_activations_key()) {
            im_rc::hashmap::Entry::Occupied(o) => {
                debug_assert_eq!(
                    &o.get().0,
                    summary,
                    "cargo does not allow two semver compatible versions"
                );
            }
            im_rc::hashmap::Entry::Vacant(v) => {
                if let Some(link) = summary.links() {
                    if self.links.insert(link, id).is_some() {
                        return Err(format_err!(
                            "Attempting to resolve a dependency with more then \
                             one crate with links={}.\nThis will not build as \
                             is. Consider rebuilding the .lock file.",
                            &*link
                        )
                        .into());
                    }
                }
                v.insert((summary.clone(), age));

                // If we've got a parent dependency which activated us, *and*
                // the dependency has a different source id listed than the
                // `summary` itself, then things get interesting. This basically
                // means that a `[patch]` was used to augment `dep.source_id()`
                // with `summary`.
                //
                // In this scenario we want to consider the activation key, as
                // viewed from the perspective of `dep.source_id()`, as being
                // fulfilled. This means that we need to add a second entry in
                // the activations map for the source that was patched, in
                // addition to the source of the actual `summary` itself.
                //
                // Without this it would be possible to have both 1.0.0 and
                // 1.1.0 "from crates.io" in a dependency graph if one of those
                // versions came from a `[patch]` source.
                if let Some((_, dep)) = parent {
                    if dep.source_id() != id.source_id() {
                        let key = (id.name(), dep.source_id(), id.version().into());
                        let prev = self.activations.insert(key, (summary.clone(), age));
                        assert!(prev.is_none());
                    }
                }

                return Ok(false);
            }
        }
        debug!("checking if {} is already activated", summary.package_id());
        if opts.all_features {
            return Ok(false);
        }

        let has_default_feature = summary.features().contains_key("default");
        Ok(match self.resolve_features.get(&id) {
            Some(prev) => {
                opts.features.is_subset(prev)
                    && (!opts.uses_default_features
                        || prev.contains("default")
                        || !has_default_feature)
            }
            None => {
                opts.features.is_empty() && (!opts.uses_default_features || !has_default_feature)
            }
        })
    }

    /// Returns the `ContextAge` of this `Context`.
    /// For now we use (len of activations) as the age.
    /// See the `ContextAge` docs for more details.
    pub fn age(&self) -> ContextAge {
        self.activations.len()
    }

    /// If the package is active returns the `ContextAge` when it was added
    pub fn is_active(&self, id: PackageId) -> Option<ContextAge> {
        self.activations
            .get(&id.as_activations_key())
            .and_then(|(s, l)| if s.package_id() == id { Some(*l) } else { None })
    }

    /// Checks whether all of `parent` and the keys of `conflicting activations`
    /// are still active.
    /// If so returns the `ContextAge` when the newest one was added.
    pub fn is_conflicting(
        &self,
        parent: Option<PackageId>,
        conflicting_activations: &ConflictMap,
    ) -> Option<usize> {
        let mut max = 0;
        for &id in conflicting_activations.keys().chain(parent.as_ref()) {
            if let Some(age) = self.is_active(id) {
                max = std::cmp::max(max, age);
            } else {
                return None;
            }
        }
        Some(max)
    }

    pub fn resolve_replacements(
        &self,
        registry: &RegistryQueryer<'_>,
    ) -> HashMap<PackageId, PackageId> {
        self.activations
            .values()
            .filter_map(|(s, _)| registry.used_replacement_for(s.package_id()))
            .collect()
    }

    pub fn graph(&self) -> Graph<PackageId, Vec<Dependency>> {
        let mut graph: Graph<PackageId, Vec<Dependency>> = Graph::new();
        self.activations
            .values()
            .for_each(|(r, _)| graph.add(r.package_id()));
        for i in self.parents.iter() {
            graph.add(*i);
            for (o, e) in self.parents.edges(i) {
                let old_link = graph.link(*o, *i);
                assert!(old_link.is_empty());
                *old_link = e.to_vec();
            }
        }
        graph
    }
}
<|endoftext|>


<commit_before>//! A graph-like structure used to represent a set of dependencies and in what
//! order they should be built.
//!
//! This structure is used to store the dependency graph and dynamically update
//! it to figure out when a dependency should be built.
//!
//! Dependencies in this queue are represented as a (node, edge) pair. This is
//! used to model nodes which produce multiple outputs at different times but
//! some nodes may only require one of the outputs and can start before the
//! whole node is finished.

use std::collections::{HashMap, HashSet};
use std::hash::Hash;

#[derive(Debug)]
pub struct DependencyQueue<N: Hash + Eq, E: Hash + Eq, V> {
    /// A list of all known keys to build.
    ///
    /// The value of the hash map is list of dependencies which still need to be
    /// built before the package can be built. Note that the set is dynamically
    /// updated as more dependencies are built.
    dep_map: HashMap<N, (HashSet<(N, E)>, V)>,

    /// A reverse mapping of a package to all packages that depend on that
    /// package.
    ///
    /// This map is statically known and does not get updated throughout the
    /// lifecycle of the DependencyQueue.
    ///
    /// This is sort of like a `HashMap<(N, E), HashSet<N>>` map, but more
    /// easily indexable with just an `N`
    reverse_dep_map: HashMap<N, HashMap<E, HashSet<N>>>,

    /// The total number of packages that are transitively waiting on this package
    priority: HashMap<N, usize>,
}

impl<N: Hash + Eq, E: Hash + Eq, V> Default for DependencyQueue<N, E, V> {
    fn default() -> DependencyQueue<N, E, V> {
        DependencyQueue::new()
    }
}

impl<N: Hash + Eq, E: Hash + Eq, V> DependencyQueue<N, E, V> {
    /// Creates a new dependency queue with 0 packages.
    pub fn new() -> DependencyQueue<N, E, V> {
        DependencyQueue {
            dep_map: HashMap::new(),
            reverse_dep_map: HashMap::new(),
            priority: HashMap::new(),
        }
    }
}

impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
    /// Adds a new ndoe and its dependencies to this queue.
    ///
    /// The `key` specified is a new node in the dependency graph, and the node
    /// depend on all the dependencies iterated by `dependencies`. Each
    /// dependency is a node/edge pair, where edges can be thought of as
    /// productions from nodes (aka if it's just `()` it's just waiting for the
    /// node to finish).
    ///
    /// An optional `value` can also be associated with `key` which is reclaimed
    /// when the node is ready to go.
    pub fn queue(&mut self, key: N, value: V, dependencies: impl IntoIterator<Item = (N, E)>) {
        assert!(!self.dep_map.contains_key(&key));

        let mut my_dependencies = HashSet::new();
        for (dep, edge) in dependencies {
            my_dependencies.insert((dep.clone(), edge.clone()));
            self.reverse_dep_map
                .entry(dep)
                .or_insert_with(HashMap::new)
                .entry(edge)
                .or_insert_with(HashSet::new)
                .insert(key.clone());
        }
        self.dep_map.insert(key, (my_dependencies, value));
    }

    /// All nodes have been added, calculate some internal metadata and prepare
    /// for `dequeue`.
    pub fn queue_finished(&mut self) {
        let mut out = HashMap::new();
        for key in self.dep_map.keys() {
            depth(key, &self.reverse_dep_map, &mut out);
        }
        self.priority = out.into_iter().map(|(n, set)| (n, set.len())).collect();

        fn depth<'a, N: Hash + Eq + Clone, E: Hash + Eq + Clone>(
            key: &N,
            map: &HashMap<N, HashMap<E, HashSet<N>>>,
            results: &'a mut HashMap<N, HashSet<N>>,
        ) -> &'a HashSet<N> {
            if results.contains_key(key) {
                let depth = &results[key];
                assert!(!depth.is_empty(), "cycle in DependencyQueue");
                return depth;
            }
            results.insert(key.clone(), HashSet::new());

            let mut set = HashSet::new();
            set.insert(key.clone());

            for dep in map
                .get(key)
                .into_iter()
                .flat_map(|it| it.values())
                .flatten()
            {
                set.extend(depth(dep, map, results).iter().cloned())
            }

            let slot = results.get_mut(key).unwrap();
            *slot = set;
            &*slot
        }
    }

    /// Dequeues a package that is ready to be built.
    ///
    /// A package is ready to be built when it has 0 un-built dependencies. If
    /// `None` is returned then no packages are ready to be built.
    pub fn dequeue(&mut self) -> Option<(N, V)> {
        // Look at all our crates and find everything that's ready to build (no
        // deps). After we've got that candidate set select the one which has
        // the maximum priority in the dependency graph. This way we should
        // hopefully keep CPUs hottest the longest by ensuring that long
        // dependency chains are scheduled early on in the build process and the
        // leafs higher in the tree can fill in the cracks later.
        //
        // TODO: it'd be best here to throw in a heuristic of crate size as
        //       well. For example how long did this crate historically take to
        //       compile? How large is its source code? etc.
        let next = self
            .dep_map
            .iter()
            .filter(|(_, (deps, _))| deps.is_empty())
            .map(|(key, _)| key.clone())
            .max_by_key(|k| self.priority[k]);
        let key = match next {
            Some(key) => key,
            None => return None,
        };
        let (_, data) = self.dep_map.remove(&key).unwrap();
        Some((key, data))
    }

    /// Returns `true` if there are remaining packages to be built.
    pub fn is_empty(&self) -> bool {
        self.dep_map.is_empty()
    }

    /// Returns the number of remaining packages to be built.
    pub fn len(&self) -> usize {
        self.dep_map.len()
    }

    /// Indicate that something has finished.
    ///
    /// Calling this function indicates that the `node` has produced `edge`. All
    /// remaining work items which only depend on this node/edge pair are now
    /// candidates to start their job.
    ///
    /// Returns the nodes that are now allowed to be dequeued as a result of
    /// finishing this node.
    pub fn finish(&mut self, node: &N, edge: &E) -> Vec<&N> {
        // hashset<Node>
        let reverse_deps = self.reverse_dep_map.get(node).and_then(|map| map.get(edge));
        let reverse_deps = match reverse_deps {
            Some(deps) => deps,
            None => return Vec::new(),
        };
        let key = (node.clone(), edge.clone());
        let mut result = Vec::new();
        for dep in reverse_deps.iter() {
            let edges = &mut self.dep_map.get_mut(dep).unwrap().0;
            assert!(edges.remove(&key));
            if edges.is_empty() {
                result.push(dep);
            }
        }
        result
    }
}

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

    #[test]
    fn deep_first() {
        let mut q = DependencyQueue::new();

        q.queue(1, (), vec![]);
        q.queue(2, (), vec![(1, ())]);
        q.queue(3, (), vec![]);
        q.queue(4, (), vec![(2, ()), (3, ())]);
        q.queue(5, (), vec![(4, ()), (3, ())]);
        q.queue_finished();

        assert_eq!(q.dequeue(), Some((1, ())));
        assert_eq!(q.dequeue(), Some((3, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&3, &());
        assert_eq!(q.dequeue(), None);
        q.finish(&1, &());
        assert_eq!(q.dequeue(), Some((2, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&2, &());
        assert_eq!(q.dequeue(), Some((4, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&4, &());
        assert_eq!(q.dequeue(), Some((5, ())));
    }
}
<commit_msg>Auto merge of #8289 - mbrobbel:patch-1, r=ehuss<commit_after>//! A graph-like structure used to represent a set of dependencies and in what
//! order they should be built.
//!
//! This structure is used to store the dependency graph and dynamically update
//! it to figure out when a dependency should be built.
//!
//! Dependencies in this queue are represented as a (node, edge) pair. This is
//! used to model nodes which produce multiple outputs at different times but
//! some nodes may only require one of the outputs and can start before the
//! whole node is finished.

use std::collections::{HashMap, HashSet};
use std::hash::Hash;

#[derive(Debug)]
pub struct DependencyQueue<N: Hash + Eq, E: Hash + Eq, V> {
    /// A list of all known keys to build.
    ///
    /// The value of the hash map is list of dependencies which still need to be
    /// built before the package can be built. Note that the set is dynamically
    /// updated as more dependencies are built.
    dep_map: HashMap<N, (HashSet<(N, E)>, V)>,

    /// A reverse mapping of a package to all packages that depend on that
    /// package.
    ///
    /// This map is statically known and does not get updated throughout the
    /// lifecycle of the DependencyQueue.
    ///
    /// This is sort of like a `HashMap<(N, E), HashSet<N>>` map, but more
    /// easily indexable with just an `N`
    reverse_dep_map: HashMap<N, HashMap<E, HashSet<N>>>,

    /// The total number of packages that are transitively waiting on this package
    priority: HashMap<N, usize>,
}

impl<N: Hash + Eq, E: Hash + Eq, V> Default for DependencyQueue<N, E, V> {
    fn default() -> DependencyQueue<N, E, V> {
        DependencyQueue::new()
    }
}

impl<N: Hash + Eq, E: Hash + Eq, V> DependencyQueue<N, E, V> {
    /// Creates a new dependency queue with 0 packages.
    pub fn new() -> DependencyQueue<N, E, V> {
        DependencyQueue {
            dep_map: HashMap::new(),
            reverse_dep_map: HashMap::new(),
            priority: HashMap::new(),
        }
    }
}

impl<N: Hash + Eq + Clone, E: Eq + Hash + Clone, V> DependencyQueue<N, E, V> {
    /// Adds a new node and its dependencies to this queue.
    ///
    /// The `key` specified is a new node in the dependency graph, and the node
    /// depend on all the dependencies iterated by `dependencies`. Each
    /// dependency is a node/edge pair, where edges can be thought of as
    /// productions from nodes (aka if it's just `()` it's just waiting for the
    /// node to finish).
    ///
    /// An optional `value` can also be associated with `key` which is reclaimed
    /// when the node is ready to go.
    pub fn queue(&mut self, key: N, value: V, dependencies: impl IntoIterator<Item = (N, E)>) {
        assert!(!self.dep_map.contains_key(&key));

        let mut my_dependencies = HashSet::new();
        for (dep, edge) in dependencies {
            my_dependencies.insert((dep.clone(), edge.clone()));
            self.reverse_dep_map
                .entry(dep)
                .or_insert_with(HashMap::new)
                .entry(edge)
                .or_insert_with(HashSet::new)
                .insert(key.clone());
        }
        self.dep_map.insert(key, (my_dependencies, value));
    }

    /// All nodes have been added, calculate some internal metadata and prepare
    /// for `dequeue`.
    pub fn queue_finished(&mut self) {
        let mut out = HashMap::new();
        for key in self.dep_map.keys() {
            depth(key, &self.reverse_dep_map, &mut out);
        }
        self.priority = out.into_iter().map(|(n, set)| (n, set.len())).collect();

        fn depth<'a, N: Hash + Eq + Clone, E: Hash + Eq + Clone>(
            key: &N,
            map: &HashMap<N, HashMap<E, HashSet<N>>>,
            results: &'a mut HashMap<N, HashSet<N>>,
        ) -> &'a HashSet<N> {
            if results.contains_key(key) {
                let depth = &results[key];
                assert!(!depth.is_empty(), "cycle in DependencyQueue");
                return depth;
            }
            results.insert(key.clone(), HashSet::new());

            let mut set = HashSet::new();
            set.insert(key.clone());

            for dep in map
                .get(key)
                .into_iter()
                .flat_map(|it| it.values())
                .flatten()
            {
                set.extend(depth(dep, map, results).iter().cloned())
            }

            let slot = results.get_mut(key).unwrap();
            *slot = set;
            &*slot
        }
    }

    /// Dequeues a package that is ready to be built.
    ///
    /// A package is ready to be built when it has 0 un-built dependencies. If
    /// `None` is returned then no packages are ready to be built.
    pub fn dequeue(&mut self) -> Option<(N, V)> {
        // Look at all our crates and find everything that's ready to build (no
        // deps). After we've got that candidate set select the one which has
        // the maximum priority in the dependency graph. This way we should
        // hopefully keep CPUs hottest the longest by ensuring that long
        // dependency chains are scheduled early on in the build process and the
        // leafs higher in the tree can fill in the cracks later.
        //
        // TODO: it'd be best here to throw in a heuristic of crate size as
        //       well. For example how long did this crate historically take to
        //       compile? How large is its source code? etc.
        let next = self
            .dep_map
            .iter()
            .filter(|(_, (deps, _))| deps.is_empty())
            .map(|(key, _)| key.clone())
            .max_by_key(|k| self.priority[k]);
        let key = match next {
            Some(key) => key,
            None => return None,
        };
        let (_, data) = self.dep_map.remove(&key).unwrap();
        Some((key, data))
    }

    /// Returns `true` if there are remaining packages to be built.
    pub fn is_empty(&self) -> bool {
        self.dep_map.is_empty()
    }

    /// Returns the number of remaining packages to be built.
    pub fn len(&self) -> usize {
        self.dep_map.len()
    }

    /// Indicate that something has finished.
    ///
    /// Calling this function indicates that the `node` has produced `edge`. All
    /// remaining work items which only depend on this node/edge pair are now
    /// candidates to start their job.
    ///
    /// Returns the nodes that are now allowed to be dequeued as a result of
    /// finishing this node.
    pub fn finish(&mut self, node: &N, edge: &E) -> Vec<&N> {
        // hashset<Node>
        let reverse_deps = self.reverse_dep_map.get(node).and_then(|map| map.get(edge));
        let reverse_deps = match reverse_deps {
            Some(deps) => deps,
            None => return Vec::new(),
        };
        let key = (node.clone(), edge.clone());
        let mut result = Vec::new();
        for dep in reverse_deps.iter() {
            let edges = &mut self.dep_map.get_mut(dep).unwrap().0;
            assert!(edges.remove(&key));
            if edges.is_empty() {
                result.push(dep);
            }
        }
        result
    }
}

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

    #[test]
    fn deep_first() {
        let mut q = DependencyQueue::new();

        q.queue(1, (), vec![]);
        q.queue(2, (), vec![(1, ())]);
        q.queue(3, (), vec![]);
        q.queue(4, (), vec![(2, ()), (3, ())]);
        q.queue(5, (), vec![(4, ()), (3, ())]);
        q.queue_finished();

        assert_eq!(q.dequeue(), Some((1, ())));
        assert_eq!(q.dequeue(), Some((3, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&3, &());
        assert_eq!(q.dequeue(), None);
        q.finish(&1, &());
        assert_eq!(q.dequeue(), Some((2, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&2, &());
        assert_eq!(q.dequeue(), Some((4, ())));
        assert_eq!(q.dequeue(), None);
        q.finish(&4, &());
        assert_eq!(q.dequeue(), Some((5, ())));
    }
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use codegen::general::version_condition;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;

//used as glib:get-type in GLib-2.0.gir
const INTERN: &str = "intern";

lazy_static! {
    static ref DEFAULT_OBJ: GObject = Default::default();
}

pub fn generate_records_funcs(
    w: &mut Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        let name = format!("{}.{}", env.config.library_name, record.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &record.c_type,
            glib_get_type,
            &record.functions,
        ));
    }

    Ok(())
}

pub fn generate_classes_funcs(w: &mut Write, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        ));
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &bitfield.c_type,
            glib_get_type,
            &bitfield.functions,
        ));
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &en.c_type,
            glib_get_type,
            &en.functions,
        ));
    }

    Ok(())
}

pub fn generate_unions_funcs(w: &mut Write, env: &Env, unions: &[&library::Union]) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            c_type,
            glib_get_type,
            &union.functions,
        ));
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        ));
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, "Other functions", INTERN, functions)
}

fn generate_object_funcs(
    w: &mut Write,
    env: &Env,
    obj: &GObject,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        try!(writeln!(w));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
    }
    if write_get_type {
        try!(writeln!(w, "    pub fn {}() -> GType;", glib_get_type));
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(version_condition(w, env, func.version, commented, 1));
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            try!(writeln!(
                w,
                "    {}#[cfg(any(windows, feature = \"dox\"))]",
                comment
            ));
            try!(writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig));
            try!(version_condition(w, env, func.version, commented, 1));
        }
        try!(writeln!(w, "    {}pub fn {}{};", comment, name, sig));
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        try!(writeln!(w, "// Callbacks"));
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        ));
    }
    if !callbacks.is_empty() {
        try!(writeln!(w));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<commit_msg>sys: generate cfg_condition for functions<commit_after>use std::io::{Result, Write};

use codegen::general::{cfg_condition, version_condition};
use config::functions::Function;
use config::gobjects::GObject;
use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;

//used as glib:get-type in GLib-2.0.gir
const INTERN: &str = "intern";

lazy_static! {
    static ref DEFAULT_OBJ: GObject = Default::default();
}

pub fn generate_records_funcs(
    w: &mut Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        let name = format!("{}.{}", env.config.library_name, record.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &record.c_type,
            glib_get_type,
            &record.functions,
        ));
    }

    Ok(())
}

pub fn generate_classes_funcs(w: &mut Write, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        ));
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &bitfield.c_type,
            glib_get_type,
            &bitfield.functions,
        ));
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &en.c_type,
            glib_get_type,
            &en.functions,
        ));
    }

    Ok(())
}

pub fn generate_unions_funcs(w: &mut Write, env: &Env, unions: &[&library::Union]) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            c_type,
            glib_get_type,
            &union.functions,
        ));
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        try!(generate_object_funcs(
            w,
            env,
            obj,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        ));
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, "Other functions", INTERN, functions)
}

fn generate_cfg_configure(
    w: &mut Write,
    configured_functions: Vec<&Function>,
    commented: bool,
) -> Result<()> {
    let cfg_condition_ = configured_functions
        .iter()
        .filter_map(|f| f.cfg_condition.clone())
        .next();
    cfg_condition(w, &cfg_condition_, commented, 1)?;
    Ok(())
}

fn generate_object_funcs(
    w: &mut Write,
    env: &Env,
    obj: &GObject,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        try!(writeln!(w));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(
            w,
            "    //========================================================================="
        ));
    }
    if write_get_type {
        let configured_functions = obj.functions.matched(&glib_get_type);
        generate_cfg_configure(w, configured_functions, false)?;
        try!(writeln!(w, "    pub fn {}() -> GType;", glib_get_type));
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(version_condition(w, env, func.version, commented, 1));
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            try!(writeln!(
                w,
                "    {}#[cfg(any(windows, feature = \"dox\"))]",
                comment
            ));
            try!(writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig));
            try!(version_condition(w, env, func.version, commented, 1));
        }
        generate_cfg_configure(w, configured_functions, commented)?;
        try!(writeln!(w, "    {}pub fn {}{};", comment, name, sig));
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        try!(writeln!(w, "// Callbacks"));
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        ));
    }
    if !callbacks.is_empty() {
        try!(writeln!(w));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<|endoftext|>


<commit_before>use super::ffi_type::*;
use crate::{
    codegen::general::{cfg_condition, version_condition},
    config::{functions::Function, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
};
use once_cell::sync::Lazy;
use std::io::{Result, Write};

//used as glib:get-type in GLib-2.0.gir
const INTERN: &str = "intern";

static DEFAULT_OBJ: Lazy<GObject> = Lazy::new(Default::default);

pub fn generate_records_funcs(
    w: &mut dyn Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        let name = format!("{}.{}", env.config.library_name, record.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            &record.c_type,
            glib_get_type,
            &record.functions,
        )?;
    }

    Ok(())
}

pub fn generate_classes_funcs(
    w: &mut dyn Write,
    env: &Env,
    classes: &[&library::Class],
) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        generate_object_funcs(
            w,
            env,
            obj,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        )?;
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut dyn Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            &bitfield.c_type,
            glib_get_type,
            &bitfield.functions,
        )?;
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut dyn Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(w, env, obj, &en.c_type, glib_get_type, &en.functions)?;
    }

    Ok(())
}

pub fn generate_unions_funcs(
    w: &mut dyn Write,
    env: &Env,
    unions: &[&library::Union],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(w, env, obj, c_type, glib_get_type, &union.functions)?;
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut dyn Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        generate_object_funcs(
            w,
            env,
            obj,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        )?;
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut dyn Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, "Other functions", INTERN, functions)
}

fn generate_cfg_configure(
    w: &mut dyn Write,
    configured_functions: &[&Function],
    commented: bool,
) -> Result<()> {
    let cfg_condition_ = configured_functions
        .iter()
        .filter_map(|f| f.cfg_condition.clone())
        .next();
    cfg_condition(w, &cfg_condition_, commented, 1)?;
    Ok(())
}

fn generate_object_funcs(
    w: &mut dyn Write,
    env: &Env,
    obj: &GObject,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        writeln!(w)?;
        writeln!(
            w,
            "    //========================================================================="
        )?;
        writeln!(w, "    // {}", c_type)?;
        writeln!(
            w,
            "    //========================================================================="
        )?;
    }
    if write_get_type {
        let configured_functions = obj.functions.matched(&glib_get_type);
        generate_cfg_configure(w, &configured_functions, false)?;
        writeln!(w, "    pub fn {}() -> GType;", glib_get_type)?;
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        version_condition(w, env, func.version, commented, 1)?;
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            writeln!(w, "    {}#[cfg(any(windows, feature = \"dox\"))]", comment)?;
            writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig)?;
            version_condition(w, env, func.version, commented, 1)?;
        }
        generate_cfg_configure(w, &configured_functions, commented)?;
        writeln!(w, "    {}pub fn {}{};", comment, name, sig)?;
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut dyn Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        writeln!(w, "// Callbacks")?;
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        )?;
    }
    if !callbacks.is_empty() {
        writeln!(w)?;
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<commit_msg>Correctly get function configuration and make use of it for get_type() functions in sys mode<commit_after>use super::ffi_type::*;
use crate::{
    codegen::general::{cfg_condition, version_condition},
    config::{functions::Function, gobjects::GObject},
    env::Env,
    library, nameutil,
    traits::*,
};
use once_cell::sync::Lazy;
use std::io::{Result, Write};

//used as glib:get-type in GLib-2.0.gir
const INTERN: &str = "intern";

static DEFAULT_OBJ: Lazy<GObject> = Lazy::new(Default::default);

pub fn generate_records_funcs(
    w: &mut dyn Write,
    env: &Env,
    records: &[&library::Record],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for record in records {
        let name = format!("{}.{}", env.config.library_name, record.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let version = std::cmp::max(record.version, obj.version);
        let glib_get_type = record.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            version,
            &record.c_type,
            glib_get_type,
            &record.functions,
        )?;
    }

    Ok(())
}

pub fn generate_classes_funcs(
    w: &mut dyn Write,
    env: &Env,
    classes: &[&library::Class],
) -> Result<()> {
    for klass in classes {
        let name = format!("{}.{}", env.config.library_name, klass.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let version = std::cmp::max(klass.version, obj.version);
        generate_object_funcs(
            w,
            env,
            obj,
            version,
            &klass.c_type,
            &klass.glib_get_type,
            &klass.functions,
        )?;
    }

    Ok(())
}

pub fn generate_bitfields_funcs(
    w: &mut dyn Write,
    env: &Env,
    bitfields: &[&library::Bitfield],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for bitfield in bitfields {
        let name = format!("{}.{}", env.config.library_name, bitfield.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let version = std::cmp::max(bitfield.version, obj.version);
        let glib_get_type = bitfield.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            version,
            &bitfield.c_type,
            glib_get_type,
            &bitfield.functions,
        )?;
    }

    Ok(())
}

pub fn generate_enums_funcs(
    w: &mut dyn Write,
    env: &Env,
    enums: &[&library::Enumeration],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for en in enums {
        let name = format!("{}.{}", env.config.library_name, en.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let version = std::cmp::max(en.version, obj.version);
        let glib_get_type = en.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            version,
            &en.c_type,
            glib_get_type,
            &en.functions,
        )?;
    }

    Ok(())
}

pub fn generate_unions_funcs(
    w: &mut dyn Write,
    env: &Env,
    unions: &[&library::Union],
) -> Result<()> {
    let intern_str = INTERN.to_string();
    for union in unions {
        let c_type = match union.c_type {
            Some(ref x) => x,
            None => return Ok(()),
        };
        let name = format!("{}.{}", env.config.library_name, union.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let glib_get_type = union.glib_get_type.as_ref().unwrap_or(&intern_str);
        generate_object_funcs(
            w,
            env,
            obj,
            obj.version,
            c_type,
            glib_get_type,
            &union.functions,
        )?;
    }

    Ok(())
}

pub fn generate_interfaces_funcs(
    w: &mut dyn Write,
    env: &Env,
    interfaces: &[&library::Interface],
) -> Result<()> {
    for interface in interfaces {
        let name = format!("{}.{}", env.config.library_name, interface.name);
        let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
        let version = std::cmp::max(interface.version, obj.version);
        generate_object_funcs(
            w,
            env,
            obj,
            version,
            &interface.c_type,
            &interface.glib_get_type,
            &interface.functions,
        )?;
    }

    Ok(())
}

pub fn generate_other_funcs(
    w: &mut dyn Write,
    env: &Env,
    functions: &[library::Function],
) -> Result<()> {
    let name = format!("{}.*", env.config.library_name);
    let obj = env.config.objects.get(&name).unwrap_or(&DEFAULT_OBJ);
    generate_object_funcs(w, env, obj, None, "Other functions", INTERN, functions)
}

fn generate_cfg_configure(
    w: &mut dyn Write,
    configured_functions: &[&Function],
    commented: bool,
) -> Result<()> {
    let cfg_condition_ = configured_functions
        .iter()
        .filter_map(|f| f.cfg_condition.clone())
        .next();
    cfg_condition(w, &cfg_condition_, commented, 1)?;
    Ok(())
}

fn generate_object_funcs(
    w: &mut dyn Write,
    env: &Env,
    obj: &GObject,
    version: Option<crate::version::Version>,
    c_type: &str,
    glib_get_type: &str,
    functions: &[library::Function],
) -> Result<()> {
    let write_get_type = glib_get_type != INTERN;
    if write_get_type || !functions.is_empty() {
        writeln!(w)?;
        writeln!(
            w,
            "    //========================================================================="
        )?;
        writeln!(w, "    // {}", c_type)?;
        writeln!(
            w,
            "    //========================================================================="
        )?;
    }
    if write_get_type {
        let configured_functions = obj.functions.matched("get_type");

        if !configured_functions.iter().any(|f| f.ignore) {
            let version = std::iter::once(version)
                .chain(configured_functions.iter().map(|f| f.version))
                .max()
                .flatten();
            version_condition(w, env, version, false, 1)?;
            generate_cfg_configure(w, &configured_functions, false)?;
            writeln!(w, "    pub fn {}() -> GType;", glib_get_type)?;
        }
    }

    for func in functions {
        let configured_functions = obj.functions.matched(&func.name);
        if configured_functions.iter().any(|f| f.ignore) {
            continue;
        }
        let is_windows_utf8 = configured_functions.iter().any(|f| f.is_windows_utf8);

        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        version_condition(w, env, func.version, commented, 1)?;
        let name = func.c_identifier.as_ref().unwrap();
        // since we work with gir-files from Linux, some function names need to be adjusted
        if is_windows_utf8 {
            writeln!(w, "    {}#[cfg(any(windows, feature = \"dox\"))]", comment)?;
            writeln!(w, "    {}pub fn {}_utf8{};", comment, name, sig)?;
            version_condition(w, env, func.version, commented, 1)?;
        }
        generate_cfg_configure(w, &configured_functions, commented)?;
        writeln!(w, "    {}pub fn {}{};", comment, name, sig)?;
    }

    Ok(())
}

pub fn generate_callbacks(
    w: &mut dyn Write,
    env: &Env,
    callbacks: &[&library::Function],
) -> Result<()> {
    if !callbacks.is_empty() {
        writeln!(w, "// Callbacks")?;
    }
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        writeln!(
            w,
            "{}pub type {} = Option<unsafe extern \"C\" fn{}>;",
            comment,
            func.c_identifier.as_ref().unwrap(),
            sig
        )?;
    }
    if !callbacks.is_empty() {
        writeln!(w)?;
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c {
            commented = true;
        }
    }

    (
        commented,
        format!("({}){}", parameter_strs.join(", "), ret_str),
    )
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() {
        return (false, String::new());
    }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.into_string()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let library::Type::Fundamental(library::Fundamental::VarArgs) = *env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.into_string()
    } else {
        format!(
            "{}: {}",
            nameutil::mangle_keywords(&*par.name),
            ffi_type.into_string()
        )
    };
    (commented, res)
}
<|endoftext|>


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

//! Format attributes and meta items.

use config::lists::*;
use syntax::ast;
use syntax::codemap::Span;

use comment::{combine_strs_with_missing_comments, contains_comment, rewrite_doc_comment};
use expr::rewrite_literal;
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use utils::{count_newlines, mk_sp};

/// Returns attributes on the given statement.
pub fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] {
    match stmt.node {
        ast::StmtKind::Local(ref local) => &local.attrs,
        ast::StmtKind::Item(ref item) => &item.attrs,
        ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => &expr.attrs,
        ast::StmtKind::Mac(ref mac) => &mac.2,
    }
}

/// Returns attributes that are within `outer_span`.
pub fn filter_inline_attrs(attrs: &[ast::Attribute], outer_span: Span) -> Vec<ast::Attribute> {
    attrs
        .iter()
        .filter(|a| outer_span.lo() <= a.span.lo() && a.span.hi() <= outer_span.hi())
        .cloned()
        .collect()
}

fn is_derive(attr: &ast::Attribute) -> bool {
    attr.check_name("derive")
}

/// Returns the arguments of `#[derive(...)]`.
fn get_derive_args<'a>(context: &'a RewriteContext, attr: &ast::Attribute) -> Option<Vec<&'a str>> {
    attr.meta_item_list().map(|meta_item_list| {
        meta_item_list
            .iter()
            .map(|nested_meta_item| context.snippet(nested_meta_item.span))
            .collect()
    })
}

// Format `#[derive(..)]`, using visual indent & mixed style when we need to go multiline.
fn format_derive(context: &RewriteContext, derive_args: &[&str], shape: Shape) -> Option<String> {
    let mut result = String::with_capacity(128);
    result.push_str("#[derive(");
    // 11 = `#[derive()]`
    let initial_budget = shape.width.checked_sub(11)?;
    let mut budget = initial_budget;
    let num = derive_args.len();
    for (i, a) in derive_args.iter().enumerate() {
        // 2 = `, ` or `)]`
        let width = a.len() + 2;
        if width > budget {
            if i > 0 {
                // Remove trailing whitespace.
                result.pop();
            }
            result.push('\n');
            // 9 = `#[derive(`
            result.push_str(&(shape.indent + 9).to_string(context.config));
            budget = initial_budget;
        } else {
            budget = budget.checked_sub(width).unwrap_or(0);
        }
        result.push_str(a);
        if i != num - 1 {
            result.push_str(", ")
        }
    }
    result.push_str(")]");
    Some(result)
}

/// Returns the first group of attributes that fills the given predicate.
/// We consider two doc comments are in different group if they are separated by normal comments.
fn take_while_with_pred<'a, P>(
    context: &RewriteContext,
    attrs: &'a [ast::Attribute],
    pred: P,
) -> &'a [ast::Attribute]
where
    P: Fn(&ast::Attribute) -> bool,
{
    let mut len = 0;
    let mut iter = attrs.iter().peekable();

    while let Some(attr) = iter.next() {
        if pred(attr) {
            len += 1;
        } else {
            break;
        }
        if let Some(next_attr) = iter.peek() {
            // Extract comments between two attributes.
            let span_between_attr = mk_sp(attr.span.hi(), next_attr.span.lo());
            let snippet = context.snippet(span_between_attr);
            if count_newlines(snippet) >= 2 || snippet.contains('/') {
                break;
            }
        }
    }

    &attrs[..len]
}

/// Rewrite the same kind of attributes at the same time. This includes doc
/// comments and derives.
fn rewrite_first_group_attrs(
    context: &RewriteContext,
    attrs: &[ast::Attribute],
    shape: Shape,
) -> Option<(usize, String)> {
    if attrs.is_empty() {
        return Some((0, String::new()));
    }
    // Rewrite doc comments
    let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_sugared_doc);
    if !sugared_docs.is_empty() {
        let snippet = sugared_docs
            .iter()
            .map(|a| context.snippet(a.span))
            .collect::<Vec<_>>()
            .join("\n");
        return Some((
            sugared_docs.len(),
            rewrite_doc_comment(&snippet, shape.comment(context.config), context.config)?,
        ));
    }
    // Rewrite `#[derive(..)]`s.
    if context.config.merge_derives() {
        let derives = take_while_with_pred(context, attrs, is_derive);
        if !derives.is_empty() {
            let mut derive_args = vec![];
            for derive in derives {
                derive_args.append(&mut get_derive_args(context, derive)?);
            }
            return Some((derives.len(), format_derive(context, &derive_args, shape)?));
        }
    }
    // Rewrite the first attribute.
    Some((1, attrs[0].rewrite(context, shape)?))
}

impl Rewrite for ast::NestedMetaItem {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        match self.node {
            ast::NestedMetaItemKind::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
            ast::NestedMetaItemKind::Literal(ref l) => rewrite_literal(context, l, shape),
        }
    }
}

fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
    // Look at before and after comment and see if there are any empty lines.
    let comment_begin = comment.find('/');
    let len = comment_begin.unwrap_or_else(|| comment.len());
    let mlb = count_newlines(&comment[..len]) > 1;
    let mla = if comment_begin.is_none() {
        mlb
    } else {
        let comment_end = comment.chars().rev().position(|c| !c.is_whitespace());
        let len = comment_end.unwrap();
        comment
            .chars()
            .rev()
            .take(len)
            .filter(|c| *c == '\n')
            .count() > 1
    };
    (if mlb { "\n" } else { "" }, if mla { "\n" } else { "" })
}

impl Rewrite for ast::MetaItem {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        Some(match self.node {
            ast::MetaItemKind::Word => String::from(&*self.name.as_str()),
            ast::MetaItemKind::List(ref list) => {
                let name = self.name.as_str();
                // 1 = `(`, 2 = `]` and `)`
                let item_shape = shape
                    .visual_indent(0)
                    .shrink_left(name.len() + 1)
                    .and_then(|s| s.sub_width(2))?;
                let items = itemize_list(
                    context.snippet_provider,
                    list.iter(),
                    ")",
                    ",",
                    |nested_meta_item| nested_meta_item.span.lo(),
                    |nested_meta_item| nested_meta_item.span.hi(),
                    |nested_meta_item| nested_meta_item.rewrite(context, item_shape),
                    self.span.lo(),
                    self.span.hi(),
                    false,
                );
                let item_vec = items.collect::<Vec<_>>();
                let fmt = ListFormatting {
                    tactic: DefinitiveListTactic::Mixed,
                    separator: ",",
                    trailing_separator: SeparatorTactic::Never,
                    separator_place: SeparatorPlace::Back,
                    shape: item_shape,
                    ends_with_newline: false,
                    preserve_newline: false,
                    config: context.config,
                };
                format!("{}({})", name, write_list(&item_vec, &fmt)?)
            }
            ast::MetaItemKind::NameValue(ref literal) => {
                let name = self.name.as_str();
                // 3 = ` = `
                let lit_shape = shape.shrink_left(name.len() + 3)?;
                // `rewrite_literal` returns `None` when `literal` exceeds max
                // width. Since a literal is basically unformattable unless it
                // is a string literal (and only if `format_strings` is set),
                // we might be better off ignoring the fact that the attribute
                // is longer than the max width and contiue on formatting.
                // See #2479 for example.
                let value = rewrite_literal(context, literal, lit_shape)
                    .unwrap_or_else(|| context.snippet(literal.span).to_owned());
                format!("{} = {}", name, value)
            }
        })
    }
}

impl Rewrite for ast::Attribute {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        let prefix = match self.style {
            ast::AttrStyle::Inner => "#!",
            ast::AttrStyle::Outer => "#",
        };
        let snippet = context.snippet(self.span);
        if self.is_sugared_doc {
            rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
        } else {
            if contains_comment(snippet) {
                return Some(snippet.to_owned());
            }
            // 1 = `[`
            let shape = shape.offset_left(prefix.len() + 1)?;
            Some(
                self.meta()
                    .and_then(|meta| meta.rewrite(context, shape))
                    .map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
            )
        }
    }
}

impl<'a> Rewrite for [ast::Attribute] {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        if self.is_empty() {
            return Some(String::new());
        }
        let (first_group_len, first_group_str) = rewrite_first_group_attrs(context, self, shape)?;
        if self.len() == 1 || first_group_len == self.len() {
            Some(first_group_str)
        } else {
            let rest_str = self[first_group_len..].rewrite(context, shape)?;
            let missing_span = mk_sp(
                self[first_group_len - 1].span.hi(),
                self[first_group_len].span.lo(),
            );
            // Preserve an empty line before/after doc comments.
            if self[0].is_sugared_doc || self[first_group_len].is_sugared_doc {
                let snippet = context.snippet(missing_span);
                let (mla, mlb) = has_newlines_before_after_comment(snippet);
                let comment = ::comment::recover_missing_comment_in_span(
                    missing_span,
                    shape.with_max_width(context.config),
                    context,
                    0,
                )?;
                let comment = if comment.is_empty() {
                    format!("\n{}", mlb)
                } else {
                    format!("{}{}\n{}", mla, comment, mlb)
                };
                Some(format!(
                    "{}{}{}{}",
                    first_group_str,
                    comment,
                    shape.indent.to_string(context.config),
                    rest_str
                ))
            } else {
                combine_strs_with_missing_comments(
                    context,
                    &first_group_str,
                    &rest_str,
                    missing_span,
                    shape,
                    false,
                )
            }
        }
    }
}
<commit_msg>Use take_while<commit_after>// Copyright 2018 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.

//! Format attributes and meta items.

use config::lists::*;
use syntax::ast;
use syntax::codemap::Span;

use comment::{combine_strs_with_missing_comments, contains_comment, rewrite_doc_comment};
use expr::rewrite_literal;
use lists::{itemize_list, write_list, ListFormatting};
use rewrite::{Rewrite, RewriteContext};
use shape::Shape;
use utils::{count_newlines, mk_sp};

/// Returns attributes on the given statement.
pub fn get_attrs_from_stmt(stmt: &ast::Stmt) -> &[ast::Attribute] {
    match stmt.node {
        ast::StmtKind::Local(ref local) => &local.attrs,
        ast::StmtKind::Item(ref item) => &item.attrs,
        ast::StmtKind::Expr(ref expr) | ast::StmtKind::Semi(ref expr) => &expr.attrs,
        ast::StmtKind::Mac(ref mac) => &mac.2,
    }
}

/// Returns attributes that are within `outer_span`.
pub fn filter_inline_attrs(attrs: &[ast::Attribute], outer_span: Span) -> Vec<ast::Attribute> {
    attrs
        .iter()
        .filter(|a| outer_span.lo() <= a.span.lo() && a.span.hi() <= outer_span.hi())
        .cloned()
        .collect()
}

fn is_derive(attr: &ast::Attribute) -> bool {
    attr.check_name("derive")
}

/// Returns the arguments of `#[derive(...)]`.
fn get_derive_args<'a>(context: &'a RewriteContext, attr: &ast::Attribute) -> Option<Vec<&'a str>> {
    attr.meta_item_list().map(|meta_item_list| {
        meta_item_list
            .iter()
            .map(|nested_meta_item| context.snippet(nested_meta_item.span))
            .collect()
    })
}

// Format `#[derive(..)]`, using visual indent & mixed style when we need to go multiline.
fn format_derive(context: &RewriteContext, derive_args: &[&str], shape: Shape) -> Option<String> {
    let mut result = String::with_capacity(128);
    result.push_str("#[derive(");
    // 11 = `#[derive()]`
    let initial_budget = shape.width.checked_sub(11)?;
    let mut budget = initial_budget;
    let num = derive_args.len();
    for (i, a) in derive_args.iter().enumerate() {
        // 2 = `, ` or `)]`
        let width = a.len() + 2;
        if width > budget {
            if i > 0 {
                // Remove trailing whitespace.
                result.pop();
            }
            result.push('\n');
            // 9 = `#[derive(`
            result.push_str(&(shape.indent + 9).to_string(context.config));
            budget = initial_budget;
        } else {
            budget = budget.checked_sub(width).unwrap_or(0);
        }
        result.push_str(a);
        if i != num - 1 {
            result.push_str(", ")
        }
    }
    result.push_str(")]");
    Some(result)
}

/// Returns the first group of attributes that fills the given predicate.
/// We consider two doc comments are in different group if they are separated by normal comments.
fn take_while_with_pred<'a, P>(
    context: &RewriteContext,
    attrs: &'a [ast::Attribute],
    pred: P,
) -> &'a [ast::Attribute]
where
    P: Fn(&ast::Attribute) -> bool,
{
    let mut len = 0;
    let mut iter = attrs.iter().peekable();

    while let Some(attr) = iter.next() {
        if pred(attr) {
            len += 1;
        } else {
            break;
        }
        if let Some(next_attr) = iter.peek() {
            // Extract comments between two attributes.
            let span_between_attr = mk_sp(attr.span.hi(), next_attr.span.lo());
            let snippet = context.snippet(span_between_attr);
            if count_newlines(snippet) >= 2 || snippet.contains('/') {
                break;
            }
        }
    }

    &attrs[..len]
}

/// Rewrite the same kind of attributes at the same time. This includes doc
/// comments and derives.
fn rewrite_first_group_attrs(
    context: &RewriteContext,
    attrs: &[ast::Attribute],
    shape: Shape,
) -> Option<(usize, String)> {
    if attrs.is_empty() {
        return Some((0, String::new()));
    }
    // Rewrite doc comments
    let sugared_docs = take_while_with_pred(context, attrs, |a| a.is_sugared_doc);
    if !sugared_docs.is_empty() {
        let snippet = sugared_docs
            .iter()
            .map(|a| context.snippet(a.span))
            .collect::<Vec<_>>()
            .join("\n");
        return Some((
            sugared_docs.len(),
            rewrite_doc_comment(&snippet, shape.comment(context.config), context.config)?,
        ));
    }
    // Rewrite `#[derive(..)]`s.
    if context.config.merge_derives() {
        let derives = take_while_with_pred(context, attrs, is_derive);
        if !derives.is_empty() {
            let mut derive_args = vec![];
            for derive in derives {
                derive_args.append(&mut get_derive_args(context, derive)?);
            }
            return Some((derives.len(), format_derive(context, &derive_args, shape)?));
        }
    }
    // Rewrite the first attribute.
    Some((1, attrs[0].rewrite(context, shape)?))
}

impl Rewrite for ast::NestedMetaItem {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        match self.node {
            ast::NestedMetaItemKind::MetaItem(ref meta_item) => meta_item.rewrite(context, shape),
            ast::NestedMetaItemKind::Literal(ref l) => rewrite_literal(context, l, shape),
        }
    }
}

fn has_newlines_before_after_comment(comment: &str) -> (&str, &str) {
    // Look at before and after comment and see if there are any empty lines.
    let comment_begin = comment.find('/');
    let len = comment_begin.unwrap_or_else(|| comment.len());
    let mlb = count_newlines(&comment[..len]) > 1;
    let mla = if comment_begin.is_none() {
        mlb
    } else {
        comment
            .chars()
            .rev()
            .take_while(|c| c.is_whitespace())
            .filter(|&c| c == '\n')
            .count() > 1
    };
    (if mlb { "\n" } else { "" }, if mla { "\n" } else { "" })
}

impl Rewrite for ast::MetaItem {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        Some(match self.node {
            ast::MetaItemKind::Word => String::from(&*self.name.as_str()),
            ast::MetaItemKind::List(ref list) => {
                let name = self.name.as_str();
                // 1 = `(`, 2 = `]` and `)`
                let item_shape = shape
                    .visual_indent(0)
                    .shrink_left(name.len() + 1)
                    .and_then(|s| s.sub_width(2))?;
                let items = itemize_list(
                    context.snippet_provider,
                    list.iter(),
                    ")",
                    ",",
                    |nested_meta_item| nested_meta_item.span.lo(),
                    |nested_meta_item| nested_meta_item.span.hi(),
                    |nested_meta_item| nested_meta_item.rewrite(context, item_shape),
                    self.span.lo(),
                    self.span.hi(),
                    false,
                );
                let item_vec = items.collect::<Vec<_>>();
                let fmt = ListFormatting {
                    tactic: DefinitiveListTactic::Mixed,
                    separator: ",",
                    trailing_separator: SeparatorTactic::Never,
                    separator_place: SeparatorPlace::Back,
                    shape: item_shape,
                    ends_with_newline: false,
                    preserve_newline: false,
                    config: context.config,
                };
                format!("{}({})", name, write_list(&item_vec, &fmt)?)
            }
            ast::MetaItemKind::NameValue(ref literal) => {
                let name = self.name.as_str();
                // 3 = ` = `
                let lit_shape = shape.shrink_left(name.len() + 3)?;
                // `rewrite_literal` returns `None` when `literal` exceeds max
                // width. Since a literal is basically unformattable unless it
                // is a string literal (and only if `format_strings` is set),
                // we might be better off ignoring the fact that the attribute
                // is longer than the max width and contiue on formatting.
                // See #2479 for example.
                let value = rewrite_literal(context, literal, lit_shape)
                    .unwrap_or_else(|| context.snippet(literal.span).to_owned());
                format!("{} = {}", name, value)
            }
        })
    }
}

impl Rewrite for ast::Attribute {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        let prefix = match self.style {
            ast::AttrStyle::Inner => "#!",
            ast::AttrStyle::Outer => "#",
        };
        let snippet = context.snippet(self.span);
        if self.is_sugared_doc {
            rewrite_doc_comment(snippet, shape.comment(context.config), context.config)
        } else {
            if contains_comment(snippet) {
                return Some(snippet.to_owned());
            }
            // 1 = `[`
            let shape = shape.offset_left(prefix.len() + 1)?;
            Some(
                self.meta()
                    .and_then(|meta| meta.rewrite(context, shape))
                    .map_or_else(|| snippet.to_owned(), |rw| format!("{}[{}]", prefix, rw)),
            )
        }
    }
}

impl<'a> Rewrite for [ast::Attribute] {
    fn rewrite(&self, context: &RewriteContext, shape: Shape) -> Option<String> {
        if self.is_empty() {
            return Some(String::new());
        }
        let (first_group_len, first_group_str) = rewrite_first_group_attrs(context, self, shape)?;
        if self.len() == 1 || first_group_len == self.len() {
            Some(first_group_str)
        } else {
            let rest_str = self[first_group_len..].rewrite(context, shape)?;
            let missing_span = mk_sp(
                self[first_group_len - 1].span.hi(),
                self[first_group_len].span.lo(),
            );
            // Preserve an empty line before/after doc comments.
            if self[0].is_sugared_doc || self[first_group_len].is_sugared_doc {
                let snippet = context.snippet(missing_span);
                let (mla, mlb) = has_newlines_before_after_comment(snippet);
                let comment = ::comment::recover_missing_comment_in_span(
                    missing_span,
                    shape.with_max_width(context.config),
                    context,
                    0,
                )?;
                let comment = if comment.is_empty() {
                    format!("\n{}", mlb)
                } else {
                    format!("{}{}\n{}", mla, comment, mlb)
                };
                Some(format!(
                    "{}{}{}{}",
                    first_group_str,
                    comment,
                    shape.indent.to_string(context.config),
                    rest_str
                ))
            } else {
                combine_strs_with_missing_comments(
                    context,
                    &first_group_str,
                    &rest_str,
                    missing_span,
                    shape,
                    false,
                )
            }
        }
    }
}
<|endoftext|>