<commit_before>use std::c_str::CString;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hashmap::{Values, MutEntries};
use std::fmt::{Show, Formatter};
use std::fmt;
use std::hash;
use std::iter;
use std::mem;
use serialize::{Decodable, Decoder, Encodable, Encoder};

use url::Url;

use core::{Summary, Package, PackageId, Registry, Dependency};
use sources::{PathSource, GitSource, DummyRegistrySource};
use sources::git;
use util::{human, Config, CargoResult, CargoError, ToUrl};

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source: Registry {
    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&self, packages: &[PackageId]) -> CargoResult<()>;

    /// The get method returns the Path of each specified package on the
    /// local file system. It assumes that `download` was already called,
    /// and that the packages are already locally available on the file
    /// system.
    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
}

#[deriving(Encodable, Decodable, Show, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceKind {
    /// GitKind(<git reference>) represents a git repository
    GitKind(String),
    /// represents a local path
    PathKind,
    /// represents the central registry
    RegistryKind
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum Location {
    Local(Path),
    Remote(Url),
}

type Error = Box<CargoError + Send>;

impl<E, D: Decoder<E>> Decodable<D, E> for Location {
    fn decode(d: &mut D) -> Result<Location, E> {
        let url: String  = raw_try!(Decodable::decode(d));
        Ok(Location::parse(url.as_slice()).unwrap())
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Location {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        self.to_string().encode(e)
    }
}

#[deriving(Clone, Eq)]
pub struct SourceId {
    pub location: Location,
    pub kind: SourceKind,
    // e.g. the exact git revision of the specified branch for a Git Source
    pub precise: Option<String>
}

impl Show for Location {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Local(ref p) => write!(f, "file:{}", p.display()),
            Remote(ref u) => write!(f, "{}", u),
        }
    }
}

impl PartialOrd for SourceId {
    fn partial_cmp(&self, other: &SourceId) -> Option<Ordering> {
        self.to_string().partial_cmp(&other.to_string())
    }
}

impl Ord for SourceId {
    fn cmp(&self, other: &SourceId) -> Ordering {
        self.to_string().cmp(&other.to_string())
    }
}

impl Location {
    pub fn parse(s: &str) -> CargoResult<Location> {
        if s.starts_with("file:") {
            Ok(Local(Path::new(s.slice_from(5))))
        } else {
            s.to_url().map(Remote).map_err(|e| {
                human(format!("invalid url `{}`: `{}", s, e))
            })
        }
    }
}

impl<'a> ToCStr for &'a Location {
    fn to_c_str(&self) -> CString {
        match **self {
            Local(ref p) => p.to_c_str(),
            Remote(ref u) => u.to_string().to_c_str(),
        }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString { self.to_c_str() }
}

impl<E, S: Encoder<E>> Encodable<S, E> for SourceId {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        if self.is_path() {
            s.emit_option_none()
        } else {
           self.to_url().encode(s)
        }
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for SourceId {
    fn decode(d: &mut D) -> Result<SourceId, E> {
        let string: String = Decodable::decode(d).ok().expect("Invalid encoded SourceId");
        Ok(SourceId::from_url(string))
    }
}

impl Show for SourceId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            SourceId { kind: PathKind, ref location, .. } => {
                try!(write!(f, "{}", location))
            },
            SourceId { kind: GitKind(ref reference), ref location, ref precise, .. } => {
                try!(write!(f, "{}", location));
                if reference.as_slice() != "master" {
                    try!(write!(f, "?ref={}", reference));
                }

                if precise.is_some() {
                    try!(write!(f, "#{}", precise.get_ref()));
                }
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                try!(write!(f, "the package registry"));
            }
        }

        Ok(())
    }
}

// This custom implementation handles situations such as when two git sources
// point at *almost* the same URL, but not quite, even when they actually point
// to the same repository.
impl PartialEq for SourceId {
    fn eq(&self, other: &SourceId) -> bool {
        if self.kind != other.kind { return false }
        if self.location == other.location { return true }

        match (&self.kind, &other.kind, &self.location, &other.location) {
            (&GitKind(..), &GitKind(..),
             &Remote(ref u1), &Remote(ref u2)) => {
                git::canonicalize_url(u1.to_string().as_slice()) ==
                    git::canonicalize_url(u2.to_string().as_slice())
            }
            _ => false,
        }
    }
}

impl<S: hash::Writer> hash::Hash<S> for SourceId {
    fn hash(&self, into: &mut S) {
        match *self {
            SourceId {
                kind: ref kind @ GitKind(..),
                location: Remote(ref url),
                precise: _,
            } => {
                kind.hash(into);
                git::canonicalize_url(url.to_string().as_slice()).hash(into);
            }
            _ => {
                self.kind.hash(into);
                self.location.hash(into);
            }
        }
    }
}

impl SourceId {
    pub fn new(kind: SourceKind, location: Location) -> SourceId {
        SourceId { kind: kind, location: location, precise: None }
    }

    pub fn from_url(string: String) -> SourceId {
        let mut parts = string.as_slice().splitn('+', 1);
        let kind = parts.next().unwrap();
        let url = parts.next().unwrap();

        match kind {
            "git" if url.starts_with("file:") => {
                let url = url.slice_from(5);
                let (url, precise) = match url.rfind('#') {
                    Some(pos) => {
                        (url.slice_to(pos), Some(url.slice_from(pos + 1)))
                    }
                    None => (url, None)
                };
                let (url, reference) = match url.find_str("?ref=") {
                    Some(pos) => {
                        (url.slice_to(pos), Some(url.slice_from(pos + 5)))
                    }
                    None => (url, None)
                };
                let reference = reference.unwrap_or("master");
                let id = SourceId::new(GitKind(reference.to_string()),
                                       Local(Path::new(url)));
                match precise {
                    Some(p) => id.with_precise(p.to_string()),
                    None => id,
                }
            }
            "git" => {
                let mut url = url.to_url().unwrap();
                let mut reference = "master".to_string();
                let pairs = url.query_pairs().unwrap_or(Vec::new());
                url.set_query_from_pairs(pairs.iter().filter(|&&(ref k, ref v)| {
                    if k.as_slice() == "ref" {
                        reference = v.clone();
                        false
                    } else {
                        true
                    }
                }).map(|&(ref a, ref b)| (a.as_slice(), b.as_slice())));

                let precise = mem::replace(&mut url.fragment, None);
                SourceId::for_git(&url, reference.as_slice(), precise)
            },
            "registry" => SourceId::for_central(),
            "path" => SourceId::for_path(&Path::new(url.slice_from(5))),
            _ => fail!("Unsupported serialized SourceId")
        }
    }

    pub fn to_url(&self) -> String {
        match *self {
            SourceId { kind: PathKind, .. } => {
                fail!("Path sources are not included in the lockfile, \
                       so this is unimplemented")
            },
            SourceId {
                kind: GitKind(ref reference), ref location, ref precise, ..
            } => {
                let ref_str = if reference.as_slice() != "master" {
                    format!("?ref={}", reference)
                } else {
                    "".to_string()
                };

                let precise_str = if precise.is_some() {
                    format!("#{}", precise.get_ref())
                } else {
                    "".to_string()
                };

                format!("git+{}{}{}", location, ref_str, precise_str)
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                "registry+https://crates.io/".to_string()
            }
        }
    }

    // Pass absolute path
    pub fn for_path(path: &Path) -> SourceId {
        SourceId::new(PathKind, Local(path.clone()))
    }

    pub fn for_git(url: &Url, reference: &str, precise: Option<String>) -> SourceId {
        let mut id = SourceId::new(GitKind(reference.to_string()), Remote(url.clone()));
        if precise.is_some() {
            id = id.with_precise(precise.unwrap());
        }

        id
    }

    pub fn for_central() -> SourceId {
        SourceId::new(RegistryKind,
                      Remote("https://example.com".to_url().unwrap()))
    }

    pub fn get_location(&self) -> &Location {
        &self.location
    }

    pub fn is_path(&self) -> bool {
        self.kind == PathKind
    }

    pub fn is_git(&self) -> bool {
        match self.kind {
            GitKind(_) => true,
            _ => false
        }
    }

    pub fn load(&self, config: &mut Config) -> Box<Source> {
        log!(5, "loading SourceId; {}", self);
        match self.kind {
            GitKind(..) => box GitSource::new(self, config) as Box<Source>,
            PathKind => {
                let path = match self.location {
                    Local(ref p) => p,
                    Remote(..) => fail!("path sources cannot be remote"),
                };
                box PathSource::new(path, self) as Box<Source>
            },
            RegistryKind => box DummyRegistrySource::new(self) as Box<Source>,
        }
    }

    pub fn with_precise(&self, v: String) -> SourceId {
        SourceId {
            precise: Some(v),
            .. self.clone()
        }
    }
}

pub struct SourceMap {
    map: HashMap<SourceId, Box<Source>>
}

pub type Sources<'a> = Values<'a, SourceId, Box<Source>>;
pub type SourcesMut<'a> = iter::Map<'static, (&'a SourceId, &'a mut Box<Source>),
                                    &'a mut Source,
                                    MutEntries<'a, SourceId, Box<Source>>>;

impl SourceMap {
    pub fn new() -> SourceMap {
        SourceMap {
            map: HashMap::new()
        }
    }

    pub fn contains(&self, id: &SourceId) -> bool {
        self.map.contains_key(id)
    }

    pub fn get(&self, id: &SourceId) -> Option<&Source> {
        let source = self.map.find(id);

        source.map(|s| {
            let s: &Source = *s;
            s
        })
    }

    pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut Source> {
        self.map.find_mut(id).map(|s| {
            let s: &mut Source = *s;
            s
        })
    }

    pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&Source> {
        self.get(pkg_id.get_source_id())
    }

    pub fn insert(&mut self, id: &SourceId, source: Box<Source>) {
        self.map.insert(id.clone(), source);
    }

    pub fn len(&self) -> uint {
        self.map.len()
    }

    pub fn sources(&self) -> Sources {
        self.map.values()
    }

    pub fn sources_mut(&mut self) -> SourcesMut {
        self.map.mut_iter().map(|(_, v)| { let s: &mut Source = *v; s })
    }
}

pub struct SourceSet {
    sources: Vec<Box<Source>>
}

impl SourceSet {
    pub fn new(sources: Vec<Box<Source>>) -> SourceSet {
        SourceSet { sources: sources }
    }
}

impl Registry for SourceSet {
    fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();

        for source in self.sources.mut_iter() {
            ret.push_all_move(try!(source.query(name)));
        }

        Ok(ret)
    }
}

impl Source for SourceSet {
    fn update(&mut self) -> CargoResult<()> {
        for source in self.sources.mut_iter() {
            try!(source.update());
        }

        Ok(())
    }

    fn download(&self, packages: &[PackageId]) -> CargoResult<()> {
        for source in self.sources.iter() {
            try!(source.download(packages));
        }

        Ok(())
    }

    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.get(packages)).as_slice());
        }

        Ok(ret)
    }

    fn fingerprint(&self, id: &Package) -> CargoResult<String> {
        let mut ret = String::new();
        for source in self.sources.iter() {
            ret.push_str(try!(source.fingerprint(id)).as_slice());
        }
        return Ok(ret);
    }
}

#[cfg(test)]
mod tests {
    use super::{SourceId, Remote, GitKind};
    use util::ToUrl;

    #[test]
    fn github_sources_equal() {
        let loc = Remote("https://github.com/foo/bar".to_url().unwrap());
        let s1 = SourceId::new(GitKind("master".to_string()), loc);

        let loc = Remote("git://github.com/foo/bar".to_url().unwrap());
        let mut s2 = SourceId::new(GitKind("master".to_string()), loc);

        assert_eq!(s1, s2);

        s2.kind = GitKind("foo".to_string());
        assert!(s1 != s2);
    }
}
<commit_msg>Be sure to not emit a lone `?` in a URL<commit_after>use std::c_str::CString;
use std::cmp::Ordering;
use std::collections::HashMap;
use std::collections::hashmap::{Values, MutEntries};
use std::fmt::{Show, Formatter};
use std::fmt;
use std::hash;
use std::iter;
use std::mem;
use serialize::{Decodable, Decoder, Encodable, Encoder};

use url::Url;

use core::{Summary, Package, PackageId, Registry, Dependency};
use sources::{PathSource, GitSource, DummyRegistrySource};
use sources::git;
use util::{human, Config, CargoResult, CargoError, ToUrl};

/// A Source finds and downloads remote packages based on names and
/// versions.
pub trait Source: Registry {
    /// The update method performs any network operations required to
    /// get the entire list of all names, versions and dependencies of
    /// packages managed by the Source.
    fn update(&mut self) -> CargoResult<()>;

    /// The download method fetches the full package for each name and
    /// version specified.
    fn download(&self, packages: &[PackageId]) -> CargoResult<()>;

    /// The get method returns the Path of each specified package on the
    /// local file system. It assumes that `download` was already called,
    /// and that the packages are already locally available on the file
    /// system.
    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>>;

    /// Generates a unique string which represents the fingerprint of the
    /// current state of the source.
    ///
    /// This fingerprint is used to determine the "fresheness" of the source
    /// later on. It must be guaranteed that the fingerprint of a source is
    /// constant if and only if the output product will remain constant.
    ///
    /// The `pkg` argument is the package which this fingerprint should only be
    /// interested in for when this source may contain multiple packages.
    fn fingerprint(&self, pkg: &Package) -> CargoResult<String>;
}

#[deriving(Encodable, Decodable, Show, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum SourceKind {
    /// GitKind(<git reference>) represents a git repository
    GitKind(String),
    /// represents a local path
    PathKind,
    /// represents the central registry
    RegistryKind
}

#[deriving(Clone, PartialEq, Eq, Hash)]
pub enum Location {
    Local(Path),
    Remote(Url),
}

type Error = Box<CargoError + Send>;

impl<E, D: Decoder<E>> Decodable<D, E> for Location {
    fn decode(d: &mut D) -> Result<Location, E> {
        let url: String  = raw_try!(Decodable::decode(d));
        Ok(Location::parse(url.as_slice()).unwrap())
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Location {
    fn encode(&self, e: &mut S) -> Result<(), E> {
        self.to_string().encode(e)
    }
}

#[deriving(Clone, Eq)]
pub struct SourceId {
    pub location: Location,
    pub kind: SourceKind,
    // e.g. the exact git revision of the specified branch for a Git Source
    pub precise: Option<String>
}

impl Show for Location {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            Local(ref p) => write!(f, "file:{}", p.display()),
            Remote(ref u) => write!(f, "{}", u),
        }
    }
}

impl PartialOrd for SourceId {
    fn partial_cmp(&self, other: &SourceId) -> Option<Ordering> {
        self.to_string().partial_cmp(&other.to_string())
    }
}

impl Ord for SourceId {
    fn cmp(&self, other: &SourceId) -> Ordering {
        self.to_string().cmp(&other.to_string())
    }
}

impl Location {
    pub fn parse(s: &str) -> CargoResult<Location> {
        if s.starts_with("file:") {
            Ok(Local(Path::new(s.slice_from(5))))
        } else {
            s.to_url().map(Remote).map_err(|e| {
                human(format!("invalid url `{}`: `{}", s, e))
            })
        }
    }
}

impl<'a> ToCStr for &'a Location {
    fn to_c_str(&self) -> CString {
        match **self {
            Local(ref p) => p.to_c_str(),
            Remote(ref u) => u.to_string().to_c_str(),
        }
    }

    unsafe fn to_c_str_unchecked(&self) -> CString { self.to_c_str() }
}

impl<E, S: Encoder<E>> Encodable<S, E> for SourceId {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        if self.is_path() {
            s.emit_option_none()
        } else {
           self.to_url().encode(s)
        }
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for SourceId {
    fn decode(d: &mut D) -> Result<SourceId, E> {
        let string: String = Decodable::decode(d).ok().expect("Invalid encoded SourceId");
        Ok(SourceId::from_url(string))
    }
}

impl Show for SourceId {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        match *self {
            SourceId { kind: PathKind, ref location, .. } => {
                try!(write!(f, "{}", location))
            },
            SourceId { kind: GitKind(ref reference), ref location, ref precise, .. } => {
                try!(write!(f, "{}", location));
                if reference.as_slice() != "master" {
                    try!(write!(f, "?ref={}", reference));
                }

                if precise.is_some() {
                    try!(write!(f, "#{}", precise.get_ref()));
                }
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                try!(write!(f, "the package registry"));
            }
        }

        Ok(())
    }
}

// This custom implementation handles situations such as when two git sources
// point at *almost* the same URL, but not quite, even when they actually point
// to the same repository.
impl PartialEq for SourceId {
    fn eq(&self, other: &SourceId) -> bool {
        if self.kind != other.kind { return false }
        if self.location == other.location { return true }

        match (&self.kind, &other.kind, &self.location, &other.location) {
            (&GitKind(..), &GitKind(..),
             &Remote(ref u1), &Remote(ref u2)) => {
                git::canonicalize_url(u1.to_string().as_slice()) ==
                    git::canonicalize_url(u2.to_string().as_slice())
            }
            _ => false,
        }
    }
}

impl<S: hash::Writer> hash::Hash<S> for SourceId {
    fn hash(&self, into: &mut S) {
        match *self {
            SourceId {
                kind: ref kind @ GitKind(..),
                location: Remote(ref url),
                precise: _,
            } => {
                kind.hash(into);
                git::canonicalize_url(url.to_string().as_slice()).hash(into);
            }
            _ => {
                self.kind.hash(into);
                self.location.hash(into);
            }
        }
    }
}

impl SourceId {
    pub fn new(kind: SourceKind, location: Location) -> SourceId {
        SourceId { kind: kind, location: location, precise: None }
    }

    pub fn from_url(string: String) -> SourceId {
        let mut parts = string.as_slice().splitn('+', 1);
        let kind = parts.next().unwrap();
        let url = parts.next().unwrap();

        match kind {
            "git" if url.starts_with("file:") => {
                let url = url.slice_from(5);
                let (url, precise) = match url.rfind('#') {
                    Some(pos) => {
                        (url.slice_to(pos), Some(url.slice_from(pos + 1)))
                    }
                    None => (url, None)
                };
                let (url, reference) = match url.find_str("?ref=") {
                    Some(pos) => {
                        (url.slice_to(pos), Some(url.slice_from(pos + 5)))
                    }
                    None => (url, None)
                };
                let reference = reference.unwrap_or("master");
                let id = SourceId::new(GitKind(reference.to_string()),
                                       Local(Path::new(url)));
                match precise {
                    Some(p) => id.with_precise(p.to_string()),
                    None => id,
                }
            }
            "git" => {
                let mut url = url.to_url().unwrap();
                let mut reference = "master".to_string();
                let pairs = url.query_pairs().unwrap_or(Vec::new());
                for &(ref k, ref v) in pairs.iter() {
                    if k.as_slice() == "ref" {
                        reference = v.clone();
                    }
                }
                url.query = None;
                let precise = mem::replace(&mut url.fragment, None);
                SourceId::for_git(&url, reference.as_slice(), precise)
            },
            "registry" => SourceId::for_central(),
            "path" => SourceId::for_path(&Path::new(url.slice_from(5))),
            _ => fail!("Unsupported serialized SourceId")
        }
    }

    pub fn to_url(&self) -> String {
        match *self {
            SourceId { kind: PathKind, .. } => {
                fail!("Path sources are not included in the lockfile, \
                       so this is unimplemented")
            },
            SourceId {
                kind: GitKind(ref reference), ref location, ref precise, ..
            } => {
                let ref_str = if reference.as_slice() != "master" {
                    format!("?ref={}", reference)
                } else {
                    "".to_string()
                };

                let precise_str = if precise.is_some() {
                    format!("#{}", precise.get_ref())
                } else {
                    "".to_string()
                };

                format!("git+{}{}{}", location, ref_str, precise_str)
            },
            SourceId { kind: RegistryKind, .. } => {
                // TODO: Central registry vs. alternates
                "registry+https://crates.io/".to_string()
            }
        }
    }

    // Pass absolute path
    pub fn for_path(path: &Path) -> SourceId {
        SourceId::new(PathKind, Local(path.clone()))
    }

    pub fn for_git(url: &Url, reference: &str, precise: Option<String>) -> SourceId {
        let mut id = SourceId::new(GitKind(reference.to_string()), Remote(url.clone()));
        if precise.is_some() {
            id = id.with_precise(precise.unwrap());
        }

        id
    }

    pub fn for_central() -> SourceId {
        SourceId::new(RegistryKind,
                      Remote("https://example.com".to_url().unwrap()))
    }

    pub fn get_location(&self) -> &Location {
        &self.location
    }

    pub fn is_path(&self) -> bool {
        self.kind == PathKind
    }

    pub fn is_git(&self) -> bool {
        match self.kind {
            GitKind(_) => true,
            _ => false
        }
    }

    pub fn load(&self, config: &mut Config) -> Box<Source> {
        log!(5, "loading SourceId; {}", self);
        match self.kind {
            GitKind(..) => box GitSource::new(self, config) as Box<Source>,
            PathKind => {
                let path = match self.location {
                    Local(ref p) => p,
                    Remote(..) => fail!("path sources cannot be remote"),
                };
                box PathSource::new(path, self) as Box<Source>
            },
            RegistryKind => box DummyRegistrySource::new(self) as Box<Source>,
        }
    }

    pub fn with_precise(&self, v: String) -> SourceId {
        SourceId {
            precise: Some(v),
            .. self.clone()
        }
    }
}

pub struct SourceMap {
    map: HashMap<SourceId, Box<Source>>
}

pub type Sources<'a> = Values<'a, SourceId, Box<Source>>;
pub type SourcesMut<'a> = iter::Map<'static, (&'a SourceId, &'a mut Box<Source>),
                                    &'a mut Source,
                                    MutEntries<'a, SourceId, Box<Source>>>;

impl SourceMap {
    pub fn new() -> SourceMap {
        SourceMap {
            map: HashMap::new()
        }
    }

    pub fn contains(&self, id: &SourceId) -> bool {
        self.map.contains_key(id)
    }

    pub fn get(&self, id: &SourceId) -> Option<&Source> {
        let source = self.map.find(id);

        source.map(|s| {
            let s: &Source = *s;
            s
        })
    }

    pub fn get_mut(&mut self, id: &SourceId) -> Option<&mut Source> {
        self.map.find_mut(id).map(|s| {
            let s: &mut Source = *s;
            s
        })
    }

    pub fn get_by_package_id(&self, pkg_id: &PackageId) -> Option<&Source> {
        self.get(pkg_id.get_source_id())
    }

    pub fn insert(&mut self, id: &SourceId, source: Box<Source>) {
        self.map.insert(id.clone(), source);
    }

    pub fn len(&self) -> uint {
        self.map.len()
    }

    pub fn sources(&self) -> Sources {
        self.map.values()
    }

    pub fn sources_mut(&mut self) -> SourcesMut {
        self.map.mut_iter().map(|(_, v)| { let s: &mut Source = *v; s })
    }
}

pub struct SourceSet {
    sources: Vec<Box<Source>>
}

impl SourceSet {
    pub fn new(sources: Vec<Box<Source>>) -> SourceSet {
        SourceSet { sources: sources }
    }
}

impl Registry for SourceSet {
    fn query(&mut self, name: &Dependency) -> CargoResult<Vec<Summary>> {
        let mut ret = Vec::new();

        for source in self.sources.mut_iter() {
            ret.push_all_move(try!(source.query(name)));
        }

        Ok(ret)
    }
}

impl Source for SourceSet {
    fn update(&mut self) -> CargoResult<()> {
        for source in self.sources.mut_iter() {
            try!(source.update());
        }

        Ok(())
    }

    fn download(&self, packages: &[PackageId]) -> CargoResult<()> {
        for source in self.sources.iter() {
            try!(source.download(packages));
        }

        Ok(())
    }

    fn get(&self, packages: &[PackageId]) -> CargoResult<Vec<Package>> {
        let mut ret = Vec::new();

        for source in self.sources.iter() {
            ret.push_all(try!(source.get(packages)).as_slice());
        }

        Ok(ret)
    }

    fn fingerprint(&self, id: &Package) -> CargoResult<String> {
        let mut ret = String::new();
        for source in self.sources.iter() {
            ret.push_str(try!(source.fingerprint(id)).as_slice());
        }
        return Ok(ret);
    }
}

#[cfg(test)]
mod tests {
    use super::{SourceId, Remote, GitKind};
    use util::ToUrl;

    #[test]
    fn github_sources_equal() {
        let loc = Remote("https://github.com/foo/bar".to_url().unwrap());
        let s1 = SourceId::new(GitKind("master".to_string()), loc);

        let loc = Remote("git://github.com/foo/bar".to_url().unwrap());
        let mut s2 = SourceId::new(GitKind("master".to_string()), loc);

        assert_eq!(s1, s2);

        s2.kind = GitKind("foo".to_string());
        assert!(s1 != s2);
    }
}
<|endoftext|>


<commit_before>use bson::Document;
use client::coll::options::WriteModel;
use client::coll::results::InsertManyResult;

pub enum Batch {
    Insert {
        documents: Vec<Document>,
    }
}
<commit_msg>added structs to use for bulk operations<commit_after>use bson::Document;
use client::coll::options::WriteModel;
use client::coll::results::InsertManyResult;

struct UpdateModel {
    pub filter: Document,
    pub update: Document,
    pub upsert: bool,
    pub multi: bool,
}

struct DeleteModel {
    pub filter: Document,
    pub multi: bool,
}

pub enum Batch {
    Insert {
        documents: Vec<Document>,
    }
}
<|endoftext|>


<commit_before>//! Implements a command for managing releases.
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::collections::HashSet;

use clap::{App, AppSettings, Arg, ArgMatches};
use walkdir::WalkDir;

use prelude::*;
use api::{Api, NewRelease, FileContents};
use config::Config;
use sourcemaputils::SourceMapProcessor;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("manage releases on Sentry")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .arg(Arg::with_name("org")
            .value_name("ORG")
            .long("org")
            .short("o")
            .help("The organization slug"))
        .arg(Arg::with_name("project")
            .value_name("PROJECT")
            .long("project")
            .short("p")
            .help("The project slug"))
        .subcommand(App::new("new")
            .about("Create a new release")
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The version identifier for this release"))
            .arg(Arg::with_name("ref")
                .long("ref")
                .value_name("REF")
                .help("Optional commit reference (commit hash)"))
            .arg(Arg::with_name("url")
                .long("url")
                .value_name("URL")
                .help("Optional URL to the release for information purposes")))
        .subcommand(App::new("delete")
            .about("Delete a release")
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The version to delete")))
        .subcommand(App::new("list").about("list the most recent releases"))
        .subcommand(App::new("files")
            .about("manage release artifact files")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The release to manage the files of"))
            .subcommand(App::new("list").about("List all release files"))
            .subcommand(App::new("delete")
                .about("Delete a release file")
                .arg(Arg::with_name("all")
                    .short("A")
                    .long("all")
                    .help("deletes all files"))
                .arg(Arg::with_name("names")
                    .value_name("NAMES")
                    .index(1)
                    .multiple(true)
                    .help("a list of filenames to delete.")))
            .subcommand(App::new("upload")
                .about("Uploads a file for a given release")
                .arg(Arg::with_name("headers")
                    .long("header")
                    .short("H")
                    .value_name("KEY VALUE")
                    .multiple(true)
                    .number_of_values(1)
                    .help("Stores a header with this file"))
                .arg(Arg::with_name("path")
                    .value_name("PATH")
                    .index(1)
                    .required(true)
                    .help("The file to upload"))
                .arg(Arg::with_name("name")
                    .index(2)
                    .value_name("NAME")
                    .help("The name of the file on the server.")))
            .subcommand(App::new("upload-sourcemaps")
                .about("Uploads sourcemap information for a given release")
                .arg(Arg::with_name("paths")
                    .value_name("PATHS")
                    .index(1)
                    .required(true)
                    .multiple(true)
                    .help("The files to upload"))
                .arg(Arg::with_name("url_prefix")
                    .short("u")
                    .long("url-prefix")
                    .value_name("PREFIX")
                    .help("The URL prefix to prepend to all filenames"))
                .arg(Arg::with_name("validate")
                    .long("validate")
                    .help("Enable basic sourcemap validation"))
                .arg(Arg::with_name("no_sourcemap_reference")
                    .long("no-sourcemap-reference")
                    .help("Disables the emitting of automatic sourcemap references. \
                            By default the tool will store a 'Sourcemap' header with \
                            minified files so that sourcemaps are located automatically \
                            if the tool can detect a link. If this causes issues it can \
                            be disabled."))
                .arg(Arg::with_name("rewrite")
                    .long("rewrite")
                    .help("Enables rewriting of matching sourcemaps \
                            so that indexed maps are flattened and missing \
                            sources are inlined if possible.  This fundamentally \
                            changes the upload process to be based on sourcemaps \
                            and minified files exclusively and comes in handy for \
                            setups like react-native that generate sourcemaps that \
                            would otherwise not work for sentry."))
                .arg(Arg::with_name("strip_prefix")
                    .long("strip-prefix")
                    .value_name("PREFIX")
                    .multiple(true)
                    .help("When passed all sources that start with the given prefix \
                            will have that prefix stripped from the filename.  This \
                            requires --rewrite to be enabled."))
                .arg(Arg::with_name("strip_common_prefix")
                    .long("strip-common-prefix")
                    .help("Similar to --strip-prefix but strips the most common \
                            prefix on all sources."))
                .arg(Arg::with_name("verbose")
                    .long("verbose")
                    .short("verbose")
                    .help("Enable verbose mode"))
                .arg(Arg::with_name("extensions")
                    .long("ext")
                    .short("x")
                    .value_name("EXT")
                    .multiple(true)
                    .help("Add a file extension to the list of files to upload."))))
}

fn validate_version(version: &str) -> Result<&str> {
    if version.len() == 0 || version == "." || version == ".." ||
       version.find(&['\n', '\t', '\x0b', '\x0c', '\t', '/'][..]).is_some() {
        return Err(format!("Invalid release version '{}'", version).into());
    }
    Ok(version)
}

#[cfg(windows)]
fn path_as_url(path: &Path) -> String {
    path.display().to_string().replace("\\", "/")
}

#[cfg(not(windows))]
fn path_as_url(path: &Path) -> String {
    path.display().to_string()
}

fn execute_new<'a>(matches: &ArgMatches<'a>,
                   config: &Config,
                   org: &str,
                   project: &str)
                   -> Result<()> {
    let info_rv = Api::new(config).new_release(org,
        project,
        &NewRelease {
            version: validate_version(matches.value_of("version").unwrap())?.to_owned(),
            reference: matches.value_of("ref").map(|x| x.to_owned()),
            url: matches.value_of("url").map(|x| x.to_owned()),
        })?;
    println!("Created release {}.", info_rv.version);
    Ok(())
}

fn execute_delete<'a>(matches: &ArgMatches<'a>,
                      config: &Config,
                      org: &str,
                      project: &str)
                      -> Result<()> {
    let version = validate_version(matches.value_of("version").unwrap())?;
    if Api::new(config).delete_release(org, project, version)? {
        println!("Deleted release {}!", version);
    } else {
        println!("Did nothing. Release with this version ({}) does not exist.",
                 version);
    }
    Ok(())
}

fn execute_list<'a>(_matches: &ArgMatches<'a>,
                    config: &Config,
                    org: &str,
                    project: &str)
                    -> Result<()> {
    for info in Api::new(config).list_releases(org, project)? {
        println!("[{}] {}: {} ({} new groups)",
                 info.date_released.unwrap_or("              unreleased".into()),
                 info.version,
                 info.reference.unwrap_or("-".into()),
                 info.new_groups);
    }
    Ok(())
}

fn execute_files_list<'a>(_matches: &ArgMatches<'a>,
                          config: &Config,
                          org: &str,
                          project: &str,
                          release: &str)
                          -> Result<()> {
    for artifact in Api::new(config).list_release_files(org, project, release)? {
        print!("{}  ({} bytes)", artifact.name, artifact.size);
        if let Some(sm_ref) = artifact.get_sourcemap_reference() {
            print!("  -> sourcemap: {}", sm_ref);
        }
        println!("");
    }
    Ok(())
}

fn execute_files_delete<'a>(matches: &ArgMatches<'a>,
                            config: &Config,
                            org: &str,
                            project: &str,
                            release: &str)
                            -> Result<()> {
    let files: HashSet<String> = match matches.values_of("names") {
        Some(paths) => paths.map(|x| x.into()).collect(),
        None => HashSet::new(),
    };
    let api = Api::new(config);
    for file in api.list_release_files(org, project, release)? {
        if !(matches.is_present("all") || files.contains(&file.name)) {
            continue;
        }
        if api.delete_release_file(org, project, release, &file.id)? {
            println!("D {}", file.name);
        }
    }
    Ok(())
}

fn execute_files_upload<'a>(matches: &ArgMatches<'a>,
                            config: &Config,
                            org: &str,
                            project: &str,
                            version: &str)
                            -> Result<()> {
    let version = validate_version(version)?;
    let path = Path::new(matches.value_of("path").unwrap());
    let name = match matches.value_of("name") {
        Some(name) => name,
        None => {
            Path::new(path).file_name()
                .and_then(|x| x.to_str())
                .ok_or("No filename provided.")?
        }
    };
    let mut headers = vec![];
    if let Some(header_list) = matches.values_of("header") {
        for header in header_list {
            if !header.contains(':') {
                fail!("Invalid header. Needs to be in key:value format");
            }
            let mut iter = header.splitn(2, ':');
            let key = iter.next().unwrap();
            let value = iter.next().unwrap();
            headers.push((key.trim().to_string(), value.trim().to_string()));
        }
    };
    if let Some(artifact) = Api::new(config).upload_release_file(org,
                             project,
                             &version,
                             FileContents::FromPath(&path),
                             &name,
                             Some(&headers[..]))? {
        println!("A {}  ({} bytes)", artifact.sha1, artifact.size);
    } else {
        fail!("File already present!");
    }
    Ok(())
}

fn execute_files_upload_sourcemaps<'a>(matches: &ArgMatches<'a>,
                                       config: &Config,
                                       org: &str,
                                       project: &str,
                                       version: &str)
                                       -> Result<()> {
    let version = validate_version(version)?;
    let api = Api::new(config);

    let url_prefix = matches.value_of("url_prefix").unwrap_or("~").trim_right_matches("/");
    let paths = matches.values_of("paths").unwrap();
    let extensions = match matches.values_of("extensions") {
        Some(matches) => matches.map(|ext| OsStr::new(ext.trim_left_matches("."))).collect(),
        None => {
            vec![OsStr::new("js"), OsStr::new("map"), OsStr::new("jsbundle"), OsStr::new("bundle")]
        }
    };

    let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));

    for path in paths {
        // if we start walking over something that is an actual file then
        // the directory iterator yields that path and terminates.  We
        // handle that case here specifically to figure out what the path is
        // we should strip off.
        let walk_path = PathBuf::from(&path);
        let (base_path, skip_ext_test) = if walk_path.is_file() {
            (walk_path.parent().unwrap(), true)
        } else {
            (walk_path.as_path(), false)
        };

        for dent in WalkDir::new(&walk_path) {
            let dent = dent?;
            if !skip_ext_test {
                let extension = dent.path().extension();
                if !extensions.iter().any(|ext| Some(*ext) == extension) {
                    continue;
                }
            }
            debug!("found: {} ({} bytes)",
                   dent.path().display(),
                   dent.metadata().unwrap().len());
            let local_path = dent.path().strip_prefix(&base_path).unwrap();
            let url = format!("{}/{}", url_prefix, path_as_url(local_path));
            processor.add(&url, dent.path())?;
        }
    }

    if matches.is_present("validate") {
        println!("Running with sourcemap validation");
        processor.validate_all()?;
    }

    if matches.is_present("rewrite") {
        let mut prefixes: Vec<&str> = match matches.values_of("strip_prefix") {
            Some(paths) => paths.collect(),
            None => vec![],
        };
        if matches.is_present("strip_common_prefix") {
            prefixes.push("~");
        }
        processor.rewrite(&prefixes)?;
    }

    if !matches.is_present("no_sourcemap_reference") {
        processor.add_sourcemap_references()?;
    }

    // make sure the release exists
    let release = api.new_release(&org, &project, &NewRelease {
        version: version.into(),
        ..Default::default()
    })?;
    println!("Uploading sourcemaps for release {}", release.version);
    processor.upload(&api, &org, &project, &release.version)?;

    Ok(())
}

fn execute_files<'a>(matches: &ArgMatches<'a>,
                     config: &Config,
                     org: &str,
                     project: &str)
                     -> Result<()> {
    let release = validate_version(matches.value_of("version").unwrap())?;
    if let Some(sub_matches) = matches.subcommand_matches("list") {
        return execute_files_list(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("delete") {
        return execute_files_delete(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("upload") {
        return execute_files_upload(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("upload-sourcemaps") {
        return execute_files_upload_sourcemaps(sub_matches, config, org, project, release);
    }
    unreachable!();
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    if let Some(sub_matches) = matches.subcommand_matches("new") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_new(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("delete") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_delete(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("list") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_list(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("files") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_files(sub_matches, config, &org, &project);
    }
    unreachable!();
}
<commit_msg>Improved error message for releases<commit_after>//! Implements a command for managing releases.
use std::path::{Path, PathBuf};
use std::ffi::OsStr;
use std::collections::HashSet;

use clap::{App, AppSettings, Arg, ArgMatches};
use walkdir::WalkDir;

use prelude::*;
use api::{Api, NewRelease, FileContents};
use config::Config;
use sourcemaputils::SourceMapProcessor;

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("manage releases on Sentry")
        .setting(AppSettings::SubcommandRequiredElseHelp)
        .arg(Arg::with_name("org")
            .value_name("ORG")
            .long("org")
            .short("o")
            .help("The organization slug"))
        .arg(Arg::with_name("project")
            .value_name("PROJECT")
            .long("project")
            .short("p")
            .help("The project slug"))
        .subcommand(App::new("new")
            .about("Create a new release")
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The version identifier for this release"))
            .arg(Arg::with_name("ref")
                .long("ref")
                .value_name("REF")
                .help("Optional commit reference (commit hash)"))
            .arg(Arg::with_name("url")
                .long("url")
                .value_name("URL")
                .help("Optional URL to the release for information purposes")))
        .subcommand(App::new("delete")
            .about("Delete a release")
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The version to delete")))
        .subcommand(App::new("list").about("list the most recent releases"))
        .subcommand(App::new("files")
            .about("manage release artifact files")
            .setting(AppSettings::SubcommandRequiredElseHelp)
            .arg(Arg::with_name("version")
                .value_name("VERSION")
                .required(true)
                .index(1)
                .help("The release to manage the files of"))
            .subcommand(App::new("list").about("List all release files"))
            .subcommand(App::new("delete")
                .about("Delete a release file")
                .arg(Arg::with_name("all")
                    .short("A")
                    .long("all")
                    .help("deletes all files"))
                .arg(Arg::with_name("names")
                    .value_name("NAMES")
                    .index(1)
                    .multiple(true)
                    .help("a list of filenames to delete.")))
            .subcommand(App::new("upload")
                .about("Uploads a file for a given release")
                .arg(Arg::with_name("headers")
                    .long("header")
                    .short("H")
                    .value_name("KEY VALUE")
                    .multiple(true)
                    .number_of_values(1)
                    .help("Stores a header with this file"))
                .arg(Arg::with_name("path")
                    .value_name("PATH")
                    .index(1)
                    .required(true)
                    .help("The file to upload"))
                .arg(Arg::with_name("name")
                    .index(2)
                    .value_name("NAME")
                    .help("The name of the file on the server.")))
            .subcommand(App::new("upload-sourcemaps")
                .about("Uploads sourcemap information for a given release")
                .arg(Arg::with_name("paths")
                    .value_name("PATHS")
                    .index(1)
                    .required(true)
                    .multiple(true)
                    .help("The files to upload"))
                .arg(Arg::with_name("url_prefix")
                    .short("u")
                    .long("url-prefix")
                    .value_name("PREFIX")
                    .help("The URL prefix to prepend to all filenames"))
                .arg(Arg::with_name("validate")
                    .long("validate")
                    .help("Enable basic sourcemap validation"))
                .arg(Arg::with_name("no_sourcemap_reference")
                    .long("no-sourcemap-reference")
                    .help("Disables the emitting of automatic sourcemap references. \
                            By default the tool will store a 'Sourcemap' header with \
                            minified files so that sourcemaps are located automatically \
                            if the tool can detect a link. If this causes issues it can \
                            be disabled."))
                .arg(Arg::with_name("rewrite")
                    .long("rewrite")
                    .help("Enables rewriting of matching sourcemaps \
                            so that indexed maps are flattened and missing \
                            sources are inlined if possible.  This fundamentally \
                            changes the upload process to be based on sourcemaps \
                            and minified files exclusively and comes in handy for \
                            setups like react-native that generate sourcemaps that \
                            would otherwise not work for sentry."))
                .arg(Arg::with_name("strip_prefix")
                    .long("strip-prefix")
                    .value_name("PREFIX")
                    .multiple(true)
                    .help("When passed all sources that start with the given prefix \
                            will have that prefix stripped from the filename.  This \
                            requires --rewrite to be enabled."))
                .arg(Arg::with_name("strip_common_prefix")
                    .long("strip-common-prefix")
                    .help("Similar to --strip-prefix but strips the most common \
                            prefix on all sources."))
                .arg(Arg::with_name("verbose")
                    .long("verbose")
                    .short("verbose")
                    .help("Enable verbose mode"))
                .arg(Arg::with_name("extensions")
                    .long("ext")
                    .short("x")
                    .value_name("EXT")
                    .multiple(true)
                    .help("Add a file extension to the list of files to upload."))))
}

fn validate_version(version: &str) -> Result<&str> {
    if version.len() == 0 || version == "." || version == ".." ||
       version.find(&['\n', '\t', '\x0b', '\x0c', '\t', '/'][..]).is_some() {
        return Err(format!("Invalid release version '{}'. Slashes and certain \
                            whitespace characters are not permitted.", version).into());
    }
    Ok(version)
}

#[cfg(windows)]
fn path_as_url(path: &Path) -> String {
    path.display().to_string().replace("\\", "/")
}

#[cfg(not(windows))]
fn path_as_url(path: &Path) -> String {
    path.display().to_string()
}

fn execute_new<'a>(matches: &ArgMatches<'a>,
                   config: &Config,
                   org: &str,
                   project: &str)
                   -> Result<()> {
    let info_rv = Api::new(config).new_release(org,
        project,
        &NewRelease {
            version: validate_version(matches.value_of("version").unwrap())?.to_owned(),
            reference: matches.value_of("ref").map(|x| x.to_owned()),
            url: matches.value_of("url").map(|x| x.to_owned()),
        })?;
    println!("Created release {}.", info_rv.version);
    Ok(())
}

fn execute_delete<'a>(matches: &ArgMatches<'a>,
                      config: &Config,
                      org: &str,
                      project: &str)
                      -> Result<()> {
    let version = validate_version(matches.value_of("version").unwrap())?;
    if Api::new(config).delete_release(org, project, version)? {
        println!("Deleted release {}!", version);
    } else {
        println!("Did nothing. Release with this version ({}) does not exist.",
                 version);
    }
    Ok(())
}

fn execute_list<'a>(_matches: &ArgMatches<'a>,
                    config: &Config,
                    org: &str,
                    project: &str)
                    -> Result<()> {
    for info in Api::new(config).list_releases(org, project)? {
        println!("[{}] {}: {} ({} new groups)",
                 info.date_released.unwrap_or("              unreleased".into()),
                 info.version,
                 info.reference.unwrap_or("-".into()),
                 info.new_groups);
    }
    Ok(())
}

fn execute_files_list<'a>(_matches: &ArgMatches<'a>,
                          config: &Config,
                          org: &str,
                          project: &str,
                          release: &str)
                          -> Result<()> {
    for artifact in Api::new(config).list_release_files(org, project, release)? {
        print!("{}  ({} bytes)", artifact.name, artifact.size);
        if let Some(sm_ref) = artifact.get_sourcemap_reference() {
            print!("  -> sourcemap: {}", sm_ref);
        }
        println!("");
    }
    Ok(())
}

fn execute_files_delete<'a>(matches: &ArgMatches<'a>,
                            config: &Config,
                            org: &str,
                            project: &str,
                            release: &str)
                            -> Result<()> {
    let files: HashSet<String> = match matches.values_of("names") {
        Some(paths) => paths.map(|x| x.into()).collect(),
        None => HashSet::new(),
    };
    let api = Api::new(config);
    for file in api.list_release_files(org, project, release)? {
        if !(matches.is_present("all") || files.contains(&file.name)) {
            continue;
        }
        if api.delete_release_file(org, project, release, &file.id)? {
            println!("D {}", file.name);
        }
    }
    Ok(())
}

fn execute_files_upload<'a>(matches: &ArgMatches<'a>,
                            config: &Config,
                            org: &str,
                            project: &str,
                            version: &str)
                            -> Result<()> {
    let version = validate_version(version)?;
    let path = Path::new(matches.value_of("path").unwrap());
    let name = match matches.value_of("name") {
        Some(name) => name,
        None => {
            Path::new(path).file_name()
                .and_then(|x| x.to_str())
                .ok_or("No filename provided.")?
        }
    };
    let mut headers = vec![];
    if let Some(header_list) = matches.values_of("header") {
        for header in header_list {
            if !header.contains(':') {
                fail!("Invalid header. Needs to be in key:value format");
            }
            let mut iter = header.splitn(2, ':');
            let key = iter.next().unwrap();
            let value = iter.next().unwrap();
            headers.push((key.trim().to_string(), value.trim().to_string()));
        }
    };
    if let Some(artifact) = Api::new(config).upload_release_file(org,
                             project,
                             &version,
                             FileContents::FromPath(&path),
                             &name,
                             Some(&headers[..]))? {
        println!("A {}  ({} bytes)", artifact.sha1, artifact.size);
    } else {
        fail!("File already present!");
    }
    Ok(())
}

fn execute_files_upload_sourcemaps<'a>(matches: &ArgMatches<'a>,
                                       config: &Config,
                                       org: &str,
                                       project: &str,
                                       version: &str)
                                       -> Result<()> {
    let version = validate_version(version)?;
    let api = Api::new(config);

    let url_prefix = matches.value_of("url_prefix").unwrap_or("~").trim_right_matches("/");
    let paths = matches.values_of("paths").unwrap();
    let extensions = match matches.values_of("extensions") {
        Some(matches) => matches.map(|ext| OsStr::new(ext.trim_left_matches("."))).collect(),
        None => {
            vec![OsStr::new("js"), OsStr::new("map"), OsStr::new("jsbundle"), OsStr::new("bundle")]
        }
    };

    let mut processor = SourceMapProcessor::new(matches.is_present("verbose"));

    for path in paths {
        // if we start walking over something that is an actual file then
        // the directory iterator yields that path and terminates.  We
        // handle that case here specifically to figure out what the path is
        // we should strip off.
        let walk_path = PathBuf::from(&path);
        let (base_path, skip_ext_test) = if walk_path.is_file() {
            (walk_path.parent().unwrap(), true)
        } else {
            (walk_path.as_path(), false)
        };

        for dent in WalkDir::new(&walk_path) {
            let dent = dent?;
            if !skip_ext_test {
                let extension = dent.path().extension();
                if !extensions.iter().any(|ext| Some(*ext) == extension) {
                    continue;
                }
            }
            debug!("found: {} ({} bytes)",
                   dent.path().display(),
                   dent.metadata().unwrap().len());
            let local_path = dent.path().strip_prefix(&base_path).unwrap();
            let url = format!("{}/{}", url_prefix, path_as_url(local_path));
            processor.add(&url, dent.path())?;
        }
    }

    if matches.is_present("validate") {
        println!("Running with sourcemap validation");
        processor.validate_all()?;
    }

    if matches.is_present("rewrite") {
        let mut prefixes: Vec<&str> = match matches.values_of("strip_prefix") {
            Some(paths) => paths.collect(),
            None => vec![],
        };
        if matches.is_present("strip_common_prefix") {
            prefixes.push("~");
        }
        processor.rewrite(&prefixes)?;
    }

    if !matches.is_present("no_sourcemap_reference") {
        processor.add_sourcemap_references()?;
    }

    // make sure the release exists
    let release = api.new_release(&org, &project, &NewRelease {
        version: version.into(),
        ..Default::default()
    })?;
    println!("Uploading sourcemaps for release {}", release.version);
    processor.upload(&api, &org, &project, &release.version)?;

    Ok(())
}

fn execute_files<'a>(matches: &ArgMatches<'a>,
                     config: &Config,
                     org: &str,
                     project: &str)
                     -> Result<()> {
    let release = validate_version(matches.value_of("version").unwrap())?;
    if let Some(sub_matches) = matches.subcommand_matches("list") {
        return execute_files_list(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("delete") {
        return execute_files_delete(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("upload") {
        return execute_files_upload(sub_matches, config, org, project, release);
    }
    if let Some(sub_matches) = matches.subcommand_matches("upload-sourcemaps") {
        return execute_files_upload_sourcemaps(sub_matches, config, org, project, release);
    }
    unreachable!();
}

pub fn execute<'a>(matches: &ArgMatches<'a>, config: &Config) -> Result<()> {
    if let Some(sub_matches) = matches.subcommand_matches("new") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_new(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("delete") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_delete(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("list") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_list(sub_matches, config, &org, &project);
    }
    if let Some(sub_matches) = matches.subcommand_matches("files") {
        let (org, project) = config.get_org_and_project(matches)?;
        return execute_files(sub_matches, config, &org, &project);
    }
    unreachable!();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_id: usize) {
    let last_block_number = format!("0x{:x}", last_block_id);
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), last_block_id) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_id = number - 1;
    let last_number = format!("0x{:x}", last_id);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, last_id);

        println!("expected: {}, actual: 0x{:x}", receipt.gasUsed, vm.real_used_gas());
        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<commit_msg>Typo: remove debugging println<commit_after>#[macro_use]
extern crate clap;
extern crate sputnikvm;
extern crate bigint;
extern crate serde_json;
extern crate gethrpc;

use serde_json::{Value};
use std::process;
use std::fs::File;
use std::path::Path;
use std::io::{BufReader};
use std::str::FromStr;
use std::collections::{HashMap, HashSet};

use sputnikvm::{Gas, Address};
use bigint::{U256, M256, read_hex};
use sputnikvm::vm::{BlockHeader, Context, SeqTransactionVM, Transaction, VM, Log, Patch, AccountCommitment, Account, FRONTIER_PATCH};
use sputnikvm::vm::errors::RequireError;
use gethrpc::{regression, GethRPCClient, RPCCall, RPCBlock, RPCTransaction, RPCLog};

fn from_rpc_block(block: &RPCBlock) -> BlockHeader {
    BlockHeader {
        coinbase: Address::from_str(&block.miner).unwrap(),
        timestamp: M256::from_str(&block.timestamp).unwrap(),
        number: M256::from_str(&block.number).unwrap(),
        difficulty: M256::from_str(&block.difficulty).unwrap(),
        gas_limit: Gas::from_str(&block.gasLimit).unwrap(),
    }
}

fn from_rpc_transaction(transaction: &RPCTransaction) -> Transaction {
    if transaction.to.is_empty() {
        Transaction::ContractCreation {
            caller: Address::from_str(&transaction.from).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            init: read_hex(&transaction.input).unwrap(),
        }
    } else {
        Transaction::MessageCall {
            caller: Address::from_str(&transaction.from).unwrap(),
            address: Address::from_str(&transaction.to).unwrap(),
            value: U256::from_str(&transaction.value).unwrap(),
            gas_limit: Gas::from_str(&transaction.gas).unwrap(),
            gas_price: Gas::from_str(&transaction.gasPrice).unwrap(),
            data: read_hex(&transaction.input).unwrap(),
        }
    }
}

fn from_rpc_log(log: &RPCLog) -> Log {
    let mut topics: Vec<M256> = Vec::new();
    for topic in &log.topics {
        topics.push(M256::from_str(&topic).unwrap());
    }
    Log {
        address: Address::from_str(&log.address).unwrap(),
        data: read_hex(&log.data).unwrap(),
        topics: topics,
    }
}

fn handle_fire(client: &mut GethRPCClient, vm: &mut SeqTransactionVM, last_block_id: usize) {
    let last_block_number = format!("0x{:x}", last_block_id);
    loop {
        match vm.fire() {
            Ok(()) => {
                println!("VM exited with {:?}.", vm.status());
                break;
            },
            Err(RequireError::Account(address)) => {
                println!("Feeding VM account at 0x{:x} ...", address);
                let nonce = M256::from_str(&client.get_transaction_count(&format!("0x{:x}", address),
                                                                         &last_block_number)).unwrap();
                let balance = U256::from_str(&client.get_balance(&format!("0x{:x}", address),
                                                                 &last_block_number)).unwrap();
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                if !client.account_exist(&format!("0x{:x}", address), last_block_id) {
                    vm.commit_account(AccountCommitment::Nonexist(address)).unwrap();
                } else {
                    vm.commit_account(AccountCommitment::Full {
                        nonce: nonce,
                        address: address,
                        balance: balance,
                        code: code,
                    }).unwrap();
                }
            },
            Err(RequireError::AccountStorage(address, index)) => {
                println!("Feeding VM account storage at 0x{:x} with index 0x{:x} ...", address, index);
                let value = M256::from_str(&client.get_storage_at(&format!("0x{:x}", address),
                                                                  &format!("0x{:x}", index),
                                                                  &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Storage {
                    address: address,
                    index: index,
                    value: value,
                }).unwrap();
            },
            Err(RequireError::AccountCode(address)) => {
                println!("Feeding VM account code at 0x{:x} ...", address);
                let code = read_hex(&client.get_code(&format!("0x{:x}", address),
                                                     &last_block_number)).unwrap();
                vm.commit_account(AccountCommitment::Code {
                    address: address,
                    code: code,
                }).unwrap();
            },
            Err(RequireError::Blockhash(number)) => {
                println!("Feeding blockhash with number 0x{:x} ...", number);
                let hash = M256::from_str(&client.get_block_by_number(&format!("0x{:x}", number))
                                          .hash).unwrap();
                vm.commit_blockhash(number, hash);
            },
        }
    }
}

fn is_miner_or_uncle(address: Address, block: &RPCBlock, client: &mut GethRPCClient) -> bool {
    // Give up balance testing if the address is a miner or an uncle.

    let miner = Address::from_str(&block.miner).unwrap();
    if miner == address {
        return true;
    }
    if block.uncles.len() > 0 {
        for i in 0..block.uncles.len() {
            let uncle = client.get_uncle_by_block_number_and_index(&block.number,
                                                                   &format!("0x{:x}", i));
            let uncle_miner = Address::from_str(&uncle.miner).unwrap();
            if uncle_miner == address {
                return true;
            }
        }
    }

    return false;
}

fn test_block(client: &mut GethRPCClient, number: usize) {
    let block = client.get_block_by_number(format!("0x{:x}", number).as_str());
    println!("block {}, transaction count: {}", block.number, block.transactions.len());
    let last_id = number - 1;
    let last_number = format!("0x{:x}", last_id);
    let cur_number = block.number.to_string();
    let block_header = from_rpc_block(&block);

    let mut last_vm: Option<SeqTransactionVM> = None;
    for transaction_hash in &block.transactions {
        println!("\nworking on transaction {}", transaction_hash);
        let transaction = from_rpc_transaction(&client.get_transaction_by_hash(&transaction_hash));
        let receipt = client.get_transaction_receipt(&transaction_hash);

        let mut vm = if last_vm.is_none() {
            SeqTransactionVM::new(transaction, block_header.clone(), &FRONTIER_PATCH)
        } else {
            SeqTransactionVM::with_previous(transaction, block_header.clone(), &FRONTIER_PATCH, last_vm.as_ref().unwrap())
        };

        handle_fire(client, &mut vm, last_id);

        assert!(Gas::from_str(&receipt.gasUsed).unwrap() == vm.real_used_gas());
        assert!(receipt.logs.len() == vm.logs().len());
        for i in 0..receipt.logs.len() {
            assert!(from_rpc_log(&receipt.logs[i]) == vm.logs()[i]);
        }

        last_vm = Some(vm);
    }

    if last_vm.is_some() {
        for account in last_vm.as_ref().unwrap().accounts() {
            match account {
                &Account::Full {
                    address,
                    balance,
                    ref changing_storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let changing_storage: HashMap<M256, M256> = changing_storage.clone().into();
                    for (key, value) in changing_storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::Create {
                    address,
                    balance,
                    ref storage,
                    ..
                } => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let expected_balance = client.get_balance(&format!("0x{:x}", address),
                                                                  &cur_number);
                        assert!(U256::from_str(&expected_balance).unwrap() == balance);
                    }
                    let storage: HashMap<M256, M256> = storage.clone().into();
                    for (key, value) in storage {
                        let expected_value = client.get_storage_at(&format!("0x{:x}", address),
                                                                   &format!("0x{:x}", key),
                                                                   &cur_number);
                        assert!(M256::from_str(&expected_value).unwrap() == value);
                    }
                },
                &Account::IncreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() + balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
                &Account::DecreaseBalance(address, balance) => {
                    if !is_miner_or_uncle(address, &block, client) {
                        let last_balance = client.get_balance(&format!("0x{:x}", address),
                                                              &last_number);
                        let cur_balance = client.get_balance(&format!("0x{:x}", address),
                                                             &cur_number);

                        assert!(U256::from_str(&last_balance).unwrap() - balance ==
                                U256::from_str(&cur_balance).unwrap());
                    }
                },
            }
        }
    }
}

fn main() {
    let matches = clap_app!(regtests =>
        (version: "0.1")
        (author: "Ethereum Classic Contributors")
        (about: "Performs an regression test on the entire Ethereum Classic blockchain.\n\nSteps to reproduce:\n* Install Ethereum Classic Geth: `$ go install github.com/ethereumproject/go-ethereum/cmd/geth`.\n* Run Geth with this command: `$ ~/go/bin/geth --rpc --rpcaddr 127.0.0.1 --rpcport 8545`.\n* Wait for the chain to sync.\n* Change directory into the `regtests` directory `$ cd regtests`\n* Run this command: `$ RUST_BACKTRACE=1 cargo run --bin regtests -- -r http://127.0.0.1:8545")
        (@arg RPC: -r --rpc +takes_value +required "Domain of Ethereum Classic Geth's RPC endpoint. e.g. `-r http://127.0.0.1:8545`.")
        (@arg NUMBER: -n --number +takes_value + required "Block number to run this test. Radix is 10. e.g. `-n 49439`.")
    ).get_matches();

    let address = matches.value_of("RPC").unwrap();
    let number = matches.value_of("NUMBER").unwrap();
    let mut client = GethRPCClient::new(address);

    if number.contains("..") {
        let number: Vec<&str> = number.split("..").collect();
        let from = usize::from_str_radix(&number[0], 10).unwrap();
        let to = usize::from_str_radix(&number[1], 10).unwrap();
        for n in from..to {
            test_block(&mut client, n);
        }
    } else {
        let number = usize::from_str_radix(&number, 10).unwrap();
        test_block(&mut client, number);
    }
}
<|endoftext|>