<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, 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 {
        target_dir = target_dir.join("doc");
        return clean_entire_folder(&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 clean_entire_folder(&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)?;

    let mut progress = CleaningPackagesBar::new(config, packages.len());
    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());
        progress.on_cleaning_package(&pkg.name())?;

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

        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_package_glob_containing_hash(
                        &pkg.name(),
                        &Path::new(&dir).join(&pkg_dir),
                        config,
                        &mut progress,
                    )?;
                }
                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, &mut progress)?;
                        rm_rf(&dir.join(&unhashed_name), config, &mut progress)?;
                        // 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, &mut progress)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config, &mut progress)?;
                        // 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, &mut progress)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config, &mut progress)?;

                        // 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, &mut progress)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config, &mut progress)?;
                        }
                    }
                    // 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, &mut progress)?;
                }
            }
        }
    }

    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_package_glob_containing_hash(
    package: &str,
    pattern: &Path,
    config: &Config,
    progress: &mut dyn CleaningProgressBar,
) -> CargoResult<()> {
    rm_rf_glob_helper(Some(package), pattern, config, progress)
}

fn rm_rf_glob(
    pattern: &Path,
    config: &Config,
    progress: &mut dyn CleaningProgressBar,
) -> CargoResult<()> {
    rm_rf_glob_helper(None, pattern, config, progress)
}

fn rm_rf_glob_helper(
    package: Option<&str>,
    pattern: &Path,
    config: &Config,
    progress: &mut dyn CleaningProgressBar,
) -> 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)? {
        let path = path?;

        // Make sure the artifact is for `package` and not another crate that is prefixed by
        // `package` by getting the original name stripped of the trialing hash and possible
        // extension
        if let Some(package) = package {
            let pkg_name = path
                .file_name()
                .and_then(std::ffi::OsStr::to_str)
                .and_then(|artifact| artifact.rsplit_once('-'))
                .expect("artifact name is valid UTF-8 and contains at least one hyphen")
                .0;

            if pkg_name != package {
                continue;
            }
        }

        rm_rf(&path, config, progress)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config, progress: &mut dyn CleaningProgressBar) -> CargoResult<()> {
    if fs::symlink_metadata(path).is_err() {
        return Ok(());
    }

    config
        .shell()
        .verbose(|shell| shell.status("Removing", path.display()))?;
    progress.display_now()?;

    for entry in walkdir::WalkDir::new(path).contents_first(true) {
        let entry = entry?;
        progress.on_clean()?;
        if entry.file_type().is_dir() {
            paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?;
        } else {
            paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?;
        }
    }

    Ok(())
}

fn clean_entire_folder(path: &Path, config: &Config) -> CargoResult<()> {
    let num_paths = walkdir::WalkDir::new(path).into_iter().count();
    let mut progress = CleaningFolderBar::new(config, num_paths);
    rm_rf(path, config, &mut progress)
}

trait CleaningProgressBar {
    fn display_now(&mut self) -> CargoResult<()>;
    fn on_clean(&mut self) -> CargoResult<()>;
}

struct CleaningFolderBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
}

impl<'cfg> CleaningFolderBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Percentage, cfg),
            max,
            cur: 0,
        }
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }
}

impl<'cfg> CleaningProgressBar for CleaningFolderBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar.tick_now(self.cur_progress(), self.max, "")
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.cur += 1;
        self.bar.tick(self.cur_progress(), self.max, "")
    }
}

struct CleaningPackagesBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
    num_files_folders_cleaned: usize,
    package_being_cleaned: String,
}

impl<'cfg> CleaningPackagesBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Ratio, cfg),
            max,
            cur: 0,
            num_files_folders_cleaned: 0,
            package_being_cleaned: String::new(),
        }
    }

    fn on_cleaning_package(&mut self, package: &str) -> CargoResult<()> {
        self.cur += 1;
        self.package_being_cleaned = String::from(package);
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }

    fn format_message(&self) -> String {
        format!(
            ": {}, {} files/folders cleaned",
            self.package_being_cleaned, self.num_files_folders_cleaned
        )
    }
}

impl<'cfg> CleaningProgressBar for CleaningPackagesBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar
            .tick_now(self.cur_progress(), self.max, &self.format_message())
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())?;
        self.num_files_folders_cleaned += 1;
        Ok(())
    }
}
<commit_msg>Address review comments<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 {
        target_dir = target_dir.join("doc");
        return clean_entire_folder(&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 clean_entire_folder(&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)?;

    let mut progress = CleaningPackagesBar::new(config, packages.len());
    for pkg in packages {
        let pkg_dir = format!("{}-*", pkg.name());
        progress.on_cleaning_package(&pkg.name())?;

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

        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_package_glob_containing_hash(
                        &pkg.name(),
                        &Path::new(&dir).join(&pkg_dir),
                        config,
                        &mut progress,
                    )?;
                }
                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, &mut progress)?;
                        rm_rf(&dir.join(&unhashed_name), config, &mut progress)?;
                        // 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, &mut progress)?;
                        let unhashed_dep_info = dir.join(format!("{}.d", crate_name));
                        rm_rf(&unhashed_dep_info, config, &mut progress)?;
                        // 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, &mut progress)?;
                        let split_debuginfo_dwo = dir_glob.join(format!("{}.*.dwo", crate_name));
                        rm_rf_glob(&split_debuginfo_dwo, config, &mut progress)?;

                        // 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, &mut progress)?;
                            // Dep-info generated by Cargo itself.
                            let dep_info = uplifted_path.with_extension("d");
                            rm_rf(&dep_info, config, &mut progress)?;
                        }
                    }
                    // 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, &mut progress)?;
                }
            }
        }
    }

    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))
}

/// Glob remove artifacts for the provided `package`
///
/// Make sure the artifact is for `package` and not another crate that is prefixed by
/// `package` by getting the original name stripped of the trailing hash and possible
/// extension
fn rm_rf_package_glob_containing_hash(
    package: &str,
    pattern: &Path,
    config: &Config,
    progress: &mut dyn CleaningProgressBar,
) -> 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)? {
        let path = path?;

        let pkg_name = path
            .file_name()
            .and_then(std::ffi::OsStr::to_str)
            .and_then(|artifact| artifact.rsplit_once('-'))
            .ok_or_else(|| anyhow::anyhow!("expected utf-8 path"))?
            .0;

        if pkg_name != package {
            continue;
        }

        rm_rf(&path, config, progress)?;
    }
    Ok(())
}

fn rm_rf_glob(
    pattern: &Path,
    config: &Config,
    progress: &mut dyn CleaningProgressBar,
) -> 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, progress)?;
    }
    Ok(())
}

fn rm_rf(path: &Path, config: &Config, progress: &mut dyn CleaningProgressBar) -> CargoResult<()> {
    if fs::symlink_metadata(path).is_err() {
        return Ok(());
    }

    config
        .shell()
        .verbose(|shell| shell.status("Removing", path.display()))?;
    progress.display_now()?;

    for entry in walkdir::WalkDir::new(path).contents_first(true) {
        let entry = entry?;
        progress.on_clean()?;
        if entry.file_type().is_dir() {
            paths::remove_dir(entry.path()).with_context(|| "could not remove build directory")?;
        } else {
            paths::remove_file(entry.path()).with_context(|| "failed to remove build artifact")?;
        }
    }

    Ok(())
}

fn clean_entire_folder(path: &Path, config: &Config) -> CargoResult<()> {
    let num_paths = walkdir::WalkDir::new(path).into_iter().count();
    let mut progress = CleaningFolderBar::new(config, num_paths);
    rm_rf(path, config, &mut progress)
}

trait CleaningProgressBar {
    fn display_now(&mut self) -> CargoResult<()>;
    fn on_clean(&mut self) -> CargoResult<()>;
}

struct CleaningFolderBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
}

impl<'cfg> CleaningFolderBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Percentage, cfg),
            max,
            cur: 0,
        }
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }
}

impl<'cfg> CleaningProgressBar for CleaningFolderBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar.tick_now(self.cur_progress(), self.max, "")
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.cur += 1;
        self.bar.tick(self.cur_progress(), self.max, "")
    }
}

struct CleaningPackagesBar<'cfg> {
    bar: Progress<'cfg>,
    max: usize,
    cur: usize,
    num_files_folders_cleaned: usize,
    package_being_cleaned: String,
}

impl<'cfg> CleaningPackagesBar<'cfg> {
    fn new(cfg: &'cfg Config, max: usize) -> Self {
        Self {
            bar: Progress::with_style("Cleaning", ProgressStyle::Ratio, cfg),
            max,
            cur: 0,
            num_files_folders_cleaned: 0,
            package_being_cleaned: String::new(),
        }
    }

    fn on_cleaning_package(&mut self, package: &str) -> CargoResult<()> {
        self.cur += 1;
        self.package_being_cleaned = String::from(package);
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())
    }

    fn cur_progress(&self) -> usize {
        std::cmp::min(self.cur, self.max)
    }

    fn format_message(&self) -> String {
        format!(
            ": {}, {} files/folders cleaned",
            self.package_being_cleaned, self.num_files_folders_cleaned
        )
    }
}

impl<'cfg> CleaningProgressBar for CleaningPackagesBar<'cfg> {
    fn display_now(&mut self) -> CargoResult<()> {
        self.bar
            .tick_now(self.cur_progress(), self.max, &self.format_message())
    }

    fn on_clean(&mut self) -> CargoResult<()> {
        self.bar
            .tick(self.cur_progress(), self.max, &self.format_message())?;
        self.num_files_folders_cleaned += 1;
        Ok(())
    }
}
<|endoftext|>


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

//! The Poisson distribution.

use Rng;
use distributions::Distribution;
use distributions::log_gamma::log_gamma;
use std::f64::consts::PI;

/// The Poisson distribution `Poisson(lambda)`.
///
/// This distribution has a density function:
/// `f(k) = lambda^k * exp(-lambda) / k!` for `k >= 0`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Poisson, Distribution};
///
/// let poi = Poisson::new(2.0);
/// let v = poi.sample(&mut rand::thread_rng());
/// println!("{} is from a Poisson(2) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Poisson {
    lambda: f64,
    // precalculated values
    exp_lambda: f64,
    log_lambda: f64,
    sqrt_2lambda: f64,
    magic_val: f64,
}

impl Poisson {
    /// Construct a new `Poisson` with the given shape parameter
    /// `lambda`. Panics if `lambda <= 0`.
    pub fn new(lambda: f64) -> Poisson {
        assert!(lambda > 0.0, "Poisson::new called with lambda <= 0");
        let log_lambda = lambda.ln();
        Poisson {
            lambda: lambda,
            exp_lambda: (-lambda).exp(),
            log_lambda: log_lambda,
            sqrt_2lambda: (2.0 * lambda).sqrt(),
            magic_val: lambda * log_lambda - log_gamma(1.0 + lambda),
        }
    }
}

impl Distribution<u64> for Poisson {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
        // using the algorithm from Numerical Recipes in C

        // for low expected values use the Knuth method
        if self.lambda < 12.0 {
            let mut result = 0;
            let mut p = 1.0;
            while p > self.exp_lambda {
                p *= rng.gen::<f64>();
                result += 1;
            }
            result - 1
        }
        // high expected values - rejection method
        else {
            let mut int_result: u64;

            loop {
                let mut result;
                let mut comp_dev;

                // we use the lorentzian distribution as the comparison distribution
                // f(x) ~ 1/(1+x/^2)
                loop {
                    // draw from the lorentzian distribution
                    comp_dev = (PI * rng.gen::<f64>()).tan();
                    // shift the peak of the comparison ditribution
                    result = self.sqrt_2lambda * comp_dev + self.lambda;
                    // repeat the drawing until we are in the range of possible values
                    if result >= 0.0 {
                        break;
                    }
                }
                // now the result is a random variable greater than 0 with Lorentzian distribution
                // the result should be an integer value
                result = result.floor();
                int_result = result as u64;

                // this is the ratio of the Poisson distribution to the comparison distribution
                // the magic value scales the distribution function to a range of approximately 0-1
                // since it is not exact, we multiply the ratio by 0.9 to avoid ratios greater than 1
                // this doesn't change the resulting distribution, only increases the rate of failed drawings
                let check = 0.9 * (1.0 + comp_dev * comp_dev)
                    * (result * self.log_lambda - log_gamma(1.0 + result) - self.magic_val).exp();

                // check with uniform random value - if below the threshold, we are within the target distribution
                if rng.gen::<f64>() <= check {
                    break;
                }
            }
            int_result
        }
    }
}

#[cfg(test)]
mod test {
    use distributions::Distribution;
    use super::Poisson;

    #[test]
    fn test_poisson() {
        let poisson = Poisson::new(10.0);
        let mut rng = ::test::rng(123);
        let mut sum = 0;
        for _ in 0..1000 {
            sum += poisson.sample(&mut rng);
        }
        let avg = (sum as f64) / 1000.0;
        println!("Poisson average: {}", avg);
        assert!((avg - 10.0).abs() < 0.5); // not 100% certain, but probable enough
    }

    #[test]
    #[should_panic]
    fn test_poisson_invalid_lambda_zero() {
        Poisson::new(0.0);
    }

    #[test]
    #[should_panic]
    fn test_poisson_invalid_lambda_neg() {
        Poisson::new(-10.0);
    }
}
<commit_msg>Test both paths in poisson distribution<commit_after>// Copyright 2016-2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// https://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! The Poisson distribution.

use Rng;
use distributions::Distribution;
use distributions::log_gamma::log_gamma;
use std::f64::consts::PI;

/// The Poisson distribution `Poisson(lambda)`.
///
/// This distribution has a density function:
/// `f(k) = lambda^k * exp(-lambda) / k!` for `k >= 0`.
///
/// # Example
///
/// ```rust
/// use rand::distributions::{Poisson, Distribution};
///
/// let poi = Poisson::new(2.0);
/// let v = poi.sample(&mut rand::thread_rng());
/// println!("{} is from a Poisson(2) distribution", v);
/// ```
#[derive(Clone, Copy, Debug)]
pub struct Poisson {
    lambda: f64,
    // precalculated values
    exp_lambda: f64,
    log_lambda: f64,
    sqrt_2lambda: f64,
    magic_val: f64,
}

impl Poisson {
    /// Construct a new `Poisson` with the given shape parameter
    /// `lambda`. Panics if `lambda <= 0`.
    pub fn new(lambda: f64) -> Poisson {
        assert!(lambda > 0.0, "Poisson::new called with lambda <= 0");
        let log_lambda = lambda.ln();
        Poisson {
            lambda: lambda,
            exp_lambda: (-lambda).exp(),
            log_lambda: log_lambda,
            sqrt_2lambda: (2.0 * lambda).sqrt(),
            magic_val: lambda * log_lambda - log_gamma(1.0 + lambda),
        }
    }
}

impl Distribution<u64> for Poisson {
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u64 {
        // using the algorithm from Numerical Recipes in C

        // for low expected values use the Knuth method
        if self.lambda < 12.0 {
            let mut result = 0;
            let mut p = 1.0;
            while p > self.exp_lambda {
                p *= rng.gen::<f64>();
                result += 1;
            }
            result - 1
        }
        // high expected values - rejection method
        else {
            let mut int_result: u64;

            loop {
                let mut result;
                let mut comp_dev;

                // we use the lorentzian distribution as the comparison distribution
                // f(x) ~ 1/(1+x/^2)
                loop {
                    // draw from the lorentzian distribution
                    comp_dev = (PI * rng.gen::<f64>()).tan();
                    // shift the peak of the comparison ditribution
                    result = self.sqrt_2lambda * comp_dev + self.lambda;
                    // repeat the drawing until we are in the range of possible values
                    if result >= 0.0 {
                        break;
                    }
                }
                // now the result is a random variable greater than 0 with Lorentzian distribution
                // the result should be an integer value
                result = result.floor();
                int_result = result as u64;

                // this is the ratio of the Poisson distribution to the comparison distribution
                // the magic value scales the distribution function to a range of approximately 0-1
                // since it is not exact, we multiply the ratio by 0.9 to avoid ratios greater than 1
                // this doesn't change the resulting distribution, only increases the rate of failed drawings
                let check = 0.9 * (1.0 + comp_dev * comp_dev)
                    * (result * self.log_lambda - log_gamma(1.0 + result) - self.magic_val).exp();

                // check with uniform random value - if below the threshold, we are within the target distribution
                if rng.gen::<f64>() <= check {
                    break;
                }
            }
            int_result
        }
    }
}

#[cfg(test)]
mod test {
    use distributions::Distribution;
    use super::Poisson;

    #[test]
    fn test_poisson_10() {
        let poisson = Poisson::new(10.0);
        let mut rng = ::test::rng(123);
        let mut sum = 0;
        for _ in 0..1000 {
            sum += poisson.sample(&mut rng);
        }
        let avg = (sum as f64) / 1000.0;
        println!("Poisson average: {}", avg);
        assert!((avg - 10.0).abs() < 0.5); // not 100% certain, but probable enough
    }

    #[test]
    fn test_poisson_15() {
        // Take the 'high expected values' path
        let poisson = Poisson::new(15.0);
        let mut rng = ::test::rng(123);
        let mut sum = 0;
        for _ in 0..1000 {
            sum += poisson.sample(&mut rng);
        }
        let avg = (sum as f64) / 1000.0;
        println!("Poisson average: {}", avg);
        assert!((avg - 15.0).abs() < 0.5); // not 100% certain, but probable enough
    }

    #[test]
    #[should_panic]
    fn test_poisson_invalid_lambda_zero() {
        Poisson::new(0.0);
    }

    #[test]
    #[should_panic]
    fn test_poisson_invalid_lambda_neg() {
        Poisson::new(-10.0);
    }
}
<|endoftext|>


<commit_before>use na::{Mat2, DMat};
//use na;
use std::num::*;
mod test;

#[test]
fn test_which_grid() {
    let loc = (-6., 6.);
    let i_rn = (-10.0, 10.0);
    let j_rn = (-10.0, 10.0);
    let i_bins = 10 as usize;
    let j_bins = 10 as usize;

    let result = grid_helpers::which_grid(loc, i_rn, j_rn, i_bins, j_bins).unwrap();
    assert_eq!((2, 8), result);

}

mod grid_helpers {
    pub fn which_grid(val: (f64, f64), i_range: (f64, f64), j_range: (f64, f64), i_bins: usize, j_bins: usize) -> Result<(usize, usize), String> {
        let i_size = (i_range.1 - i_range.0) / i_bins as f64;
        let i_number_of_sizes = ((val.0 - i_range.0) / i_size) as usize;
        let j_size = (j_range.1 - j_range.0) / j_bins as f64;
        let j_number_of_sizes = ((val.1 - j_range.0) / j_size) as usize;

        assert!(i_number_of_sizes < i_bins);
        assert!(j_number_of_sizes < j_bins);

        Ok((i_number_of_sizes, j_number_of_sizes))
    }
}

pub trait DensityGrid {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String>;

    fn compute_decay_coeff_at_time(&self, t_n: u32, t_l: u32, lambda: f64) -> f64 {
        lambda.powf((t_n - t_l) as f64)
    }
}

pub trait DensityGridSpace {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String>;

}

#[derive(Debug)]
#[derive(Copy)]
#[derive(Clone)]
pub struct GridPoint {
    i: usize, //location
    j: usize,
    density: f64,
    data_time: u32,
}

#[derive(Clone)]
pub struct Dat {
    x: f64,
    y: f64,
    val: f64,
    t: u32,
}

pub struct DStreamProps {
    c_m: f64,
    c_l: f64,
    lambda: f64,
    beta: f64,
    i_bins: usize,
    j_bins: usize,
    i_range: (f64, f64),
    j_range: (f64, f64),
}

pub struct TheGrid {
    mat: DMat<GridPoint>,
    props: DStreamProps,
    dats: Vec<((usize, usize), Dat)>,
}

impl Default for DStreamProps {
    fn default() -> DStreamProps {
        DStreamProps {
            c_m: 3.0,
            c_l: 0.8,
            lambda: 0.998,
            beta: 0.3,
            i_bins: 10 as usize,
            j_bins: 10 as usize,
            i_range: (-10.0, 10.0),
            j_range: (-10.0, 10.0),
        }
    }
}

impl DensityGrid for GridPoint {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String> {

        //TODO time - 1 is wrong, it should be biggest time smaller than current
        let coeff = self.compute_decay_coeff_at_time(time, time - 5, lambda);

        let sum_vals = dats.iter().fold(0.0, |sum, x| sum + x.1.val);
        println!("sum vals: {}", sum_vals);
        let new_d = coeff * sum_vals + 1.0;
        println!("new D: {}", new_d);

        Ok(new_d)
    }

//    fn update_with_values(&mut self, vals: &[f64], current_time: u32, lambda: f64) -> Result<(), String> {
//        //        D(g, t n ) = λ t n −t l D(g, t l ) + 1
//        let coeff = self.compute_decay_coeff_at_time(current_time, self.data_time, lambda);
//        println!("coeff: {}", coeff);
//        let sum_vals = vals.iter().fold(0.0, |sum, x| sum + x);
//        let new_d = coeff * coeff * sum_vals + 1.0;
//        println!("sum vals: {}", sum_vals);
//        println!("new D: {}", new_d);
//        self.density = new_d;
//        self.data_time = current_time;
//        Ok(())
//    }

}

impl DensityGridSpace for TheGrid {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String> {

//        println!("{}", self.density);
//        let props: DStreamProps = DStreamProps { ..Default::default() };
//        let idxs = grid_helpers::which_grid(val, props.i_range, props.j_range, props.i_bins, props.j_bins);
//        let res_update = self.update_grid_pt_density(val, 0, props.lambda);
//        println!("{}", self.density);

        if ((loc2d.0 <= self.props.i_range.1) &&
            (loc2d.0 >= self.props.i_range.0) &&
            (loc2d.1 <= self.props.j_range.1) &&
            (loc2d.1 >= self.props.j_range.0)) {

            println!("valid!");
            let idxs =
                grid_helpers::which_grid(loc2d, self.props.i_range, self.props.j_range,
                                         self.props.i_bins, self.props.j_bins).unwrap();
            let gp = &mut self.mat[idxs];

//            gp.update_with_values(&[100.0], t, self.props.lambda);

            self.dats.push((idxs, Dat {x: loc2d.0, y: loc2d.1, val: val, t:t}));

            println!("value at next time: {}",
                gp.get_value_at_time(
                    t+10,
                    self.props.lambda,
                        self.dats
                            .clone()
                            .into_iter()
                            .filter(|i| i.0 == idxs)
                            .collect::<Vec<((usize, usize), Dat)>>()).unwrap());

            println!("dats len: {}", self.dats.len());

            Ok(())
        } else {
            println!("invalid");
            Err(String::from("Invalid range"))
        }
    }
}
#[test]
fn test_put() {

    let mut grid_pt_1: GridPoint = GridPoint {i: 0, j: 0,
        density: 0.0, data_time: 0,};

    let mut grid_pt_2: GridPoint = GridPoint {i: 1, j: 0,
        density: 0.0, data_time: 0, };

    let mut grid_pt_3: GridPoint = GridPoint {i: 0, j: 1,
        density: 0.0, data_time: 0, };

    let mut grid_pt_4: GridPoint = GridPoint {i: 1, j: 1,
        density: 0.0, data_time: 0,};

    let mut v = Vec::new();
    for i in 0..100 {
        v.push(grid_pt_1)
    }

    let mut mat_thing = DMat::from_col_vec(
        10,
        10,
        &v
    );

    for i in 0..10 {

        for j in 0..10 {

            let mut g1= GridPoint
            {i: i, j: j,
                density: 0.0, data_time: 0, };

            mat_thing[(i,j)] = g1;
        }

    }

    let mut the_grid = TheGrid {mat: mat_thing, props: DStreamProps { ..Default::default() }, dats: Vec::new()};

    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 100).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1000).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10000).unwrap();

    for i in 0..10 {
        for j in 0..10 {
            let gp: &GridPoint = &the_grid.mat[(i as usize, j as usize)];
            assert_eq!(0.0, gp.density)
        }
    }
}

#[test]
fn test_initialize_clustering() {
    const loc_i: usize = 0 as usize;
    const loc_j: usize = 0 as usize;
    const G1: GridPoint = GridPoint
    {i: loc_i, j: loc_j,
        density: 0.0, data_time: 0, };
    let result = initialize_clustering(DMat::from_row_vec(2,2, &[G1, G1, G1, G1]));
    assert_eq!(Ok(()), result);
}

pub fn initialize_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update density of all grids in grid_list
    //assign each dense grid to a distinct cluster
    //label all other grids as NO_CLASS; bad grids!
    /*
    do until no change in cluster labels
          foreach cluster c
            foreach outside grid g of c
                foreach neighboring grid h of g
                    if h belongs to cluster c'
                        if |c| > |c'| label all grids in c' as c
                        else label all grids in c as c'
                    else if (h is translational) label h as in c
    end do
    */
    Ok(())
}

pub fn adjust_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update the density of all grids in grid_list
    /*
    foreach grid g whose attribute (dense/sparse/transitional) is changed since last call to adjust_clustering()
        if g is a sparse grid
            delete g from its cluster c, label g as NO_CLASS
            if (c becomes unconnected) split c into two clusters
        else if g is a dense grid
            among all neighboring grids of g, find out the grid h whoe cluster c_h has the largest size
            if h is a dense grid
                if (g is labelled as NO_CLASS) label g as in c_h
                else if (g is in cluster c and |c| > |c_h|)
                    label all grids in c_h as in c
                else if (g is in cluster c and |c| <= |c_h|)
                    label all grids in c as in c_h
            else if h is a transitional grid
                if ((g is NO_CLASS) and (h is an outside grid if g is added to c_h)) label g as in c_h
                else if (g is in cluster c and |c| >= |c_h|)
                    move h from cluster c_h to c
        else if (g is transitional grid)
            among neighboring clusters of g, find the largest one c'
            satisfying that g is an outside grid if added to it and
            label g as in c'
    */

    Ok(())
}<commit_msg>reorg wip<commit_after>use na::{Mat2, DMat};
//use na;
use std::num::*;
mod test;

#[test]
fn test_which_grid() {
    let loc = (-6., 6.);
    let i_rn = (-10.0, 10.0);
    let j_rn = (-10.0, 10.0);
    let i_bins = 10 as usize;
    let j_bins = 10 as usize;

    let result = grid_helpers::which_grid(loc, i_rn, j_rn, i_bins, j_bins).unwrap();
    assert_eq!((2, 8), result);

}

mod grid_helpers {
    pub fn which_grid(val: (f64, f64), i_range: (f64, f64), j_range: (f64, f64), i_bins: usize, j_bins: usize) -> Result<(usize, usize), String> {
        let i_size = (i_range.1 - i_range.0) / i_bins as f64;
        let i_number_of_sizes = ((val.0 - i_range.0) / i_size) as usize;
        let j_size = (j_range.1 - j_range.0) / j_bins as f64;
        let j_number_of_sizes = ((val.1 - j_range.0) / j_size) as usize;

        assert!(i_number_of_sizes < i_bins);
        assert!(j_number_of_sizes < j_bins);

        Ok((i_number_of_sizes, j_number_of_sizes))
    }
}

pub trait DensityGrid {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String>;

    fn compute_decay_coeff_at_time(&self, t_n: u32, t_l: u32, lambda: f64) -> f64 {
        lambda.powf((t_n - t_l) as f64)
    }
}

pub trait DensityGridSpace {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String>;

}

/*

input data: [(x, y, v)]

///some impl
for some time t in 0..:
  _.put t, [(x, y, v)]

//some other impl
fn put (t, [(x, y, v)]):
  [(i, j, sum_vals)] = [x,y,v].map(i,j,v).groupby(i,j).reduce(_+_)
  foreach:
    g = density_grids[i, j]
    g.update(t, v)
    print g.get()
*/
struct RawData;
struct TheWorld;
impl TheWorld {
    fn do_time_steps() {}
    fn do_one_time_step() {}
    fn put() {}
}
struct DG;
impl DG {
    fn update() {}
    fn get() {}
}

#[derive(Debug)]
#[derive(Copy)]
#[derive(Clone)]
pub struct GridPoint {
    i: usize, //location
    j: usize,
    density: f64,
    data_time: u32,
}

#[derive(Clone)]
pub struct Dat {
    x: f64,
    y: f64,
    val: f64,
    t: u32,
}

pub struct DStreamProps {
    c_m: f64,
    c_l: f64,
    lambda: f64,
    beta: f64,
    i_bins: usize,
    j_bins: usize,
    i_range: (f64, f64),
    j_range: (f64, f64),
}

pub struct TheGrid {
    mat: DMat<GridPoint>,
    props: DStreamProps,
    dats: Vec<((usize, usize), Dat)>,
}

impl Default for DStreamProps {
    fn default() -> DStreamProps {
        DStreamProps {
            c_m: 3.0,
            c_l: 0.8,
            lambda: 0.998,
            beta: 0.3,
            i_bins: 10 as usize,
            j_bins: 10 as usize,
            i_range: (-10.0, 10.0),
            j_range: (-10.0, 10.0),
        }
    }
}

impl DensityGrid for GridPoint {

    fn get_value_at_time(&self, time: u32, lambda: f64, dats: Vec<((usize,usize),Dat)>) -> Result<f64, String> {

        //TODO time - 1 is wrong, it should be biggest time smaller than current
        let coeff = self.compute_decay_coeff_at_time(time, time - 5, lambda);

        let sum_vals = dats.iter().fold(0.0, |sum, x| sum + x.1.val);
        println!("sum vals: {}", sum_vals);
        let new_d = coeff * sum_vals + 1.0;
        println!("new D: {}", new_d);

        Ok(new_d)
    }

//    fn update_with_values(&mut self, vals: &[f64], current_time: u32, lambda: f64) -> Result<(), String> {
//        //        D(g, t n ) = λ t n −t l D(g, t l ) + 1
//        let coeff = self.compute_decay_coeff_at_time(current_time, self.data_time, lambda);
//        println!("coeff: {}", coeff);
//        let sum_vals = vals.iter().fold(0.0, |sum, x| sum + x);
//        let new_d = coeff * coeff * sum_vals + 1.0;
//        println!("sum vals: {}", sum_vals);
//        println!("new D: {}", new_d);
//        self.density = new_d;
//        self.data_time = current_time;
//        Ok(())
//    }

}

impl DensityGridSpace for TheGrid {

    fn put(&mut self, loc2d: (f64, f64), val: f64, t: u32) -> Result<(), String> {

//        println!("{}", self.density);
//        let props: DStreamProps = DStreamProps { ..Default::default() };
//        let idxs = grid_helpers::which_grid(val, props.i_range, props.j_range, props.i_bins, props.j_bins);
//        let res_update = self.update_grid_pt_density(val, 0, props.lambda);
//        println!("{}", self.density);

        if ((loc2d.0 <= self.props.i_range.1) &&
            (loc2d.0 >= self.props.i_range.0) &&
            (loc2d.1 <= self.props.j_range.1) &&
            (loc2d.1 >= self.props.j_range.0)) {

            println!("valid!");
            let idxs =
                grid_helpers::which_grid(loc2d, self.props.i_range, self.props.j_range,
                                         self.props.i_bins, self.props.j_bins).unwrap();
            let gp = &mut self.mat[idxs];

//            gp.update_with_values(&[100.0], t, self.props.lambda);

            self.dats.push((idxs, Dat {x: loc2d.0, y: loc2d.1, val: val, t:t}));

            println!("value at next time: {}",
                gp.get_value_at_time(
                    t+10,
                    self.props.lambda,
                        self.dats
                            .clone()
                            .into_iter()
                            .filter(|i| i.0 == idxs)
                            .collect::<Vec<((usize, usize), Dat)>>()).unwrap());

            println!("dats len: {}", self.dats.len());

            Ok(())
        } else {
            println!("invalid");
            Err(String::from("Invalid range"))
        }
    }
}
#[test]
fn test_put() {

    let mut grid_pt_1: GridPoint = GridPoint {i: 0, j: 0,
        density: 0.0, data_time: 0,};

    let mut grid_pt_2: GridPoint = GridPoint {i: 1, j: 0,
        density: 0.0, data_time: 0, };

    let mut grid_pt_3: GridPoint = GridPoint {i: 0, j: 1,
        density: 0.0, data_time: 0, };

    let mut grid_pt_4: GridPoint = GridPoint {i: 1, j: 1,
        density: 0.0, data_time: 0,};

    let mut v = Vec::new();
    for i in 0..100 {
        v.push(grid_pt_1)
    }

    let mut mat_thing = DMat::from_col_vec(
        10,
        10,
        &v
    );

    for i in 0..10 {

        for j in 0..10 {

            let mut g1= GridPoint
            {i: i, j: j,
                density: 0.0, data_time: 0, };

            mat_thing[(i,j)] = g1;
        }

    }

    let mut the_grid = TheGrid {mat: mat_thing, props: DStreamProps { ..Default::default() }, dats: Vec::new()};

    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 100).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 1000).unwrap();
    let res_put = the_grid.put((-6.0, 6.0), 100.0, 10000).unwrap();

    for i in 0..10 {
        for j in 0..10 {
            let gp: &GridPoint = &the_grid.mat[(i as usize, j as usize)];
            assert_eq!(0.0, gp.density)
        }
    }
}

#[test]
fn test_initialize_clustering() {
    const loc_i: usize = 0 as usize;
    const loc_j: usize = 0 as usize;
    const G1: GridPoint = GridPoint
    {i: loc_i, j: loc_j,
        density: 0.0, data_time: 0, };
    let result = initialize_clustering(DMat::from_row_vec(2,2, &[G1, G1, G1, G1]));
    assert_eq!(Ok(()), result);
}

pub fn initialize_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update density of all grids in grid_list
    //assign each dense grid to a distinct cluster
    //label all other grids as NO_CLASS; bad grids!
    /*
    do until no change in cluster labels
          foreach cluster c
            foreach outside grid g of c
                foreach neighboring grid h of g
                    if h belongs to cluster c'
                        if |c| > |c'| label all grids in c' as c
                        else label all grids in c as c'
                    else if (h is translational) label h as in c
    end do
    */
    Ok(())
}

pub fn adjust_clustering(grid_list: DMat<GridPoint>) -> Result<(), String> {
    //update the density of all grids in grid_list
    /*
    foreach grid g whose attribute (dense/sparse/transitional) is changed since last call to adjust_clustering()
        if g is a sparse grid
            delete g from its cluster c, label g as NO_CLASS
            if (c becomes unconnected) split c into two clusters
        else if g is a dense grid
            among all neighboring grids of g, find out the grid h whoe cluster c_h has the largest size
            if h is a dense grid
                if (g is labelled as NO_CLASS) label g as in c_h
                else if (g is in cluster c and |c| > |c_h|)
                    label all grids in c_h as in c
                else if (g is in cluster c and |c| <= |c_h|)
                    label all grids in c as in c_h
            else if h is a transitional grid
                if ((g is NO_CLASS) and (h is an outside grid if g is added to c_h)) label g as in c_h
                else if (g is in cluster c and |c| >= |c_h|)
                    move h from cluster c_h to c
        else if (g is transitional grid)
            among neighboring clusters of g, find the largest one c'
            satisfying that g is an outside grid if added to it and
            label g as in c'
    */

    Ok(())
}<|endoftext|>


<commit_before>use std::io::Result as IoResult;
use std::result::Result;
use std::iter::Iterator;
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> IoResult<String>;
    fn rescale(amount: Pixels, dir: Direction) -> IoResult<String>;
    fn expand_left_edge(amount: Pixels)        -> IoResult<String>;
    fn expand_right_edge(amount: Pixels)       -> IoResult<String>;
    fn expand_top_edge(amount: Pixels)         -> IoResult<String>;
    fn expand_bottom_edge(amount: Pixels)      -> IoResult<String>;
    fn trim_left_edge(amount: Pixels)          -> IoResult<String>;
    fn trim_right_edge(amount: Pixels)         -> IoResult<String>;
    fn trim_top_edge(amount: Pixels)           -> IoResult<String>;
    fn trim_bottom_edge(amount: Pixels)        -> IoResult<String>;
    fn set_resolution(res: ImageResolution)    -> IoResult<String>;
}

trait RunOperation<OpTrait, OtherOp> {
    fn run_operation(op: OtherOp) -> IoResult<String>;
}

impl<Op> RunOperation<Op, PageOps> for Op where Op: ElementaryPageOperations {
    fn run_operation(page_op: PageOps) -> IoResult<String> {
        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Vec<PageOps>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {
        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: vec,
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation::new(String::from(""), String::from(""), &[])
    }

    fn is_noop(&self) -> bool {
        self.ops.is_empty()
    }

    fn run_operation<Op>(&self) -> IoResult<String>
        where Op: ElementaryPageOperations {

        if self.is_noop() {
            return Ok(String::from("No Operation"));
        }

        let mut result = Ok(String::from(""));
        for op in self.ops.iter() {
            let res = Op::run_operation(op.clone());
            match res {
                Ok(s) => {
                    continue;
                }
                Err(e) => {
                    result = Err(e);
                    break;
                }
            }
        }

        result
    }

}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResults {
    results: Vec<IoResult<String>>,
}

impl OperationResults {
    fn new(vec: Vec<IoResult<String>>) -> Self {
        OperationResults {
            results: vec,
        }
    }
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn run_schedule<Op>(schedule: &OperationSchedule) -> OperationResults
        where Op: ElementaryPageOperations {
/*
        let mut results = Vec::new();

        for (page, op) in (*schedule).schedule {
            let result = op.run_operation();
            results.push(result);
        }

        OperationResults::new(results)
*/
        unimplemented!();
    }

}

struct OperationScheduleIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OperationScheduleIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}
<commit_msg>Add Iterator and IntoIterator instances for OperationSchedule.<commit_after>use std::io::Result as IoResult;
use std::result::Result;
use std::iter::{Iterator, IntoIterator};
use std::collections::HashMap;
use std::collections::hash_map;
use std::vec::Vec;
use std::cmp::{Eq, PartialEq};
use std::hash::{Hash, Hasher};

type Pixels = usize;
type FileName = String;
type FilePath = String;

#[derive(Clone, Eq, PartialEq, Hash)]
enum ResolutionUnits {
    PixelsPerInch,
    PixelsPerCentimeter,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum Direction {
    Horizontal,
    Vertical,
}

#[derive(Clone, Eq, PartialEq, Hash)]
enum ImageFileFormat {
    TIFF,
    PNG,
    JPEG,
    UNKNOWN,
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageDimensions {
    x_pixels: Pixels,
    y_pixels: Pixels,
}

impl ImageDimensions {
    fn new(x: Pixels, y: Pixels) -> ImageDimensions {
        ImageDimensions {
            x_pixels: x,
            y_pixels: y,
        }
    }
}

#[derive(Clone, Eq, PartialEq, Hash)]
struct ImageResolution {
    amount: Pixels,
    units: ResolutionUnits,
}

impl ImageResolution {
    fn new(amount: usize, units: ResolutionUnits) -> ImageResolution {
        ImageResolution {
            amount: amount,
            units: units,
        }
    }
}

#[derive(Clone)]
enum PageOps {
    Identify(FilePath),
    Rescale(Pixels, Direction),
    ExpandLeftEdge(Pixels),
    ExpandRightEdge(Pixels),
    ExpandTopEdge(Pixels),
    ExpandBottomEdge(Pixels),
    TrimLeftEdge(Pixels),
    TrimRightEdge(Pixels),
    TrimTopEdge(Pixels),
    TrimBottomEdge(Pixels),
    SetResolution(ImageResolution),
}

trait ElementaryPageOperations {
    fn identify(path: FilePath)                -> IoResult<String>;
    fn rescale(amount: Pixels, dir: Direction) -> IoResult<String>;
    fn expand_left_edge(amount: Pixels)        -> IoResult<String>;
    fn expand_right_edge(amount: Pixels)       -> IoResult<String>;
    fn expand_top_edge(amount: Pixels)         -> IoResult<String>;
    fn expand_bottom_edge(amount: Pixels)      -> IoResult<String>;
    fn trim_left_edge(amount: Pixels)          -> IoResult<String>;
    fn trim_right_edge(amount: Pixels)         -> IoResult<String>;
    fn trim_top_edge(amount: Pixels)           -> IoResult<String>;
    fn trim_bottom_edge(amount: Pixels)        -> IoResult<String>;
    fn set_resolution(res: ImageResolution)    -> IoResult<String>;
}

trait RunOperation<OpTrait, OtherOp> {
    fn run_operation(op: OtherOp) -> IoResult<String>;
}

impl<Op> RunOperation<Op, PageOps> for Op where Op: ElementaryPageOperations {
    fn run_operation(page_op: PageOps) -> IoResult<String> {
        match page_op {
            PageOps::Identify(path)           => Op::identify(path),
            PageOps::Rescale(amount, dir)     => Op::rescale(amount, dir),
            PageOps::ExpandLeftEdge(amount)   => Op::expand_left_edge(amount),
            PageOps::ExpandRightEdge(amount)  => Op::expand_right_edge(amount),
            PageOps::ExpandTopEdge(amount)    => Op::expand_top_edge(amount),
            PageOps::ExpandBottomEdge(amount) => Op::expand_bottom_edge(amount),
            PageOps::TrimLeftEdge(amount)     => Op::trim_left_edge(amount),
            PageOps::TrimRightEdge(amount)    => Op::trim_right_edge(amount),
            PageOps::TrimTopEdge(amount)      => Op::trim_top_edge(amount),
            PageOps::TrimBottomEdge(amount)   => Op::trim_bottom_edge(amount),
            PageOps::SetResolution(res)       => Op::set_resolution(res),
        }
    }
}

#[derive(Clone)]
struct CompoundPageOperation {
    page_name: FileName,
    page_path: FilePath,
    ops: Vec<PageOps>,
}

impl CompoundPageOperation {
    fn new(page_name: FileName, page_path: FilePath, ops: &[PageOps]) -> CompoundPageOperation {
        let mut vec = Vec::new();
        for op in ops.iter() {
            vec.push(op.clone());
        }

        CompoundPageOperation {
            page_name: page_name,
            page_path: page_path,
            ops: vec,
        }
    }

    fn make_noop() -> CompoundPageOperation {
        CompoundPageOperation::new(String::from(""), String::from(""), &[])
    }

    fn is_noop(&self) -> bool {
        self.ops.is_empty()
    }

    fn run_operation<Op>(&self) -> IoResult<String>
        where Op: ElementaryPageOperations {

        if self.is_noop() {
            return Ok(String::from("No Operation"));
        }

        let mut result = Ok(String::from(""));
        for op in self.ops.iter() {
            let res = Op::run_operation(op.clone());
            match res {
                Ok(s) => {
                    continue;
                }
                Err(e) => {
                    result = Err(e);
                    break;
                }
            }
        }

        result
    }

}

#[derive(Clone, Eq)]
struct Page {
    file_name: FileName,
    file_extension: ImageFileFormat,
    file_path: FilePath,
    dimensions: ImageDimensions,
    resolution: ImageResolution,
}

impl Page {
    fn new  (
            file_name: FileName,
            file_extension: ImageFileFormat,
            file_path: FilePath,
            dimensions: ImageDimensions,
            resolution: ImageResolution
            ) -> Page
    {
        Page {
            file_name: file_name,
            file_extension: file_extension,
            file_path: file_path,
            dimensions: dimensions,
            resolution: resolution,
        }
    }
}

impl PartialEq for Page {
    fn eq(&self, other: &Page) -> bool {
            self.file_name      == other.file_name
        &&  self.file_extension == other.file_extension
        &&  self.file_path      == other.file_path
        &&  self.dimensions     == other.dimensions
        &&  self.resolution     == other.resolution
    }
}

impl Hash for Page {
    fn hash<H>(&self, state: &mut H)
        where H: Hasher {

        self.file_name.hash(state);
        self.file_extension.hash(state);
        self.file_path.hash(state);
        self.dimensions.hash(state);
        self.resolution.hash(state);
    }
}

struct OperationSchedule {
    schedule: HashMap<Page, CompoundPageOperation>,
}

struct OperationResults {
    results: Vec<IoResult<String>>,
}

impl OperationResults {
    fn new(vec: Vec<IoResult<String>>) -> Self {
        OperationResults {
            results: vec,
        }
    }
}

#[derive(Clone, Eq, PartialEq)]
enum OperationScheduleError {
    LengthMismatch,
}

impl OperationSchedule {
    fn new() -> Self {
        OperationSchedule {
            schedule: HashMap::new(),
        }
    }

    fn add_operation(&mut self, page: Page, op: CompoundPageOperation) {
        self.schedule.insert(page, op);
    }

    fn build_schedule(pages: &[Page], ops: &[CompoundPageOperation]) -> Result<Self, OperationScheduleError> {
        if pages.len() == ops.len() {

            let mut schedule = OperationSchedule::new();

            for page_number in 0..pages.len() {
                schedule.add_operation(pages[page_number].clone(), ops[page_number].clone());
            }

            Ok(schedule)

        } else {
            Err(OperationScheduleError::LengthMismatch)
        }

    }

    fn iter(&self) -> OpSchedIter {
        OpSchedIter {
            inner: self.schedule.iter()
        }
    }

    fn run_schedule(schedule: &OperationSchedule) -> OperationResults {
/*
        let mut results = Vec::new();

        for (page, op) in (*schedule).schedule {
            let result = op.run_operation();
            results.push(result);
        }

        OperationResults::new(results)
*/
        unimplemented!();
    }

}

struct OpSchedIter<'a> {
    inner:  hash_map::Iter<'a, Page, CompoundPageOperation>
}

impl<'a> Iterator for OpSchedIter<'a> {
    type Item = (&'a Page, &'a CompoundPageOperation);

    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }

}

impl<'a> IntoIterator for &'a OperationSchedule {
    type Item = (&'a Page, &'a CompoundPageOperation);
    type IntoIter = OpSchedIter<'a>;

    fn into_iter(self) -> Self::IntoIter {
        OpSchedIter {
            inner: self.schedule.iter(),
        }
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::renderer_server::{TurtleId, ExportError};
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// If a request initiates an animation on a resource, further requests for that resource will be
/// held until the animation is completed. For example, if a request animates a turtle and then
/// a property on that turtle is requested, this will cause the second request to wait for the
/// animation to be completed. Other requests may still continue in the meantime. Only requests
/// pertaining to that resource (the turtle) will be held. This is important to ensure consistency
/// and avoid race conditions while an animation is running.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Sent initially and on creating any turtle in order to get a `TurtleId`
    ///
    /// Response: `ServerResponse::NewTurtle`
    CreateTurtle,

    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event
    ///
    /// Response: `ServerResponse::Event`
    NextEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(DrawingProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(DrawingPropValue),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon using a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The turtle will not move. None of its position, heading, pen, or other properties will
    /// change. No properties on the drawing will change, including the background image/title/etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The ID of a newly created turtle, guaranteed to be unique
    NewTurtle(TurtleId),

    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), ExportError>),

    /// The next event
    Event(Event),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    Background,
    Center,
    Size,
    Width,
    Height,
    IsMaximized,
    IsFullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    Background(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    IsMaximized(bool),
    IsFullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    Position(Point),
    PositionX(f64),
    PositionY(f64),
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<commit_msg>Added missing MoveTo request<commit_after>use std::path::PathBuf;

use serde::{Serialize, Deserialize};

use crate::{Color, Point, Speed, Event, Distance, Size};
use crate::renderer_server::{TurtleId, ExportError};
use crate::radians::Radians;

/// The different kinds of requests that can be sent from a client
///
/// Not all requests warrant a response. The response for each request is listed in its
/// documentation.
///
/// If a request initiates an animation on a resource, further requests for that resource will be
/// held until the animation is completed. For example, if a request animates a turtle and then
/// a property on that turtle is requested, this will cause the second request to wait for the
/// animation to be completed. Other requests may still continue in the meantime. Only requests
/// pertaining to that resource (the turtle) will be held. This is important to ensure consistency
/// and avoid race conditions while an animation is running.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ClientRequest {
    /// Sent initially and on creating any turtle in order to get a `TurtleId`
    ///
    /// Response: `ServerResponse::NewTurtle`
    CreateTurtle,

    /// Export the drawing in its current state to the given path using the given format
    ///
    /// Response: `ServerResponse::ExportComplete`
    Export(PathBuf, ExportFormat),

    /// Get the next event
    ///
    /// Response: `ServerResponse::Event`
    NextEvent,

    /// Get the given property of the drawing
    ///
    /// Response: `ServerResponse::DrawingProp`
    DrawingProp(DrawingProp),
    /// Set the given property of the drawing
    ///
    /// Response: N/A
    SetDrawingProp(DrawingPropValue),

    /// Get the given property of a turtle
    ///
    /// Response: `ServerResponse::TurtleProp`
    TurtleProp(TurtleId, TurtleProp),
    /// Set the given property of a turtle
    ///
    /// Response: N/A
    SetTurtleProp(TurtleId, TurtlePropValue),

    /// Move a turtle forward by the given amount
    ///
    /// The turtle moves in the direction of its heading and is animated at its current speed. This
    /// may draw a line if the turtle's pen is down. It may also result in a change to the current
    /// fill if the turtle is currently filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveForward(TurtleId, Distance),
    /// Move a turtle to the given position
    ///
    /// The turtle movement is animated at its current speed. This may draw a line if the turtle's
    /// pen is down. It may also result in a change to the current fill if the turtle is currently
    /// filling a shape.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    MoveTo(TurtleId, Point),
    /// Rotate a turtle in place by the given angle in the given direction
    ///
    /// The turtle rotates with an animation at its current speed. It will not draw any line while
    /// rotating since it has not moved.
    ///
    /// The response to this request provides no additional information, but is necessary to ensure
    /// that animations that should be sequenced, can be sequenced correctly. Without a signal for
    /// when the animation is complete, there would be no way to know when to start the next one.
    ///
    /// Response: `ServerResponse::AnimationComplete`
    RotateInPlace(TurtleId, Radians, RotationDirection),

    /// Creates a fill polygon from a turtle's current position
    ///
    /// The color of the fill will always be consistent with the fill color property of the turtle.
    /// That is, if the color changes in the middle of filling, the entire filled area will change
    /// color. It does not matter what the fill color was when this request was first sent.
    ///
    /// Response: N/A
    BeginFill(TurtleId),
    /// Completes a fill polygon using a turtle's current position
    ///
    /// No further points will be added to the polygon and it will remain the color that it was at
    /// the time that this request was sent.
    ///
    /// Response: N/A
    EndFill(TurtleId),

    /// Clears all drawings created by a turtle
    ///
    /// If no ID is provided, all drawings from all turtles will be cleared.
    ///
    /// The turtle will not move. None of its position, heading, pen, or other properties will
    /// change. No properties on the drawing will change, including the background image/title/etc.
    ///
    /// Response: N/A
    Clear(Option<TurtleId>),
}

#[derive(Debug, Serialize, Deserialize)]
pub enum ServerResponse {
    /// The ID of a newly created turtle, guaranteed to be unique
    NewTurtle(TurtleId),

    /// The result of the export, possibly an error if something went wrong
    ExportComplete(Result<(), ExportError>),

    /// The next event
    Event(Event),

    /// The value of the given property of the drawing
    DrawingProp(DrawingPropValue),

    /// The value of the given property of a turtle
    TurtleProp(TurtleId, TurtlePropValue),

    /// An animation was completed for a given turtle
    AnimationComplete(TurtleId),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotationDirection {
    Clockwise,
    Counterclockwise,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ExportFormat {
    Svg,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum DrawingProp {
    Title,
    Background,
    Center,
    Size,
    Width,
    Height,
    IsMaximized,
    IsFullscreen,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum DrawingPropValue {
    Title(String),
    Background(Color),
    Center(Point),
    Size(Size),
    Width(u32),
    Height(u32),
    IsMaximized(bool),
    IsFullscreen(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum TurtleProp {
    Pen(PenProp),
    FillColor,
    IsFilling,
    Position,
    PositionX,
    PositionY,
    Heading,
    Speed,
    IsVisible,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TurtlePropValue {
    Pen(PenPropValue),
    FillColor(Color),
    /// NOTE: Instead of using this with `SetTurtleProp`, use `BeginFill` and `EndFill` instead.
    IsFilling(bool),
    Position(Point),
    PositionX(f64),
    PositionY(f64),
    Heading(Radians),
    Speed(Speed),
    IsVisible(bool),
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum PenProp {
    IsEnabled,
    Thickness,
    Color,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum PenPropValue {
    IsEnabled(bool),
    Thickness(f64),
    Color(Color),
}
<|endoftext|>


<commit_before>//! Pointers and their events

use types::input_device::InputDevice;

use wlroots_sys::{wlr_button_state, wlr_event_pointer_axis, wlr_event_pointer_button,
                  wlr_event_pointer_motion, wlr_event_pointer_motion_absolute};

pub type ButtonState = wlr_button_state;

#[derive(Debug)]
/// Event that triggers when the pointer device scrolls (e.g using a wheel
// or in the case of a touchpad when you use two fingers to scroll).
pub struct AxisEvent {
    event: *mut wlr_event_pointer_axis,
    device: InputDevice
}

#[derive(Debug)]
/// Event that triggers when a button is pressed (e.g left click, right click,
/// a gaming mouse button, etc.).
pub struct ButtonEvent {
    event: *mut wlr_event_pointer_button,
    device: InputDevice
}

#[derive(Debug)]
/// Event that triggers when the pointer moves.
pub struct MotionEvent {
    event: *mut wlr_event_pointer_motion,
    device: InputDevice
}

#[derive(Debug)]
/// Event that triggers when data from a device that supports absolute motion
/// sends data to the compositor.
///
/// For more information on absolute motion, [see this link](https://wayland.freedesktop.org/libinput/doc/latest/absolute_axes.html).
pub struct AbsoluteMotionEvent {
    event: *mut wlr_event_pointer_motion_absolute,
    device: InputDevice
}

impl ButtonEvent {
    /// Constructs a `ButtonEvent` from the raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_button) -> Self {
        ButtonEvent { device: InputDevice::from_ptr((*event).device),
                      event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the state of the button (e.g pressed or released).
    pub fn state(&self) -> ButtonState {
        unsafe { (*self.event).state }
    }

    /// Get the value of the button pressed. This will generally be an atomically
    /// increasing value, with e.g left click being 1 and right click being 2...
    ///
    /// We make no guarantees that 1 always maps to left click, as this is device
    /// driver specific.
    pub fn button(&self) -> u32 {
        unsafe { (*self.event).button }
    }
}

impl AxisEvent {
    /// Constructs a `AxisEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_axis) -> Self {
        AxisEvent { device: InputDevice::from_ptr((*event).device),
                    event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the change from the last axis value.
    ///
    /// Useful to determine e.g how much to scroll.
    pub fn delta(&self) -> f64 {
        unsafe { (*self.event).delta }
    }
}

impl MotionEvent {
    /// Constructs a `MotionEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_motion) -> Self {
        MotionEvent { device: InputDevice::from_ptr((*event).device),
                      event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the change from the last positional value.
    ///
    /// Returned in (x, y) form.
    ///
    /// Note you should not cast this to a type with less precision,
    /// otherwise you'll lose important motion data which can cause bugs
    /// (e.g see [this fun wlc bug](https://github.com/Cloudef/wlc/issues/181)).
    pub fn delta(&self) -> (f64, f64) {
        unsafe { ((*self.event).delta_x, (*self.event).delta_y) }
    }
}

impl AbsoluteMotionEvent {
    /// Construct an `AbsoluteMotionEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_motion_absolute) -> Self {
        AbsoluteMotionEvent { device: InputDevice::from_ptr((*event).device),
                              event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }
}
<commit_msg>Put the derives in the right place in the code<commit_after>//! Pointers and their events

use types::input_device::InputDevice;

use wlroots_sys::{wlr_button_state, wlr_event_pointer_axis, wlr_event_pointer_button,
                  wlr_event_pointer_motion, wlr_event_pointer_motion_absolute};

pub type ButtonState = wlr_button_state;

/// Event that triggers when the pointer device scrolls (e.g using a wheel
// or in the case of a touchpad when you use two fingers to scroll).
#[derive(Debug)]
pub struct AxisEvent {
    event: *mut wlr_event_pointer_axis,
    device: InputDevice
}

/// Event that triggers when a button is pressed (e.g left click, right click,
/// a gaming mouse button, etc.).
#[derive(Debug)]
pub struct ButtonEvent {
    event: *mut wlr_event_pointer_button,
    device: InputDevice
}

/// Event that triggers when the pointer moves.
#[derive(Debug)]
pub struct MotionEvent {
    event: *mut wlr_event_pointer_motion,
    device: InputDevice
}

/// Event that triggers when data from a device that supports absolute motion
/// sends data to the compositor.
///
/// For more information on absolute motion, [see this link](https://wayland.freedesktop.org/libinput/doc/latest/absolute_axes.html).
#[derive(Debug)]
pub struct AbsoluteMotionEvent {
    event: *mut wlr_event_pointer_motion_absolute,
    device: InputDevice
}

impl ButtonEvent {
    /// Constructs a `ButtonEvent` from the raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_button) -> Self {
        ButtonEvent { device: InputDevice::from_ptr((*event).device),
                      event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the state of the button (e.g pressed or released).
    pub fn state(&self) -> ButtonState {
        unsafe { (*self.event).state }
    }

    /// Get the value of the button pressed. This will generally be an atomically
    /// increasing value, with e.g left click being 1 and right click being 2...
    ///
    /// We make no guarantees that 1 always maps to left click, as this is device
    /// driver specific.
    pub fn button(&self) -> u32 {
        unsafe { (*self.event).button }
    }
}

impl AxisEvent {
    /// Constructs a `AxisEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_axis) -> Self {
        AxisEvent { device: InputDevice::from_ptr((*event).device),
                    event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the change from the last axis value.
    ///
    /// Useful to determine e.g how much to scroll.
    pub fn delta(&self) -> f64 {
        unsafe { (*self.event).delta }
    }
}

impl MotionEvent {
    /// Constructs a `MotionEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_motion) -> Self {
        MotionEvent { device: InputDevice::from_ptr((*event).device),
                      event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }

    /// Get the change from the last positional value.
    ///
    /// Returned in (x, y) form.
    ///
    /// Note you should not cast this to a type with less precision,
    /// otherwise you'll lose important motion data which can cause bugs
    /// (e.g see [this fun wlc bug](https://github.com/Cloudef/wlc/issues/181)).
    pub fn delta(&self) -> (f64, f64) {
        unsafe { ((*self.event).delta_x, (*self.event).delta_y) }
    }
}

impl AbsoluteMotionEvent {
    /// Construct an `AbsoluteMotionEvent` from a raw event pointer.
    pub(crate) unsafe fn from_ptr(event: *mut wlr_event_pointer_motion_absolute) -> Self {
        AbsoluteMotionEvent { device: InputDevice::from_ptr((*event).device),
                              event }
    }

    /// Get the device this event refers to.
    pub fn device(&self) -> &InputDevice {
        &self.device
    }
}
<|endoftext|>


<commit_before>//! Support for the wlroots Screencopy (Version 1) Protocol
//!
//! Warning: This protocol is unstable and can change in the future
//! Current Protocol: https://github.com/swaywm/wlroots/blob/master/protocol/wlr-screencopy-unstable-v1.xml

use wayland_sys::server::wl_display as wl_server_display;
use wlroots_sys::{wl_display, wlr_screencopy_manager_v1, wlr_screencopy_manager_v1_create,
    wlr_screencopy_manager_v1_destroy};

#[derive(Debug)]
/// Manager that offers requests to start capturing from a source
pub struct ZManagerV1 {
    manager: *mut wlr_screencopy_manager_v1
}

impl ZManagerV1 {
    pub(crate) unsafe fn new(display: *mut wl_server_display) -> Option<Self> {
        let manager_raw = wlr_screencopy_manager_v1_create(display as *mut wl_display);

        if !manager_raw.is_null() {
            Some(ZmanagerV1 { manager: manager_raw })
        } else {
            None
        }
    }

}

impl Drop for ZManagerV1 {
    fn drop(&mut self) {
        unsafe { wlr_screencopy_manager_v1_destroy(self.manager) }
    }
}
<commit_msg>missed a lowercase<commit_after>//! Support for the wlroots Screencopy (Version 1) Protocol
//!
//! Warning: This protocol is unstable and can change in the future
//! Current Protocol: https://github.com/swaywm/wlroots/blob/master/protocol/wlr-screencopy-unstable-v1.xml

use wayland_sys::server::wl_display as wl_server_display;
use wlroots_sys::{wl_display, wlr_screencopy_manager_v1, wlr_screencopy_manager_v1_create,
    wlr_screencopy_manager_v1_destroy};

#[derive(Debug)]
/// Manager that offers requests to start capturing from a source
pub struct ZManagerV1 {
    manager: *mut wlr_screencopy_manager_v1
}

impl ZManagerV1 {
    pub(crate) unsafe fn new(display: *mut wl_server_display) -> Option<Self> {
        let manager_raw = wlr_screencopy_manager_v1_create(display as *mut wl_display);

        if !manager_raw.is_null() {
            Some(ZManagerV1 { manager: manager_raw })
        } else {
            None
        }
    }

}

impl Drop for ZManagerV1 {
    fn drop(&mut self) {
        unsafe { wlr_screencopy_manager_v1_destroy(self.manager) }
    }
}
<|endoftext|>