<commit_before>use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::{self, Read, Write};
use std::process;
use std::error::Error;
use std::collections::HashSet;

use tempdir::TempDir;
use toml::Value;
use toml::value::Table;
use cargo::core::{PackageId, Workspace};
use cargo::util::{CargoError, CargoErrorKind, CargoResult, Config};

use super::{ElaborateWorkspace, Manifest};

/// A temporary project
pub struct TempProject<'tmp> {
    pub workspace: Workspace<'tmp>,
    pub temp_dir: TempDir,
    manifest_paths: Vec<PathBuf>,
}

impl<'tmp> TempProject<'tmp> {
    /// Copy needed manifest and lock files from an existing workspace
    pub fn from_workspace(
        orig_workspace: &ElaborateWorkspace,
        config: &'tmp Config,
    ) -> CargoResult<TempProject<'tmp>> {
        // e.g. /path/to/project
        let workspace_root = orig_workspace.workspace.root().to_str().ok_or_else(|| {
            CargoError::from_kind(CargoErrorKind::Msg(format!(
                "Invalid character found in path {}",
                orig_workspace.workspace.root().to_string_lossy()
            )))
        })?;

        let temp_dir = TempDir::new("cargo-outdated")?;
        let manifest_paths = manifest_paths(orig_workspace)?;
        let mut tmp_manifest_paths = vec![];
        for from in &manifest_paths {
            // e.g. /path/to/project/src/sub
            let mut from_dir = from.clone();
            from_dir.pop();
            let from_dir = from_dir.to_string_lossy();
            // e.g. /tmp/cargo.xxx/src/sub
            let mut dest = PathBuf::from(format!(
                "{}/{}",
                temp_dir.path().to_string_lossy(),
                &from_dir[workspace_root.len()..]
            ));
            fs::create_dir_all(&dest)?;
            // e.g. /tmp/cargo.xxx/src/sub/Cargo.toml
            dest.push("Cargo.toml");
            tmp_manifest_paths.push(dest.clone());
            fs::copy(from, &dest)?;
            let lockfile = PathBuf::from(format!("{}/Cargo.lock", from_dir));
            if lockfile.is_file() {
                dest.pop();
                dest.push("Cargo.lock");
                fs::copy(lockfile, dest)?;
            }
        }
        Self::write_manifest_semver_with_paths(&tmp_manifest_paths)?;

        let root_manifest = PathBuf::from(
            String::from(temp_dir.path().to_string_lossy()) + "/Cargo.toml",
        );
        Ok(TempProject {
            workspace: Workspace::new(&root_manifest, config)?,
            temp_dir: temp_dir,
            manifest_paths: tmp_manifest_paths,
        })
    }

    // TODO: instead of process::Command, call cargo update internally
    /// Run `cargo update` against the temporary project
    pub fn cargo_update(&mut self, config: &'tmp Config) -> CargoResult<()> {
        let root_manifest = String::from(self.workspace.root().to_string_lossy()) + "/Cargo.toml";
        if let Err(e) = process::Command::new("cargo")
            .arg("update")
            .arg("--manifest-path")
            .arg(&root_manifest)
            .output()
            .and_then(|v| if v.status.success() {
                Ok(v)
            } else {
                Err(io::Error::new(
                    io::ErrorKind::Other,
                    "did not exit successfully",
                ))
            }) {
            return Err(CargoError::from_kind(CargoErrorKind::Msg(format!(
                "Failed to run 'cargo update' with error '{}'",
                e.description()
            ))));
        }
        self.workspace = Workspace::new(Path::new(&root_manifest), config)?;
        Ok(())
    }

    fn write_manifest<P: AsRef<Path>>(manifest: &Manifest, path: P) -> CargoResult<()> {
        let mut file = try!(File::create(path));
        let serialized = ::toml::to_string(manifest).expect("Failed to serialized Cargo.toml");
        try!(write!(file, "{}", serialized));
        Ok(())
    }

    /// Write manifests with SemVer requirements
    pub fn write_manifest_semver(&self) -> CargoResult<()> {
        Self::write_manifest_semver_with_paths(&self.manifest_paths)
    }

    fn write_manifest_semver_with_paths(manifest_paths: &Vec<PathBuf>) -> CargoResult<()> {
        let bin = {
            let mut bin = Table::new();
            bin.insert("name".to_owned(), Value::String("test".to_owned()));
            bin.insert("path".to_owned(), Value::String("test.rs".to_owned()));
            bin
        };
        for manifest_path in manifest_paths {
            let mut manifest: Manifest = {
                let mut buf = String::new();
                let mut file = File::open(manifest_path)?;<commit_msg>imp: print out cargo update output on error<commit_after>use std::path::{Path, PathBuf};
use std::fs::{self, File};
use std::io::{Read, Write};
use std::process;
use std::error::Error;
use std::collections::HashSet;

use tempdir::TempDir;
use toml::Value;
use toml::value::Table;
use cargo::core::{PackageId, Workspace};
use cargo::util::{CargoError, CargoErrorKind, CargoResult, Config, ProcessError};

use super::{ElaborateWorkspace, Manifest};

/// A temporary project
pub struct TempProject<'tmp> {
    pub workspace: Workspace<'tmp>,
    pub temp_dir: TempDir,
    manifest_paths: Vec<PathBuf>,
}

impl<'tmp> TempProject<'tmp> {
    /// Copy needed manifest and lock files from an existing workspace
    pub fn from_workspace(
        orig_workspace: &ElaborateWorkspace,
        config: &'tmp Config,
    ) -> CargoResult<TempProject<'tmp>> {
        // e.g. /path/to/project
        let workspace_root = orig_workspace.workspace.root().to_str().ok_or_else(|| {
            CargoError::from_kind(CargoErrorKind::Msg(format!(
                "Invalid character found in path {}",
                orig_workspace.workspace.root().to_string_lossy()
            )))
        })?;

        let temp_dir = TempDir::new("cargo-outdated")?;
        let manifest_paths = manifest_paths(orig_workspace)?;
        let mut tmp_manifest_paths = vec![];
        for from in &manifest_paths {
            // e.g. /path/to/project/src/sub
            let mut from_dir = from.clone();
            from_dir.pop();
            let from_dir = from_dir.to_string_lossy();
            // e.g. /tmp/cargo.xxx/src/sub
            let mut dest = PathBuf::from(format!(
                "{}/{}",
                temp_dir.path().to_string_lossy(),
                &from_dir[workspace_root.len()..]
            ));
            fs::create_dir_all(&dest)?;
            // e.g. /tmp/cargo.xxx/src/sub/Cargo.toml
            dest.push("Cargo.toml");
            tmp_manifest_paths.push(dest.clone());
            fs::copy(from, &dest)?;
            let lockfile = PathBuf::from(format!("{}/Cargo.lock", from_dir));
            if lockfile.is_file() {
                dest.pop();
                dest.push("Cargo.lock");
                fs::copy(lockfile, dest)?;
            }
        }
        Self::write_manifest_semver_with_paths(&tmp_manifest_paths)?;

        let root_manifest = PathBuf::from(
            String::from(temp_dir.path().to_string_lossy()) + "/Cargo.toml",
        );
        Ok(TempProject {
            workspace: Workspace::new(&root_manifest, config)?,
            temp_dir: temp_dir,
            manifest_paths: tmp_manifest_paths,
        })
    }

    // TODO: instead of process::Command, call cargo update internally
    /// Run `cargo update` against the temporary project
    pub fn cargo_update(&mut self, config: &'tmp Config) -> CargoResult<()> {
        let root_manifest = String::from(self.workspace.root().to_string_lossy()) + "/Cargo.toml";
        process::Command::new("cargo")
            .arg("update")
            .arg("--manifest-path")
            .arg(&root_manifest)
            .output()
            .map_err(|e| {
                CargoError::from_kind(CargoErrorKind::Msg(format!(
                    "Failed to run 'cargo update' with error '{}'",
                    e.description()
                )))
            })
            .and_then(|v| if v.status.success() {
                Ok(v)
            } else {
                let mut message = String::new();
                if !v.stdout.is_empty() {
                    message += "\nStdout:\n";
                    message += &String::from_utf8_lossy(&v.stdout);
                }
                if !v.stderr.is_empty() {
                    message += "\nStderr:\n";
                    message += &String::from_utf8_lossy(&v.stderr);
                }
                Err(CargoError::from_kind(
                    CargoErrorKind::ProcessErrorKind(ProcessError {
                        desc: format!("`cargo update` did not exit successfully{}", message),
                        exit: Some(v.status),
                        output: Some(v),
                    }),
                ))
            })?;
        self.workspace = Workspace::new(Path::new(&root_manifest), config)?;
        Ok(())
    }

    fn write_manifest<P: AsRef<Path>>(manifest: &Manifest, path: P) -> CargoResult<()> {
        let mut file = try!(File::create(path));
        let serialized = ::toml::to_string(manifest).expect("Failed to serialized Cargo.toml");
        try!(write!(file, "{}", serialized));
        Ok(())
    }

    /// Write manifests with SemVer requirements
    pub fn write_manifest_semver(&self) -> CargoResult<()> {
        Self::write_manifest_semver_with_paths(&self.manifest_paths)
    }

    fn write_manifest_semver_with_paths(manifest_paths: &Vec<PathBuf>) -> CargoResult<()> {
        let bin = {
            let mut bin = Table::new();
            bin.insert("name".to_owned(), Value::String("test".to_owned()));
            bin.insert("path".to_owned(), Value::String("test.rs".to_owned()));
            bin
        };
        for manifest_path in manifest_paths {
            let mut manifest: Manifest = {
                let mut buf = String::new();
                let mut file = File::open(manifest_path)?;
{'fraction_non_alphanumeric': 0.14665996986438976, 'fraction_numerical': 0.00020090406830738323, 'mean_word_length': 2.0909655386525925, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 41, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3371179', 'n_tokens_mistral': 2975, 'n_tokens_neox': 2924, 'n_words': 555}