<commit_before>use std::borrow::Borrow;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::iter::FromIterator;

use crate::core::dependency::Kind;
use crate::core::{Dependency, PackageId, PackageIdSpec, Summary, Target};
use crate::util::errors::CargoResult;
use crate::util::Graph;

use super::encode::Metadata;

/// Represents a fully-resolved package dependency graph. Each node in the graph
/// is a package and edges represent dependencies between packages.
///
/// Each instance of `Resolve` also understands the full set of features used
/// for each package.
pub struct Resolve {
    /// A graph, whose vertices are packages and edges are dependency specifications
    /// from `Cargo.toml`. We need a `Vec` here because the same package
    /// might be present in both `[dependencies]` and `[build-dependencies]`.
    graph: Graph<PackageId, Vec<Dependency>>,
    /// Replacements from the `[replace]` table.
    replacements: HashMap<PackageId, PackageId>,
    /// Inverted version of `replacements`.
    reverse_replacements: HashMap<PackageId, PackageId>,
    /// An empty `HashSet` to avoid creating a new `HashSet` for every package
    /// that does not have any features, and to avoid using `Option` to
    /// simplify the API.
    empty_features: HashSet<String>,
    /// Features enabled for a given package.
    features: HashMap<PackageId, HashSet<String>>,
    /// Checksum for each package. A SHA256 hash of the `.crate` file used to
    /// validate the correct crate file is used. This is `None` for sources
    /// that do not use `.crate` files, like path or git dependencies.
    checksums: HashMap<PackageId, Option<String>>,
    /// "Unknown" metadata. This is a collection of extra, unrecognized data
    /// found in the `[metadata]` section of `Cargo.lock`, preserved for
    /// forwards compatibility.
    metadata: Metadata,
    /// `[patch]` entries that did not match anything, preserved in
    /// `Cargo.lock` as the `[[patch.unused]]` table array. Tracking unused
    /// patches helps prevent Cargo from being forced to re-update the
    /// registry every time it runs, and keeps the resolve in a locked state
    /// so it doesn't re-resolve the unused entries.
    unused_patches: Vec<PackageId>,
    /// A map from packages to a set of their public dependencies
    public_dependencies: HashMap<PackageId, HashSet<PackageId>>,
    /// Version of the `Cargo.lock` format, see
    /// `cargo::core::resolver::encode` for more.
    version: ResolveVersion,
}

/// A version to indicate how a `Cargo.lock` should be serialized. Currently V1
/// is the default and dates back to the origins of Cargo. A V2 is currently
/// being proposed which provides a much more compact representation of
/// dependency edges and also moves checksums out of `[metadata]`.
///
/// It's theorized that we can add more here over time to track larger changes
/// to the `Cargo.lock` format, but we've yet to see how that strategy pans out.
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
pub enum ResolveVersion {
    // Historical baseline for when this abstraction was added.
    V1,
    // Update around 2019 where `dependencies` arrays got compressed and
    // checksums are listed inline.
    V2,
}

impl Resolve {
    pub fn new(
        graph: Graph<PackageId, Vec<Dependency>>,
        replacements: HashMap<PackageId, PackageId>,
        features: HashMap<PackageId, HashSet<String>>,
        checksums: HashMap<PackageId, Option<String>>,
        metadata: Metadata,
        unused_patches: Vec<PackageId>,
        version: ResolveVersion,
    ) -> Resolve {
        let reverse_replacements = replacements.iter().map(|(&p, &r)| (r, p)).collect();
        let public_dependencies = graph
            .iter()
            .map(|p| {
                let public_deps = graph
                    .edges(p)
                    .filter(|(_, deps)| {
                        deps.iter()
                            .any(|d| d.kind() == Kind::Normal && d.is_public())
                    })
                    .map(|(dep_package, _)| *dep_package)
                    .collect::<HashSet<PackageId>>();

                (*p, public_deps)
            })
            .collect();

        Resolve {
            graph,
            replacements,
            features,
            checksums,
            metadata,
            unused_patches,
            empty_features: HashSet::new(),
            reverse_replacements,
            public_dependencies,
            version,
        }
    }

    /// Resolves one of the paths from the given dependent package up to
    /// the root.
    pub fn path_to_top<'a>(&'a self, pkg: &'a PackageId) -> Vec<&'a PackageId> {
        self.graph.path_to_top(pkg)
    }

    pub fn register_used_patches(&mut self, patches: &[Summary]) {
        for summary in patches {
            if self.iter().any(|id| id == summary.package_id()) {
                continue;
            }
            self.unused_patches.push(summary.package_id());
        }
    }

    pub fn merge_from(&mut self, previous: &Resolve) -> CargoResult<()> {
        // Given a previous instance of resolve, it should be forbidden to ever
        // have a checksums which *differ*. If the same package ID has differing
        // checksums, then something has gone wrong such as:
        //
        // * Something got seriously corrupted
        // * A "mirror" isn't actually a mirror as some changes were made
        // * A replacement source wasn't actually a replacement, some changes
        //   were made
        //
        // In all of these cases, we want to report an error to indicate that
        // something is awry. Normal execution (esp just using crates.io) should
        // never run into this.
        for (id, cksum) in previous.checksums.iter() {
            if let Some(mine) = self.checksums.get(id) {
                if mine == cksum {
                    continue;
                }

                // If the previous checksum wasn't calculated, the current
                // checksum is `Some`. This may indicate that a source was
                // erroneously replaced or was replaced with something that
                // desires stronger checksum guarantees than can be afforded
                // elsewhere.
                if cksum.is_none() {
                    failure::bail!(
                        "\
checksum for `{}` was not previously calculated, but a checksum could now \
be calculated

this could be indicative of a few possible situations:

    * the source `{}` did not previously support checksums,
      but was replaced with one that does
    * newer Cargo implementations know how to checksum this source, but this
      older implementation does not
    * the lock file is corrupt
",
                        id,
                        id.source_id()
                    )

                // If our checksum hasn't been calculated, then it could mean
                // that future Cargo figured out how to checksum something or
                // more realistically we were overridden with a source that does
                // not have checksums.
                } else if mine.is_none() {
                    failure::bail!(
                        "\
checksum for `{}` could not be calculated, but a checksum is listed in \
the existing lock file

this could be indicative of a few possible situations:

    * the source `{}` supports checksums,
      but was replaced with one that doesn't
    * the lock file is corrupt

unable to verify that `{0}` is the same as when the lockfile was generated
",
                        id,
                        id.source_id()
                    )

                // If the checksums aren't equal, and neither is None, then they
                // must both be Some, in which case the checksum now differs.
                // That's quite bad!
                } else {
                    failure::bail!(
                        "\
checksum for `{}` changed between lock files

this could be indicative of a few possible errors:

    * the lock file is corrupt
    * a replacement source in use (e.g., a mirror) returned a different checksum
    * the source itself may be corrupt in one way or another

unable to verify that `{0}` is the same as when the lockfile was generated
",
                        id
                    );
                }
            }
        }

        // Be sure to just copy over any unknown metadata.
        self.metadata = previous.metadata.clone();

        // The goal of Cargo is largely to preserve the encoding of `Cargo.lock`
        // that it finds on the filesystem. Sometimes `Cargo.lock` changes are
        // in the works where they haven't been set as the default yet but will
        // become the default soon.
        //
        // The scenarios we could be in are:
        //
        // * This is a brand new lock file with nothing previous. In that case
        //   this method isn't actually called at all, but instead
        //   `default_for_new_lockfiles` called below was encoded during the
        //   resolution step, so that's what we're gonna use.
        //
        // * We have an old lock file. In this case we want to switch the
        //   version to `default_for_old_lockfiles`. That puts us in one of
        //   three cases:
        //
        //   * Our version is older than the default. This means that we're
        //     migrating someone forward, so we switch the encoding.
        //   * Our version is equal to the default, nothing to do!
        //   * Our version is *newer* than the default. This is where we
        //     critically keep the new version of encoding.
        //
        // This strategy should get new lockfiles into the pipeline more quickly
        // while ensuring that any time an old cargo sees a future lock file it
        // keeps the future lockfile encoding.
        self.version = cmp::max(
            previous.version,
            ResolveVersion::default_for_old_lockfiles(),
        );

        Ok(())
    }

    pub fn contains<Q: ?Sized>(&self, k: &Q) -> bool
    where
        PackageId: Borrow<Q>,
        Q: Ord + Eq,
    {
        self.graph.contains(k)
    }

    pub fn sort(&self) -> Vec<PackageId> {
        self.graph.sort()
    }

    pub fn iter<'a>(&'a self) -> impl Iterator<Item = PackageId> + 'a {
        self.graph.iter().cloned()
    }

    pub fn deps(&self, pkg: PackageId) -> impl Iterator<Item = (PackageId, &[Dependency])> {
        self.deps_not_replaced(pkg)
            .map(move |(id, deps)| (self.replacement(id).unwrap_or(id), deps))
    }

    pub fn deps_not_replaced(
        &self,
        pkg: PackageId,
    ) -> impl Iterator<Item = (PackageId, &[Dependency])> {
        self.graph
            .edges(&pkg)
            .map(|(id, deps)| (*id, deps.as_slice()))
    }

    pub fn replacement(&self, pkg: PackageId) -> Option<PackageId> {
        self.replacements.get(&pkg).cloned()
    }

    pub fn replacements(&self) -> &HashMap<PackageId, PackageId> {
        &self.replacements
    }

    pub fn features(&self, pkg: PackageId) -> &HashSet<String> {
        self.features.get(&pkg).unwrap_or(&self.empty_features)
    }

    pub fn is_public_dep(&self, pkg: PackageId, dep: PackageId) -> bool {
        self.public_dependencies
            .get(&pkg)
            .map(|public_deps| public_deps.contains(&dep))
            .unwrap_or_else(|| panic!("Unknown dependency {:?} for package {:?}", dep, pkg))
    }

    pub fn features_sorted(&self, pkg: PackageId) -> Vec<&str> {
        let mut v = Vec::from_iter(self.features(pkg).iter().map(|s| s.as_ref()));
        v.sort_unstable();
        v
    }

    pub fn query(&self, spec: &str) -> CargoResult<PackageId> {
        PackageIdSpec::query_str(spec, self.iter())
    }

    pub fn unused_patches(&self) -> &[PackageId] {
        &self.unused_patches
    }

    pub fn checksums(&self) -> &HashMap<PackageId, Option<String>> {
        &self.checksums
    }

    pub fn metadata(&self) -> &Metadata {
        &self.metadata
    }

    pub fn extern_crate_name(
        &self,
        from: PackageId,
        to: PackageId,
        to_target: &Target,
    ) -> CargoResult<String> {
        let deps = if from == to {
            &[]
        } else {
            self.dependencies_listed(from, to)
        };

        let crate_name = to_target.crate_name();
        let mut names = deps.iter().map(|d| {
            d.explicit_name_in_toml()
                .map(|s| s.as_str().replace("-", "_"))
                .unwrap_or_else(|| crate_name.clone())
        });
        let name = names.next().unwrap_or_else(|| crate_name.clone());
        for n in names {
            failure::ensure!(
                n == name,
                "the crate `{}` depends on crate `{}` multiple times with different names",
                from,
                to,
            );
        }
        Ok(name)
    }

    fn dependencies_listed(&self, from: PackageId, to: PackageId) -> &[Dependency] {
        // We've got a dependency on `from` to `to`, but this dependency edge
        // may be affected by [replace]. If the `to` package is listed as the
        // target of a replacement (aka the key of a reverse replacement map)
        // then we try to find our dependency edge through that. If that fails
        // then we go down below assuming it's not replaced.
        //
        // Note that we don't treat `from` as if it's been replaced because
        // that's where the dependency originates from, and we only replace
        // targets of dependencies not the originator.
        if let Some(replace) = self.reverse_replacements.get(&to) {
            if let Some(deps) = self.graph.edge(&from, replace) {
                return deps;
            }
        }
        match self.graph.edge(&from, &to) {
            Some(ret) => ret,
            None => panic!("no Dependency listed for `{}` => `{}`", from, to),
        }
    }

    /// Returns the version of the encoding that's being used for this lock
    /// file.
    pub fn version(&self) -> &ResolveVersion {
        &self.version
    }
}

impl PartialEq for Resolve {
    fn eq(&self, other: &Resolve) -> bool {
        macro_rules! compare {
            ($($fields:ident)* | $($ignored:ident)*) => {
                let Resolve { $($fields,)* $($ignored,)* } = self;
                $(drop($ignored);)*
                $($fields == &other.$fields)&&*
            }
        }
        compare! {
            // fields to compare
            graph replacements reverse_replacements empty_features features
            checksums metadata unused_patches public_dependencies
            |
            // fields to ignore
            version
        }
    }
}

impl fmt::Debug for Resolve {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(fmt, "graph: {:?}", self.graph)?;
        writeln!(fmt, "\nfeatures: {{")?;
        for (pkg, features) in &self.features {
            writeln!(fmt, "  {}: {:?}", pkg, features)?;
        }
        write!(fmt, "}}")
    }
}

impl ResolveVersion {
    /// The default way to encode new `Cargo.lock` files.
    ///
    /// It's important that if a new version of `ResolveVersion` is added that
    /// this is not updated until *at least* the support for the version is in
    /// the stable release of Rust. It's ok for this to be newer than
    /// `default_for_old_lockfiles` below.
    pub fn default_for_new_lockfiles() -> ResolveVersion {
        ResolveVersion::V2
    }

    /// The default way to encode old preexisting `Cargo.lock` files. This is
    /// often trailing the new lockfiles one above to give older projects a
    /// longer time to catch up.
    ///
    /// It's important that this trails behind `default_for_new_lockfiles` for
    /// quite some time. This gives projects a quite large window to update in
    /// where we don't force updates, so if projects span many versions of Cargo
    /// all those versions of Cargo will have support for a new version of the
    /// lock file.
    pub fn default_for_old_lockfiles() -> ResolveVersion {
        ResolveVersion::V1
    }
}
<commit_msg>Update comment about ResolveVersion default version.<commit_after>use std::borrow::Borrow;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::iter::FromIterator;

use crate::core::dependency::Kind;
use crate::core::{Dependency, PackageId, PackageIdSpec, Summary, Target};
use crate::util::errors::CargoResult;
use crate::util::Graph;

use super::encode::Metadata;

/// Represents a fully-resolved package dependency graph. Each node in the graph
/// is a package and edges represent dependencies between packages.
///
/// Each instance of `Resolve` also understands the full set of features used
/// for each package.
pub struct Resolve {
    /// A graph, whose vertices are packages and edges are dependency specifications
    /// from `Cargo.toml`. We need a `Vec` here because the same package
    /// might be present in both `[dependencies]` and `[build-dependencies]`.
    graph: Graph<PackageId, Vec<Dependency>>,
    /// Replacements from the `[replace]` table.
    replacements: HashMap<PackageId, PackageId>,
    /// Inverted version of `replacements`.
    reverse_replacements: HashMap<PackageId, PackageId>,
    /// An empty `HashSet` to avoid creating a new `HashSet` for every package
    /// that does not have any features, and to avoid using `Option` to
    /// simplify the API.
    empty_features: HashSet<String>,
    /// Features enabled for a given package.
    features: HashMap<PackageId, HashSet<String>>,
    /// Checksum for each package. A SHA256 hash of the `.crate` file used to
    /// validate the correct crate file is used. This is `None` for sources
    /// that do not use `.crate` files, like path or git dependencies.
    checksums: HashMap<PackageId, Option<String>>,
    /// "Unknown" metadata. This is a collection of extra, unrecognized data
    /// found in the `[metadata]` section of `Cargo.lock`, preserved for
    /// forwards compatibility.
    metadata: Metadata,
    /// `[patch]` entries that did not match anything, preserved in
    /// `Cargo.lock` as the `[[patch.unused]]` table array. Tracking unused
    /// patches helps prevent Cargo from being forced to re-update the
    /// registry every time it runs, and keeps the resolve in a locked state
    /// so it doesn't re-resolve the unused entries.
    unused_patches: Vec<PackageId>,
    /// A map from packages to a set of their public dependencies
    public_dependencies: HashMap<PackageId, HashSet<PackageId>>,
    /// Version of the `Cargo.lock` format, see
    /// `cargo::core::resolver::encode` for more.
    version: ResolveVersion,
}

/// A version to indicate how a `Cargo.lock` should be serialized. Currently
/// V2 is the default when creating a new lockfile. If a V1 lockfile already
/// exists, it will stay as V1.
///
/// It's theorized that we can add more here over time to track larger changes
/// to the `Cargo.lock` format, but we've yet to see how that strategy pans out.
#[derive(PartialEq, Eq, Clone, Copy, Debug, PartialOrd, Ord)]
pub enum ResolveVersion {
    /// Historical baseline for when this abstraction was added.
    V1,
    /// A more compact format, more amenable to avoiding source-control merge
    /// conflicts. The `dependencies` arrays are compressed and checksums are
    /// listed inline. Introduced in 2019 in version 1.38. New lockfiles use
    /// V2 by default starting in 1.41.
    V2,
}

impl Resolve {
    pub fn new(
        graph: Graph<PackageId, Vec<Dependency>>,
        replacements: HashMap<PackageId, PackageId>,
        features: HashMap<PackageId, HashSet<String>>,
        checksums: HashMap<PackageId, Option<String>>,
        metadata: Metadata,
        unused_patches: Vec<PackageId>,
        version: ResolveVersion,
    ) -> Resolve {
        let reverse_replacements = replacements.iter().map(|(&p, &r)| (r, p)).collect();
        let public_dependencies = graph
            .iter()
            .map(|p| {
                let public_deps = graph
                    .edges(p)
                    .filter(|(_, deps)| {
                        deps.iter()
                            .any(|d| d.kind() == Kind::Normal && d.is_public())
                    })
                    .map(|(dep_package, _)| *dep_package)
                    .collect::<HashSet<PackageId>>();

                (*p, public_deps)
            })
            .collect();

        Resolve {
            graph,
            replacements,
            features,
            checksums,
            metadata,
            unused_patches,
            empty_features: HashSet::new(),
            reverse_replacements,
            public_dependencies,
            version,
        }
    }

    /// Resolves one of the paths from the given dependent package up to
    /// the root.
    pub fn path_to_top<'a>(&'a self, pkg: &'a PackageId) -> Vec<&'a PackageId> {
        self.graph.path_to_top(pkg)
    }

    pub fn register_used_patches(&mut self, patches: &[Summary]) {
        for summary in patches {
            if self.iter().any(|id| id == summary.package_id()) {
                continue;
            }
            self.unused_patches.push(summary.package_id());
        }
    }

    pub fn merge_from(&mut self, previous: &Resolve) -> CargoResult<()> {
        // Given a previous instance of resolve, it should be forbidden to ever
        // have a checksums which *differ*. If the same package ID has differing
        // checksums, then something has gone wrong such as:
        //
        // * Something got seriously corrupted
        // * A "mirror" isn't actually a mirror as some changes were made
        // * A replacement source wasn't actually a replacement, some changes
        //   were made
        //
        // In all of these cases, we want to report an error to indicate that
        // something is awry. Normal execution (esp just using crates.io) should
        // never run into this.
        for (id, cksum) in previous.checksums.iter() {
            if let Some(mine) = self.checksums.get(id) {
                if mine == cksum {
                    continue;
                }

                // If the previous checksum wasn't calculated, the current
                // checksum is `Some`. This may indicate that a source was
                // erroneously replaced or was replaced with something that
                // desires stronger checksum guarantees than can be afforded
                // elsewhere.
                if cksum.is_none() {
                    failure::bail!(
                        "\
checksum for `{}` was not previously calculated, but a checksum could now \
be calculated

this could be indicative of a few possible situations:

    * the source `{}` did not previously support checksums,
      but was replaced with one that does
    * newer Cargo implementations know how to checksum this source, but this
      older implementation does not
    * the lock file is corrupt
",
                        id,
                        id.source_id()
                    )

                // If our checksum hasn't been calculated, then it could mean
                // that future Cargo figured out how to checksum something or
                // more realistically we were overridden with a source that does
                // not have checksums.
                } else if mine.is_none() {
                    failure::bail!(
                        "\
checksum for `{}` could not be calculated, but a checksum is listed in \
the existing lock file

this could be indicative of a few possible situations:

    * the source `{}` supports checksums,
      but was replaced with one that doesn't
    * the lock file is corrupt

unable to verify that `{0}` is the same as when the lockfile was generated
",
                        id,
                        id.source_id()
                    )

                // If the checksums aren't equal, and neither is None, then they
                // must both be Some, in which case the checksum now differs.
                // That's quite bad!
                } else {
                    failure::bail!(
                        "\
checksum for `{}` changed between lock files

this could be indicative of a few possible errors:

    * the lock file is corrupt
    * a replacement source in use (e.g., a mirror) returned a different checksum
    * the source itself may be corrupt in one way or another

unable to verify that `{0}` is the same as when the lockfile was generated
",
                        id
                    );
                }
            }
        }

        // Be sure to just copy over any unknown metadata.
        self.metadata = previous.metadata.clone();

        // The goal of Cargo is largely to preserve the encoding of `Cargo.lock`
        // that it finds on the filesystem. Sometimes `Cargo.lock` changes are
        // in the works where they haven't been set as the default yet but will
        // become the default soon.
        //
        // The scenarios we could be in are:
        //
        // * This is a brand new lock file with nothing previous. In that case
        //   this method isn't actually called at all, but instead
        //   `default_for_new_lockfiles` called below was encoded during the
        //   resolution step, so that's what we're gonna use.
        //
        // * We have an old lock file. In this case we want to switch the
        //   version to `default_for_old_lockfiles`. That puts us in one of
        //   three cases:
        //
        //   * Our version is older than the default. This means that we're
        //     migrating someone forward, so we switch the encoding.
        //   * Our version is equal to the default, nothing to do!
        //   * Our version is *newer* than the default. This is where we
        //     critically keep the new version of encoding.
        //
        // This strategy should get new lockfiles into the pipeline more quickly
        // while ensuring that any time an old cargo sees a future lock file it
        // keeps the future lockfile encoding.
        self.version = cmp::max(
            previous.version,
            ResolveVersion::default_for_old_lockfiles(),
        );

        Ok(())
    }

    pub fn contains<Q: ?Sized>(&self, k: &Q) -> bool
    where
        PackageId: Borrow<Q>,
        Q: Ord + Eq,
    {
        self.graph.contains(k)
    }

    pub fn sort(&self) -> Vec<PackageId> {
        self.graph.sort()
    }

    pub fn iter<'a>(&'a self) -> impl Iterator<Item = PackageId> + 'a {
        self.graph.iter().cloned()
    }

    pub fn deps(&self, pkg: PackageId) -> impl Iterator<Item = (PackageId, &[Dependency])> {
        self.deps_not_replaced(pkg)
            .map(move |(id, deps)| (self.replacement(id).unwrap_or(id), deps))
    }

    pub fn deps_not_replaced(
        &self,
        pkg: PackageId,
    ) -> impl Iterator<Item = (PackageId, &[Dependency])> {
        self.graph
            .edges(&pkg)
            .map(|(id, deps)| (*id, deps.as_slice()))
    }

    pub fn replacement(&self, pkg: PackageId) -> Option<PackageId> {
        self.replacements.get(&pkg).cloned()
    }

    pub fn replacements(&self) -> &HashMap<PackageId, PackageId> {
        &self.replacements
    }

    pub fn features(&self, pkg: PackageId) -> &HashSet<String> {
        self.features.get(&pkg).unwrap_or(&self.empty_features)
    }

    pub fn is_public_dep(&self, pkg: PackageId, dep: PackageId) -> bool {
        self.public_dependencies
            .get(&pkg)
            .map(|public_deps| public_deps.contains(&dep))
            .unwrap_or_else(|| panic!("Unknown dependency {:?} for package {:?}", dep, pkg))
    }

    pub fn features_sorted(&self, pkg: PackageId) -> Vec<&str> {
        let mut v = Vec::from_iter(self.features(pkg).iter().map(|s| s.as_ref()));
        v.sort_unstable();
        v
    }

    pub fn query(&self, spec: &str) -> CargoResult<PackageId> {
        PackageIdSpec::query_str(spec, self.iter())
    }

    pub fn unused_patches(&self) -> &[PackageId] {
        &self.unused_patches
    }

    pub fn checksums(&self) -> &HashMap<PackageId, Option<String>> {
        &self.checksums
    }

    pub fn metadata(&self) -> &Metadata {
        &self.metadata
    }

    pub fn extern_crate_name(
        &self,
        from: PackageId,
        to: PackageId,
        to_target: &Target,
    ) -> CargoResult<String> {
        let deps = if from == to {
            &[]
        } else {
            self.dependencies_listed(from, to)
        };

        let crate_name = to_target.crate_name();
        let mut names = deps.iter().map(|d| {
            d.explicit_name_in_toml()
                .map(|s| s.as_str().replace("-", "_"))
                .unwrap_or_else(|| crate_name.clone())
        });
        let name = names.next().unwrap_or_else(|| crate_name.clone());
        for n in names {
            failure::ensure!(
                n == name,
                "the crate `{}` depends on crate `{}` multiple times with different names",
                from,
                to,
            );
        }
        Ok(name)
    }

    fn dependencies_listed(&self, from: PackageId, to: PackageId) -> &[Dependency] {
        // We've got a dependency on `from` to `to`, but this dependency edge
        // may be affected by [replace]. If the `to` package is listed as the
        // target of a replacement (aka the key of a reverse replacement map)
        // then we try to find our dependency edge through that. If that fails
        // then we go down below assuming it's not replaced.
        //
        // Note that we don't treat `from` as if it's been replaced because
        // that's where the dependency originates from, and we only replace
        // targets of dependencies not the originator.
        if let Some(replace) = self.reverse_replacements.get(&to) {
            if let Some(deps) = self.graph.edge(&from, replace) {
                return deps;
            }
        }
        match self.graph.edge(&from, &to) {
            Some(ret) => ret,
            None => panic!("no Dependency listed for `{}` => `{}`", from, to),
        }
    }

    /// Returns the version of the encoding that's being used for this lock
    /// file.
    pub fn version(&self) -> &ResolveVersion {
        &self.version
    }
}

impl PartialEq for Resolve {
    fn eq(&self, other: &Resolve) -> bool {
        macro_rules! compare {
            ($($fields:ident)* | $($ignored:ident)*) => {
                let Resolve { $($fields,)* $($ignored,)* } = self;
                $(drop($ignored);)*
                $($fields == &other.$fields)&&*
            }
        }
        compare! {
            // fields to compare
            graph replacements reverse_replacements empty_features features
            checksums metadata unused_patches public_dependencies
            |
            // fields to ignore
            version
        }
    }
}

impl fmt::Debug for Resolve {
    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
        writeln!(fmt, "graph: {:?}", self.graph)?;
        writeln!(fmt, "\nfeatures: {{")?;
        for (pkg, features) in &self.features {
            writeln!(fmt, "  {}: {:?}", pkg, features)?;
        }
        write!(fmt, "}}")
    }
}

impl ResolveVersion {
    /// The default way to encode new `Cargo.lock` files.
    ///
    /// It's important that if a new version of `ResolveVersion` is added that
    /// this is not updated until *at least* the support for the version is in
    /// the stable release of Rust. It's ok for this to be newer than
    /// `default_for_old_lockfiles` below.
    pub fn default_for_new_lockfiles() -> ResolveVersion {
        ResolveVersion::V2
    }

    /// The default way to encode old preexisting `Cargo.lock` files. This is
    /// often trailing the new lockfiles one above to give older projects a
    /// longer time to catch up.
    ///
    /// It's important that this trails behind `default_for_new_lockfiles` for
    /// quite some time. This gives projects a quite large window to update in
    /// where we don't force updates, so if projects span many versions of Cargo
    /// all those versions of Cargo will have support for a new version of the
    /// lock file.
    pub fn default_for_old_lockfiles() -> ResolveVersion {
        ResolveVersion::V1
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(plugin)]
#![cfg_attr(feature = "dev", plugin(clippy))]

extern crate tikv;
extern crate getopts;
#[macro_use]
extern crate log;
extern crate rocksdb;
extern crate mio;

use std::env;
use std::fs;
use std::path::Path;
use std::sync::{Arc, RwLock};

use getopts::{Options, Matches};
use log::LogLevelFilter;
use rocksdb::DB;
use mio::tcp::TcpListener;

use tikv::storage::{Storage, Dsn};
use tikv::util::{self, logger};
use tikv::server::{DEFAULT_LISTENING_ADDR, SendCh, Server, Node, Config, bind, create_event_loop,
                   create_raft_storage};
use tikv::server::{ServerTransport, ServerRaftStoreRouter, MockRaftStoreRouter};
use tikv::pd::new_rpc_client;

const MEM_DSN: &'static str = "mem";
const ROCKSDB_DSN: &'static str = "rocksdb";
const RAFTKV_DSN: &'static str = "raftkv";

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn initial_log(matches: &Matches) {
    let log_filter = match matches.opt_str("L") {
        Some(level) => logger::get_level_by_string(&level),
        None => LogLevelFilter::Info,
    };
    util::init_log(log_filter).unwrap();
}

fn build_raftkv(matches: &Matches,
                ch: SendCh,
                addr: String)
                -> (Storage, Arc<RwLock<ServerRaftStoreRouter>>) {
    let pd_addr = matches.opt_str("pd").expect("raftkv needs pd client");
    let pd_client = Arc::new(RwLock::new(new_rpc_client(&pd_addr).unwrap()));
    let id = matches.opt_str("I").expect("raftkv requires cluster id");
    let cluster_id = u64::from_str_radix(&id, 10).expect("invalid cluster id");

    let trans = Arc::new(RwLock::new(ServerTransport::new(cluster_id, ch, pd_client.clone())));

    let path = get_store_path(matches);
    let mut opts = rocksdb::Options::new();
    let mut block_base_opts = rocksdb::BlockBasedOptions::new();
    block_base_opts.set_block_size(64*1024);
    opts.set_block_based_table_factory(&block_base_opts);
    opts.set_target_file_size_base(64*1024*1024);

    let engine = Arc::new(DB::open(&opts, &path).unwrap());
    let mut cfg = Config::new();
    cfg.cluster_id = cluster_id;

    cfg.addr = addr.clone();

    // Set advertise address for outer node and client use.
    // If no advertise listening address set, use the associated listening address.
    cfg.advertise_addr = matches.opt_str("advertise-addr")
                                .unwrap_or_else(|| addr);

    let mut node = Node::new(&cfg, pd_client, trans.clone());
    node.start(engine.clone()).unwrap();
    let raft_router = node.raft_store_router();

    (create_raft_storage(node, engine).unwrap(), raft_router)
}

fn get_store_path(matches: &Matches) -> String {
    let path = matches.opt_str("s").expect("need store path, but none is specified!");

    let p = Path::new(&path);
    if p.exists() && p.is_file() {
        panic!("{} is not a directory!", path);
    }
    if !p.exists() {
        fs::create_dir_all(p).unwrap();
    }
    let absolute_path = p.canonicalize().unwrap();
    format!("{}", absolute_path.display())
}

fn run_local_server(listener: TcpListener, store: Storage) {
    let mut event_loop = create_event_loop().unwrap();
    let router = Arc::new(RwLock::new(MockRaftStoreRouter));
    let mut svr = Server::new(&mut event_loop, listener, store, router).unwrap();
    svr.run(&mut event_loop).unwrap();
}

fn run_raft_server(listener: TcpListener, matches: &Matches) {
    let mut event_loop = create_event_loop().unwrap();
    let ch = SendCh::new(event_loop.channel());

    let (store, raft_router) = build_raftkv(&matches,
                                            ch,
                                            format!("{}", listener.local_addr().unwrap()));
    let mut svr = Server::new(&mut event_loop, listener, store, raft_router).unwrap();
    svr.run(&mut event_loop).unwrap();
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("A",
                "addr",
                "set listening address",
                "default is 127.0.0.1:20160");
    opts.optopt("",
                "advertise-addr",
                "set advertise listening address for client communication",
                "127.0.0.1:20160, if not set, use addr instead.");
    opts.optopt("L",
                "log",
                "set log level",
                "log level: trace, debug, info, warn, error, off");
    opts.optflag("h", "help", "print this help menu");
    // TODO: support loading config file
    // opts.optopt("C", "config", "set configuration file", "file path");
    opts.optopt("s",
                "store",
                "set the path to rocksdb directory",
                "/tmp/tikv/store");
    opts.optopt("S",
                "dsn",
                "set which dsn to use, default is mem",
                "dsn: mem, rocksdb, raftkv");
    opts.optopt("I", "cluster-id", "set cluster id", "must greater than 0.");
    opts.optopt("", "pd", "set pd address", "host:port");
    let matches = opts.parse(&args[1..]).expect("opts parse failed");
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    initial_log(&matches);

    let addr = matches.opt_str("A").unwrap_or_else(|| DEFAULT_LISTENING_ADDR.to_owned());
    info!("Start listening on {}...", addr);
    let listener = bind(&addr).unwrap();

    let dsn_name = matches.opt_str("S").unwrap_or_else(|| MEM_DSN.to_owned());

    match dsn_name.as_ref() {
        MEM_DSN => {
            let store = Storage::new(Dsn::Memory).unwrap();
            run_local_server(listener, store);
        }
        ROCKSDB_DSN => {
            let path = get_store_path(&matches);
            let store = Storage::new(Dsn::RocksDBPath(&path)).unwrap();
            run_local_server(listener, store);
        }
        RAFTKV_DSN => {
            run_raft_server(listener, &matches);
        }
        n => panic!("unrecognized dns name: {}", n),
    };
}
<commit_msg>tikv-server: rocksdb add create_if_missing option<commit_after>// Copyright 2016 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.

#![feature(plugin)]
#![cfg_attr(feature = "dev", plugin(clippy))]

extern crate tikv;
extern crate getopts;
#[macro_use]
extern crate log;
extern crate rocksdb;
extern crate mio;

use std::env;
use std::fs;
use std::path::Path;
use std::sync::{Arc, RwLock};

use getopts::{Options, Matches};
use log::LogLevelFilter;
use rocksdb::DB;
use mio::tcp::TcpListener;

use tikv::storage::{Storage, Dsn};
use tikv::util::{self, logger};
use tikv::server::{DEFAULT_LISTENING_ADDR, SendCh, Server, Node, Config, bind, create_event_loop,
                   create_raft_storage};
use tikv::server::{ServerTransport, ServerRaftStoreRouter, MockRaftStoreRouter};
use tikv::pd::new_rpc_client;

const MEM_DSN: &'static str = "mem";
const ROCKSDB_DSN: &'static str = "rocksdb";
const RAFTKV_DSN: &'static str = "raftkv";

fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options]", program);
    print!("{}", opts.usage(&brief));
}

fn initial_log(matches: &Matches) {
    let log_filter = match matches.opt_str("L") {
        Some(level) => logger::get_level_by_string(&level),
        None => LogLevelFilter::Info,
    };
    util::init_log(log_filter).unwrap();
}

fn build_raftkv(matches: &Matches,
                ch: SendCh,
                addr: String)
                -> (Storage, Arc<RwLock<ServerRaftStoreRouter>>) {
    let pd_addr = matches.opt_str("pd").expect("raftkv needs pd client");
    let pd_client = Arc::new(RwLock::new(new_rpc_client(&pd_addr).unwrap()));
    let id = matches.opt_str("I").expect("raftkv requires cluster id");
    let cluster_id = u64::from_str_radix(&id, 10).expect("invalid cluster id");

    let trans = Arc::new(RwLock::new(ServerTransport::new(cluster_id, ch, pd_client.clone())));

    let path = get_store_path(matches);
    let mut opts = rocksdb::Options::new();
    let mut block_base_opts = rocksdb::BlockBasedOptions::new();
    block_base_opts.set_block_size(64*1024);
    opts.set_block_based_table_factory(&block_base_opts);
    opts.set_target_file_size_base(64*1024*1024);
    opts.create_if_missing(true);

    let engine = Arc::new(DB::open(&opts, &path).unwrap());
    let mut cfg = Config::new();
    cfg.cluster_id = cluster_id;

    cfg.addr = addr.clone();

    // Set advertise address for outer node and client use.
    // If no advertise listening address set, use the associated listening address.
    cfg.advertise_addr = matches.opt_str("advertise-addr")
                                .unwrap_or_else(|| addr);

    let mut node = Node::new(&cfg, pd_client, trans.clone());
    node.start(engine.clone()).unwrap();
    let raft_router = node.raft_store_router();

    (create_raft_storage(node, engine).unwrap(), raft_router)
}

fn get_store_path(matches: &Matches) -> String {
    let path = matches.opt_str("s").expect("need store path, but none is specified!");

    let p = Path::new(&path);
    if p.exists() && p.is_file() {
        panic!("{} is not a directory!", path);
    }
    if !p.exists() {
        fs::create_dir_all(p).unwrap();
    }
    let absolute_path = p.canonicalize().unwrap();
    format!("{}", absolute_path.display())
}

fn run_local_server(listener: TcpListener, store: Storage) {
    let mut event_loop = create_event_loop().unwrap();
    let router = Arc::new(RwLock::new(MockRaftStoreRouter));
    let mut svr = Server::new(&mut event_loop, listener, store, router).unwrap();
    svr.run(&mut event_loop).unwrap();
}

fn run_raft_server(listener: TcpListener, matches: &Matches) {
    let mut event_loop = create_event_loop().unwrap();
    let ch = SendCh::new(event_loop.channel());

    let (store, raft_router) = build_raftkv(&matches,
                                            ch,
                                            format!("{}", listener.local_addr().unwrap()));
    let mut svr = Server::new(&mut event_loop, listener, store, raft_router).unwrap();
    svr.run(&mut event_loop).unwrap();
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();
    let mut opts = Options::new();
    opts.optopt("A",
                "addr",
                "set listening address",
                "default is 127.0.0.1:20160");
    opts.optopt("",
                "advertise-addr",
                "set advertise listening address for client communication",
                "127.0.0.1:20160, if not set, use addr instead.");
    opts.optopt("L",
                "log",
                "set log level",
                "log level: trace, debug, info, warn, error, off");
    opts.optflag("h", "help", "print this help menu");
    // TODO: support loading config file
    // opts.optopt("C", "config", "set configuration file", "file path");
    opts.optopt("s",
                "store",
                "set the path to rocksdb directory",
                "/tmp/tikv/store");
    opts.optopt("S",
                "dsn",
                "set which dsn to use, default is mem",
                "dsn: mem, rocksdb, raftkv");
    opts.optopt("I", "cluster-id", "set cluster id", "must greater than 0.");
    opts.optopt("", "pd", "set pd address", "host:port");
    let matches = opts.parse(&args[1..]).expect("opts parse failed");
    if matches.opt_present("h") {
        print_usage(&program, opts);
        return;
    }
    initial_log(&matches);

    let addr = matches.opt_str("A").unwrap_or_else(|| DEFAULT_LISTENING_ADDR.to_owned());
    info!("Start listening on {}...", addr);
    let listener = bind(&addr).unwrap();

    let dsn_name = matches.opt_str("S").unwrap_or_else(|| MEM_DSN.to_owned());

    match dsn_name.as_ref() {
        MEM_DSN => {
            let store = Storage::new(Dsn::Memory).unwrap();
            run_local_server(listener, store);
        }
        ROCKSDB_DSN => {
            let path = get_store_path(&matches);
            let store = Storage::new(Dsn::RocksDBPath(&path)).unwrap();
            run_local_server(listener, store);
        }
        RAFTKV_DSN => {
            run_raft_server(listener, &matches);
        }
        n => panic!("unrecognized dns name: {}", n),
    };
}
<|endoftext|>


<commit_before>// Traits in Rust are a way of guaranteeing particular functionality for a type.
// They let the compiler know that a type is capable of necessary functions to
// help it ensure safety.
#[test]
fn implementing_traits() {
  struct Person {
    first_name: &'static str,
    last_name: &'static str
  }

  // Any type that implements the HasName trait is guaranteed to have a
  // function called full_name() that returns a String
  trait HasName {
    fn full_name(&self) -> String;
  }

  impl Person {
    fn full_name(&self) -> String {
      format!("{} {}", self.first_name, self.last_name)
    }
  }

  let person = Person {
    first_name: "Chris",
    last_name: "Cerami"
  };

  // The assert_full_name function needs to know that its argument can call
  // full_name(). In order to guarantee this, it is cast to receive any
  // argument type that has implemented the HasName trait.
  fn assert_full_name<T: HasName>(person: T) {
    assert_eq!(person.full_name(), "Chris Cerami");
  }

  assert_full_name(person);
}

// In order to implement a trait, a type must implement ALL of its functions.
// This is required in order to guarantee that any type that shares that trait
// will be able to respond to those functions.
#[test]
fn implementing_traits2() {
  struct Character {
    name: &'static str,
    level: u16
  }

  trait HasLevel {
    fn level_up(&mut self) -> u16;

    fn print_level(&self);
  }

  impl HasLevel for Character {
    fn level_up(&mut self) -> u16 {
      self.level += 1;
      self.level
    }
  }

  let mut durz = Character {
    name: "Durz",
    level: 2
  };

  fn test_level_up<T: HasLevel>(character: &mut T) {
    assert_eq!(character.level_up(), 3);
  }

  test_level_up(&mut durz);
}

// Now let's try creating a trait an implementing it for an existing type.
#[test]
fn creating_traits() {
  let num_one: u16 = 3;
  let num_two: u16 = 4;

  assert!(!num_one.is_even());
  assert!(num_two.is_even());
}
<commit_msg>Add more trait constraint examples<commit_after>// Traits in Rust are a way of guaranteeing particular functionality for a type.
// They let the compiler know that a type is capable of necessary functions to
// help it ensure safety.
#[test]
fn implementing_traits() {
  struct Person {
    first_name: &'static str,
    last_name: &'static str
  }

  // Any type that implements the HasName trait is guaranteed to have a
  // function called full_name() that returns a String
  trait HasName {
    fn full_name(&self) -> String;
  }

  impl Person {
    fn full_name(&self) -> String {
      format!("{} {}", self.first_name, self.last_name)
    }
  }

  let person = Person {
    first_name: "Chris",
    last_name: "Cerami"
  };

  // The assert_full_name function needs to know that its argument can call
  // full_name(). In order to guarantee this, it is cast to receive any
  // argument type that has implemented the HasName trait.
  fn assert_full_name<T: HasName>(person: T) {
    assert_eq!(person.full_name(), "Chris Cerami");
  }

  assert_full_name(person);
}

// In order to implement a trait, a type must implement ALL of its functions.
// This is required in order to guarantee that any type that shares that trait
// will be able to respond to those functions.
#[test]
fn implementing_traits2() {
  struct Character {
    name: &'static str,
    level: u16
  }

  trait HasLevel {
    fn level_up(&mut self) -> u16;

    fn print_level(&self);
  }

  impl HasLevel for Character {
    fn level_up(&mut self) -> u16 {
      self.level += 1;
      self.level
    }
  }

  let mut durz = Character {
    name: "Durz",
    level: 2
  };

  fn test_level_up<T: HasLevel>(character: &mut T) {
    assert_eq!(character.level_up(), 3);
  }

  test_level_up(&mut durz);
}

// Now let's try creating a trait and implementing it for an existing type.
#[test]
fn creating_traits() {
  let num_one: u16 = 3;
  let num_two: u16 = 4;

  fn asserts<T: IsEvenOrOdd>(x: T, y: T) {
    assert!(!x.is_even());
    assert!(y.is_even());
  }

  asserts(num_one, num_two);
}

// We can also add trait constraints to structs that we create. Using this pattern,
// we can use generic types and still ensure type safety.
#[test]
fn trait_constraints_on_structs() {
  struct Language<T> {
    stable_version: T,
    latest_version: T
  }

  impl<__> Language<T> {
    fn is_stable(&self) -> bool {
      self.latest_version >= self.stable_version
    }
  }

  let rust = Language {
    stable_version: "1.3.0",
    latest_version: "1.5.0"
  };

  assert!(rust.is_stable());
}
<|endoftext|>


<commit_before>use std::{cmp::min, env, fs::File, io, mem, time::Duration};

use pgn_reader::{BufferedReader, Color, RawHeader, SanPlus, Skip, Visitor};
use rand::{distributions::OpenClosed01, rngs::SmallRng, Rng, SeedableRng};
use serde::Serialize;
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};

const BATCH_SIZE: usize = 100;

#[derive(Debug, Serialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
enum Speed {
    UltraBullet,
    Bullet,
    Blitz,
    Rapid,
    Classical,
    Correspondence,
}

impl Speed {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> Speed {
        let total = seconds + 40 * increment;

        if total < 30 {
            Speed::UltraBullet
        } else if total < 180 {
            Speed::Bullet
        } else if total < 480 {
            Speed::Blitz
        } else if total < 1500 {
            Speed::Rapid
        } else if total < 21_600 {
            Speed::Classical
        } else {
            Speed::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<Speed, ()> {
        if bytes == b"-" {
            return Ok(Speed::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next().ok_or(())?).map_err(|_| ())?;
        let increment = btoi::btou(parts.next().ok_or(())?).map_err(|_| ())?;
        Ok(Speed::from_seconds_and_increment(seconds, increment))
    }
}

struct Importer {
    filename: String,
    client: reqwest::blocking::Client,
    rng: SmallRng,

    current: Game,
    skip: bool,
    batch: Vec<Game>,
}

#[serde_as]
#[derive(Default, Serialize, Debug)]
struct Game {
    variant: Option<String>,
    speed: Option<Speed>,
    fen: Option<String>,
    id: Option<String>,
    date: Option<String>,
    white: Player,
    black: Player,
    #[serde_as(as = "Option<DisplayFromStr>")]
    winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, SanPlus>")]
    moves: Vec<SanPlus>,
}

#[derive(Default, Serialize, Debug)]
struct Player {
    name: Option<String>,
    rating: Option<u16>,
}

impl Importer {
    fn new(filename: String) -> Importer {
        Importer {
            filename,
            client: reqwest::blocking::Client::builder()
                .timeout(Duration::from_secs(60))
                .build()
                .expect("client"),
            rng: SmallRng::from_seed([
                0x19, 0x29, 0xab, 0x17, 0xc6, 0xfa, 0xb0, 0xe9, 0x4b, 0x44, 0xd8, 0x07, 0x09, 0xbf,
                0x1d, 0x87, 0xbd, 0xd8, 0xb3, 0x2f, 0xe1, 0xe2, 0xa0, 0x1a, 0x9e, 0x30, 0x98, 0xd7,
                0xef, 0xd5, 0x7a, 0x1d,
            ]),
            current: Game::default(),
            skip: false,
            batch: Vec::with_capacity(BATCH_SIZE),
        }
    }

    pub fn send(&mut self) {
        // println!("{}", serde_json::to_string(&self.batch).expect("serialize"));

        let res = self
            .client
            .put("https://explorer.lichess.ovh/import/lichess")
            .json(&self.batch)
            .send()
            .expect("send batch");

        println!(
            "{}: {}: {} - {}",
            self.filename,
            self.batch
                .last()
                .and_then(|g| g.date.as_ref())
                .unwrap_or(&String::new()),
            res.status(),
            res.text().expect("decode response")
        );

        self.batch.clear();
    }
}

impl Visitor for Importer {
    type Result = ();

    fn begin_game(&mut self) {
        self.skip = false;
        self.current = Game::default();
    }

    fn header(&mut self, key: &[u8], value: RawHeader<'_>) {
        if key == b"White" {
            self.current.white.name = Some(value.decode_utf8().expect("White").into_owned());
        } else if key == b"Black" {
            self.current.black.name = Some(value.decode_utf8().expect("Black").into_owned());
        } else if key == b"WhiteElo" {
            if value.as_bytes() != b"?" {
                self.current.white.rating = Some(btoi::btoi(value.as_bytes()).expect("WhiteElo"));
            }
        } else if key == b"BlackElo" {
            if value.as_bytes() != b"?" {
                self.current.black.rating = Some(btoi::btoi(value.as_bytes()).expect("BlackElo"));
            }
        } else if key == b"TimeControl" {
            self.current.speed = Some(Speed::from_bytes(value.as_bytes()).expect("TimeControl"));
        } else if key == b"Variant" {
            self.current.variant = Some(value.decode_utf8().expect("Variant").into_owned());
        } else if key == b"Date" || key == b"UTCDate" {
            self.current.date = Some(value.decode_utf8().expect("Date").into_owned());
        } else if key == b"WhiteTitle" || key == b"BlackTitle" {
            if value.as_bytes() == b"BOT" {
                self.skip = true;
            }
        } else if key == b"Site" {
            self.current.id = Some(
                String::from_utf8(
                    value
                        .as_bytes()
                        .rsplitn(2, |ch| *ch == b'/')
                        .next()
                        .expect("Site")
                        .to_owned(),
                )
                .expect("Site"),
            );
        } else if key == b"Result" {
            match value.as_bytes() {
                b"1-0" => self.current.winner = Some(Color::White),
                b"0-1" => self.current.winner = Some(Color::Black),
                b"1/2-1/2" => self.current.winner = None,
                _ => self.skip = true,
            }
        } else if key == b"FEN" {
            if value.as_bytes() == b"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" {
                // https://github.com/ornicar/lichess-db/issues/40
                self.current.fen = None;
            } else {
                self.current.fen = Some(value.decode_utf8().expect("FEN").into_owned());
            }
        }
    }

    fn end_headers(&mut self) -> Skip {
        let rating =
            (self.current.white.rating.unwrap_or(0) + self.current.black.rating.unwrap_or(0)) / 2;

        let standard = self
            .current
            .variant
            .as_ref()
            .map_or(true, |name| name == "Standard");

        let probability = if standard {
            match self.current.speed.unwrap_or(Speed::Correspondence) {
                Speed::Correspondence | Speed::Classical => 1.00,

                _ if rating >= 2500 => 1.00,

                Speed::Rapid if rating >= 2200 => 1.00,
                Speed::Rapid if rating >= 2000 => 0.50,
                Speed::Rapid if rating >= 1800 => 0.28,
                Speed::Rapid if rating >= 1600 => 0.24,

                Speed::Blitz if rating >= 2200 => 0.24,
                Speed::Blitz if rating >= 2000 => 0.11,
                Speed::Blitz if rating >= 1600 => 0.08,

                Speed::Bullet if rating >= 2200 => 0.30,
                Speed::Bullet if rating >= 2000 => 0.17,
                Speed::Bullet if rating >= 1600 => 0.11,

                Speed::UltraBullet => 1.00,

                _ => 0.02,
            }
        } else {
            // variant games
            if rating >= 1600 {
                1.00
            } else {
                0.50
            }
        };

        let accept = min(
            self.current.white.rating.unwrap_or(0),
            self.current.black.rating.unwrap_or(0),
        ) >= 1501
            && probability >= self.rng.sample(OpenClosed01)
            && !self.skip;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: SanPlus) {
        self.current.moves.push(san);
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self) {
        if !self.skip {
            self.batch
                .push(mem::replace(&mut self.current, Default::default()));

            if self.batch.len() >= BATCH_SIZE {
                self.send();
            }
        }
    }
}

fn main() -> Result<(), io::Error> {
    for arg in env::args().skip(1) {
        let file = File::open(&arg)?;

        let uncompressed: Box<dyn io::Read> = if arg.ends_with(".bz2") {
            Box::new(bzip2::read::MultiBzDecoder::new(file))
        } else {
            Box::new(file)
        };

        let mut reader = BufferedReader::new(uncompressed);

        let mut importer = Importer::new(arg);
        reader.read_all(&mut importer)?;
    }

    Ok(())
}
<commit_msg>add progress spinner to lichess indexer<commit_after>use std::{cmp::min, env, fs::File, io, mem, num::Wrapping, time::Duration};

use pgn_reader::{BufferedReader, Color, RawHeader, SanPlus, Skip, Visitor};
use rand::{distributions::OpenClosed01, rngs::SmallRng, Rng, SeedableRng};
use serde::Serialize;
use serde_with::{serde_as, DisplayFromStr, SpaceSeparator, StringWithSeparator};

const BATCH_SIZE: usize = 100;

#[derive(Debug, Serialize, Copy, Clone)]
#[serde(rename_all = "camelCase")]
enum Speed {
    UltraBullet,
    Bullet,
    Blitz,
    Rapid,
    Classical,
    Correspondence,
}

impl Speed {
    fn from_seconds_and_increment(seconds: u64, increment: u64) -> Speed {
        let total = seconds + 40 * increment;

        if total < 30 {
            Speed::UltraBullet
        } else if total < 180 {
            Speed::Bullet
        } else if total < 480 {
            Speed::Blitz
        } else if total < 1500 {
            Speed::Rapid
        } else if total < 21_600 {
            Speed::Classical
        } else {
            Speed::Correspondence
        }
    }

    fn from_bytes(bytes: &[u8]) -> Result<Speed, ()> {
        if bytes == b"-" {
            return Ok(Speed::Correspondence);
        }

        let mut parts = bytes.splitn(2, |ch| *ch == b'+');
        let seconds = btoi::btou(parts.next().ok_or(())?).map_err(|_| ())?;
        let increment = btoi::btou(parts.next().ok_or(())?).map_err(|_| ())?;
        Ok(Speed::from_seconds_and_increment(seconds, increment))
    }
}

struct Importer {
    filename: String,
    client: reqwest::blocking::Client,
    rng: SmallRng,
    spinner_idx: Wrapping<usize>,

    current: Game,
    skip: bool,
    batch: Vec<Game>,
}

#[serde_as]
#[derive(Default, Serialize, Debug)]
struct Game {
    variant: Option<String>,
    speed: Option<Speed>,
    fen: Option<String>,
    id: Option<String>,
    date: Option<String>,
    white: Player,
    black: Player,
    #[serde_as(as = "Option<DisplayFromStr>")]
    winner: Option<Color>,
    #[serde_as(as = "StringWithSeparator<SpaceSeparator, SanPlus>")]
    moves: Vec<SanPlus>,
}

#[derive(Default, Serialize, Debug)]
struct Player {
    name: Option<String>,
    rating: Option<u16>,
}

impl Importer {
    fn new(filename: String) -> Importer {
        Importer {
            filename,
            client: reqwest::blocking::Client::builder()
                .timeout(Duration::from_secs(60))
                .build()
                .expect("client"),
            rng: SmallRng::from_seed([
                0x19, 0x29, 0xab, 0x17, 0xc6, 0xfa, 0xb0, 0xe9, 0x4b, 0x44, 0xd8, 0x07, 0x09, 0xbf,
                0x1d, 0x87, 0xbd, 0xd8, 0xb3, 0x2f, 0xe1, 0xe2, 0xa0, 0x1a, 0x9e, 0x30, 0x98, 0xd7,
                0xef, 0xd5, 0x7a, 0x1d,
            ]),
            spinner_idx: Wrapping(0),
            current: Game::default(),
            skip: false,
            batch: Vec::with_capacity(BATCH_SIZE),
        }
    }

    pub fn send(&mut self) {
        // println!("{}", serde_json::to_string(&self.batch).expect("serialize"));

        let res = self
            .client
            .put("https://explorer.lichess.ovh/import/lichess")
            .json(&self.batch)
            .send()
            .expect("send batch");

        self.spinner_idx += Wrapping(1);
        let spinner = &['⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷'];

        println!(
            "{} {}: {}: {} - {}",
            spinner[self.spinner_idx.0 % spinner.len()],
            self.filename,
            self.batch
                .last()
                .and_then(|g| g.date.as_ref())
                .unwrap_or(&String::new()),
            res.status(),
            res.text().expect("decode response")
        );

        self.batch.clear();
    }
}

impl Visitor for Importer {
    type Result = ();

    fn begin_game(&mut self) {
        self.skip = false;
        self.current = Game::default();
    }

    fn header(&mut self, key: &[u8], value: RawHeader<'_>) {
        if key == b"White" {
            self.current.white.name = Some(value.decode_utf8().expect("White").into_owned());
        } else if key == b"Black" {
            self.current.black.name = Some(value.decode_utf8().expect("Black").into_owned());
        } else if key == b"WhiteElo" {
            if value.as_bytes() != b"?" {
                self.current.white.rating = Some(btoi::btoi(value.as_bytes()).expect("WhiteElo"));
            }
        } else if key == b"BlackElo" {
            if value.as_bytes() != b"?" {
                self.current.black.rating = Some(btoi::btoi(value.as_bytes()).expect("BlackElo"));
            }
        } else if key == b"TimeControl" {
            self.current.speed = Some(Speed::from_bytes(value.as_bytes()).expect("TimeControl"));
        } else if key == b"Variant" {
            self.current.variant = Some(value.decode_utf8().expect("Variant").into_owned());
        } else if key == b"Date" || key == b"UTCDate" {
            self.current.date = Some(value.decode_utf8().expect("Date").into_owned());
        } else if key == b"WhiteTitle" || key == b"BlackTitle" {
            if value.as_bytes() == b"BOT" {
                self.skip = true;
            }
        } else if key == b"Site" {
            self.current.id = Some(
                String::from_utf8(
                    value
                        .as_bytes()
                        .rsplitn(2, |ch| *ch == b'/')
                        .next()
                        .expect("Site")
                        .to_owned(),
                )
                .expect("Site"),
            );
        } else if key == b"Result" {
            match value.as_bytes() {
                b"1-0" => self.current.winner = Some(Color::White),
                b"0-1" => self.current.winner = Some(Color::Black),
                b"1/2-1/2" => self.current.winner = None,
                _ => self.skip = true,
            }
        } else if key == b"FEN" {
            if value.as_bytes() == b"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1" {
                // https://github.com/ornicar/lichess-db/issues/40
                self.current.fen = None;
            } else {
                self.current.fen = Some(value.decode_utf8().expect("FEN").into_owned());
            }
        }
    }

    fn end_headers(&mut self) -> Skip {
        let rating =
            (self.current.white.rating.unwrap_or(0) + self.current.black.rating.unwrap_or(0)) / 2;

        let standard = self
            .current
            .variant
            .as_ref()
            .map_or(true, |name| name == "Standard");

        let probability = if standard {
            match self.current.speed.unwrap_or(Speed::Correspondence) {
                Speed::Correspondence | Speed::Classical => 1.00,

                _ if rating >= 2500 => 1.00,

                Speed::Rapid if rating >= 2200 => 1.00,
                Speed::Rapid if rating >= 2000 => 0.50,
                Speed::Rapid if rating >= 1800 => 0.28,
                Speed::Rapid if rating >= 1600 => 0.24,

                Speed::Blitz if rating >= 2200 => 0.24,
                Speed::Blitz if rating >= 2000 => 0.11,
                Speed::Blitz if rating >= 1600 => 0.08,

                Speed::Bullet if rating >= 2200 => 0.30,
                Speed::Bullet if rating >= 2000 => 0.17,
                Speed::Bullet if rating >= 1600 => 0.11,

                Speed::UltraBullet => 1.00,

                _ => 0.02,
            }
        } else {
            // variant games
            if rating >= 1600 {
                1.00
            } else {
                0.50
            }
        };

        let accept = min(
            self.current.white.rating.unwrap_or(0),
            self.current.black.rating.unwrap_or(0),
        ) >= 1501
            && probability >= self.rng.sample(OpenClosed01)
            && !self.skip;

        self.skip = !accept;
        Skip(self.skip)
    }

    fn san(&mut self, san: SanPlus) {
        self.current.moves.push(san);
    }

    fn begin_variation(&mut self) -> Skip {
        Skip(true) // stay in the mainline
    }

    fn end_game(&mut self) {
        if !self.skip {
            self.batch
                .push(mem::replace(&mut self.current, Default::default()));

            if self.batch.len() >= BATCH_SIZE {
                self.send();
            }
        }
    }
}

fn main() -> Result<(), io::Error> {
    for arg in env::args().skip(1) {
        let file = File::open(&arg)?;

        let uncompressed: Box<dyn io::Read> = if arg.ends_with(".bz2") {
            Box::new(bzip2::read::MultiBzDecoder::new(file))
        } else {
            Box::new(file)
        };

        let mut reader = BufferedReader::new(uncompressed);

        let mut importer = Importer::new(arg);
        reader.read_all(&mut importer)?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>extern crate rs_nes;

use rs_nes::memory::Memory;

#[test]
fn store_and_load() {
  let mut mem = Memory::new();
  let stored = 0x1234;
  mem.store(0x0, stored);
  let loaded = mem.load(0x0).unwrap();
  assert_eq!(stored, loaded);
}

#[test]
fn store_and_load_errors_when_address_out_of_bounds() {
  let mut mem = Memory::new();
  match mem.store(0xffffff, 0xb) {
    Err(_) => {
      // we want an error to be thrown!
      assert!(true);
    }
    _ => {
      assert!(false);
    }
  };
}
<commit_msg>Fix some warnings<commit_after>extern crate rs_nes;

use rs_nes::memory::Memory;

#[test]
fn store_and_load() {
  let mut mem = Memory::new();
  let stored = 0xB;
  let _ = mem.store(0x0, stored);
  let loaded = mem.load(0x0).unwrap();
  assert_eq!(stored, loaded);
}

#[test]
fn store_and_load_errors_when_address_out_of_bounds() {
  let mut mem = Memory::new();
  match mem.store(0xffffff, 0xb) {
    Err(_) => {
      // we want an error to be thrown!
      assert!(true);
    }
    _ => {
      assert!(false);
    }
  };
}
<|endoftext|>


<commit_before>// -*- rust -*-

#[doc = "Classic Boolean logic reified as ADT"];

export t;
export not, and, or, xor, implies;
export eq, ne, is_true, is_false;
export from_str, to_str, all_values, to_bit;

#[doc = "The type of boolean logic values"]
type t = bool;

#[doc(
  brief = "Negation/Inverse"
)]
pure fn not(v: t) -> t { !v }

#[doc(
  brief = "Conjunction"
)]
pure fn and(a: t, b: t) -> t { a && b }

#[doc(
  brief = "Disjunction"
)]
pure fn or(a: t, b: t) -> t { a || b }

#[doc(
  brief = "Exclusive or, i.e. `or(and(a, not(b)), and(not(a), b))`"
)]
pure fn xor(a: t, b: t) -> t { (a && !b) || (!a && b) }

#[doc(
  brief = "Implication in the logic, i.e. from `a` follows `b`"
)]
pure fn implies(a: t, b: t) -> t { !a || b }

#[doc(
  brief = "true if truth values `a` and `b` are indistinguishable in the logic"
)]
pure fn eq(a: t, b: t) -> bool { a == b }

#[doc(
  brief = "true if truth values `a` and `b` are distinguishable in the logic"
)]
pure fn ne(a: t, b: t) -> bool { a != b }

#[doc(
  brief = "true if `v` represents truth in the logic"
)]
pure fn is_true(v: t) -> bool { v }

#[doc(
  brief = "true if `v` represents falsehood in the logic"
)]
pure fn is_false(v: t) -> bool { !v }

#[doc(
  brief = "Parse logic value from `s`"
)]
pure fn from_str(s: str) -> t {
    alt s {
      "true" { true }
      "false" { false }
    }
}

#[doc(
  brief = "Convert `v` into a string"
)]
pure fn to_str(v: t) -> str { if v { "true" } else { "false" } }

#[doc(
  brief = "Iterates over all truth values by passing them to `blk` in an unspecified order"
)]
fn all_values(blk: block(v: t)) {
    blk(true);
    blk(false);
}

#[doc(
  brief = "converts truth value to an 8 bit byte"
)]
pure fn to_bit(v: t) -> u8 { if v { 1u8 } else { 0u8 } }

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<commit_msg>libcore: Fix long lines<commit_after>// -*- rust -*-

#[doc = "Classic Boolean logic reified as ADT"];

export t;
export not, and, or, xor, implies;
export eq, ne, is_true, is_false;
export from_str, to_str, all_values, to_bit;

#[doc = "The type of boolean logic values"]
type t = bool;

#[doc(
  brief = "Negation/Inverse"
)]
pure fn not(v: t) -> t { !v }

#[doc(
  brief = "Conjunction"
)]
pure fn and(a: t, b: t) -> t { a && b }

#[doc(
  brief = "Disjunction"
)]
pure fn or(a: t, b: t) -> t { a || b }

#[doc(
  brief = "Exclusive or, i.e. `or(and(a, not(b)), and(not(a), b))`"
)]
pure fn xor(a: t, b: t) -> t { (a && !b) || (!a && b) }

#[doc(
  brief = "Implication in the logic, i.e. from `a` follows `b`"
)]
pure fn implies(a: t, b: t) -> t { !a || b }

#[doc(
  brief = "true if truth values `a` and `b` \
           are indistinguishable in the logic"
)]
pure fn eq(a: t, b: t) -> bool { a == b }

#[doc(
  brief = "true if truth values `a` and `b` are distinguishable in the logic"
)]
pure fn ne(a: t, b: t) -> bool { a != b }

#[doc(
  brief = "true if `v` represents truth in the logic"
)]
pure fn is_true(v: t) -> bool { v }

#[doc(
  brief = "true if `v` represents falsehood in the logic"
)]
pure fn is_false(v: t) -> bool { !v }

#[doc(
  brief = "Parse logic value from `s`"
)]
pure fn from_str(s: str) -> t {
    alt s {
      "true" { true }
      "false" { false }
    }
}

#[doc(
  brief = "Convert `v` into a string"
)]
pure fn to_str(v: t) -> str { if v { "true" } else { "false" } }

#[doc(
  brief = "Iterates over all truth values by passing them to `blk` \
           in an unspecified order"
)]
fn all_values(blk: block(v: t)) {
    blk(true);
    blk(false);
}

#[doc(
  brief = "converts truth value to an 8 bit byte"
)]
pure fn to_bit(v: t) -> u8 { if v { 1u8 } else { 0u8 } }

// Local Variables:
// mode: rust;
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
<|endoftext|>


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

/*!

The iteration traits and common implementation

*/

use cmp::{Eq, Ord};
use kinds::Copy;
use option::{None, Option, Some};
use vec;

/// A function used to initialize the elements of a sequence
pub type InitOp<T> = &'self fn(uint) -> T;

pub trait BaseIter<A> {
    fn each(&self, blk: &fn(v: &A) -> bool);
    fn size_hint(&self) -> Option<uint>;
}

pub trait ReverseIter<A>: BaseIter<A> {
    fn each_reverse(&self, blk: &fn(&A) -> bool);
}

pub trait MutableIter<A>: BaseIter<A> {
    fn each_mut(&mut self, blk: &fn(&mut A) -> bool);
}

pub trait ExtendedIter<A> {
    fn eachi(&self, blk: &fn(uint, v: &A) -> bool);
    fn all(&self, blk: &fn(&A) -> bool) -> bool;
    fn any(&self, blk: &fn(&A) -> bool) -> bool;
    fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B;
    fn position(&self, f: &fn(&A) -> bool) -> Option<uint>;
    fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B];
    fn flat_map_to_vec<B,IB: BaseIter<B>>(&self, op: &fn(&A) -> IB) -> ~[B];
}

pub trait EqIter<A:Eq> {
    fn contains(&self, x: &A) -> bool;
    fn count(&self, x: &A) -> uint;
}

pub trait Times {
    fn times(&self, it: &fn() -> bool);
}

pub trait CopyableIter<A:Copy> {
    fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A];
    fn to_vec(&self) -> ~[A];
    fn find(&self, p: &fn(&A) -> bool) -> Option<A>;
}

pub trait CopyableOrderedIter<A:Copy + Ord> {
    fn min(&self) -> A;
    fn max(&self) -> A;
}

pub trait CopyableNonstrictIter<A:Copy> {
    // Like "each", but copies out the value. If the receiver is mutated while
    // iterating over it, the semantics must not be memory-unsafe but are
    // otherwise undefined.
    fn each_val(&const self, f: &fn(A) -> bool);
}

// A trait for sequences that can be built by imperatively pushing elements
// onto them.
pub trait Buildable<A> {
    /**
     * Builds a buildable sequence by calling a provided function with
     * an argument function that pushes an element onto the back of
     * the sequence.
     * This version takes an initial size for the sequence.
     *
     * # Arguments
     *
     * * size - A hint for an initial size of the sequence
     * * builder - A function that will construct the sequence. It receives
     *             as an argument a function that will push an element
     *             onto the sequence being constructed.
     */
     fn build_sized(size: uint, builder: &fn(push: &fn(A))) -> Self;
}

#[inline(always)]
pub fn eachi<A,IA:BaseIter<A>>(self: &IA, blk: &fn(uint, &A) -> bool) {
    let mut i = 0;
    for self.each |a| {
        if !blk(i, a) { break; }
        i += 1;
    }
}

#[inline(always)]
pub fn all<A,IA:BaseIter<A>>(self: &IA, blk: &fn(&A) -> bool) -> bool {
    for self.each |a| {
        if !blk(a) { return false; }
    }
    return true;
}

#[inline(always)]
pub fn any<A,IA:BaseIter<A>>(self: &IA, blk: &fn(&A) -> bool) -> bool {
    for self.each |a| {
        if blk(a) { return true; }
    }
    return false;
}

#[inline(always)]
pub fn filter_to_vec<A:Copy,IA:BaseIter<A>>(self: &IA,
                                            prd: &fn(&A) -> bool)
                                         -> ~[A] {
    do vec::build_sized_opt(self.size_hint()) |push| {
        for self.each |a| {
            if prd(a) { push(*a); }
        }
    }
}

#[inline(always)]
pub fn map_to_vec<A,B,IA:BaseIter<A>>(self: &IA, op: &fn(&A) -> B) -> ~[B] {
    do vec::build_sized_opt(self.size_hint()) |push| {
        for self.each |a| {
            push(op(a));
        }
    }
}

#[inline(always)]
pub fn flat_map_to_vec<A,B,IA:BaseIter<A>,IB:BaseIter<B>>(self: &IA,
                                                          op: &fn(&A) -> IB)
                                                       -> ~[B] {
    do vec::build |push| {
        for self.each |a| {
            for op(a).each |&b| {
                push(b);
            }
        }
    }
}

#[inline(always)]
pub fn foldl<A,B,IA:BaseIter<A>>(self: &IA, b0: B, blk: &fn(&B, &A) -> B)
                              -> B {
    let mut b = b0;
    for self.each |a| {
        b = blk(&b, a);
    }
    b
}

#[inline(always)]
pub fn to_vec<A:Copy,IA:BaseIter<A>>(self: &IA) -> ~[A] {
    foldl::<A,~[A],IA>(self, ~[], |r, a| vec::append(copy (*r), ~[*a]))
}

#[inline(always)]
pub fn contains<A:Eq,IA:BaseIter<A>>(self: &IA, x: &A) -> bool {
    for self.each |a| {
        if *a == *x { return true; }
    }
    return false;
}

#[inline(always)]
pub fn count<A:Eq,IA:BaseIter<A>>(self: &IA, x: &A) -> uint {
    do foldl(self, 0) |count, value| {
        if *value == *x {
            *count + 1
        } else {
            *count
        }
    }
}

#[inline(always)]
pub fn position<A,IA:BaseIter<A>>(self: &IA, f: &fn(&A) -> bool)
                               -> Option<uint> {
    let mut i = 0;
    for self.each |a| {
        if f(a) { return Some(i); }
        i += 1;
    }
    return None;
}

// note: 'rposition' would only make sense to provide with a bidirectional
// iter interface, such as would provide "reach" in addition to "each". As is,
// it would have to be implemented with foldr, which is too inefficient.

#[inline(always)]
pub fn repeat(times: uint, blk: &fn() -> bool) {
    let mut i = 0;
    while i < times {
        if !blk() { break }
        i += 1;
    }
}

#[inline(always)]
pub fn min<A:Copy + Ord,IA:BaseIter<A>>(self: &IA) -> A {
    match do foldl::<A,Option<A>,IA>(self, None) |a, b| {
        match a {
          &Some(ref a_) if *a_ < *b => {
             *(a)
          }
          _ => Some(*b)
        }
    } {
        Some(val) => val,
        None => fail!(~"min called on empty iterator")
    }
}

#[inline(always)]
pub fn max<A:Copy + Ord,IA:BaseIter<A>>(self: &IA) -> A {
    match do foldl::<A,Option<A>,IA>(self, None) |a, b| {
        match a {
          &Some(ref a_) if *a_ > *b => {
              *(a)
          }
          _ => Some(*b)
        }
    } {
        Some(val) => val,
        None => fail!(~"max called on empty iterator")
    }
}

#[inline(always)]
pub fn find<A:Copy,IA:BaseIter<A>>(self: &IA, f: &fn(&A) -> bool)
                                -> Option<A> {
    for self.each |i| {
        if f(i) { return Some(*i) }
    }
    return None;
}

// Some functions for just building

/**
 * Builds a sequence by calling a provided function with an argument
 * function that pushes an element to the back of a sequence.
 *
 * # Arguments
 *
 * * builder - A function that will construct the sequence. It receives
 *             as an argument a function that will push an element
 *             onto the sequence being constructed.
 */
#[inline(always)]
pub fn build<A,B: Buildable<A>>(builder: &fn(push: &fn(A))) -> B {
    Buildable::build_sized(4, builder)
}

/**
 * Builds a sequence by calling a provided function with an argument
 * function that pushes an element to the back of the sequence.
 * This version takes an initial size for the sequence.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the sequence
 *          to reserve.
 * * builder - A function that will construct the sequence. It receives
 *             as an argument a function that will push an element
 *             onto the sequence being constructed.
 */
#[inline(always)]
pub fn build_sized_opt<A,B: Buildable<A>>(size: Option<uint>,
                                          builder: &fn(push: &fn(A))) -> B {
    Buildable::build_sized(size.get_or_default(4), builder)
}

// Functions that combine iteration and building

/// Applies a function to each element of an iterable and returns the results.
#[inline(always)]
pub fn map<T,IT: BaseIter<T>,U,BU: Buildable<U>>(v: &IT, f: &fn(&T) -> U)
    -> BU {
    do build_sized_opt(v.size_hint()) |push| {
        for v.each() |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes a generic sequence from a function.
 *
 * Creates a generic sequence of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
#[inline(always)]
pub fn from_fn<T,BT: Buildable<T>>(n_elts: uint, op: InitOp<T>) -> BT {
    do Buildable::build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes a generic sequence with some elements.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
#[inline(always)]
pub fn from_elem<T:Copy,BT:Buildable<T>>(n_elts: uint, t: T) -> BT {
    do Buildable::build_sized(n_elts) |push| {
        let mut i: uint = 0;
        while i < n_elts { push(t); i += 1; }
    }
}

/// Appends two generic sequences.
#[inline(always)]
pub fn append<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(lhs: &IT, rhs: &IT)
                                                  -> BT {
    let size_opt = lhs.size_hint().chain_ref(
        |sz1| rhs.size_hint().map(|sz2| *sz1+*sz2));
    do build_sized_opt(size_opt) |push| {
        for lhs.each |x| { push(*x); }
        for rhs.each |x| { push(*x); }
    }
}

/// Copies a generic sequence, possibly converting it to a different
/// type of sequence.
#[inline(always)]
pub fn copy_seq<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(v: &IT) -> BT {
    do build_sized_opt(v.size_hint()) |push| {
        for v.each |x| { push(*x); }
    }
}
<commit_msg>Fixes mozilla/rust#3612.<commit_after>// Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

The iteration traits and common implementation

*/

use cmp::{Eq, Ord};
use kinds::Copy;
use option::{None, Option, Some};
use vec;

/// A function used to initialize the elements of a sequence
pub type InitOp<T> = &'self fn(uint) -> T;

pub trait BaseIter<A> {
    fn each(&self, blk: &fn(v: &A) -> bool);
    fn size_hint(&self) -> Option<uint>;
}

pub trait ReverseIter<A>: BaseIter<A> {
    fn each_reverse(&self, blk: &fn(&A) -> bool);
}

pub trait MutableIter<A>: BaseIter<A> {
    fn each_mut(&mut self, blk: &fn(&mut A) -> bool);
}

pub trait ExtendedIter<A> {
    fn eachi(&self, blk: &fn(uint, v: &A) -> bool);
    fn all(&self, blk: &fn(&A) -> bool) -> bool;
    fn any(&self, blk: &fn(&A) -> bool) -> bool;
    fn foldl<B>(&self, b0: B, blk: &fn(&B, &A) -> B) -> B;
    fn position(&self, f: &fn(&A) -> bool) -> Option<uint>;
    fn map_to_vec<B>(&self, op: &fn(&A) -> B) -> ~[B];
    fn flat_map_to_vec<B,IB: BaseIter<B>>(&self, op: &fn(&A) -> IB) -> ~[B];
}

pub trait EqIter<A:Eq> {
    fn contains(&self, x: &A) -> bool;
    fn count(&self, x: &A) -> uint;
}

pub trait Times {
    fn times(&self, it: &fn() -> bool);
}

pub trait CopyableIter<A:Copy> {
    fn filter_to_vec(&self, pred: &fn(&A) -> bool) -> ~[A];
    fn to_vec(&self) -> ~[A];
    fn find(&self, p: &fn(&A) -> bool) -> Option<A>;
}

pub trait CopyableOrderedIter<A:Copy + Ord> {
    fn min(&self) -> A;
    fn max(&self) -> A;
}

pub trait CopyableNonstrictIter<A:Copy> {
    // Like "each", but copies out the value. If the receiver is mutated while
    // iterating over it, the semantics must not be memory-unsafe but are
    // otherwise undefined.
    fn each_val(&const self, f: &fn(A) -> bool);
}

// A trait for sequences that can be built by imperatively pushing elements
// onto them.
pub trait Buildable<A> {
    /**
     * Builds a buildable sequence by calling a provided function with
     * an argument function that pushes an element onto the back of
     * the sequence.
     * This version takes an initial size for the sequence.
     *
     * # Arguments
     *
     * * size - A hint for an initial size of the sequence
     * * builder - A function that will construct the sequence. It receives
     *             as an argument a function that will push an element
     *             onto the sequence being constructed.
     */
     fn build_sized(size: uint, builder: &fn(push: &fn(A))) -> Self;
}

#[inline(always)]
pub fn eachi<A,IA:BaseIter<A>>(self: &IA, blk: &fn(uint, &A) -> bool) {
    let mut i = 0;
    for self.each |a| {
        if !blk(i, a) { break; }
        i += 1;
    }
}

#[inline(always)]
pub fn all<A,IA:BaseIter<A>>(self: &IA, blk: &fn(&A) -> bool) -> bool {
    for self.each |a| {
        if !blk(a) { return false; }
    }
    return true;
}

#[inline(always)]
pub fn any<A,IA:BaseIter<A>>(self: &IA, blk: &fn(&A) -> bool) -> bool {
    for self.each |a| {
        if blk(a) { return true; }
    }
    return false;
}

#[inline(always)]
pub fn filter_to_vec<A:Copy,IA:BaseIter<A>>(self: &IA,
                                            prd: &fn(&A) -> bool)
                                         -> ~[A] {
    do vec::build_sized_opt(self.size_hint()) |push| {
        for self.each |a| {
            if prd(a) { push(*a); }
        }
    }
}

#[inline(always)]
pub fn map_to_vec<A,B,IA:BaseIter<A>>(self: &IA, op: &fn(&A) -> B) -> ~[B] {
    do vec::build_sized_opt(self.size_hint()) |push| {
        for self.each |a| {
            push(op(a));
        }
    }
}

#[inline(always)]
pub fn flat_map_to_vec<A,B,IA:BaseIter<A>,IB:BaseIter<B>>(self: &IA,
                                                          op: &fn(&A) -> IB)
                                                       -> ~[B] {
    do vec::build |push| {
        for self.each |a| {
            for op(a).each |&b| {
                push(b);
            }
        }
    }
}

#[inline(always)]
pub fn foldl<A,B,IA:BaseIter<A>>(self: &IA, b0: B, blk: &fn(&B, &A) -> B)
                              -> B {
    let mut b = b0;
    for self.each |a| {
        b = blk(&b, a);
    }
    b
}

#[inline(always)]
pub fn to_vec<A:Copy,IA:BaseIter<A>>(self: &IA) -> ~[A] {
    foldl::<A,~[A],IA>(self, ~[], |r, a| vec::append(copy (*r), ~[*a]))
}

#[inline(always)]
pub fn contains<A:Eq,IA:BaseIter<A>>(self: &IA, x: &A) -> bool {
    for self.each |a| {
        if *a == *x { return true; }
    }
    return false;
}

#[inline(always)]
pub fn count<A:Eq,IA:BaseIter<A>>(self: &IA, x: &A) -> uint {
    do foldl(self, 0) |count, value| {
        if *value == *x {
            *count + 1
        } else {
            *count
        }
    }
}

#[inline(always)]
pub fn position<A,IA:BaseIter<A>>(self: &IA, f: &fn(&A) -> bool)
                               -> Option<uint> {
    let mut i = 0;
    for self.each |a| {
        if f(a) { return Some(i); }
        i += 1;
    }
    return None;
}

// note: 'rposition' would only make sense to provide with a bidirectional
// iter interface, such as would provide "reach" in addition to "each". As is,
// it would have to be implemented with foldr, which is too inefficient.

#[inline(always)]
pub fn repeat(times: uint, blk: &fn() -> bool) {
    let mut i = 0;
    while i < times {
        if !blk() { break }
        i += 1;
    }
}

#[inline(always)]
pub fn min<A:Copy + Ord,IA:BaseIter<A>>(self: &IA) -> A {
    match do foldl::<A,Option<A>,IA>(self, None) |a, b| {
        match a {
          &Some(ref a_) if *a_ < *b => {
             *(a)
          }
          _ => Some(*b)
        }
    } {
        Some(val) => val,
        None => fail!(~"min called on empty iterator")
    }
}

#[inline(always)]
pub fn max<A:Copy + Ord,IA:BaseIter<A>>(self: &IA) -> A {
    match do foldl::<A,Option<A>,IA>(self, None) |a, b| {
        match a {
          &Some(ref a_) if *a_ > *b => {
              *(a)
          }
          _ => Some(*b)
        }
    } {
        Some(val) => val,
        None => fail!(~"max called on empty iterator")
    }
}

#[inline(always)]
pub fn find<A:Copy,IA:BaseIter<A>>(self: &IA, f: &fn(&A) -> bool)
                                -> Option<A> {
    for self.each |i| {
        if f(i) { return Some(*i) }
    }
    return None;
}

// Some functions for just building

/**
 * Builds a sequence by calling a provided function with an argument
 * function that pushes an element to the back of a sequence.
 *
 * # Arguments
 *
 * * builder - A function that will construct the sequence. It receives
 *             as an argument a function that will push an element
 *             onto the sequence being constructed.
 */
#[inline(always)]
pub fn build<A,B: Buildable<A>>(builder: &fn(push: &fn(A))) -> B {
    Buildable::build_sized(4, builder)
}

/**
 * Builds a sequence by calling a provided function with an argument
 * function that pushes an element to the back of the sequence.
 * This version takes an initial size for the sequence.
 *
 * # Arguments
 *
 * * size - An option, maybe containing initial size of the sequence
 *          to reserve.
 * * builder - A function that will construct the sequence. It receives
 *             as an argument a function that will push an element
 *             onto the sequence being constructed.
 */
#[inline(always)]
pub fn build_sized_opt<A,B: Buildable<A>>(size: Option<uint>,
                                          builder: &fn(push: &fn(A))) -> B {
    Buildable::build_sized(size.get_or_default(4), builder)
}

// Functions that combine iteration and building

/// Applies a function to each element of an iterable and returns the results
/// in a sequence built via `BU`.  See also `map_to_vec`.
#[inline(always)]
pub fn map<T,IT: BaseIter<T>,U,BU: Buildable<U>>(v: &IT, f: &fn(&T) -> U)
    -> BU {
    do build_sized_opt(v.size_hint()) |push| {
        for v.each() |elem| {
            push(f(elem));
        }
    }
}

/**
 * Creates and initializes a generic sequence from a function.
 *
 * Creates a generic sequence of size `n_elts` and initializes the elements
 * to the value returned by the function `op`.
 */
#[inline(always)]
pub fn from_fn<T,BT: Buildable<T>>(n_elts: uint, op: InitOp<T>) -> BT {
    do Buildable::build_sized(n_elts) |push| {
        let mut i: uint = 0u;
        while i < n_elts { push(op(i)); i += 1u; }
    }
}

/**
 * Creates and initializes a generic sequence with some elements.
 *
 * Creates an immutable vector of size `n_elts` and initializes the elements
 * to the value `t`.
 */
#[inline(always)]
pub fn from_elem<T:Copy,BT:Buildable<T>>(n_elts: uint, t: T) -> BT {
    do Buildable::build_sized(n_elts) |push| {
        let mut i: uint = 0;
        while i < n_elts { push(t); i += 1; }
    }
}

/// Appends two generic sequences.
#[inline(always)]
pub fn append<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(lhs: &IT, rhs: &IT)
                                                  -> BT {
    let size_opt = lhs.size_hint().chain_ref(
        |sz1| rhs.size_hint().map(|sz2| *sz1+*sz2));
    do build_sized_opt(size_opt) |push| {
        for lhs.each |x| { push(*x); }
        for rhs.each |x| { push(*x); }
    }
}

/// Copies a generic sequence, possibly converting it to a different
/// type of sequence.
#[inline(always)]
pub fn copy_seq<T:Copy,IT:BaseIter<T>,BT:Buildable<T>>(v: &IT) -> BT {
    do build_sized_opt(v.size_hint()) |push| {
        for v.each |x| { push(*x); }
    }
}
<|endoftext|>