<commit_before>use std::fmt::{mod, Show, Formatter};
use std::io::{USER_DIR};
use std::io::fs::{mkdir_recursive, rmdir_recursive, PathExtensions};
use serialize::{Encodable, Encoder};
use url::Url;
use git2;

use util::{CargoResult, ChainError, human, ToUrl, internal, Require};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            GitReference::Master
        } else {
            GitReference::Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            GitReference::Master => "master",
            GitReference::Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = match git2::Repository::open(into) {
            Ok(repo) => {
                try!(self.fetch_into(&repo).chain_error(|| {
                    internal(format!("failed to fetch into {}", into.display()))
                }));
                repo
            }
            Err(..) => {
                try!(self.clone_into(into).chain_error(|| {
                    internal(format!("failed to clone into: {}", into.display()))
                }))
            }
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        // Create a local anonymous remote in the repository to fetch the url
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        fetch(dst, url.as_slice(), refspec)
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        if dst.exists() {
            try!(rmdir_recursive(dst));
        }
        try!(mkdir_recursive(dst, USER_DIR));
        let repo = try!(git2::Repository::init_bare(dst));
        try!(fetch(&repo, url.as_slice(), "refs/heads/*:refs/heads/*"));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = match git2::Repository::open(dest) {
            Ok(repo) => {
                let checkout = GitCheckout::new(dest, self, rev, repo);
                if !checkout.is_fresh() {
                    try!(checkout.fetch());
                    try!(checkout.reset());
                    assert!(checkout.is_fresh());
                }
                checkout
            }
            Err(..) => try!(GitCheckout::clone_into(dest, self, rev)),
        };
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));
        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        let rev = try!(self.repo.revparse_single(reference.as_slice()));
        Ok(GitRevision(rev.id().to_string()))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new<'a>(path: &Path, database: &'a GitDatabase, revision: GitRevision,
               repo: git2::Repository)
               -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into<'a>(into: &Path, database: &'a GitDatabase,
                      revision: GitRevision)
                      -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);
        try!(checkout.reset());
        Ok(checkout)
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, USER_DIR).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn is_fresh(&self) -> bool {
        match self.repo.revparse_single("HEAD") {
            Ok(head) => head.id().to_string() == self.revision.to_string(),
            _ => false,
        }
    }

    fn fetch(&self) -> CargoResult<()> {
        info!("fetch {}", self.repo.path().display());
        let url = try!(self.database.path.to_url().map_err(human));
        let url = url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        try!(fetch(&self.repo, url.as_slice(), refspec));
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(),
              self.revision.as_slice());
        let oid = try!(git2::Oid::from_str(self.revision.as_slice()));
        let object = try!(self.repo.find_object(oid, None));
        try!(self.repo.reset(&object, git2::ResetType::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).into_iter() {
                try!(child.init(false));
                let url = try!(child.url().require(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let repo = match child.open() {
                    Ok(repo) => {
                        if child.head_id() == try!(repo.head()).target() {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                try!(fetch(&repo, url, refspec).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(repo.find_object(head, None));
                try!(repo.reset(&obj, git2::ResetType::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}

fn with_authentication<T>(url: &str,
                          cfg: &git2::Config,
                          f: |git2::Credentials| -> CargoResult<T>)
                          -> CargoResult<T> {
    // Prepare the authentication callbacks.
    //
    // We check the `allowed` types of credentials, and we try to do as much as
    // possible based on that:
    //
    // * Prioritize SSH keys from the local ssh agent as they're likely the most
    //   reliable. The username here is prioritized from the credential
    //   callback, then from whatever is configured in git itself, and finally
    //   we fall back to the generic user of `git`.
    //
    // * If a username/password is allowed, then we fallback to git2-rs's
    //   implementation of the credential helper. This is what is configured
    //   with `credential.helper` in git, and is the interface for the OSX
    //   keychain, for example.
    //
    // * After the above two have failed, we just kinda grapple attempting to
    //   return *something*.
    let mut cred_helper = git2::CredentialHelper::new(url);
    cred_helper.config(cfg);
    let mut cred_error = false;
    let ret = f(|url, username, allowed| {
        let creds = if allowed.contains(git2::SSH_KEY) {
            let user = username.map(|s| s.to_string())
                               .or_else(|| cred_helper.username.clone())
                               .unwrap_or("git".to_string());
            git2::Cred::ssh_key_from_agent(user.as_slice())
        } else if allowed.contains(git2::USER_PASS_PLAINTEXT) {
            git2::Cred::credential_helper(cfg, url, username)
        } else if allowed.contains(git2::DEFAULT) {
            git2::Cred::default()
        } else {
            Err(git2::Error::from_str("no authentication available"))
        };
        cred_error = creds.is_err();
        creds
    });
    if cred_error {
        ret.chain_error(|| {
            human("Failed to authenticate when downloading repository")
        })
    } else {
        ret
    }
}

pub fn fetch(repo: &git2::Repository, url: &str,
             refspec: &str) -> CargoResult<()> {
    // Create a local anonymous remote in the repository to fetch the url

    with_authentication(url, &try!(repo.config()), |f| {
        let mut cb = git2::RemoteCallbacks::new()
                                       .credentials(f);
        let mut remote = try!(repo.remote_anonymous(url.as_slice(), refspec));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        remote.set_callbacks(&mut cb);
        try!(remote.fetch(None, None));
        Ok(())
    })
}
<commit_msg>auto merge of #997 : alexcrichton/cargo/issue-993-attempt, r=brson<commit_after>use std::fmt::{mod, Show, Formatter};
use std::io::{USER_DIR};
use std::io::fs::{mkdir_recursive, rmdir_recursive, PathExtensions};
use serialize::{Encodable, Encoder};
use url::Url;
use git2;

use util::{CargoResult, ChainError, human, ToUrl, internal, Require};

#[deriving(PartialEq,Clone,Encodable)]
pub enum GitReference {
    Master,
    Other(String)
}

#[deriving(PartialEq,Clone,Encodable)]
pub struct GitRevision(String);

impl GitReference {
    pub fn for_str<S: Str>(string: S) -> GitReference {
        if string.as_slice() == "master" {
            GitReference::Master
        } else {
            GitReference::Other(string.as_slice().to_string())
        }
    }
}

impl Str for GitReference {
    fn as_slice(&self) -> &str {
        match *self {
            GitReference::Master => "master",
            GitReference::Other(ref string) => string.as_slice()
        }
    }
}

impl Show for GitReference {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

impl Str for GitRevision {
    fn as_slice(&self) -> &str {
        let GitRevision(ref me) = *self;
        me.as_slice()
    }
}

impl Show for GitRevision {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        self.as_slice().fmt(f)
    }
}

/// GitRemote represents a remote repository. It gets cloned into a local
/// GitDatabase.
#[deriving(PartialEq,Clone,Show)]
pub struct GitRemote {
    url: Url,
}

#[deriving(PartialEq,Clone,Encodable)]
struct EncodableGitRemote {
    url: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitRemote {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitRemote {
            url: self.url.to_string()
        }.encode(s)
    }
}

/// GitDatabase is a local clone of a remote repository's database. Multiple
/// GitCheckouts can be cloned from this GitDatabase.
pub struct GitDatabase {
    remote: GitRemote,
    path: Path,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitDatabase {
    remote: GitRemote,
    path: String,
}

impl<E, S: Encoder<E>> Encodable<S, E> for GitDatabase {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitDatabase {
            remote: self.remote.clone(),
            path: self.path.display().to_string()
        }.encode(s)
    }
}

/// GitCheckout is a local checkout of a particular revision. Calling
/// `clone_into` with a reference will resolve the reference into a revision,
/// and return a CargoError if no revision for that reference was found.
pub struct GitCheckout<'a> {
    database: &'a GitDatabase,
    location: Path,
    revision: GitRevision,
    repo: git2::Repository,
}

#[deriving(Encodable)]
pub struct EncodableGitCheckout {
    database: EncodableGitDatabase,
    location: String,
    revision: String,
}

impl<'a, E, S: Encoder<E>> Encodable<S, E> for GitCheckout<'a> {
    fn encode(&self, s: &mut S) -> Result<(), E> {
        EncodableGitCheckout {
            location: self.location.display().to_string(),
            revision: self.revision.to_string(),
            database: EncodableGitDatabase {
                remote: self.database.remote.clone(),
                path: self.database.path.display().to_string(),
            },
        }.encode(s)
    }
}

// Implementations

impl GitRemote {
    pub fn new(url: &Url) -> GitRemote {
        GitRemote { url: url.clone() }
    }

    pub fn get_url(&self) -> &Url {
        &self.url
    }

    pub fn rev_for<S: Str>(&self, path: &Path, reference: S)
                           -> CargoResult<GitRevision> {
        let db = try!(self.db_at(path));
        db.rev_for(reference)
    }

    pub fn checkout(&self, into: &Path) -> CargoResult<GitDatabase> {
        let repo = match git2::Repository::open(into) {
            Ok(repo) => {
                try!(self.fetch_into(&repo).chain_error(|| {
                    internal(format!("failed to fetch into {}", into.display()))
                }));
                repo
            }
            Err(..) => {
                try!(self.clone_into(into).chain_error(|| {
                    internal(format!("failed to clone into: {}", into.display()))
                }))
            }
        };

        Ok(GitDatabase { remote: self.clone(), path: into.clone(), repo: repo })
    }

    pub fn db_at(&self, db_path: &Path) -> CargoResult<GitDatabase> {
        let repo = try!(git2::Repository::open(db_path));
        Ok(GitDatabase {
            remote: self.clone(),
            path: db_path.clone(),
            repo: repo,
        })
    }

    fn fetch_into(&self, dst: &git2::Repository) -> CargoResult<()> {
        // Create a local anonymous remote in the repository to fetch the url
        let url = self.url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        fetch(dst, url.as_slice(), refspec)
    }

    fn clone_into(&self, dst: &Path) -> CargoResult<git2::Repository> {
        let url = self.url.to_string();
        if dst.exists() {
            try!(rmdir_recursive(dst));
        }
        try!(mkdir_recursive(dst, USER_DIR));
        let repo = try!(git2::Repository::init_bare(dst));
        try!(fetch(&repo, url.as_slice(), "refs/heads/*:refs/heads/*"));
        Ok(repo)
    }
}

impl GitDatabase {
    fn get_path<'a>(&'a self) -> &'a Path {
        &self.path
    }

    pub fn copy_to(&self, rev: GitRevision, dest: &Path)
                   -> CargoResult<GitCheckout> {
        let checkout = match git2::Repository::open(dest) {
            Ok(repo) => {
                let checkout = GitCheckout::new(dest, self, rev, repo);
                if !checkout.is_fresh() {
                    try!(checkout.fetch());
                    try!(checkout.reset());
                    assert!(checkout.is_fresh());
                }
                checkout
            }
            Err(..) => try!(GitCheckout::clone_into(dest, self, rev)),
        };
        try!(checkout.update_submodules().chain_error(|| {
            internal("failed to update submodules")
        }));
        Ok(checkout)
    }

    pub fn rev_for<S: Str>(&self, reference: S) -> CargoResult<GitRevision> {
        let rev = try!(self.repo.revparse_single(reference.as_slice()));
        Ok(GitRevision(rev.id().to_string()))
    }

    pub fn has_ref<S: Str>(&self, reference: S) -> CargoResult<()> {
        try!(self.repo.revparse_single(reference.as_slice()));
        Ok(())
    }
}

impl<'a> GitCheckout<'a> {
    fn new<'a>(path: &Path, database: &'a GitDatabase, revision: GitRevision,
               repo: git2::Repository)
               -> GitCheckout<'a>
    {
        GitCheckout {
            location: path.clone(),
            database: database,
            revision: revision,
            repo: repo,
        }
    }

    fn clone_into<'a>(into: &Path, database: &'a GitDatabase,
                      revision: GitRevision)
                      -> CargoResult<GitCheckout<'a>>
    {
        let repo = try!(GitCheckout::clone_repo(database.get_path(), into));
        let checkout = GitCheckout::new(into, database, revision, repo);
        try!(checkout.reset());
        Ok(checkout)
    }

    pub fn get_rev(&self) -> &str {
        self.revision.as_slice()
    }

    fn clone_repo(source: &Path, into: &Path) -> CargoResult<git2::Repository> {
        let dirname = into.dir_path();

        try!(mkdir_recursive(&dirname, USER_DIR).chain_error(|| {
            human(format!("Couldn't mkdir {}", dirname.display()))
        }));

        if into.exists() {
            try!(rmdir_recursive(into).chain_error(|| {
                human(format!("Couldn't rmdir {}", into.display()))
            }));
        }

        let url = try!(source.to_url().map_err(human));
        let url = url.to_string();
        let repo = try!(git2::Repository::clone(url.as_slice(),
                                                into).chain_error(|| {
            internal(format!("failed to clone {} into {}", source.display(),
                             into.display()))
        }));
        Ok(repo)
    }

    fn is_fresh(&self) -> bool {
        match self.repo.revparse_single("HEAD") {
            Ok(head) => head.id().to_string() == self.revision.to_string(),
            _ => false,
        }
    }

    fn fetch(&self) -> CargoResult<()> {
        info!("fetch {}", self.repo.path().display());
        let url = try!(self.database.path.to_url().map_err(human));
        let url = url.to_string();
        let refspec = "refs/heads/*:refs/heads/*";
        try!(fetch(&self.repo, url.as_slice(), refspec));
        Ok(())
    }

    fn reset(&self) -> CargoResult<()> {
        info!("reset {} to {}", self.repo.path().display(),
              self.revision.as_slice());
        let oid = try!(git2::Oid::from_str(self.revision.as_slice()));
        let object = try!(self.repo.find_object(oid, None));
        try!(self.repo.reset(&object, git2::ResetType::Hard, None, None));
        Ok(())
    }

    fn update_submodules(&self) -> CargoResult<()> {
        return update_submodules(&self.repo);

        fn update_submodules(repo: &git2::Repository) -> CargoResult<()> {
            info!("update submodules for: {}", repo.path().display());

            for mut child in try!(repo.submodules()).into_iter() {
                try!(child.init(false));
                let url = try!(child.url().require(|| {
                    internal("non-utf8 url for submodule")
                }));

                // A submodule which is listed in .gitmodules but not actually
                // checked out will not have a head id, so we should ignore it.
                let head = match child.head_id() {
                    Some(head) => head,
                    None => continue,
                };

                // If the submodule hasn't been checked out yet, we need to
                // clone it. If it has been checked out and the head is the same
                // as the submodule's head, then we can bail out and go to the
                // next submodule.
                let head_and_repo = child.open().and_then(|repo| {
                    Ok((try!(repo.head()).target(), repo))
                });
                let repo = match head_and_repo {
                    Ok((head, repo)) => {
                        if child.head_id() == head {
                            continue
                        }
                        repo
                    }
                    Err(..) => {
                        let path = repo.path().dir_path().join(child.path());
                        try!(git2::Repository::clone(url, &path))
                    }
                };

                // Fetch data from origin and reset to the head commit
                let refspec = "refs/heads/*:refs/heads/*";
                try!(fetch(&repo, url, refspec).chain_error(|| {
                    internal(format!("failed to fetch submodule `{}` from {}",
                                     child.name().unwrap_or(""), url))
                }));

                let obj = try!(repo.find_object(head, None));
                try!(repo.reset(&obj, git2::ResetType::Hard, None, None));
                try!(update_submodules(&repo));
            }
            Ok(())
        }
    }
}

fn with_authentication<T>(url: &str,
                          cfg: &git2::Config,
                          f: |git2::Credentials| -> CargoResult<T>)
                          -> CargoResult<T> {
    // Prepare the authentication callbacks.
    //
    // We check the `allowed` types of credentials, and we try to do as much as
    // possible based on that:
    //
    // * Prioritize SSH keys from the local ssh agent as they're likely the most
    //   reliable. The username here is prioritized from the credential
    //   callback, then from whatever is configured in git itself, and finally
    //   we fall back to the generic user of `git`.
    //
    // * If a username/password is allowed, then we fallback to git2-rs's
    //   implementation of the credential helper. This is what is configured
    //   with `credential.helper` in git, and is the interface for the OSX
    //   keychain, for example.
    //
    // * After the above two have failed, we just kinda grapple attempting to
    //   return *something*.
    let mut cred_helper = git2::CredentialHelper::new(url);
    cred_helper.config(cfg);
    let mut cred_error = false;
    let ret = f(|url, username, allowed| {
        let creds = if allowed.contains(git2::SSH_KEY) {
            let user = username.map(|s| s.to_string())
                               .or_else(|| cred_helper.username.clone())
                               .unwrap_or("git".to_string());
            git2::Cred::ssh_key_from_agent(user.as_slice())
        } else if allowed.contains(git2::USER_PASS_PLAINTEXT) {
            git2::Cred::credential_helper(cfg, url, username)
        } else if allowed.contains(git2::DEFAULT) {
            git2::Cred::default()
        } else {
            Err(git2::Error::from_str("no authentication available"))
        };
        cred_error = creds.is_err();
        creds
    });
    if cred_error {
        ret.chain_error(|| {
            human("Failed to authenticate when downloading repository")
        })
    } else {
        ret
    }
}

pub fn fetch(repo: &git2::Repository, url: &str,
             refspec: &str) -> CargoResult<()> {
    // Create a local anonymous remote in the repository to fetch the url

    with_authentication(url, &try!(repo.config()), |f| {
        let mut cb = git2::RemoteCallbacks::new()
                                       .credentials(f);
        let mut remote = try!(repo.remote_anonymous(url.as_slice(), refspec));
        try!(remote.add_fetch("refs/tags/*:refs/tags/*"));
        remote.set_callbacks(&mut cb);
        try!(remote.fetch(None, None));
        Ok(())
    })
}
<|endoftext|>


<commit_before>extern crate gcc;

use std::path::Path;
use std::process::Command;

fn main() {
    if ! Path::new("clingo").exists() {
        Command::new("git")
            .args(&["clone", "https://github.com/potassco/clingo.git"])
            .status()
            .unwrap();
    }
    Command::new("git")
        .args(&["checkout", "tags/v5.1.0"])
        .current_dir("./clingo")
        .status()
        .unwrap();

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libgringo/src/control.cc")
        .file("clingo/libgringo/src/term.cc")
        .file("clingo/libgringo/src/symbol.cc")
        .file("clingo/libgringo/src/primes.cc")
        .file("clingo/libgringo/src/backend.cc")
        .file("clingo/libgringo/src/lua.cc")
        .file("clingo/libgringo/src/scripts.cc")
        .file("clingo/libgringo/src/python.cc")
        .file("clingo/libgringo/src/terms.cc")
        .file("clingo/libgringo/src/ground/statements.cc")
        .file("clingo/libgringo/src/ground/literals.cc")
        .file("clingo/libgringo/src/ground/instantiation.cc")
        .file("clingo/libgringo/src/input/groundtermparser.cc")
        .file("generated/input/nongroundgrammar/grammar.cc")
        .file("generated/input/groundtermgrammar/grammar.cc")
        .file("clingo/libgringo/src/input/nongroundparser.cc")
        .file("clingo/libgringo/src/input/aggregate.cc")
        .file("clingo/libgringo/src/input/aggregates.cc")
        .file("clingo/libgringo/src/input/program.cc")
        .file("clingo/libgringo/src/ground/program.cc")
        .file("clingo/libgringo/src/input/programbuilder.cc")
        .file("clingo/libgringo/src/input/literals.cc")
        .file("clingo/libgringo/src/input/statement.cc")
        .file("clingo/libgringo/src/input/literal.cc")
        .file("clingo/libgringo/src/input/theory.cc")
        .file("clingo/libgringo/src/output/statements.cc")
        .file("clingo/libgringo/src/output/literals.cc")
        .file("clingo/libgringo/src/output/aggregates.cc")
        .file("clingo/libgringo/src/output/output.cc")
        .file("clingo/libgringo/src/output/statement.cc")
        .file("clingo/libgringo/src/output/literal.cc")
        .file("clingo/libgringo/src/output/theory.cc")
        .include("clingo/libgringo")
        .include("generated")
        .include("clingo/liblp")
        .include("clingo/libreify")
        .compile("libgringo.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libprogram_opts/src/application.cpp")
        .file("clingo/libprogram_opts/src/alarm.cpp")
        .file("clingo/libprogram_opts/src/program_options.cpp")
        .file("clingo/libprogram_opts/src/string_convert.cpp")
        .file("clingo/libprogram_opts/src/value_store.cpp")
        .include("clingo/libprogram_opts")
        .compile("libprogram_opts.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libreify/src/program.cc")
        .include("clingo/libreify")
        .include("clingo/liblp")
        .include("clingo/libgringo")
        .compile("libreify.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/liblp/src/aspif.cpp")
        .file("clingo/liblp/src/clingo.cpp")
        .file("clingo/liblp/src/theory_data.cpp")
        .file("clingo/liblp/src/smodels.cpp")
        .file("clingo/liblp/src/convert.cpp")
        .file("clingo/liblp/src/rule_utils.cpp")
        .file("clingo/liblp/src/match_basic_types.cpp")
        .file("clingo/liblp/src/aspif_text.cpp")
        .include("clingo/liblp")
        .compile("liblp.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclingo/src/clingocontrol.cc")
        .include("clingo/libclingo")
        .include("clingo/libgringo")
        .include("clingo/liblp")
        .include("clingo/libclasp")
        .include("clingo/libprogram_opts")
        .compile("libclingo.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclasp/src/parallel_solve.cpp")
        .file("clingo/libclasp/src/solver_types.cpp")
        .file("clingo/libclasp/src/solver_strategies.cpp")
        .file("clingo/libclasp/src/clause.cpp")
        .file("clingo/libclasp/src/logic_program.cpp")
        .file("clingo/libclasp/src/clasp_facade.cpp")
        .file("clingo/libclasp/src/cb_enumerator.cpp")
        .file("clingo/libclasp/src/minimize_constraint.cpp")
        .file("clingo/libclasp/src/clingo.cpp")
        .file("clingo/libclasp/src/shared_context.cpp")
        .file("clingo/libclasp/src/dependency_graph.cpp")
        .file("clingo/libclasp/src/logic_program_types.cpp")
        .file("clingo/libclasp/src/asp_preprocessor.cpp")
        .file("clingo/libclasp/src/clasp_options.cpp")
        .file("clingo/libclasp/src/heuristics.cpp")
        .file("clingo/libclasp/src/timer.cpp")
        .file("clingo/libclasp/src/lookahead.cpp")
        .file("clingo/libclasp/src/constraint.cpp")
        .file("clingo/libclasp/src/unfounded_check.cpp")
        .file("clingo/libclasp/src/solver.cpp")
        .file("clingo/libclasp/src/model_enumerators.cpp")
        .file("clingo/libclasp/src/clasp_output.cpp")
        .file("clingo/libclasp/src/parser.cpp")
        .file("clingo/libclasp/src/solve_algorithms.cpp")
        .file("clingo/libclasp/src/enumerator.cpp")
        .file("clingo/libclasp/src/program_builder.cpp")
        .file("clingo/libclasp/src/satelite.cpp")
        .file("clingo/libclasp/src/statistics.cpp")
        .file("clingo/libclasp/src/weight_constraint.cpp")
        .include("clingo/libclasp")
        .include("clingo/libclingo")
        .include("clingo/liblp")
        .include("clingo/libprogram_opts")
        .compile("libclasp.a");

    println!("cargo:rustc-link-lib=static=clingo");
    println!("cargo:rustc-link-lib=static=clasp");
    println!("cargo:rustc-link-lib=static=program_opts");
    println!("cargo:rustc-link-lib=static=lp");
    println!("cargo:rustc-link-lib=static=reify");
    println!("cargo:rustc-link-lib=static=gringo");

//     println!("cargo:rustc-link-lib=python3.6m");
//     -DWITH_PYTHON=1 -I/usr/include/python3.6m

}
<commit_msg>only checkout tag v5.1.0 if clingo has just been cloned<commit_after>extern crate gcc;

use std::path::Path;
use std::process::Command;

fn main() {
    if !Path::new("clingo").exists() {
        Command::new("git")
            .args(&["clone", "https://github.com/potassco/clingo.git"])
            .status()
            .unwrap();

        Command::new("git")
            .args(&["checkout", "tags/v5.1.0"])
            .current_dir("./clingo")
            .status()
            .unwrap();
    }

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libgringo/src/control.cc")
        .file("clingo/libgringo/src/term.cc")
        .file("clingo/libgringo/src/symbol.cc")
        .file("clingo/libgringo/src/primes.cc")
        .file("clingo/libgringo/src/backend.cc")
        .file("clingo/libgringo/src/lua.cc")
        .file("clingo/libgringo/src/scripts.cc")
        .file("clingo/libgringo/src/python.cc")
        .file("clingo/libgringo/src/terms.cc")
        .file("clingo/libgringo/src/ground/statements.cc")
        .file("clingo/libgringo/src/ground/literals.cc")
        .file("clingo/libgringo/src/ground/instantiation.cc")
        .file("clingo/libgringo/src/input/groundtermparser.cc")
        .file("generated/input/nongroundgrammar/grammar.cc")
        .file("generated/input/groundtermgrammar/grammar.cc")
        .file("clingo/libgringo/src/input/nongroundparser.cc")
        .file("clingo/libgringo/src/input/aggregate.cc")
        .file("clingo/libgringo/src/input/aggregates.cc")
        .file("clingo/libgringo/src/input/program.cc")
        .file("clingo/libgringo/src/ground/program.cc")
        .file("clingo/libgringo/src/input/programbuilder.cc")
        .file("clingo/libgringo/src/input/literals.cc")
        .file("clingo/libgringo/src/input/statement.cc")
        .file("clingo/libgringo/src/input/literal.cc")
        .file("clingo/libgringo/src/input/theory.cc")
        .file("clingo/libgringo/src/output/statements.cc")
        .file("clingo/libgringo/src/output/literals.cc")
        .file("clingo/libgringo/src/output/aggregates.cc")
        .file("clingo/libgringo/src/output/output.cc")
        .file("clingo/libgringo/src/output/statement.cc")
        .file("clingo/libgringo/src/output/literal.cc")
        .file("clingo/libgringo/src/output/theory.cc")
        .include("clingo/libgringo")
        .include("generated")
        .include("clingo/liblp")
        .include("clingo/libreify")
        .compile("libgringo.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libprogram_opts/src/application.cpp")
        .file("clingo/libprogram_opts/src/alarm.cpp")
        .file("clingo/libprogram_opts/src/program_options.cpp")
        .file("clingo/libprogram_opts/src/string_convert.cpp")
        .file("clingo/libprogram_opts/src/value_store.cpp")
        .include("clingo/libprogram_opts")
        .compile("libprogram_opts.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/libreify/src/program.cc")
        .include("clingo/libreify")
        .include("clingo/liblp")
        .include("clingo/libgringo")
        .compile("libreify.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .file("clingo/liblp/src/aspif.cpp")
        .file("clingo/liblp/src/clingo.cpp")
        .file("clingo/liblp/src/theory_data.cpp")
        .file("clingo/liblp/src/smodels.cpp")
        .file("clingo/liblp/src/convert.cpp")
        .file("clingo/liblp/src/rule_utils.cpp")
        .file("clingo/liblp/src/match_basic_types.cpp")
        .file("clingo/liblp/src/aspif_text.cpp")
        .include("clingo/liblp")
        .compile("liblp.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclingo/src/clingocontrol.cc")
        .include("clingo/libclingo")
        .include("clingo/libgringo")
        .include("clingo/liblp")
        .include("clingo/libclasp")
        .include("clingo/libprogram_opts")
        .compile("libclingo.a");

    gcc::Config::new()
        .cpp(true)
        .define("NDEBUG", Some("1"))
        .define("WITH_THREADS", Some("0"))
        .file("clingo/libclasp/src/parallel_solve.cpp")
        .file("clingo/libclasp/src/solver_types.cpp")
        .file("clingo/libclasp/src/solver_strategies.cpp")
        .file("clingo/libclasp/src/clause.cpp")
        .file("clingo/libclasp/src/logic_program.cpp")
        .file("clingo/libclasp/src/clasp_facade.cpp")
        .file("clingo/libclasp/src/cb_enumerator.cpp")
        .file("clingo/libclasp/src/minimize_constraint.cpp")
        .file("clingo/libclasp/src/clingo.cpp")
        .file("clingo/libclasp/src/shared_context.cpp")
        .file("clingo/libclasp/src/dependency_graph.cpp")
        .file("clingo/libclasp/src/logic_program_types.cpp")
        .file("clingo/libclasp/src/asp_preprocessor.cpp")
        .file("clingo/libclasp/src/clasp_options.cpp")
        .file("clingo/libclasp/src/heuristics.cpp")
        .file("clingo/libclasp/src/timer.cpp")
        .file("clingo/libclasp/src/lookahead.cpp")
        .file("clingo/libclasp/src/constraint.cpp")
        .file("clingo/libclasp/src/unfounded_check.cpp")
        .file("clingo/libclasp/src/solver.cpp")
        .file("clingo/libclasp/src/model_enumerators.cpp")
        .file("clingo/libclasp/src/clasp_output.cpp")
        .file("clingo/libclasp/src/parser.cpp")
        .file("clingo/libclasp/src/solve_algorithms.cpp")
        .file("clingo/libclasp/src/enumerator.cpp")
        .file("clingo/libclasp/src/program_builder.cpp")
        .file("clingo/libclasp/src/satelite.cpp")
        .file("clingo/libclasp/src/statistics.cpp")
        .file("clingo/libclasp/src/weight_constraint.cpp")
        .include("clingo/libclasp")
        .include("clingo/libclingo")
        .include("clingo/liblp")
        .include("clingo/libprogram_opts")
        .compile("libclasp.a");

    println!("cargo:rustc-link-lib=static=clingo");
    println!("cargo:rustc-link-lib=static=clasp");
    println!("cargo:rustc-link-lib=static=program_opts");
    println!("cargo:rustc-link-lib=static=lp");
    println!("cargo:rustc-link-lib=static=reify");
    println!("cargo:rustc-link-lib=static=gringo");

    //     println!("cargo:rustc-link-lib=python3.6m");
    //     -DWITH_PYTHON=1 -I/usr/include/python3.6m

}
<|endoftext|>


<commit_before>#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod models;
pub mod schema;

use diesel::prelude::*;
use diesel::pg::PgConnection;
use dotenv::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
<commit_msg>rustfmt for import cleanup<commit_after>#[macro_use]
extern crate diesel;
extern crate dotenv;

pub mod models;
pub mod schema;

use diesel::pg::PgConnection;
use diesel::prelude::*;
use dotenv::dotenv;
use std::env;

pub fn establish_connection() -> PgConnection {
    dotenv().ok();

    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    PgConnection::establish(&database_url)
        .unwrap_or_else(|_| panic!("Error connecting to {}", database_url))
}
<|endoftext|>


<commit_before>use scaly::containers::Ref;
use scaly::io::Stream;
use scaly::memory::Region;
use scaly::{Page, String, StringBuilder};
use scaly::Equal;

pub struct Lexer {
    pub token: Ref<Token>,
    character: char,
    stream: *mut Stream,
    is_at_end: bool,
    previous_line: usize,
    previous_column: usize,
    pub line: usize,
    pub column: usize,
}

impl Lexer {
    pub fn new(_pr: &Region, _rp: *mut Page, stream: *mut Stream) -> Ref<Lexer> {
        let _r = Region::create(_pr);
        let _token_page = unsafe { (*_rp).allocate_exclusive_page() };
        unsafe { (*_token_page).reset() };
        let mut lexer = Ref::new(
            _rp,
            Lexer {
                stream: stream,
                is_at_end: false,
                token: Ref::new(_token_page, Token::InvalidToken),
                character: 0 as char,
                previous_line: 1,
                previous_column: 0,
                line: 1,
                column: 0,
            },
        );
        lexer.read_character();
        lexer.advance(&_r);
        lexer
    }

    fn read_character(&mut self) {
        unsafe {
            let read_result: i32 = (*self.stream).read_byte();
            if read_result == -1 {
                self.is_at_end = true;
                self.character = 0 as char;
            }
        }
    }

    pub fn advance(&mut self, _pr: &Region) {
        let _r = Region::create(_pr);
        self.skip_whitespace();
        self.previous_line = self.line;
        self.previous_column = self.previous_column;
        if self.is_at_end {
            return;
        }

        let c = self.character;

        if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
            {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_identifier(&_r, _token_page);
            }
            return;
        }

        if (c >= '0') && (c <= '9') {
            {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_numeric_literal(&_r, _token_page);
            }
            return;
        }

        match c {
            '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_operator(&_r, _token_page);
            }

            '\"' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_string_literal(&_r, _token_page);
            }

            '\'' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_character_literal(&_r, _token_page);
            }

            '{' | '}' | '(' | ')' | '[' | ']' | '.' | ',' | ':' | ';' | '?' | '!' | '@' | '#'
            | '$' | '_' | '`' => {
                {
                    let _token_page = Page::own(&self.token);
                    unsafe {
                        (*_token_page).reset();
                    }
                    self.token = Ref::new(
                        _token_page,
                        Token::Punctuation(String::from_character(Page::own(self), c)),
                    );
                }
                self.read_character();
                self.column = self.column + 1;
            }

            _ => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = Ref::new(_token_page, Token::InvalidToken);
            }
        }
    }

    fn scan_identifier(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut name: Ref<StringBuilder> = StringBuilder::from_character(_r.page, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Identifier(name.to_string(_rp)));
            }

            let c = self.character;
            if ((c >= 'a') && (c <= 'z'))
                || ((c >= 'A') && (c <= 'Z'))
                || ((c >= '0') && (c <= '9'))
                || (c == '_')
            {
                name.append_character(c);
            } else {
                return Ref::new(_rp, Token::Identifier(name.to_string(_rp)));
            }
        }
    }

    fn scan_operator(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut operation: Ref<StringBuilder> =
            StringBuilder::from_character(_r.page, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Identifier(operation.to_string(_rp)));
            }

            match self.character {
                '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                    operation.append_character(self.character)
                }

                _ => return Ref::new(_rp, Token::Identifier(operation.to_string(_rp))),
            }
        }
    }

    fn scan_string_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::new(_r.page);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::InvalidToken);
            }

            match self.character {
                '\"' => {
                    self.read_character();
                    self.column = self.column + 1;
                    return Ref::new(_rp, Token::Literal(Literal::String(value.to_string(_rp))));
                }

                '\\' => {
                    self.read_character();
                    self.column = self.column + 1;
                    match self.character {
                        '\"' | '\\' | '\'' => {
                            value.append_character('\\');
                            value.append_character(self.character);
                        }
                        'n' => {
                            value.append_character('\\');
                            value.append_character('n');
                        }
                        'r' => {
                            value.append_character('\\');
                            value.append_character('r');
                        }
                        't' => {
                            value.append_character('\\');
                            value.append_character('t');
                        }
                        '0' => {
                            value.append_character('\\');
                            value.append_character('0');
                        }
                        _ => return Ref::new(_rp, Token::InvalidToken),
                    }
                }
                _ => value.append_character(self.character),
            }
        }
    }

    fn scan_character_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::new(_r.page);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::InvalidToken);
            }

            match self.character {
                '\'' => {
                    self.read_character();
                    self.column = self.column + 1;
                    return Ref::new(_rp, Token::Literal(Literal::Character(value.to_string(_rp))));
                }

                '\\' => {
                    self.read_character();
                    self.column = self.column + 1;
                    match self.character {
                        '\"' | '\\' | '\'' => {
                            value.append_character('\\');
                            value.append_character(self.character);
                        }
                        'n' => {
                            value.append_character('\\');
                            value.append_character('n');
                        }
                        'r' => {
                            value.append_character('\\');
                            value.append_character('r');
                        }
                        't' => {
                            value.append_character('\\');
                            value.append_character('t');
                        }
                        '0' => {
                            value.append_character('\\');
                            value.append_character('0');
                        }
                        _ => return Ref::new(_rp, Token::InvalidToken),
                    }
                }
                _ => value.append_character(self.character),
            }
        }
    }

    fn scan_numeric_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::from_character(_rp, self.character);

        self.read_character();
        self.column = self.column + 1;

        if self.is_at_end() {
            return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
        }
        if self.character == 'x' {
            return self.scan_hex_literal(&_r, _rp);
        } else {
            self.read_character();
            self.column = self.column + 1;
        }

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
            }

            let c = self.character;
            if (c >= '0') && (c <= '9') {
                value.append_character(self.character)
            } else {
                return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
            }
        }
    }

    fn scan_hex_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::from_character(_rp, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Literal(Literal::Hex(value.to_string(_rp))));
            }

            let c = self.character;
            if ((c >= '0') && (c <= '9'))
                || ((c >= 'a') && (c <= 'f'))
                || ((c >= 'A') && (c <= 'F'))
            {
                value.append_character(self.character)
            } else {
                return Ref::new(_rp, Token::Literal(Literal::Hex(value.to_string(_rp))));
            }
        }
    }

    fn skip_whitespace(&mut self) {
        loop {
            if self.is_at_end {
                return;
            }

            match self.character {
                ' ' => {
                    self.read_character();;
                    self.column = self.column + 1;
                    continue;
                }

                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    continue;
                }

                '/' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end {
                        return;
                    }

                    if self.character == '/' {
                        self.handle_single_line_comment();
                    } else {
                        if self.character == '*' {
                            self.handle_multi_line_comment();
                        } else {
                            return;
                        }
                    }
                }

                _ => return,
            }
        }
    }

    fn handle_single_line_comment(&mut self) {
        loop {
            if self.is_at_end() {
                return;
            }

            match self.character {
                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    return;
                }

                _ => {
                    self.read_character();
                    self.column = self.column + 1;
                    continue;
                }
            }
        }
    }

    fn handle_multi_line_comment(&mut self) {
        loop {
            if self.is_at_end() {
                return;
            }

            match self.character {
                '/' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end() {
                        return;
                    } else {
                        if self.character == '*' {
                            self.handle_multi_line_comment();
                        } else {
                            return;
                        }
                    }
                }

                '*' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end() {
                        return;
                    } else {
                        if self.character == '/' {
                            self.read_character();
                            self.column = self.column + 1;
                            return;
                        }
                    }
                }

                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    return;
                }

                _ => {
                    self.read_character();
                    self.column = self.column + 1;
                    continue;
                }
            }
        }
    }

    pub fn parse_keyword(&self, fixed_string: String) -> bool
    {
        match *self.token {
            Token::Identifier(name) => return name.equals(&fixed_string),
            _ => return false,
        }
    }

    pub fn parse_identifier(&self, _rp: *mut Page) -> Option<String> {
        match *self.token {
            Token::Identifier(name) => return Some(String::copy(_rp, name)),
            _ => return None,
        }
    }

    pub fn parse_punctuation(&self, fixed_string: String) -> bool
    {
        match *self.token {
            Token::Punctuation(name) => return name.equals(&fixed_string),
            _ => return false,
        }
    }

    pub fn parse_literal(&self, _rp: *mut Page) -> Option<Literal>
    {
        match *self.token {
            Token::Literal(Literal::String(name)) => Some(Literal::String(String::copy(_rp, name))),
            Token::Literal(Literal::Character(name)) => Some(Literal::Character(String::copy(_rp, name))),
            Token::Literal(Literal::Numeric(name)) => Some(Literal::Numeric(String::copy(_rp, name))),
            Token::Literal(Literal::Hex(name)) => Some(Literal::Hex(String::copy(_rp, name))),
            _ => None
        }
    }

    pub fn is_at_end(&self) -> bool {
        self.is_at_end
    }

    pub fn get_position(&self) -> Position {
        Position {
            line: self.line,
            column: self.column,
        }
    }

    pub fn get_previous_position(&self) -> Position {
        Position {
            line: self.previous_line,
            column: self.previous_column,
        }
    }
}

#[derive(Copy, Clone)]
pub struct Position {
    pub line: usize,
    pub column: usize,
}

#[derive(Copy, Clone)]
pub enum Token {
    InvalidToken,
    Identifier(String),
    Literal(Literal),
    Punctuation(String),
}

#[derive(Copy, Clone)]
pub enum Literal {
    String(String),
    Character(String),
    Numeric(String),
    Hex(String),
}<commit_msg>fixed read_character<commit_after>use scaly::containers::Ref;
use scaly::io::Stream;
use scaly::memory::Region;
use scaly::Equal;
use scaly::{Page, String, StringBuilder};

pub struct Lexer {
    pub token: Ref<Token>,
    character: char,
    stream: *mut Stream,
    is_at_end: bool,
    previous_line: usize,
    previous_column: usize,
    pub line: usize,
    pub column: usize,
}

impl Lexer {
    pub fn new(_pr: &Region, _rp: *mut Page, stream: *mut Stream) -> Ref<Lexer> {
        let _r = Region::create(_pr);
        let _token_page = unsafe { (*_rp).allocate_exclusive_page() };
        unsafe { (*_token_page).reset() };
        let mut lexer = Ref::new(
            _rp,
            Lexer {
                stream: stream,
                is_at_end: false,
                token: Ref::new(_token_page, Token::InvalidToken),
                character: 0 as char,
                previous_line: 1,
                previous_column: 0,
                line: 1,
                column: 0,
            },
        );
        lexer.read_character();
        lexer.advance(&_r);
        lexer
    }

    fn read_character(&mut self) {
        unsafe {
            let read_result: i32 = (*self.stream).read_byte();
            if read_result == -1 {
                self.is_at_end = true;
                self.character = 0 as char;
            } else {
                self.character = read_result as u8 as char;
            }
        }
    }

    pub fn advance(&mut self, _pr: &Region) {
        let _r = Region::create(_pr);
        self.skip_whitespace();
        self.previous_line = self.line;
        self.previous_column = self.previous_column;
        if self.is_at_end {
            return;
        }

        let c = self.character;

        if ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) {
            {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_identifier(&_r, _token_page);
            }
            return;
        }

        if (c >= '0') && (c <= '9') {
            {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_numeric_literal(&_r, _token_page);
            }
            return;
        }

        match c {
            '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_operator(&_r, _token_page);
            }

            '\"' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_string_literal(&_r, _token_page);
            }

            '\'' => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = self.scan_character_literal(&_r, _token_page);
            }

            '{' | '}' | '(' | ')' | '[' | ']' | '.' | ',' | ':' | ';' | '?' | '!' | '@' | '#'
            | '$' | '_' | '`' => {
                {
                    let _token_page = Page::own(&self.token);
                    unsafe {
                        (*_token_page).reset();
                    }
                    self.token = Ref::new(
                        _token_page,
                        Token::Punctuation(String::from_character(Page::own(self), c)),
                    );
                }
                self.read_character();
                self.column = self.column + 1;
            }

            _ => {
                let _token_page = Page::own(&self.token);
                unsafe {
                    (*_token_page).reset();
                }
                self.token = Ref::new(_token_page, Token::InvalidToken);
            }
        }
    }

    fn scan_identifier(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut name: Ref<StringBuilder> = StringBuilder::from_character(_r.page, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Identifier(name.to_string(_rp)));
            }

            let c = self.character;
            if ((c >= 'a') && (c <= 'z'))
                || ((c >= 'A') && (c <= 'Z'))
                || ((c >= '0') && (c <= '9'))
                || (c == '_')
            {
                name.append_character(c);
            } else {
                return Ref::new(_rp, Token::Identifier(name.to_string(_rp)));
            }
        }
    }

    fn scan_operator(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut operation: Ref<StringBuilder> =
            StringBuilder::from_character(_r.page, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Identifier(operation.to_string(_rp)));
            }

            match self.character {
                '+' | '-' | '*' | '/' | '=' | '%' | '&' | '|' | '^' | '~' | '<' | '>' => {
                    operation.append_character(self.character)
                }

                _ => return Ref::new(_rp, Token::Identifier(operation.to_string(_rp))),
            }
        }
    }

    fn scan_string_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::new(_r.page);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::InvalidToken);
            }

            match self.character {
                '\"' => {
                    self.read_character();
                    self.column = self.column + 1;
                    return Ref::new(_rp, Token::Literal(Literal::String(value.to_string(_rp))));
                }

                '\\' => {
                    self.read_character();
                    self.column = self.column + 1;
                    match self.character {
                        '\"' | '\\' | '\'' => {
                            value.append_character('\\');
                            value.append_character(self.character);
                        }
                        'n' => {
                            value.append_character('\\');
                            value.append_character('n');
                        }
                        'r' => {
                            value.append_character('\\');
                            value.append_character('r');
                        }
                        't' => {
                            value.append_character('\\');
                            value.append_character('t');
                        }
                        '0' => {
                            value.append_character('\\');
                            value.append_character('0');
                        }
                        _ => return Ref::new(_rp, Token::InvalidToken),
                    }
                }
                _ => value.append_character(self.character),
            }
        }
    }

    fn scan_character_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::new(_r.page);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::InvalidToken);
            }

            match self.character {
                '\'' => {
                    self.read_character();
                    self.column = self.column + 1;
                    return Ref::new(
                        _rp,
                        Token::Literal(Literal::Character(value.to_string(_rp))),
                    );
                }

                '\\' => {
                    self.read_character();
                    self.column = self.column + 1;
                    match self.character {
                        '\"' | '\\' | '\'' => {
                            value.append_character('\\');
                            value.append_character(self.character);
                        }
                        'n' => {
                            value.append_character('\\');
                            value.append_character('n');
                        }
                        'r' => {
                            value.append_character('\\');
                            value.append_character('r');
                        }
                        't' => {
                            value.append_character('\\');
                            value.append_character('t');
                        }
                        '0' => {
                            value.append_character('\\');
                            value.append_character('0');
                        }
                        _ => return Ref::new(_rp, Token::InvalidToken),
                    }
                }
                _ => value.append_character(self.character),
            }
        }
    }

    fn scan_numeric_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::from_character(_rp, self.character);

        self.read_character();
        self.column = self.column + 1;

        if self.is_at_end() {
            return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
        }
        if self.character == 'x' {
            return self.scan_hex_literal(&_r, _rp);
        } else {
            self.read_character();
            self.column = self.column + 1;
        }

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
            }

            let c = self.character;
            if (c >= '0') && (c <= '9') {
                value.append_character(self.character)
            } else {
                return Ref::new(_rp, Token::Literal(Literal::Numeric(value.to_string(_rp))));
            }
        }
    }

    fn scan_hex_literal(&mut self, _pr: &Region, _rp: *mut Page) -> Ref<Token> {
        let _r = Region::create(_pr);
        let mut value: Ref<StringBuilder> = StringBuilder::from_character(_rp, self.character);

        loop {
            self.read_character();
            self.column = self.column + 1;

            if self.is_at_end() {
                return Ref::new(_rp, Token::Literal(Literal::Hex(value.to_string(_rp))));
            }

            let c = self.character;
            if ((c >= '0') && (c <= '9'))
                || ((c >= 'a') && (c <= 'f'))
                || ((c >= 'A') && (c <= 'F'))
            {
                value.append_character(self.character)
            } else {
                return Ref::new(_rp, Token::Literal(Literal::Hex(value.to_string(_rp))));
            }
        }
    }

    fn skip_whitespace(&mut self) {
        loop {
            if self.is_at_end {
                return;
            }

            match self.character {
                ' ' => {
                    self.read_character();;
                    self.column = self.column + 1;
                    continue;
                }

                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    continue;
                }

                '/' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end {
                        return;
                    }

                    if self.character == '/' {
                        self.handle_single_line_comment();
                    } else {
                        if self.character == '*' {
                            self.handle_multi_line_comment();
                        } else {
                            return;
                        }
                    }
                }

                _ => return,
            }
        }
    }

    fn handle_single_line_comment(&mut self) {
        loop {
            if self.is_at_end() {
                return;
            }

            match self.character {
                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    return;
                }

                _ => {
                    self.read_character();
                    self.column = self.column + 1;
                    continue;
                }
            }
        }
    }

    fn handle_multi_line_comment(&mut self) {
        loop {
            if self.is_at_end() {
                return;
            }

            match self.character {
                '/' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end() {
                        return;
                    } else {
                        if self.character == '*' {
                            self.handle_multi_line_comment();
                        } else {
                            return;
                        }
                    }
                }

                '*' => {
                    self.read_character();
                    self.column = self.column + 1;

                    if self.is_at_end() {
                        return;
                    } else {
                        if self.character == '/' {
                            self.read_character();
                            self.column = self.column + 1;
                            return;
                        }
                    }
                }

                '\t' => {
                    self.read_character();
                    self.column = self.column + 4;
                    continue;
                }

                '\r' => {
                    self.read_character();
                    continue;
                }

                '\n' => {
                    self.read_character();
                    self.column = 1;
                    self.line = self.line + 1;
                    return;
                }

                _ => {
                    self.read_character();
                    self.column = self.column + 1;
                    continue;
                }
            }
        }
    }

    pub fn parse_keyword(&self, fixed_string: String) -> bool {
        match *self.token {
            Token::Identifier(name) => return name.equals(&fixed_string),
            _ => return false,
        }
    }

    pub fn parse_identifier(&self, _rp: *mut Page) -> Option<String> {
        match *self.token {
            Token::Identifier(name) => return Some(String::copy(_rp, name)),
            _ => return None,
        }
    }

    pub fn parse_punctuation(&self, fixed_string: String) -> bool {
        match *self.token {
            Token::Punctuation(name) => return name.equals(&fixed_string),
            _ => return false,
        }
    }

    pub fn parse_literal(&self, _rp: *mut Page) -> Option<Literal> {
        match *self.token {
            Token::Literal(Literal::String(name)) => Some(Literal::String(String::copy(_rp, name))),
            Token::Literal(Literal::Character(name)) => {
                Some(Literal::Character(String::copy(_rp, name)))
            }
            Token::Literal(Literal::Numeric(name)) => {
                Some(Literal::Numeric(String::copy(_rp, name)))
            }
            Token::Literal(Literal::Hex(name)) => Some(Literal::Hex(String::copy(_rp, name))),
            _ => None,
        }
    }

    pub fn is_at_end(&self) -> bool {
        self.is_at_end
    }

    pub fn get_position(&self) -> Position {
        Position {
            line: self.line,
            column: self.column,
        }
    }

    pub fn get_previous_position(&self) -> Position {
        Position {
            line: self.previous_line,
            column: self.previous_column,
        }
    }
}

#[derive(Copy, Clone)]
pub struct Position {
    pub line: usize,
    pub column: usize,
}

#[derive(Copy, Clone)]
pub enum Token {
    InvalidToken,
    Identifier(String),
    Literal(Literal),
    Punctuation(String),
}

#[derive(Copy, Clone)]
pub enum Literal {
    String(String),
    Character(String),
    Numeric(String),
    Hex(String),
}
<|endoftext|>


<commit_before>use std::fs;
use std::fs::File;
use std::sync::mpsc;
use std::thread;
use regex::Regex;

const STATS: Vec<String> = vec![
    String::from("cpu.shares"),
    String::from("cpu.cfs_period_us"),
    String::from("cpu.rt_period_us"),
    String::from("cpu.rt_runtime_us"),
    String::from("cpuacct.usage"),
    String::from("cpuacct.usage_percpu"),
    String::from("memory.failcnt"),
    String::from("memory.limit_in_bytes"),
    String::from("memory.max_usage_in_bytes"),
    String::from("memory.move_charge_at_immigrate"),
    String::from("memory.pressure_level"),
    String::from("memory.swappiness"),
    String::from("memory.usage_in_bytes"),
    String::from("blkio.io_merged"),
    String::from("blkio.io_queued"),
    String::from("blkio.io_service_bytes"),
    String::from("blkio.io_service_time"),
    String::from("blkio.io_wait_time"),
    String::from("blkio.io_time"),
    String::from("net_cls.classid"),
    String::from("net_prio.ifpriomap"),
    String::from("net_prio.prioidx"),
];

trait CGroup_Reader {
    fn read(&self, stat: String) -> String;
}

fn which_cgroup(pid: u32) -> (Fn(String) -> String) {
    use std::fs;
    use std::fs::File;
    let path = format!("/proc/{}/cgroup", pid);
    let mut cgroup_f = try!(File::open(path));
    let mut buffer = String::new();
    try!(cgroup_f.read_to_string(&mut buffer));
    |resource| {
        let r_cnt = 0;
        let begin = -1;
        let end = -1;
        let colon_cnt = 0;
        for (c, i) in buffer {
            if c == ':' {
                colon_cnt += 1;
            } else if begin > 0 && end < 0 && c == '\n'{
                end = i;
            } else if colon_cnt == 1 {
                if resource[r_cnt] == c {
                    r_cnt += 1;
                } else if r_cnt > 0 && r_cnt + 1 < resource.capacity() {
                    continue;
                }
            } else {
                if begin < 0 {
                    begin = i + 1;
                }
            }
        }
        return buffer[begin..end];
    }
}

fn read_cgroup(resource: String, name: String, stat: String) -> String {
    let path = format!("/sys/fs/cgroup/{}/{}/{}", resource, name, stat);
    let mut stat_f = try!(fs::File::open(path));
    let mut buffer = String::new();
    try!(stat_f.read_to_string(&mut buffer));
    return buffer;
}

struct CPU_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for CPU_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("cpu,cpuacct", self.name, stat);
    }
}

struct Mem_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for Mem_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("memory", self.name, stat);
    }
}

struct BLKIO_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for BLKIO_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("blkio", self.name, stat);
    }
}

struct Net_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for Net_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("net_cls,net_prio", self.name, stat);
    }
}

pub struct Reader {
    cpu: CPU_Reader,
    mem: Mem_Reader,
    blkio: BLKIO_Reader,
    net: Net_Reader,
}

pub type CGroup_Stat = (String, String);

impl Reader {
    fn read_res(&self, res: String, tx: mpsc::Sender<String>) {
        let ctx = self;
        thread::spawn(|| {
            let shares;
            let cpu_re = Regex::new(r"^cpu(.*)").unwrap();
            let mem_re = Regex::new(r"^mem(.*)").unwrap();
            let blkio_re = Regex::new(r"^mem(.*)").unwrap();
            let net_re = Regex::new(r"^net(.*)").unwrap();
            if cpu_re.is_match(res) {
                shares = ctx.cpu.read(res);
            } else if mem_re.is_match(res) {
                shares = ctx.mem.read(res);
            } else if blkio_re.is_match(res) {
                shares = ctx.blkio.read(res);
            } else {
                shares = ctx.net.read(res);
            }
            tx.send(shares).unwrap();
        })
    }
    fn read(&self) -> Vec<CGroup_Stat> {
        let stats = Vec::<CGroup_Stat>::new();
        let (tx, rx) = mpsc::channel();
        for stat in STATS {
            self.read_res(stat, tx);
        }
        for _ in STATS {
            stats.push(rx.recv().unwrap());
        }
        return stats;
    }
}

pub fn new(pid: u32) -> Reader {
    let name = which_cgroup(pid);
    return Reader{
        cpu: CPU_Reader{
            name: name("cpu"),
            pid: pid,
        },
        mem: Mem_Reader{
            name: name("memory"),
            pid: pid,
        },
        blkio: BLKIO_Reader{
            name: name("blkio"),
            pid: pid,
        },
        net: Net_Reader{
            name: name("net_cls"),
            pid: pid,
        },
    };
}
<commit_msg>WIP handle returning closure<commit_after>use std::fs;
use std::fs::File;
use std::sync::mpsc;
use std::thread;
use regex::Regex;

const STATS: Vec<String> = vec![
    String::from("cpu.shares"),
    String::from("cpu.cfs_period_us"),
    String::from("cpu.rt_period_us"),
    String::from("cpu.rt_runtime_us"),
    String::from("cpuacct.usage"),
    String::from("cpuacct.usage_percpu"),
    String::from("memory.failcnt"),
    String::from("memory.limit_in_bytes"),
    String::from("memory.max_usage_in_bytes"),
    String::from("memory.move_charge_at_immigrate"),
    String::from("memory.pressure_level"),
    String::from("memory.swappiness"),
    String::from("memory.usage_in_bytes"),
    String::from("blkio.io_merged"),
    String::from("blkio.io_queued"),
    String::from("blkio.io_service_bytes"),
    String::from("blkio.io_service_time"),
    String::from("blkio.io_wait_time"),
    String::from("blkio.io_time"),
    String::from("net_cls.classid"),
    String::from("net_prio.ifpriomap"),
    String::from("net_prio.prioidx"),
];

trait CGroup_Reader {
    fn read(&self, stat: String) -> String;
}

fn which_cgroup(pid: u32) -> Box<Fn(String) -> String> {
    use std::fs;
    use std::fs::File;
    let path = format!("/proc/{}/cgroup", pid);
    let mut cgroup_f = try!(File::open(path));
    let mut buffer = String::new();
    try!(cgroup_f.read_to_string(&mut buffer));
    Box::new(|resource| {
        let mut r_cnt = 0;
        let mut begin = -1;
        let mut end = -1;
        let mut colon_cnt = 0;
        for (c, i) in buffer {
            if c == ':' {
                colon_cnt += 1;
            } else if begin > 0 && end < 0 && c == '\n'{
                end = i;
            } else if colon_cnt == 1 {
                if resource[r_cnt] == c {
                    r_cnt += 1;
                } else if r_cnt > 0 && r_cnt + 1 < resource.capacity() {
                    continue;
                }
            } else {
                if begin < 0 {
                    begin = i + 1;
                }
            }
        }
        return buffer[begin..end];
    }).unwrap()
}

fn read_cgroup(resource: String, name: String, stat: String) -> String {
    let path = format!("/sys/fs/cgroup/{}/{}/{}", resource, name, stat);
    let mut stat_f = try!(fs::File::open(path));
    let mut buffer = String::new();
    try!(stat_f.read_to_string(&mut buffer));
    return buffer;
}

struct CPU_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for CPU_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("cpu,cpuacct", self.name, stat);
    }
}

struct Mem_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for Mem_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("memory", self.name, stat);
    }
}

struct BLKIO_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for BLKIO_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("blkio", self.name, stat);
    }
}

struct Net_Reader {
    name: String,
    pid: u32,
}

impl CGroup_Reader for Net_Reader {
    fn read(&self, stat: String) -> String {
        return read_cgroup("net_cls,net_prio", self.name, stat);
    }
}

pub struct Reader {
    cpu: CPU_Reader,
    mem: Mem_Reader,
    blkio: BLKIO_Reader,
    net: Net_Reader,
}

pub type CGroup_Stat = (String, String);

impl Reader {
    fn read_res(&self, res: String, tx: mpsc::Sender<String>) {
        let ctx = self;
        thread::spawn(|| {
            let shares;
            let cpu_re = Regex::new(r"^cpu(.*)").unwrap();
            let mem_re = Regex::new(r"^mem(.*)").unwrap();
            let blkio_re = Regex::new(r"^mem(.*)").unwrap();
            let net_re = Regex::new(r"^net(.*)").unwrap();
            if cpu_re.is_match(res) {
                shares = ctx.cpu.read(res);
            } else if mem_re.is_match(res) {
                shares = ctx.mem.read(res);
            } else if blkio_re.is_match(res) {
                shares = ctx.blkio.read(res);
            } else {
                shares = ctx.net.read(res);
            }
            tx.send(shares).unwrap();
        })
    }
    fn read(&self) -> Vec<CGroup_Stat> {
        let stats = Vec::<CGroup_Stat>::new();
        let (tx, rx) = mpsc::channel();
        for stat in STATS {
            self.read_res(stat, tx);
        }
        for _ in STATS {
            stats.push(rx.recv().unwrap());
        }
        return stats;
    }
}

pub fn new(pid: u32) -> Reader {
    let name = which_cgroup(pid);
    return Reader{
        cpu: CPU_Reader{
            name: name("cpu"),
            pid: pid,
        },
        mem: Mem_Reader{
            name: name("memory"),
            pid: pid,
        },
        blkio: BLKIO_Reader{
            name: name("blkio"),
            pid: pid,
        },
        net: Net_Reader{
            name: name("net_cls"),
            pid: pid,
        },
    };
}
<|endoftext|>