<commit_before>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::Config;

use anyhow::Context as _;
use cargo_util::paths;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        target_dir = target_dir.join("doc");
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    let profiles = Profiles::new(ws, opts.requested_profile)?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        return rm_rf(&target_dir.into_path_unlocked(), config);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            let mut suggestion = String::new();
            suggestion.push_str(&lev_distance::closest_msg(
                &spec.name(),
                resolve.iter(),
                |id| id.name().as_str(),
            ));
            anyhow::bail!(
                "package ID specification `{}` did not match any packages{}",
                spec,
                suggestion
            );
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            let dir = escape_glob_path(layout.fingerprint())?;
            rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    let dir = escape_glob_path(layout.build())?;
                    rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = match target.kind() {
                        TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
                            (layout.examples(), Some(layout.examples()))
                        }
                        // Tests/benchmarks are never uplifted.
                        TargetKind::Test | TargetKind::Bench => (layout.deps(), None),
                        _ => (layout.deps(), Some(layout.dest())),
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        let dir_glob = escape_glob_path(dir)?;
                        let dir_glob = Path::new(&dir_glob);

                        rm_rf_glob(&dir_glob.join(&hashed_name), config)?;
                        rm_rf(&dir.join(&unhashed_name), config)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir_glob.join(format!("{}-*.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config)?;
                        // Remove split-debuginfo files generated by rustc.
                        let split_debuginfo_obj = dir_glob.join(format!("{}.*.o", crate_name));
                        rm_rf_glob(&split_debuginfo_obj, config)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config)?;

                        // Remove the uplifted copy.
                        if let Some(uplift_dir) = uplift_dir {
                            let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                            rm_rf(&uplifted_path, config)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config)?;
                        }
                    }
                    // TODO: what to do about build_script_build?
                    let dir = escape_glob_path(layout.incremental())?;
                    let incremental = Path::new(&dir).join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config)?;
                }
            }
        }
    }

    Ok(())
}

fn escape_glob_path(pattern: &Path) -> CargoResult<String> {
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    Ok(glob::Pattern::escape(pattern))
}

fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::symlink_metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path).with_context(|| "could not remove build directory")?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path).with_context(|| "failed to remove build artifact")?;
    }
    Ok(())
}
<commit_msg>Add a progress indicator for cargo clean<commit_after>use crate::core::compiler::{CompileKind, CompileMode, Layout, RustcTargetData};
use crate::core::profiles::Profiles;
use crate::core::{PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::util::errors::CargoResult;
use crate::util::interning::InternedString;
use crate::util::lev_distance;
use crate::util::{Config, Progress, ProgressStyle};

use anyhow::Context as _;
use cargo_util::paths;
use std::fs;
use std::path::Path;

pub struct CleanOptions<'a> {
    pub config: &'a Config,
    /// A list of packages to clean. If empty, everything is cleaned.
    pub spec: Vec<String>,
    /// The target arch triple to clean, or None for the host arch
    pub targets: Vec<String>,
    /// Whether to clean the release directory
    pub profile_specified: bool,
    /// Whether to clean the directory of a certain build profile
    pub requested_profile: InternedString,
    /// Whether to just clean the doc directory
    pub doc: bool,
}

/// Cleans the package's build artifacts.
pub fn clean(ws: &Workspace<'_>, opts: &CleanOptions<'_>) -> CargoResult<()> {
    let mut target_dir = ws.target_dir();
    let config = ws.config();

    // If the doc option is set, we just want to delete the doc directory.
    if opts.doc {
        let mut progress = Progress::with_style("Cleaning", ProgressStyle::Percentage, config);
        target_dir = target_dir.join("doc");
        return rm_rf_with_progress(&target_dir.into_path_unlocked(), &mut progress);
    }

    let profiles = Profiles::new(ws, opts.requested_profile)?;

    if opts.profile_specified {
        // After parsing profiles we know the dir-name of the profile, if a profile
        // was passed from the command line. If so, delete only the directory of
        // that profile.
        let dir_name = profiles.get_dir_name();
        target_dir = target_dir.join(dir_name);
    }

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let mut progress = Progress::with_style("Cleaning", ProgressStyle::Percentage, config);
        return rm_rf_with_progress(&target_dir.into_path_unlocked(), &mut progress);
    }

    // Clean specific packages.
    let requested_kinds = CompileKind::from_requested_targets(config, &opts.targets)?;
    let target_data = RustcTargetData::new(ws, &requested_kinds)?;
    let (pkg_set, resolve) = ops::resolve_ws(ws)?;
    let prof_dir_name = profiles.get_dir_name();
    let host_layout = Layout::new(ws, None, &prof_dir_name)?;
    // Convert requested kinds to a Vec of layouts.
    let target_layouts: Vec<(CompileKind, Layout)> = requested_kinds
        .into_iter()
        .filter_map(|kind| match kind {
            CompileKind::Target(target) => match Layout::new(ws, Some(target), &prof_dir_name) {
                Ok(layout) => Some(Ok((kind, layout))),
                Err(e) => Some(Err(e)),
            },
            CompileKind::Host => None,
        })
        .collect::<CargoResult<_>>()?;
    // A Vec of layouts. This is a little convoluted because there can only be
    // one host_layout.
    let layouts = if opts.targets.is_empty() {
        vec![(CompileKind::Host, &host_layout)]
    } else {
        target_layouts
            .iter()
            .map(|(kind, layout)| (*kind, layout))
            .collect()
    };
    // Create a Vec that also includes the host for things that need to clean both.
    let layouts_with_host: Vec<(CompileKind, &Layout)> =
        std::iter::once((CompileKind::Host, &host_layout))
            .chain(layouts.iter().map(|(k, l)| (*k, *l)))
            .collect();

    // Cleaning individual rustdoc crates is currently not supported.
    // For example, the search index would need to be rebuilt to fully
    // remove it (otherwise you're left with lots of broken links).
    // Doc tests produce no output.

    // Get Packages for the specified specs.
    let mut pkg_ids = Vec::new();
    for spec_str in opts.spec.iter() {
        // Translate the spec to a Package.
        let spec = PackageIdSpec::parse(spec_str)?;
        if spec.version().is_some() {
            config.shell().warn(&format!(
                "version qualifier in `-p {}` is ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        if spec.url().is_some() {
            config.shell().warn(&format!(
                "url qualifier in `-p {}` ignored, \
                cleaning all versions of `{}` found",
                spec_str,
                spec.name()
            ))?;
        }
        let matches: Vec<_> = resolve.iter().filter(|id| spec.matches(*id)).collect();
        if matches.is_empty() {
            let mut suggestion = String::new();
            suggestion.push_str(&lev_distance::closest_msg(
                &spec.name(),
                resolve.iter(),
                |id| id.name().as_str(),
            ));
            anyhow::bail!(
                "package ID specification `{}` did not match any packages{}",
                spec,
                suggestion
            );
        }
        pkg_ids.extend(matches);
    }
    let packages = pkg_set.get_many(pkg_ids)?;

    let mut progress = Progress::with_style("Cleaning", ProgressStyle::Ratio, config);
    for (pkg_idx, pkg) in packages.iter().enumerate() {
        let pkg_dir = format!("{}-*", pkg.name());
        progress.tick_now(pkg_idx + 1, packages.len(), &format!(": {}", pkg.name()))?;

        // Clean fingerprints.
        for (_, layout) in &layouts_with_host {
            let dir = escape_glob_path(layout.fingerprint())?;
            rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
        }

        for target in pkg.targets() {
            if target.is_custom_build() {
                // Get both the build_script_build and the output directory.
                for (_, layout) in &layouts_with_host {
                    let dir = escape_glob_path(layout.build())?;
                    rm_rf_glob(&Path::new(&dir).join(&pkg_dir), config)?;
                }
                continue;
            }
            let crate_name = target.crate_name();
            for &mode in &[
                CompileMode::Build,
                CompileMode::Test,
                CompileMode::Check { test: false },
            ] {
                for (compile_kind, layout) in &layouts {
                    let triple = target_data.short_name(compile_kind);

                    let (file_types, _unsupported) = target_data
                        .info(*compile_kind)
                        .rustc_outputs(mode, target.kind(), triple)?;
                    let (dir, uplift_dir) = match target.kind() {
                        TargetKind::ExampleBin | TargetKind::ExampleLib(..) => {
                            (layout.examples(), Some(layout.examples()))
                        }
                        // Tests/benchmarks are never uplifted.
                        TargetKind::Test | TargetKind::Bench => (layout.deps(), None),
                        _ => (layout.deps(), Some(layout.dest())),
                    };
                    for file_type in file_types {
                        // Some files include a hash in the filename, some don't.
                        let hashed_name = file_type.output_filename(target, Some("*"));
                        let unhashed_name = file_type.output_filename(target, None);
                        let dir_glob = escape_glob_path(dir)?;
                        let dir_glob = Path::new(&dir_glob);

                        rm_rf_glob(&dir_glob.join(&hashed_name), config)?;
                        rm_rf(&dir.join(&unhashed_name), config)?;
                        // Remove dep-info file generated by rustc. It is not tracked in
                        // file_types. It does not have a prefix.
                        let hashed_dep_info = dir_glob.join(format!("{}-*.d", crate_name));
                        rm_rf_glob(&hashed_dep_info, config)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config)?;
                        // Remove split-debuginfo files generated by rustc.
                        let split_debuginfo_obj = dir_glob.join(format!("{}.*.o", crate_name));
                        rm_rf_glob(&split_debuginfo_obj, config)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config)?;

                        // Remove the uplifted copy.
                        if let Some(uplift_dir) = uplift_dir {
                            let uplifted_path = uplift_dir.join(file_type.uplift_filename(target));
                            rm_rf(&uplifted_path, config)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config)?;
                        }
                    }
                    // TODO: what to do about build_script_build?
                    let dir = escape_glob_path(layout.incremental())?;
                    let incremental = Path::new(&dir).join(format!("{}-*", crate_name));
                    rm_rf_glob(&incremental, config)?;
                }
            }
        }
    }

    Ok(())
}

fn escape_glob_path(pattern: &Path) -> CargoResult<String> {
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    Ok(glob::Pattern::escape(pattern))
}

fn rm_rf_glob(pattern: &Path, config: &Config) -> CargoResult<()> {
    // TODO: Display utf8 warning to user?  Or switch to globset?
    let pattern = pattern
        .to_str()
        .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?;
    for path in glob::glob(pattern)? {
        rm_rf(&path?, config)?;
    }
    Ok(())
}

fn rm_rf_with_progress(path: &Path, progress: &mut Progress<'_>) -> CargoResult<()> {
    let num_paths = walkdir::WalkDir::new(path).into_iter().count();
    for (idx, entry) in walkdir::WalkDir::new(path)
        .contents_first(true)
        .into_iter()
        .enumerate()
    {
        progress.tick(std::cmp::min(idx + 1, num_paths), num_paths, "")?;
        if let Ok(entry) = entry {
            if entry.file_type().is_dir() {
                paths::remove_dir(entry.path())?;
            } else {
                paths::remove_file(entry.path())?;
            }
        }
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config) -> CargoResult<()> {
    let m = fs::symlink_metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_dir_all(path).with_context(|| "could not remove build directory")?;
    } else if m.is_ok() {
        config
            .shell()
            .verbose(|shell| shell.status("Removing", path.display()))?;
        paths::remove_file(path).with_context(|| "failed to remove build artifact")?;
    }
    Ok(())
}
<|endoftext|>


<commit_before>use crate::exceptions::{CalculateError, IOError};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::DirEntry;
use std::path::{Path, PathBuf};

// This type exactly matches the type of array elements
// from hyperfine's output. Deriving `Serialize` and `Deserialize`
// gives us read and write capabilities via json_serde.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

// This type exactly matches the type of hyperfine's output.
// Deriving `Serialize` and `Deserialize` gives us read and
// write capabilities via json_serde.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

// Output data from a comparison between runs on the baseline
// and dev branches.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

// The full output from a comparison between runs on the baseline
// and dev branches.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

// A type to describe which measurement we are working with. This
// information is parsed from the filename of hyperfine's output.
#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

// Given two measurements, return all the calculations. Calculations are
// flagged as regressions or not regressions.
fn calculate(metric: &str, dev: &Measurement, baseline: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.20; // 20% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median,
                dev: dev.median,
            },
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev,
                dev: dev.stddev,
            },
        },
    ]
}

// Given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file.
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, CalculateError> {
    fs::read_dir(results_directory)
        .or_else(|e| Err(IOError::ReadErr(results_directory.to_path_buf(), Some(e))))
        .or_else(|e| Err(CalculateError::CalculateIOError(e)))?
        .into_iter()
        .map(|entry| {
            let ent: DirEntry = entry
                .or_else(|e| Err(IOError::ReadErr(results_directory.to_path_buf(), Some(e))))
                .or_else(|e| Err(CalculateError::CalculateIOError(e)))?;

            Ok(ent.path())
        })
        .collect::<Result<Vec<PathBuf>, CalculateError>>()?
        .iter()
        .filter(|path| {
            path.extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|path| {
            fs::read_to_string(path)
                .or_else(|e| Err(IOError::BadFileContentsErr(path.clone(), Some(e))))
                .or_else(|e| Err(CalculateError::CalculateIOError(e)))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(CalculateError::BadJSONErr(path.clone(), Some(e))))
                })
                .map(|m| (path.clone(), m))
        })
        .collect()
}

// Given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename.
fn calculate_regressions(
    measurements: &[(&PathBuf, &Measurement)],
) -> Result<Vec<Calculation>, CalculateError> {
    /*
        Strategy of this function body:
        1. [Measurement] -> [MeasurementGroup]
        2. Sort the MeasurementGroups
        3. Group the MeasurementGroups by "run"
        4. Call `calculate` with the two resulting Measurements as input
    */

    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| IOError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| IOError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: (*m).clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, IOError>>()
        .or_else(|e| Err(CalculateError::CalculateIOError(e)))?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let calculations: Vec<Calculation> = sorted_measurement_groups
        .iter()
        .group_by(|x| &x.run)
        .into_iter()
        .map(|(_, g)| {
            let mut groups: Vec<&MeasurementGroup> = g.collect();
            groups.sort_by(|x, y| x.version.cmp(&y.version));

            match groups.len() {
                2 => {
                    let dev = &groups[1];
                    let baseline = &groups[0];

                    if dev.version == "dev" && baseline.version == "baseline" {
                        Ok(calculate(&dev.run, &dev.measurement, &baseline.measurement))
                    } else {
                        Err(CalculateError::BadBranchNameErr(
                            baseline.version.clone(),
                            dev.version.clone(),
                        ))
                    }
                }
                i => {
                    let gs: Vec<MeasurementGroup> = groups.into_iter().map(|x| x.clone()).collect();
                    Err(CalculateError::BadGroupSizeErr(i, gs))
                }
            }
        })
        .collect::<Result<Vec<Vec<Calculation>>, CalculateError>>()?
        .concat();

    Ok(calculations)
}

// Top-level function. Given a path for the result directory, call the above
// functions to compare and collect calculations. Calculations include both
// metrics that fall within the threshold and regressions.
pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, CalculateError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        if v.len() <= 0 {
            Err(CalculateError::NoResultsErr(results_directory.clone()))
        // we expect two runs for each project-metric pairing: one for each branch, baseline
        // and dev. An odd result count is unexpected.
        } else if v.len() % 2 == 1 {
            Err(CalculateError::OddResultsCountErr(
                v.len(),
                results_directory.clone(),
            ))
        } else {
            // otherwise, we can do our comparisons
            let measurements = v
                .iter()
                // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
                .map(|(p, ms)| (p, &ms.results[0]))
                .collect::<Vec<(&PathBuf, &Measurement)>>();

            calculate_regressions(&measurements[..])
        }
    })
}
<commit_msg>add test for regression calculation<commit_after>use crate::exceptions::{CalculateError, IOError};
use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::fs;
use std::fs::DirEntry;
use std::path::{Path, PathBuf};

// This type exactly matches the type of array elements
// from hyperfine's output. Deriving `Serialize` and `Deserialize`
// gives us read and write capabilities via json_serde.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurement {
    pub command: String,
    pub mean: f64,
    pub stddev: f64,
    pub median: f64,
    pub user: f64,
    pub system: f64,
    pub min: f64,
    pub max: f64,
    pub times: Vec<f64>,
}

// This type exactly matches the type of hyperfine's output.
// Deriving `Serialize` and `Deserialize` gives us read and
// write capabilities via json_serde.
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Measurements {
    pub results: Vec<Measurement>,
}

// Output data from a comparison between runs on the baseline
// and dev branches.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Data {
    pub threshold: f64,
    pub difference: f64,
    pub baseline: f64,
    pub dev: f64,
}

// The full output from a comparison between runs on the baseline
// and dev branches.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Calculation {
    pub metric: String,
    pub regression: bool,
    pub data: Data,
}

// A type to describe which measurement we are working with. This
// information is parsed from the filename of hyperfine's output.
#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    pub version: String,
    pub run: String,
    pub measurement: Measurement,
}

// Given two measurements, return all the calculations. Calculations are
// flagged as regressions or not regressions.
fn calculate(metric: &str, dev: &Measurement, baseline: &Measurement) -> Vec<Calculation> {
    let median_threshold = 1.05; // 5% regression threshold
    let median_difference = dev.median / baseline.median;

    let stddev_threshold = 1.20; // 20% regression threshold
    let stddev_difference = dev.stddev / baseline.stddev;

    vec![
        Calculation {
            metric: ["median", metric].join("_"),
            regression: median_difference > median_threshold,
            data: Data {
                threshold: median_threshold,
                difference: median_difference,
                baseline: baseline.median,
                dev: dev.median,
            },
        },
        Calculation {
            metric: ["stddev", metric].join("_"),
            regression: stddev_difference > stddev_threshold,
            data: Data {
                threshold: stddev_threshold,
                difference: stddev_difference,
                baseline: baseline.stddev,
                dev: dev.stddev,
            },
        },
    ]
}

// Given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file.
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, CalculateError> {
    fs::read_dir(results_directory)
        .or_else(|e| Err(IOError::ReadErr(results_directory.to_path_buf(), Some(e))))
        .or_else(|e| Err(CalculateError::CalculateIOError(e)))?
        .into_iter()
        .map(|entry| {
            let ent: DirEntry = entry
                .or_else(|e| Err(IOError::ReadErr(results_directory.to_path_buf(), Some(e))))
                .or_else(|e| Err(CalculateError::CalculateIOError(e)))?;

            Ok(ent.path())
        })
        .collect::<Result<Vec<PathBuf>, CalculateError>>()?
        .iter()
        .filter(|path| {
            path.extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|path| {
            fs::read_to_string(path)
                .or_else(|e| Err(IOError::BadFileContentsErr(path.clone(), Some(e))))
                .or_else(|e| Err(CalculateError::CalculateIOError(e)))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(CalculateError::BadJSONErr(path.clone(), Some(e))))
                })
                .map(|m| (path.clone(), m))
        })
        .collect()
}

// Given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename.
fn calculate_regressions(
    measurements: &[(&PathBuf, &Measurement)],
) -> Result<Vec<Calculation>, CalculateError> {
    /*
        Strategy of this function body:
        1. [Measurement] -> [MeasurementGroup]
        2. Sort the MeasurementGroups
        3. Group the MeasurementGroups by "run"
        4. Call `calculate` with the two resulting Measurements as input
    */

    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| IOError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| IOError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join("_"),
                        measurement: (*m).clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, IOError>>()
        .or_else(|e| Err(CalculateError::CalculateIOError(e)))?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let calculations: Vec<Calculation> = sorted_measurement_groups
        .iter()
        .group_by(|x| &x.run)
        .into_iter()
        .map(|(_, g)| {
            let mut groups: Vec<&MeasurementGroup> = g.collect();
            groups.sort_by(|x, y| x.version.cmp(&y.version));

            match groups.len() {
                2 => {
                    let dev = &groups[1];
                    let baseline = &groups[0];

                    if dev.version == "dev" && baseline.version == "baseline" {
                        Ok(calculate(&dev.run, &dev.measurement, &baseline.measurement))
                    } else {
                        Err(CalculateError::BadBranchNameErr(
                            baseline.version.clone(),
                            dev.version.clone(),
                        ))
                    }
                }
                i => {
                    let gs: Vec<MeasurementGroup> = groups.into_iter().map(|x| x.clone()).collect();
                    Err(CalculateError::BadGroupSizeErr(i, gs))
                }
            }
        })
        .collect::<Result<Vec<Vec<Calculation>>, CalculateError>>()?
        .concat();

    Ok(calculations)
}

// Top-level function. Given a path for the result directory, call the above
// functions to compare and collect calculations. Calculations include both
// metrics that fall within the threshold and regressions.
pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Calculation>, CalculateError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        if v.len() <= 0 {
            Err(CalculateError::NoResultsErr(results_directory.clone()))
        // we expect two runs for each project-metric pairing: one for each branch, baseline
        // and dev. An odd result count is unexpected.
        } else if v.len() % 2 == 1 {
            Err(CalculateError::OddResultsCountErr(
                v.len(),
                results_directory.clone(),
            ))
        } else {
            // otherwise, we can do our comparisons
            let measurements = v
                .iter()
                // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
                .map(|(p, ms)| (p, &ms.results[0]))
                .collect::<Vec<(&PathBuf, &Measurement)>>();

            calculate_regressions(&measurements[..])
        }
    })
}

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

    #[test]
    fn detects_5_percent_regression() {
        let dev = Measurement {
            command: "some command".to_owned(),
            mean:    1.06,
            stddev:  1.06,
            median:  1.06,
            user:    1.06,
            system:  1.06,
            min:     1.06,
            max:     1.06,
            times:  vec![],
        };

        let baseline = Measurement {
            command: "some command".to_owned(),
            mean:    1.00,
            stddev:  1.00,
            median:  1.00,
            user:    1.00,
            system:  1.00,
            min:     1.00,
            max:     1.00,
            times:  vec![],
        };

        let calculations = calculate("test_metric", &dev, &baseline);
        let regressions: Vec<&Calculation> = calculations
            .iter()
            .filter(|calc| calc.regression)
            .collect();

        // expect one regression for median
        println!("{:#?}", regressions);
        assert_eq!(regressions.len(), 1);
        assert_eq!(regressions[0].metric, "median_test_metric");
    }

}<|endoftext|>


<commit_before>use crate::cached_size::CachedSize;
use crate::descriptor::field_descriptor_proto::Type;
use crate::message_dyn::MessageDyn;
use crate::reflect::dynamic::map::DynamicMap;
use crate::reflect::dynamic::optional::DynamicOptional;
use crate::reflect::dynamic::repeated::DynamicRepeated;
use crate::reflect::map::ReflectMap;
use crate::reflect::repeated::ReflectRepeated;
use crate::reflect::value::value_ref::ReflectValueMut;
use crate::reflect::FieldDescriptor;
use crate::reflect::MessageDescriptor;
use crate::reflect::ReflectFieldRef;
use crate::reflect::ReflectMapMut;
use crate::reflect::ReflectMapRef;
use crate::reflect::ReflectRepeatedMut;
use crate::reflect::ReflectRepeatedRef;
use crate::reflect::ReflectValueBox;
use crate::reflect::ReflectValueRef;
use crate::reflect::RuntimeFieldType;
use crate::reflect::Syntax;
use crate::rt::bytes_size;
use crate::rt::compute_raw_varint32_size;
use crate::rt::string_size;
use crate::rt::tag_size;
use crate::rt::value_size;
use crate::rt::value_varint_zigzag_size;
use crate::wire_format::WireType;
use crate::Clear;
use crate::CodedInputStream;
use crate::CodedOutputStream;
use crate::Message;
use crate::ProtobufResult;
use crate::UnknownFields;

pub(crate) mod map;
pub(crate) mod optional;
pub(crate) mod repeated;

#[derive(Debug, Clone)]
enum DynamicFieldValue {
    Singular(DynamicOptional),
    Repeated(DynamicRepeated),
    Map(DynamicMap),
}

impl DynamicFieldValue {
    fn as_ref(&self) -> ReflectFieldRef {
        match self {
            DynamicFieldValue::Singular(v) => ReflectFieldRef::Optional(v.get()),
            DynamicFieldValue::Repeated(r) => ReflectFieldRef::Repeated(ReflectRepeatedRef::new(r)),
            DynamicFieldValue::Map(m) => ReflectFieldRef::Map(ReflectMapRef::new(m)),
        }
    }

    fn clear(&mut self) {
        match self {
            DynamicFieldValue::Singular(o) => o.clear(),
            DynamicFieldValue::Repeated(r) => r.clear(),
            DynamicFieldValue::Map(m) => m.clear(),
        }
    }
}

impl DynamicFieldValue {
    fn default_for_field(field: &FieldDescriptor) -> DynamicFieldValue {
        match field.runtime_field_type() {
            RuntimeFieldType::Singular(s) => DynamicFieldValue::Singular(DynamicOptional::none(s)),
            RuntimeFieldType::Repeated(r) => DynamicFieldValue::Repeated(DynamicRepeated::new(r)),
            RuntimeFieldType::Map(k, v) => DynamicFieldValue::Map(DynamicMap::new(k, v)),
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct DynamicMessage {
    pub(crate) descriptor: MessageDescriptor,
    /// Fields by index in the description.
    /// This field is lazy-init: it is empty when created.
    fields: Box<[DynamicFieldValue]>,
    unknown_fields: UnknownFields,
    cached_size: CachedSize,
}

impl DynamicMessage {
    pub(crate) fn new(descriptor: MessageDescriptor) -> DynamicMessage {
        DynamicMessage {
            descriptor,
            fields: Vec::new().into_boxed_slice(),
            unknown_fields: UnknownFields::new(),
            cached_size: CachedSize::new(),
        }
    }

    fn init_fields(&mut self) {
        if self.fields.is_empty() {
            self.fields = self
                .descriptor
                .fields()
                .map(|f| DynamicFieldValue::default_for_field(&f))
                .collect();
        }
    }

    pub(crate) fn get_reflect<'a>(&'a self, field: &FieldDescriptor) -> ReflectFieldRef<'a> {
        assert_eq!(self.descriptor, field.message_descriptor);
        if self.fields.is_empty() {
            ReflectFieldRef::default_for_field(field)
        } else {
            self.fields[field.index].as_ref()
        }
    }

    pub fn clear_field(&mut self, field: &FieldDescriptor) {
        assert_eq!(field.message_descriptor, self.descriptor);
        if self.fields.is_empty() {
            return;
        }

        self.fields[field.index].clear();
    }

    fn clear_oneof_group_fields_except(&mut self, field: &FieldDescriptor) {
        if let Some(oneof) = field.containing_oneof() {
            for next in oneof.fields() {
                if &next == field {
                    continue;
                }
                self.clear_field(&next);
            }
        }
    }

    pub(crate) fn mut_singular_field_or_default<'a>(
        &'a mut self,
        field: &FieldDescriptor,
    ) -> ReflectValueMut<'a> {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        self.clear_oneof_group_fields_except(field);
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Singular(f) => f.mut_or_default(),
            _ => panic!("Not a singular field"),
        }
    }

    pub(crate) fn mut_repeated<'a>(
        &'a mut self,
        field: &FieldDescriptor,
    ) -> ReflectRepeatedMut<'a> {
        assert_eq!(self.descriptor, field.message_descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Repeated(r) => ReflectRepeatedMut::new(r),
            _ => panic!("Not a repeated field: {}", field),
        }
    }

    pub(crate) fn mut_map<'a>(&'a mut self, field: &FieldDescriptor) -> ReflectMapMut<'a> {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Map(m) => ReflectMapMut::new(m),
            _ => panic!("Not a map field: {}", field),
        }
    }

    pub(crate) fn set_field(&mut self, field: &FieldDescriptor, value: ReflectValueBox) {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Singular(s) => s.set(value),
            _ => panic!("Not a singular field: {}", field),
        }
    }

    pub fn downcast_ref(message: &dyn MessageDyn) -> &DynamicMessage {
        <dyn MessageDyn>::downcast_ref(message).unwrap()
    }

    pub fn downcast_mut(message: &mut dyn MessageDyn) -> &mut DynamicMessage {
        <dyn MessageDyn>::downcast_mut(message).unwrap()
    }
}

impl Clear for DynamicMessage {
    fn clear(&mut self) {
        unimplemented!()
    }
}

impl Message for DynamicMessage {
    fn descriptor_by_instance(&self) -> MessageDescriptor {
        self.descriptor.clone()
    }

    fn is_initialized(&self) -> bool {
        // TODO: this check can be much faster for proto3 without contained proto2 messages.
        for f in self.descriptor.fields() {
            let fv = self.get_reflect(&f);
            match fv {
                ReflectFieldRef::Optional(s) => match s {
                    None => {
                        if f.is_required() {
                            return false;
                        }
                    }
                    Some(v) => {
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                },
                ReflectFieldRef::Repeated(r) => {
                    for v in &r {
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                }
                ReflectFieldRef::Map(m) => {
                    for (_k, v) in &m {
                        // Keys cannot be messages, so only check values.
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                }
            }
        }
        true
    }

    fn merge_from(&mut self, _is: &mut CodedInputStream) -> ProtobufResult<()> {
        unimplemented!()
    }

    fn write_to_with_cached_sizes(&self, _os: &mut CodedOutputStream) -> ProtobufResult<()> {
        unimplemented!()
    }

    fn compute_size(&self) -> u32 {
        let is_proto3 = self.descriptor.file_descriptor().syntax() == Some(Syntax::Proto3);
        let mut m_size = 0;
        for field_desc in self.descriptor.fields() {
            let field_number = field_desc.get_proto().get_number() as u32;
            match field_desc.get_reflect(self) {
                ReflectFieldRef::Optional(v) => {
                    if let Some(v) = v {
                        if !is_proto3 || v.is_non_zero() {
                            m_size += compute_singular_size(
                                &field_desc.get_proto().get_field_type(),
                                field_number,
                                &v,
                            );
                        }
                    }
                }
                ReflectFieldRef::Repeated(vs) => {
                    if !vs.is_empty() {
                        // TODO: use packed when needed.
                        for v in vs {
                            m_size += compute_singular_size(
                                &field_desc.get_proto().get_field_type(),
                                field_number,
                                &v,
                            );
                        }
                    }
                }
                ReflectFieldRef::Map(_v) => {
                    unimplemented!()
                }
            }
        }
        // TODO: unknown fields
        m_size
    }

    fn get_cached_size(&self) -> u32 {
        self.cached_size.get()
    }

    fn get_unknown_fields(&self) -> &UnknownFields {
        &self.unknown_fields
    }

    fn mut_unknown_fields(&mut self) -> &mut UnknownFields {
        &mut self.unknown_fields
    }

    fn new() -> Self
    where
        Self: Sized,
    {
        panic!("DynamicMessage cannot be constructed directly")
    }

    fn default_instance() -> &'static Self
    where
        Self: Sized,
    {
        panic!("There's no default instance for dynamic message")
    }
}

/// Compute singular field size
fn compute_singular_size(proto_type: &Type, field_number: u32, v: &ReflectValueRef) -> u32 {
    match proto_type {
        Type::TYPE_ENUM => {
            if let ReflectValueRef::Enum(_e, enum_v) = v {
                value_size(field_number, *enum_v, WireType::WireTypeVarint)
            } else {
                panic!("Protobuf type and Runtime type mismatch");
            }
        }
        Type::TYPE_MESSAGE => {
            let msg_v = v.to_message().unwrap();
            let len = msg_v.compute_size_dyn();
            tag_size(field_number) + compute_raw_varint32_size(len) + len
        }
        Type::TYPE_GROUP => {
            unimplemented!()
        }
        Type::TYPE_UINT32 => {
            let typed_v = v.to_u32().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_UINT64 => {
            let typed_v = v.to_u64().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_INT32 => {
            let typed_v = v.to_i32().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_INT64 => {
            let typed_v = v.to_i64().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_SINT32 => {
            let typed_v = v.to_i32().unwrap();
            value_varint_zigzag_size(field_number, typed_v)
        }
        Type::TYPE_SINT64 => {
            let typed_v = v.to_i64().unwrap();
            value_varint_zigzag_size(field_number, typed_v)
        }
        Type::TYPE_FIXED32 => tag_size(field_number) + 4,
        Type::TYPE_FIXED64 => tag_size(field_number) + 8,
        Type::TYPE_SFIXED32 => tag_size(field_number) + 4,
        Type::TYPE_SFIXED64 => tag_size(field_number) + 8,
        Type::TYPE_BOOL => {
            let typed_v = v.to_bool().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_STRING => {
            let typed_v = v.to_str().unwrap();
            string_size(field_number, typed_v)
        }
        Type::TYPE_BYTES => {
            let typed_v = v.to_bytes().unwrap();
            bytes_size(field_number, typed_v)
        }
        Type::TYPE_FLOAT => tag_size(field_number) + 4,
        Type::TYPE_DOUBLE => tag_size(field_number) + 8,
    }
}
<commit_msg>singular_write_to utility<commit_after>use crate::cached_size::CachedSize;
use crate::descriptor::field_descriptor_proto::Type;
use crate::message_dyn::MessageDyn;
use crate::reflect::dynamic::map::DynamicMap;
use crate::reflect::dynamic::optional::DynamicOptional;
use crate::reflect::dynamic::repeated::DynamicRepeated;
use crate::reflect::map::ReflectMap;
use crate::reflect::repeated::ReflectRepeated;
use crate::reflect::value::value_ref::ReflectValueMut;
use crate::reflect::FieldDescriptor;
use crate::reflect::MessageDescriptor;
use crate::reflect::ReflectFieldRef;
use crate::reflect::ReflectMapMut;
use crate::reflect::ReflectMapRef;
use crate::reflect::ReflectRepeatedMut;
use crate::reflect::ReflectRepeatedRef;
use crate::reflect::ReflectValueBox;
use crate::reflect::ReflectValueRef;
use crate::reflect::RuntimeFieldType;
use crate::reflect::Syntax;
use crate::rt::bytes_size;
use crate::rt::compute_raw_varint32_size;
use crate::rt::string_size;
use crate::rt::tag_size;
use crate::rt::value_size;
use crate::rt::value_varint_zigzag_size;
use crate::wire_format::WireType;
use crate::Clear;
use crate::CodedInputStream;
use crate::CodedOutputStream;
use crate::Message;
use crate::ProtobufResult;
use crate::UnknownFields;

pub(crate) mod map;
pub(crate) mod optional;
pub(crate) mod repeated;

#[derive(Debug, Clone)]
enum DynamicFieldValue {
    Singular(DynamicOptional),
    Repeated(DynamicRepeated),
    Map(DynamicMap),
}

impl DynamicFieldValue {
    fn as_ref(&self) -> ReflectFieldRef {
        match self {
            DynamicFieldValue::Singular(v) => ReflectFieldRef::Optional(v.get()),
            DynamicFieldValue::Repeated(r) => ReflectFieldRef::Repeated(ReflectRepeatedRef::new(r)),
            DynamicFieldValue::Map(m) => ReflectFieldRef::Map(ReflectMapRef::new(m)),
        }
    }

    fn clear(&mut self) {
        match self {
            DynamicFieldValue::Singular(o) => o.clear(),
            DynamicFieldValue::Repeated(r) => r.clear(),
            DynamicFieldValue::Map(m) => m.clear(),
        }
    }
}

impl DynamicFieldValue {
    fn default_for_field(field: &FieldDescriptor) -> DynamicFieldValue {
        match field.runtime_field_type() {
            RuntimeFieldType::Singular(s) => DynamicFieldValue::Singular(DynamicOptional::none(s)),
            RuntimeFieldType::Repeated(r) => DynamicFieldValue::Repeated(DynamicRepeated::new(r)),
            RuntimeFieldType::Map(k, v) => DynamicFieldValue::Map(DynamicMap::new(k, v)),
        }
    }
}

#[derive(Debug, Clone)]
pub(crate) struct DynamicMessage {
    pub(crate) descriptor: MessageDescriptor,
    /// Fields by index in the description.
    /// This field is lazy-init: it is empty when created.
    fields: Box<[DynamicFieldValue]>,
    unknown_fields: UnknownFields,
    cached_size: CachedSize,
}

impl DynamicMessage {
    pub(crate) fn new(descriptor: MessageDescriptor) -> DynamicMessage {
        DynamicMessage {
            descriptor,
            fields: Vec::new().into_boxed_slice(),
            unknown_fields: UnknownFields::new(),
            cached_size: CachedSize::new(),
        }
    }

    fn init_fields(&mut self) {
        if self.fields.is_empty() {
            self.fields = self
                .descriptor
                .fields()
                .map(|f| DynamicFieldValue::default_for_field(&f))
                .collect();
        }
    }

    pub(crate) fn get_reflect<'a>(&'a self, field: &FieldDescriptor) -> ReflectFieldRef<'a> {
        assert_eq!(self.descriptor, field.message_descriptor);
        if self.fields.is_empty() {
            ReflectFieldRef::default_for_field(field)
        } else {
            self.fields[field.index].as_ref()
        }
    }

    pub fn clear_field(&mut self, field: &FieldDescriptor) {
        assert_eq!(field.message_descriptor, self.descriptor);
        if self.fields.is_empty() {
            return;
        }

        self.fields[field.index].clear();
    }

    fn clear_oneof_group_fields_except(&mut self, field: &FieldDescriptor) {
        if let Some(oneof) = field.containing_oneof() {
            for next in oneof.fields() {
                if &next == field {
                    continue;
                }
                self.clear_field(&next);
            }
        }
    }

    pub(crate) fn mut_singular_field_or_default<'a>(
        &'a mut self,
        field: &FieldDescriptor,
    ) -> ReflectValueMut<'a> {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        self.clear_oneof_group_fields_except(field);
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Singular(f) => f.mut_or_default(),
            _ => panic!("Not a singular field"),
        }
    }

    pub(crate) fn mut_repeated<'a>(
        &'a mut self,
        field: &FieldDescriptor,
    ) -> ReflectRepeatedMut<'a> {
        assert_eq!(self.descriptor, field.message_descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Repeated(r) => ReflectRepeatedMut::new(r),
            _ => panic!("Not a repeated field: {}", field),
        }
    }

    pub(crate) fn mut_map<'a>(&'a mut self, field: &FieldDescriptor) -> ReflectMapMut<'a> {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Map(m) => ReflectMapMut::new(m),
            _ => panic!("Not a map field: {}", field),
        }
    }

    pub(crate) fn set_field(&mut self, field: &FieldDescriptor, value: ReflectValueBox) {
        assert_eq!(field.message_descriptor, self.descriptor);
        self.init_fields();
        // TODO: reset oneof group fields
        match &mut self.fields[field.index] {
            DynamicFieldValue::Singular(s) => s.set(value),
            _ => panic!("Not a singular field: {}", field),
        }
    }

    pub fn downcast_ref(message: &dyn MessageDyn) -> &DynamicMessage {
        <dyn MessageDyn>::downcast_ref(message).unwrap()
    }

    pub fn downcast_mut(message: &mut dyn MessageDyn) -> &mut DynamicMessage {
        <dyn MessageDyn>::downcast_mut(message).unwrap()
    }
}

impl Clear for DynamicMessage {
    fn clear(&mut self) {
        unimplemented!()
    }
}

impl Message for DynamicMessage {
    fn descriptor_by_instance(&self) -> MessageDescriptor {
        self.descriptor.clone()
    }

    fn is_initialized(&self) -> bool {
        // TODO: this check can be much faster for proto3 without contained proto2 messages.
        for f in self.descriptor.fields() {
            let fv = self.get_reflect(&f);
            match fv {
                ReflectFieldRef::Optional(s) => match s {
                    None => {
                        if f.is_required() {
                            return false;
                        }
                    }
                    Some(v) => {
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                },
                ReflectFieldRef::Repeated(r) => {
                    for v in &r {
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                }
                ReflectFieldRef::Map(m) => {
                    for (_k, v) in &m {
                        // Keys cannot be messages, so only check values.
                        if !v.is_initialized() {
                            return false;
                        }
                    }
                }
            }
        }
        true
    }

    fn merge_from(&mut self, _is: &mut CodedInputStream) -> ProtobufResult<()> {
        unimplemented!()
    }

    fn write_to_with_cached_sizes(&self, _os: &mut CodedOutputStream) -> ProtobufResult<()> {
        unimplemented!()
    }

    fn compute_size(&self) -> u32 {
        let is_proto3 = self.descriptor.file_descriptor().syntax() == Some(Syntax::Proto3);
        let mut m_size = 0;
        for field_desc in self.descriptor.fields() {
            let field_number = field_desc.get_proto().get_number() as u32;
            match field_desc.get_reflect(self) {
                ReflectFieldRef::Optional(v) => {
                    if let Some(v) = v {
                        if !is_proto3 || v.is_non_zero() {
                            m_size += compute_singular_size(
                                &field_desc.get_proto().get_field_type(),
                                field_number,
                                &v,
                            );
                        }
                    }
                }
                ReflectFieldRef::Repeated(vs) => {
                    if !vs.is_empty() {
                        // TODO: use packed when needed.
                        for v in vs {
                            m_size += compute_singular_size(
                                &field_desc.get_proto().get_field_type(),
                                field_number,
                                &v,
                            );
                        }
                    }
                }
                ReflectFieldRef::Map(_v) => {
                    unimplemented!()
                }
            }
        }
        // TODO: unknown fields
        m_size
    }

    fn get_cached_size(&self) -> u32 {
        self.cached_size.get()
    }

    fn get_unknown_fields(&self) -> &UnknownFields {
        &self.unknown_fields
    }

    fn mut_unknown_fields(&mut self) -> &mut UnknownFields {
        &mut self.unknown_fields
    }

    fn new() -> Self
    where
        Self: Sized,
    {
        panic!("DynamicMessage cannot be constructed directly")
    }

    fn default_instance() -> &'static Self
    where
        Self: Sized,
    {
        panic!("There's no default instance for dynamic message")
    }
}

/// Write singular field to output stream
fn _singular_write_to(
    proto_type: &Type,
    field_number: u32,
    v: &ReflectValueRef,
    os: &mut CodedOutputStream,
) -> ProtobufResult<()> {
    match proto_type {
        Type::TYPE_ENUM => {
            if let ReflectValueRef::Enum(_e, enum_v) = v {
                os.write_enum(field_number, *enum_v)
            } else {
                panic!("Protobuf type and Runtime type mismatch");
            }
        }
        Type::TYPE_MESSAGE => {
            let msg_v = v.to_message().unwrap();
            os.write_message_dyn(field_number, &*msg_v)
        }
        Type::TYPE_GROUP => {
            unimplemented!()
        }
        Type::TYPE_UINT32 => os.write_uint32(field_number, v.to_u32().unwrap()),
        Type::TYPE_UINT64 => os.write_uint64(field_number, v.to_u64().unwrap()),
        Type::TYPE_INT32 => os.write_int32(field_number, v.to_i32().unwrap()),
        Type::TYPE_INT64 => os.write_int64(field_number, v.to_i64().unwrap()),
        Type::TYPE_SINT32 => os.write_sint32(field_number, v.to_i32().unwrap()),
        Type::TYPE_SINT64 => os.write_sint64(field_number, v.to_i64().unwrap()),
        Type::TYPE_FIXED32 => os.write_fixed32(field_number, v.to_u32().unwrap()),
        Type::TYPE_FIXED64 => os.write_fixed64(field_number, v.to_u64().unwrap()),
        Type::TYPE_SFIXED64 => os.write_sfixed64(field_number, v.to_i64().unwrap()),
        Type::TYPE_SFIXED32 => os.write_sfixed32(field_number, v.to_i32().unwrap()),
        Type::TYPE_BOOL => os.write_bool(field_number, v.to_bool().unwrap()),
        Type::TYPE_STRING => os.write_string(field_number, v.to_str().unwrap()),
        Type::TYPE_BYTES => os.write_bytes(field_number, v.to_bytes().unwrap()),
        Type::TYPE_FLOAT => os.write_float(field_number, v.to_f32().unwrap()),
        Type::TYPE_DOUBLE => os.write_double(field_number, v.to_f64().unwrap()),
    }
}

/// Compute singular field size
fn compute_singular_size(proto_type: &Type, field_number: u32, v: &ReflectValueRef) -> u32 {
    match proto_type {
        Type::TYPE_ENUM => {
            if let ReflectValueRef::Enum(_e, enum_v) = v {
                value_size(field_number, *enum_v, WireType::WireTypeVarint)
            } else {
                panic!("Protobuf type and Runtime type mismatch");
            }
        }
        Type::TYPE_MESSAGE => {
            let msg_v = v.to_message().unwrap();
            let len = msg_v.compute_size_dyn();
            tag_size(field_number) + compute_raw_varint32_size(len) + len
        }
        Type::TYPE_GROUP => {
            unimplemented!()
        }
        Type::TYPE_UINT32 => {
            let typed_v = v.to_u32().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_UINT64 => {
            let typed_v = v.to_u64().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_INT32 => {
            let typed_v = v.to_i32().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_INT64 => {
            let typed_v = v.to_i64().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_SINT32 => {
            let typed_v = v.to_i32().unwrap();
            value_varint_zigzag_size(field_number, typed_v)
        }
        Type::TYPE_SINT64 => {
            let typed_v = v.to_i64().unwrap();
            value_varint_zigzag_size(field_number, typed_v)
        }
        Type::TYPE_FIXED32 => tag_size(field_number) + 4,
        Type::TYPE_FIXED64 => tag_size(field_number) + 8,
        Type::TYPE_SFIXED32 => tag_size(field_number) + 4,
        Type::TYPE_SFIXED64 => tag_size(field_number) + 8,
        Type::TYPE_BOOL => {
            let typed_v = v.to_bool().unwrap();
            value_size(field_number, typed_v, WireType::WireTypeVarint)
        }
        Type::TYPE_STRING => {
            let typed_v = v.to_str().unwrap();
            string_size(field_number, typed_v)
        }
        Type::TYPE_BYTES => {
            let typed_v = v.to_bytes().unwrap();
            bytes_size(field_number, typed_v)
        }
        Type::TYPE_FLOAT => tag_size(field_number) + 4,
        Type::TYPE_DOUBLE => tag_size(field_number) + 8,
    }
}
<|endoftext|>


<commit_before>use std::io::Read;
use std::io::Write;

use crate::coded_output_stream::with::WithCodedOutputStream;
use crate::error::ProtobufError;
use crate::wire_format::check_message_size;
use crate::CodedInputStream;
use crate::CodedOutputStream;
use crate::SpecialFields;
use crate::UnknownFields;

/// Trait which is implemented by all generated message.
///
/// Note, by default all generated messages also implement [`MessageFull`](crate::MessageFull)
/// trait which provides access to reflection and features which depend on reflection
/// (text format and JSON serialization).
pub trait Message: Default + Clone + Send + Sync + Sized + PartialEq + 'static {
    /// Message name as specified in `.proto` file.
    ///
    /// Message name can be accessed using
    /// [`MessageFull::descriptor`](crate::MessageFull::descriptor),
    /// but when lite runtime is requested, this field can be used.
    const NAME: &'static str;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> crate::Result<()>;

    /// Parse message from stream.
    fn parse_from(is: &mut CodedInputStream) -> crate::Result<Self> {
        let mut r: Self = Message::new();
        r.merge_from(is)?;
        r.check_initialized()?;
        Ok(r)
    }

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> crate::Result<()>;

    /// Compute and cache size of this message and all nested messages.
    ///
    /// Note if the computation overflows u32, the cached size is stored truncated.
    fn compute_size(&self) -> u64;

    /// Get size previously computed by `compute_size`.
    ///
    /// Note if message size exceeds u32, the cached size is stored truncated.
    fn cached_size(&self) -> u32;

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> crate::Result<()> {
        self.check_initialized()?;

        // cache sizes
        let size = self.compute_size();
        let size = check_message_size(size)?;
        os.reserve_additional(size as u32, Self::NAME)?;
        self.write_to_with_cached_sizes(os)?;

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> crate::Result<()> {
        let size = self.compute_size();
        let size = check_message_size(size)?;

        os.reserve_additional_for_length_delimited(size, Self::NAME)?;

        os.write_raw_varint32(size)?;

        let written = os.total_bytes_written();

        self.write_to_with_cached_sizes(os)?;

        // Self-check.
        assert_eq!(
            written + size as u64,
            os.total_bytes_written(),
            "Expected to write {}, actually wrote {}",
            size,
            os.total_bytes_written() - written
        );

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> crate::Result<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> crate::Result<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Parse message from reader.
    /// Parse stops on EOF or when error encountered.
    fn parse_from_reader(reader: &mut dyn Read) -> crate::Result<Self> {
        let mut is = CodedInputStream::new(reader);
        let r = Message::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Parse message from byte array.
    fn parse_from_bytes(bytes: &[u8]) -> crate::Result<Self> {
        let mut is = CodedInputStream::from_bytes(bytes);
        let r = Message::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Parse message from `Bytes` object.
    /// Resulting message may share references to the passed bytes object.
    #[cfg(feature = "bytes")]
    fn parse_from_tokio_bytes(bytes: &bytes::Bytes) -> crate::Result<Self> {
        let mut is = CodedInputStream::from_tokio_bytes(bytes);
        let r = Self::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> crate::Result<()> {
        if !self.is_initialized() {
            Err(ProtobufError::MessageNotInitialized(Self::NAME.to_owned()).into())
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut dyn Write) -> crate::Result<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> crate::Result<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use [`Message::parse_from_bytes`]
    /// to do the reverse.
    fn write_to_bytes(&self) -> crate::Result<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        let mut os = CodedOutputStream::vec(&mut v);
        self.write_to_with_cached_sizes(&mut os)?;
        os.flush()?;
        drop(os);
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut dyn Write) -> crate::Result<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> crate::Result<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Special fields (unknown fields and cached size).
    fn special_fields(&self) -> &SpecialFields;
    /// Special fields (unknown fields and cached size).
    fn mut_special_fields(&mut self) -> &mut SpecialFields;

    /// Get a reference to unknown fields.
    fn unknown_fields(&self) -> &UnknownFields {
        &self.special_fields().unknown_fields()
    }
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields {
        self.mut_special_fields().mut_unknown_fields()
    }

    /// Create an empty message object.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let m = MyMessage::new();
    /// # }
    /// ```
    fn new() -> Self;

    /// Reset all fields.
    fn clear(&mut self) {
        *self = Self::new();
    }

    /// Return a pointer to default immutable message with static lifetime.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let m: &MyMessage = MyMessage::default_instance();
    /// # }
    /// ```
    fn default_instance() -> &'static Self;
}
<commit_msg>Default implementation of `cached_size`<commit_after>use std::io::Read;
use std::io::Write;

use crate::coded_output_stream::with::WithCodedOutputStream;
use crate::error::ProtobufError;
use crate::wire_format::check_message_size;
use crate::CodedInputStream;
use crate::CodedOutputStream;
use crate::SpecialFields;
use crate::UnknownFields;

/// Trait which is implemented by all generated message.
///
/// Note, by default all generated messages also implement [`MessageFull`](crate::MessageFull)
/// trait which provides access to reflection and features which depend on reflection
/// (text format and JSON serialization).
pub trait Message: Default + Clone + Send + Sync + Sized + PartialEq + 'static {
    /// Message name as specified in `.proto` file.
    ///
    /// Message name can be accessed using
    /// [`MessageFull::descriptor`](crate::MessageFull::descriptor),
    /// but when lite runtime is requested, this field can be used.
    const NAME: &'static str;

    /// True iff all required fields are initialized.
    /// Always returns `true` for protobuf 3.
    fn is_initialized(&self) -> bool;

    /// Update this message object with fields read from given stream.
    fn merge_from(&mut self, is: &mut CodedInputStream) -> crate::Result<()>;

    /// Parse message from stream.
    fn parse_from(is: &mut CodedInputStream) -> crate::Result<Self> {
        let mut r: Self = Message::new();
        r.merge_from(is)?;
        r.check_initialized()?;
        Ok(r)
    }

    /// Write message to the stream.
    ///
    /// Sizes of this messages and nested messages must be cached
    /// by calling `compute_size` prior to this call.
    fn write_to_with_cached_sizes(&self, os: &mut CodedOutputStream) -> crate::Result<()>;

    /// Compute and cache size of this message and all nested messages.
    ///
    /// Note if the computation overflows u32, the cached size is stored truncated.
    fn compute_size(&self) -> u64;

    /// Get size previously computed by `compute_size`.
    ///
    /// Note if message size exceeds u32, the cached size is stored truncated.
    fn cached_size(&self) -> u32 {
        self.special_fields().cached_size().get()
    }

    /// Write the message to the stream.
    ///
    /// Results in error if message is not fully initialized.
    fn write_to(&self, os: &mut CodedOutputStream) -> crate::Result<()> {
        self.check_initialized()?;

        // cache sizes
        let size = self.compute_size();
        let size = check_message_size(size)?;
        os.reserve_additional(size as u32, Self::NAME)?;
        self.write_to_with_cached_sizes(os)?;

        Ok(())
    }

    /// Write the message to the stream prepending the message with message length
    /// encoded as varint.
    fn write_length_delimited_to(&self, os: &mut CodedOutputStream) -> crate::Result<()> {
        let size = self.compute_size();
        let size = check_message_size(size)?;

        os.reserve_additional_for_length_delimited(size, Self::NAME)?;

        os.write_raw_varint32(size)?;

        let written = os.total_bytes_written();

        self.write_to_with_cached_sizes(os)?;

        // Self-check.
        assert_eq!(
            written + size as u64,
            os.total_bytes_written(),
            "Expected to write {}, actually wrote {}",
            size,
            os.total_bytes_written() - written
        );

        Ok(())
    }

    /// Write the message to the vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_vec(&self, vec: &mut Vec<u8>) -> crate::Result<()> {
        let mut os = CodedOutputStream::vec(vec);
        self.write_length_delimited_to(&mut os)?;
        os.flush()?;
        Ok(())
    }

    /// Update this message object with fields read from given stream.
    fn merge_from_bytes(&mut self, bytes: &[u8]) -> crate::Result<()> {
        let mut is = CodedInputStream::from_bytes(bytes);
        self.merge_from(&mut is)
    }

    /// Parse message from reader.
    /// Parse stops on EOF or when error encountered.
    fn parse_from_reader(reader: &mut dyn Read) -> crate::Result<Self> {
        let mut is = CodedInputStream::new(reader);
        let r = Message::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Parse message from byte array.
    fn parse_from_bytes(bytes: &[u8]) -> crate::Result<Self> {
        let mut is = CodedInputStream::from_bytes(bytes);
        let r = Message::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Parse message from `Bytes` object.
    /// Resulting message may share references to the passed bytes object.
    #[cfg(feature = "bytes")]
    fn parse_from_tokio_bytes(bytes: &bytes::Bytes) -> crate::Result<Self> {
        let mut is = CodedInputStream::from_tokio_bytes(bytes);
        let r = Self::parse_from(&mut is)?;
        is.check_eof()?;
        Ok(r)
    }

    /// Check if all required fields of this object are initialized.
    fn check_initialized(&self) -> crate::Result<()> {
        if !self.is_initialized() {
            Err(ProtobufError::MessageNotInitialized(Self::NAME.to_owned()).into())
        } else {
            Ok(())
        }
    }

    /// Write the message to the writer.
    fn write_to_writer(&self, w: &mut dyn Write) -> crate::Result<()> {
        w.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    fn write_to_vec(&self, v: &mut Vec<u8>) -> crate::Result<()> {
        v.with_coded_output_stream(|os| self.write_to(os))
    }

    /// Write the message to bytes vec.
    ///
    /// > **Note**: You can use [`Message::parse_from_bytes`]
    /// to do the reverse.
    fn write_to_bytes(&self) -> crate::Result<Vec<u8>> {
        self.check_initialized()?;

        let size = self.compute_size() as usize;
        let mut v = Vec::with_capacity(size);
        let mut os = CodedOutputStream::vec(&mut v);
        self.write_to_with_cached_sizes(&mut os)?;
        os.flush()?;
        drop(os);
        Ok(v)
    }

    /// Write the message to the writer, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_writer(&self, w: &mut dyn Write) -> crate::Result<()> {
        w.with_coded_output_stream(|os| self.write_length_delimited_to(os))
    }

    /// Write the message to the bytes vec, prepend the message with message length
    /// encoded as varint.
    fn write_length_delimited_to_bytes(&self) -> crate::Result<Vec<u8>> {
        let mut v = Vec::new();
        v.with_coded_output_stream(|os| self.write_length_delimited_to(os))?;
        Ok(v)
    }

    /// Special fields (unknown fields and cached size).
    fn special_fields(&self) -> &SpecialFields;
    /// Special fields (unknown fields and cached size).
    fn mut_special_fields(&mut self) -> &mut SpecialFields;

    /// Get a reference to unknown fields.
    fn unknown_fields(&self) -> &UnknownFields {
        &self.special_fields().unknown_fields()
    }
    /// Get a mutable reference to unknown fields.
    fn mut_unknown_fields(&mut self) -> &mut UnknownFields {
        self.mut_special_fields().mut_unknown_fields()
    }

    /// Create an empty message object.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let m = MyMessage::new();
    /// # }
    /// ```
    fn new() -> Self;

    /// Reset all fields.
    fn clear(&mut self) {
        *self = Self::new();
    }

    /// Return a pointer to default immutable message with static lifetime.
    ///
    /// ```
    /// # use protobuf::MessageFull;
    /// # fn foo<MyMessage: MessageFull>() {
    /// let m: &MyMessage = MyMessage::default_instance();
    /// # }
    /// ```
    fn default_instance() -> &'static Self;
}
<|endoftext|>


<commit_before>extern crate lua = "rust-hl-lua";

#[test]
fn readwrite() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Foo {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Foo> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);
   // let x: Foo = lua.get("a").unwrap();
}

#[test]
fn destructor_called() {
    use std::sync::{Arc, Mutex};

    let called = Arc::new(Mutex::new(false));

    struct Foo {
        called: Arc<Mutex<bool>>
    }

    impl Drop for Foo {
        fn drop(&mut self) {
            let mut called = self.called.lock();
            (*called) = true;
        }
    }

    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }

    {
        let mut lua = lua::Lua::new();
        lua.set("a", Foo{called: called.clone()});
    }

    let locked = called.lock();
    assert!(*locked);
}

#[test]
fn type_check() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Foo {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Foo> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    #[deriving(Clone)]
    struct Bar;
    impl<L: lua::HasLua> lua::Push<L> for Bar {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Bar {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Bar> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);

    /*let x: Option<Bar> = lua.get("a");
    assert!(x.is_none())*/
}

#[test]
fn metatables() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |table| {
                table.set("__index".to_string(), vec!(
                    ("test".to_string(), || 5i)
                ));
            })
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);

    let x: int = lua.execute("return a.test()").unwrap();
    assert_eq!(x, 5);
}
<commit_msg>Fix ignored tests in userdata_tests<commit_after>extern crate lua = "rust-hl-lua";

#[test]
fn readwrite() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Foo {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Foo> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);
    let _: Foo = lua.get("a").unwrap();
}

#[test]
fn destructor_called() {
    use std::sync::{Arc, Mutex};

    let called = Arc::new(Mutex::new(false));

    struct Foo {
        called: Arc<Mutex<bool>>
    }

    impl Drop for Foo {
        fn drop(&mut self) {
            let mut called = self.called.lock();
            (*called) = true;
        }
    }

    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }

    {
        let mut lua = lua::Lua::new();
        lua.set("a", Foo{called: called.clone()});
    }

    let locked = called.lock();
    assert!(*locked);
}

#[test]
fn type_check() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Foo {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Foo> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    #[deriving(Clone)]
    struct Bar;
    impl<L: lua::HasLua> lua::Push<L> for Bar {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |_|{})
        }
    }
    impl<L: lua::HasLua> lua::CopyRead<L> for Bar {
        fn read_from_lua(lua: &mut L, index: i32) -> Option<Bar> {
            lua::userdata::read_copy_userdata(lua, index)
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);

    let x: Option<Bar> = lua.get("a");
    assert!(x.is_none())
}

#[test]
fn metatables() {
    #[deriving(Clone)]
    struct Foo;
    impl<L: lua::HasLua> lua::Push<L> for Foo {
        fn push_to_lua(self, lua: &mut L) -> uint {
            lua::userdata::push_userdata(self, lua, |table| {
                table.set("__index".to_string(), vec!(
                    ("test".to_string(), || 5i)
                ));
            })
        }
    }

    let mut lua = lua::Lua::new();

    lua.set("a", Foo);

    let x: int = lua.execute("return a.test()").unwrap();
    assert_eq!(x, 5);
}
<|endoftext|>


<commit_before>extern crate sdl2;
extern crate native;

fn main() {
    sdl2::init(sdl2::InitEverything);

    let win = match sdl2::video::Window::new("Hello World!", sdl2::video::Positioned(100), sdl2::video::Positioned(100), 960, 540, sdl2::video::Shown) {
        Ok(window) => window,
        Err(err) => fail!(format!("SDL_CreateWindow Error: {}", err))
    };

    let ren = match sdl2::render::Renderer::from_window(win, sdl2::render::DriverAuto, sdl2::render::Accelerated) {
        Ok(renderer) => renderer,
        Err(err) => fail!(format!("SDL_CreateRenderer Error: {}", err))
    };

    let bmp = match sdl2::surface::Surface::from_bmp(&Path::new("../img/boxes.bmp")) {
    	Ok(bmp) => bmp,
	Err(err) => fail!(format!("SDL_LoadBMP Error: {}", err))
    };

    let tex = match ren.create_texture_from_surface(&bmp) {
    	Ok(tex) => tex,
	Err(err) => fail!(format!("SDL_CreateTextureFromSurface Error: {}", err))
    };

    let _ = ren.clear();
    let _ = ren.copy(&tex, None, None);
    ren.present();

    sdl2::timer::delay(2000);

    sdl2::quit();
}
<commit_msg>Rust: Reduce verbosity of the example code<commit_after>extern crate sdl2;
extern crate native;

use sdl2::timer;
use sdl2::render::Renderer;
use sdl2::render;
use sdl2::surface::Surface;
use sdl2::video::Window;
use sdl2::video;

fn main() {
    sdl2::init(sdl2::InitEverything);

    let win = Window::new("Hello World!",
                          video::Positioned(100),
                          video::Positioned(100),
                          960, 540,
                          video::Shown).unwrap();

    let ren = Renderer::from_window(win,
                                    render::DriverAuto,
                                    render::Accelerated).unwrap();

    let bmp = Surface::from_bmp(&Path::new("../img/boxes.bmp")).unwrap();

    let tex = ren.create_texture_from_surface(&bmp).unwrap();

    let _ = ren.clear();
    let _ = ren.copy(&tex, None, None);
    ren.present();

    timer::delay(2000);

    sdl2::quit();
}
<|endoftext|>


<commit_before>use common::{R, vec_with_len};
use dense_matrix::DenseMatrix;
use sparse_matrix::{SparseMatrix, Symmetric, StructurallySymmetric};

use std::libc::{c_double, c_ulong, c_int, c_uint, c_void, malloc, calloc, realloc, free};
use std::cast;
use std::vec;
use std::libc;

pub type lapack_int = c_int; // Adjust according to whether LP64 or ILP64 libraries are being linked.
pub type mkl_int = c_int;    // Adjust according to whether LP64 or ILP64 libraries are being linked.
//pub type umf_int = c_int;

#[inline(never)]
pub fn init() {
  unsafe {
    init_allocator(cast::transmute(malloc), cast::transmute(calloc), cast::transmute(realloc), cast::transmute(free));
  }
}

#[inline(never)]
pub fn solve_sparse(sys: &SparseMatrix, rhs: &DenseMatrix) -> ~[R] {
  let n = sys.num_rows();

  unsafe {
    let (a, ia, ja) = sys.csr3_ptrs();
    let mut sol = vec_with_len(n);
    let cpu_cores = num_cpus() as c_uint;

    let stat = match sys.matrix_type() {
      Symmetric =>
        mkl_solve_sparse_symmetric_as_ut_csr3(n as mkl_int, ia, ja, a,
                                              rhs.col_maj_data_ptr(), rhs.num_cols() as mkl_int,
                                              sol.as_mut_ptr(),
                                              cpu_cores),
      StructurallySymmetric =>
        mkl_solve_sparse_structurally_symmetric_csr3(n as mkl_int, ia, ja, a,
                                                     rhs.col_maj_data_ptr(), rhs.num_cols() as mkl_int,
                                                     sol.as_mut_ptr(),
                                                     cpu_cores),
      _ =>
        fail!("TODO: Support umfpack here on OS X."),
        //umf_solve_sparse_csr3(n as umf_int, ia, ja, a, rhs.col_maj_data_ptr(), sol.as_mut_ptr()),
    };

    if stat != 0 {
      fail!(format!("solve_sparse_symmetric_as_ut_csr3 failed with error {:d}", stat));
    }

    sol
  }
}

/* TODO: This isn't the preferred way to link anymore (too platform specific), so requires feature gate in wgfem.rs.
         I'm not sure how to specify the -L option otherwise though. */
#[link_args = "lib/linear_algebra.o -Llib/mkl -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -lmkl_core -lmkl_intel_thread -lmkl_core -liomp5 -lpthread"] // -lumfpack
extern {

  pub fn init_allocator(malloc_fn: *c_void, calloc_fn: *c_void, realloc_fn: *c_void, free_fn: *c_void);

  pub fn alloc_doubles(num_doubles: c_ulong) -> *mut c_double;

  pub fn alloc_ints(num_ints: c_ulong) -> *mut lapack_int;

  pub fn free_doubles(mem: *mut c_double);

  pub fn free_ints(mem: *mut lapack_int);

  pub fn copy_matrix(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  pub fn copy_upper_triangle(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  /* Dense symmetric matrix system solver. */
  pub fn solve_symmetric_as_col_maj_with_ut_sys(a: *mut c_double,
                                                n: lapack_int,
                                                b: *mut c_double,
                                                nrhs: lapack_int,
                                                ipiv: *mut lapack_int) -> lapack_int;

  /* MKL sparse symmetric matrix system solver. */
  pub fn mkl_solve_sparse_symmetric_as_ut_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                               b: *c_double, nrhs: mkl_int,
                                               x: *mut c_double,
                                               num_cpu_cores: c_uint) -> mkl_int;

  /* MKL sparse structurally symmetric matrix system solver. */
  pub fn mkl_solve_sparse_structurally_symmetric_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                                      b: *c_double, nrhs: mkl_int,
                                                      x: *mut c_double,
                                                      num_cpu_cores: c_uint) -> mkl_int;

  /* UMFPACK general sparse matrix system solver. */
  // Works, commented out for now for convenience on OS X.
  //pub fn umf_solve_sparse_csr3(n: umf_int, ia: *umf_int, ja: *umf_int, a: *c_double, b: *c_double, x: *mut c_double) -> umf_int;
}

fn num_cpus() -> uint {
  unsafe {
    return rust_get_num_cpus();
  }

  extern {
    fn rust_get_num_cpus() -> libc::uintptr_t;
  }
}

<commit_msg>Remove unused import.<commit_after>use common::{R, vec_with_len};
use dense_matrix::DenseMatrix;
use sparse_matrix::{SparseMatrix, Symmetric, StructurallySymmetric};

use std::libc::{c_double, c_ulong, c_int, c_uint, c_void, malloc, calloc, realloc, free};
use std::cast;
use std::libc;

pub type lapack_int = c_int; // Adjust according to whether LP64 or ILP64 libraries are being linked.
pub type mkl_int = c_int;    // Adjust according to whether LP64 or ILP64 libraries are being linked.

#[inline(never)]
pub fn init() {
  unsafe {
    init_allocator(cast::transmute(malloc), cast::transmute(calloc), cast::transmute(realloc), cast::transmute(free));
  }
}

#[inline(never)]
pub fn solve_sparse(sys: &SparseMatrix, rhs: &DenseMatrix) -> ~[R] {
  let n = sys.num_rows();

  unsafe {
    let (a, ia, ja) = sys.csr3_ptrs();
    let mut sol = vec_with_len(n);
    let cpu_cores = num_cpus() as c_uint;

    let stat = match sys.matrix_type() {
      Symmetric =>
        mkl_solve_sparse_symmetric_as_ut_csr3(n as mkl_int, ia, ja, a,
                                              rhs.col_maj_data_ptr(), rhs.num_cols() as mkl_int,
                                              sol.as_mut_ptr(),
                                              cpu_cores),
      StructurallySymmetric =>
        mkl_solve_sparse_structurally_symmetric_csr3(n as mkl_int, ia, ja, a,
                                                     rhs.col_maj_data_ptr(), rhs.num_cols() as mkl_int,
                                                     sol.as_mut_ptr(),
                                                     cpu_cores),
      _ =>
        fail!("TODO: Support umfpack here on OS X."),
        //umf_solve_sparse_csr3(n as umf_int, ia, ja, a, rhs.col_maj_data_ptr(), sol.as_mut_ptr()),
    };

    if stat != 0 {
      fail!(format!("solve_sparse_symmetric_as_ut_csr3 failed with error {:d}", stat));
    }

    sol
  }
}

/* TODO: This isn't the preferred way to link anymore (too platform specific), so requires feature gate in wgfem.rs.
         I'm not sure how to specify the -L option otherwise though. */
#[link_args = "lib/linear_algebra.o -Llib/mkl -lmkl_intel_lp64 -lmkl_core -lmkl_intel_thread -lmkl_core -lmkl_intel_thread -lmkl_core -liomp5 -lpthread"] // -lumfpack
extern {

  pub fn init_allocator(malloc_fn: *c_void, calloc_fn: *c_void, realloc_fn: *c_void, free_fn: *c_void);

  pub fn alloc_doubles(num_doubles: c_ulong) -> *mut c_double;

  pub fn alloc_ints(num_ints: c_ulong) -> *mut lapack_int;

  pub fn free_doubles(mem: *mut c_double);

  pub fn free_ints(mem: *mut lapack_int);

  pub fn copy_matrix(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  pub fn copy_upper_triangle(from_data: *c_double, num_rows: c_ulong, num_cols: c_ulong, to_data: *mut c_double);

  /* Dense symmetric matrix system solver. */
  pub fn solve_symmetric_as_col_maj_with_ut_sys(a: *mut c_double,
                                                n: lapack_int,
                                                b: *mut c_double,
                                                nrhs: lapack_int,
                                                ipiv: *mut lapack_int) -> lapack_int;

  /* MKL sparse symmetric matrix system solver. */
  pub fn mkl_solve_sparse_symmetric_as_ut_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                               b: *c_double, nrhs: mkl_int,
                                               x: *mut c_double,
                                               num_cpu_cores: c_uint) -> mkl_int;

  /* MKL sparse structurally symmetric matrix system solver. */
  pub fn mkl_solve_sparse_structurally_symmetric_csr3(n: mkl_int, ia: *mkl_int, ja: *mkl_int, a: *c_double,
                                                      b: *c_double, nrhs: mkl_int,
                                                      x: *mut c_double,
                                                      num_cpu_cores: c_uint) -> mkl_int;

  /* UMFPACK general sparse matrix system solver. */
  // Works, commented out for now for convenience on OS X.
  //pub fn umf_solve_sparse_csr3(n: umf_int, ia: *umf_int, ja: *umf_int, a: *c_double, b: *c_double, x: *mut c_double) -> umf_int;
}

fn num_cpus() -> uint {
  unsafe {
    return rust_get_num_cpus();
  }

  extern {
    fn rust_get_num_cpus() -> libc::uintptr_t;
  }
}

<|endoftext|>