<commit_before>use std::path::{Path, PathBuf};
use std::borrow::Cow;
use std::env;
use std::io;
use std::process::Command;
use std::fmt::{self, Display};
use std::sync::Arc;

use errors::*;
use notifications::*;
use rustup_dist::{temp, dist};
use rustup_utils::utils;
use toolchain::{Toolchain, UpdateStatus};
use telemetry_analysis::*;
use settings::{TelemetryMode, SettingsFile, DEFAULT_METADATA_VERSION};

#[derive(Debug)]
pub enum OverrideReason {
    Environment,
    OverrideDB(PathBuf),
}

impl Display for OverrideReason {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            OverrideReason::Environment => write!(f, "environment override by RUSTUP_TOOLCHAIN"),
            OverrideReason::OverrideDB(ref path) => {
                write!(f, "directory override for '{}'", path.display())
            }
        }
    }
}

pub struct Cfg {
    pub multirust_dir: PathBuf,
    pub settings_file: SettingsFile,
    pub toolchains_dir: PathBuf,
    pub update_hash_dir: PathBuf,
    pub temp_cfg: temp::Cfg,
    pub gpg_key: Cow<'static, str>,
    pub env_override: Option<String>,
    pub dist_root_url: Cow<'static, str>,
    pub notify_handler: Arc<Fn(Notification)>,
}

impl Cfg {
    pub fn from_env(notify_handler: Arc<Fn(Notification)>) -> Result<Self> {
        // Set up the multirust home directory
        let multirust_dir = try!(utils::multirust_home());

        try!(utils::ensure_dir_exists("home", &multirust_dir,
                                      &|n| notify_handler(n.into())));

        let settings_file = SettingsFile::new(multirust_dir.join("settings.toml"));
        // Convert from old settings format if necessary
        try!(settings_file.maybe_upgrade_from_legacy(&multirust_dir));

        let toolchains_dir = multirust_dir.join("toolchains");
        let update_hash_dir = multirust_dir.join("update-hashes");

        let notify_clone = notify_handler.clone();
        let temp_cfg = temp::Cfg::new(multirust_dir.join("tmp"),
                                      Box::new(move |n| {
                                          (notify_clone)(n.into())
                                      }));

        // GPG key
        let gpg_key = if let Some(path) = env::var_os("RUSTUP_GPG_KEY")
                                              .and_then(utils::if_not_empty) {
            Cow::Owned(try!(utils::read_file("public key", Path::new(&path))))
        } else {
            Cow::Borrowed(include_str!("rust-key.gpg.ascii"))
        };

        // Environment override
        let env_override = env::var("RUSTUP_TOOLCHAIN")
                               .ok()
                               .and_then(utils::if_not_empty);

        let dist_root_url = env::var("RUSTUP_DIST_ROOT")
                                .ok()
                                .and_then(utils::if_not_empty)
                                .map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned);

        Ok(Cfg {
            multirust_dir: multirust_dir,
            settings_file: settings_file,
            toolchains_dir: toolchains_dir,
            update_hash_dir: update_hash_dir,
            temp_cfg: temp_cfg,
            gpg_key: gpg_key,
            notify_handler: notify_handler,
            env_override: env_override,
            dist_root_url: dist_root_url,
        })
    }

    pub fn set_default(&self, toolchain: &str) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.default_toolchain = Some(toolchain.to_owned());
            Ok(())
        }));
        (self.notify_handler)(Notification::SetDefaultToolchain(toolchain));
        Ok(())
    }

    pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain> {
        if create_parent {
            try!(utils::ensure_dir_exists("toolchains",
                                          &self.toolchains_dir,
                                          &|n| (self.notify_handler)(n.into())));
        }

        Toolchain::from(self, name)
    }

    pub fn verify_toolchain(&self, name: &str) -> Result<Toolchain> {
        let toolchain = try!(self.get_toolchain(name, false));
        try!(toolchain.verify());
        Ok(toolchain)
    }

    pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result<PathBuf> {
        if create_parent {
            try!(utils::ensure_dir_exists("update-hash",
                                          &self.update_hash_dir,
                                          &|n| (self.notify_handler)(n.into())));
        }

        Ok(self.update_hash_dir.join(toolchain))
    }

    pub fn which_binary(&self, path: &Path, binary: &str) -> Result<Option<PathBuf>> {

        if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) {
            Ok(Some(toolchain.binary_file(binary)))
        } else {
            Ok(None)
        }
    }

    pub fn upgrade_data(&self) -> Result<()> {

        let current_version = try!(self.settings_file.with(|s| Ok(s.version.clone())));

        if current_version == DEFAULT_METADATA_VERSION {
            (self.notify_handler)
                (Notification::MetadataUpgradeNotNeeded(&current_version));
            return Ok(());
        }

        (self.notify_handler)
            (Notification::UpgradingMetadata(&current_version, DEFAULT_METADATA_VERSION));

        match &*current_version {
            "2" => {
                // The toolchain installation format changed. Just delete them all.
                (self.notify_handler)(Notification::UpgradeRemovesToolchains);

                let dirs = try!(utils::read_dir("toolchains", &self.toolchains_dir));
                for dir in dirs {
                    let dir = try!(dir.chain_err(|| ErrorKind::UpgradeIoError));
                    try!(utils::remove_dir("toolchain", &dir.path(),
                                           &|n| (self.notify_handler)(n.into())));
                }

                // Also delete the update hashes
                let files = try!(utils::read_dir("update hashes", &self.update_hash_dir));
                for file in files {
                    let file = try!(file.chain_err(|| ErrorKind::UpgradeIoError));
                    try!(utils::remove_file("update hash", &file.path()));
                }

                self.settings_file.with_mut(|s| {
                    s.version = DEFAULT_METADATA_VERSION.to_owned();
                    Ok(())
                })
            }
            _ => Err(ErrorKind::UnknownMetadataVersion(current_version).into()),
        }
    }

    pub fn delete_data(&self) -> Result<()> {
        if utils::path_exists(&self.multirust_dir) {
            Ok(try!(utils::remove_dir("home", &self.multirust_dir,
                                      &|n| (self.notify_handler)(n.into()))))
        } else {
            Ok(())
        }
    }

    pub fn find_default(&self) -> Result<Option<Toolchain>> {
        let opt_name = try!(self.settings_file.with(|s| Ok(s.default_toolchain.clone())));

        if let Some(name) = opt_name {
            let toolchain = try!(self.verify_toolchain(&name)
                                 .chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));

            Ok(Some(toolchain))
        } else {
            Ok(None)
        }
    }

    pub fn find_override(&self, path: &Path) -> Result<Option<(Toolchain, OverrideReason)>> {
        if let Some(ref name) = self.env_override {
            let toolchain = try!(self.verify_toolchain(name).chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));

            return Ok(Some((toolchain, OverrideReason::Environment)));
        }

        let result = try!(self.settings_file.with(|s| {
            Ok(s.find_override(path, self.notify_handler.as_ref()))
        }));
        if let Some((name, reason_path)) = result {
            let toolchain = try!(self.verify_toolchain(&name).chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));
            return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path))));
        }

        Ok(None)
    }

    pub fn find_override_toolchain_or_default
        (&self,
         path: &Path)
         -> Result<Option<(Toolchain, Option<OverrideReason>)>> {
        Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) {
            Some((toolchain, Some(reason)))
        } else {
            try!(self.find_default()).map(|toolchain| (toolchain, None))
        })
    }

    pub fn list_toolchains(&self) -> Result<Vec<String>> {
        if utils::is_directory(&self.toolchains_dir) {
            let mut toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir))
                                         .filter_map(io::Result::ok)
                                         .filter_map(|e| e.file_name().into_string().ok())
                                         .collect();

            utils::toolchain_sort(&mut toolchains);

            Ok(toolchains)
        } else {
            Ok(Vec::new())
        }
    }

    pub fn update_all_channels(&self) -> Result<Vec<(String, Result<UpdateStatus>)>> {
        let toolchains = try!(self.list_toolchains());

        // Convert the toolchain strings to Toolchain values
        let toolchains = toolchains.into_iter();
        let toolchains = toolchains.map(|n| (n.clone(), self.get_toolchain(&n, true)));

        // Filter out toolchains that don't track a release channel
        let toolchains = toolchains.filter(|&(_, ref t)| {
            t.as_ref().map(|t| t.is_tracking()).unwrap_or(false)
        });

        // Update toolchains and collect the results
        let toolchains = toolchains.map(|(n, t)| {
            let t = t.and_then(|t| {
                let t = t.install_from_dist();
                if let Err(ref e) = t {
                    (self.notify_handler)(Notification::NonFatalError(e));
                }
                t
            });

            (n, t)
        });

        Ok(toolchains.collect())
    }

    pub fn check_metadata_version(&self) -> Result<()> {
        try!(utils::assert_is_directory(&self.multirust_dir));

        self.settings_file.with(|s| {
            (self.notify_handler)(Notification::ReadMetadataVersion(&s.version));
            if s.version == DEFAULT_METADATA_VERSION {
                Ok(())
            } else {
                Err(ErrorKind::NeedMetadataUpgrade.into())
            }
        })
    }

    pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option<OverrideReason>)> {
        self.find_override_toolchain_or_default(path)
            .and_then(|r| r.ok_or("no default toolchain configured".into()))
    }

    pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result<Command> {
        let (ref toolchain, _) = try!(self.toolchain_for_dir(path));

        if let Some(cmd) = try!(self.maybe_do_cargo_fallback(toolchain, binary)) {
            Ok(cmd)
        } else {
            toolchain.create_command(binary)
        }
    }

    pub fn create_command_for_toolchain(&self, toolchain: &str, binary: &str) -> Result<Command> {
        let ref toolchain = try!(self.get_toolchain(toolchain, false));

        if let Some(cmd) = try!(self.maybe_do_cargo_fallback(toolchain, binary)) {
            Ok(cmd)
        } else {
            toolchain.create_command(binary)
        }
    }

    // Custom toolchains don't have cargo, so here we detect that situation and
    // try to find a different cargo.
    fn maybe_do_cargo_fallback(&self, toolchain: &Toolchain, binary: &str) -> Result<Option<Command>> {
        if !toolchain.is_custom() {
            return Ok(None);
        }

        if binary != "cargo" && binary != "cargo.exe" {
            return Ok(None);
        }

        let cargo_path = toolchain.path().join("bin/cargo");
        let cargo_exe_path = toolchain.path().join("bin/cargo.exe");

        if cargo_path.exists() || cargo_exe_path.exists() {
            return Ok(None);
        }

        for fallback in &["nightly", "beta", "stable"] {
            let fallback = try!(self.get_toolchain(fallback, false));
            if fallback.exists() {
                let cmd = try!(fallback.create_fallback_command("cargo", toolchain));
                return Ok(Some(cmd));
            }
        }

        Ok(None)
    }

    pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result<PathBuf> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.doc_path(relative)
    }

    pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.open_docs(relative)
    }

    pub fn set_default_host_triple(&self, host_triple: &str) -> Result<()> {
        self.settings_file.with_mut(|s| {
            s.default_host_triple = Some(host_triple.to_owned());
            Ok(())
        })
    }

    pub fn get_default_host_triple(&self) -> Result<dist::TargetTriple> {
        Ok(try!(self.settings_file.with(|s| {
            Ok(s.default_host_triple.as_ref().map(|s| dist::TargetTriple::from_str(&s)))
        })).unwrap_or_else(dist::TargetTriple::from_build))
    }

    pub fn resolve_toolchain(&self, name: &str) -> Result<String> {
        if let Ok(desc) = dist::PartialToolchainDesc::from_str(name) {
            let host = try!(self.get_default_host_triple());
            Ok(desc.resolve(&host).to_string())
        } else {
            Ok(name.to_owned())
        }
    }

    pub fn set_telemetry(&self, telemetry_enabled: bool) -> Result<()> {
        match telemetry_enabled {
            true => self.enable_telemetry(),
            false => self.disable_telemetry(),
        }
    }

    fn enable_telemetry(&self) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.telemetry = TelemetryMode::On;
            Ok(())
        }));

        let _ = utils::ensure_dir_exists("telemetry", &self.multirust_dir.join("telemetry"),
                                         &|_| ());

        (self.notify_handler)(Notification::SetTelemetry("on"));

        Ok(())
    }

    fn disable_telemetry(&self) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.telemetry = TelemetryMode::Off;
            Ok(())
        }));

        (self.notify_handler)(Notification::SetTelemetry("off"));

        Ok(())
    }

    pub fn telemetry_enabled(&self) -> Result<bool> {
        Ok(match try!(self.settings_file.with(|s| Ok(s.telemetry))) {
            TelemetryMode::On => true,
            TelemetryMode::Off => false,
        })
    }

    pub fn analyze_telemetry(&self) -> Result<TelemetryAnalysis> {
        let mut t = TelemetryAnalysis::new(self.multirust_dir.join("telemetry"));

        let events = try!(t.import_telemery());
        try!(t.analyze_telemetry_events(&events));

        Ok(t)
    }
}
<commit_msg>rustup::config: Alter `struct Cfg` and `fn from_env`<commit_after>use std::path::{Path, PathBuf};
use std::borrow::Cow;
use std::env;
use std::io;
use std::process::Command;
use std::fmt::{self, Display};
use std::sync::Arc;

use errors::*;
use notifications::*;
use rustup_dist::{temp, dist};
use rustup_utils::utils;
use toolchain::{Toolchain, UpdateStatus};
use telemetry_analysis::*;
use settings::{TelemetryMode, SettingsFile, DEFAULT_METADATA_VERSION};

#[derive(Debug)]
pub enum OverrideReason {
    Environment,
    OverrideDB(PathBuf),
}

impl Display for OverrideReason {
    fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
        match *self {
            OverrideReason::Environment => write!(f, "environment override by RUSTUP_TOOLCHAIN"),
            OverrideReason::OverrideDB(ref path) => {
                write!(f, "directory override for '{}'", path.display())
            }
        }
    }
}

pub struct Cfg {
    pub multirust_dir: PathBuf,
    pub settings_file: SettingsFile,
    pub toolchains_dir: PathBuf,
    pub update_hash_dir: PathBuf,
    pub temp_cfg: temp::Cfg,
    pub gpg_key: Cow<'static, str>,
    pub env_override: Option<String>,
    pub dist_root_url: String,
    pub dist_root_server: String,
    pub notify_handler: Arc<Fn(Notification)>,
}

impl Cfg {
    pub fn from_env(notify_handler: Arc<Fn(Notification)>) -> Result<Self> {
        // Set up the multirust home directory
        let multirust_dir = try!(utils::multirust_home());

        try!(utils::ensure_dir_exists("home", &multirust_dir,
                                      &|n| notify_handler(n.into())));

        let settings_file = SettingsFile::new(multirust_dir.join("settings.toml"));
        // Convert from old settings format if necessary
        try!(settings_file.maybe_upgrade_from_legacy(&multirust_dir));

        let toolchains_dir = multirust_dir.join("toolchains");
        let update_hash_dir = multirust_dir.join("update-hashes");

        // GPG key
        let gpg_key = if let Some(path) = env::var_os("RUSTUP_GPG_KEY")
                                              .and_then(utils::if_not_empty) {
            Cow::Owned(try!(utils::read_file("public key", Path::new(&path))))
        } else {
            Cow::Borrowed(include_str!("rust-key.gpg.ascii"))
        };

        // Environment override
        let env_override = env::var("RUSTUP_TOOLCHAIN")
                               .ok()
                               .and_then(utils::if_not_empty);

        let dist_root_server = match env::var("RUSTUP_DIST_SERVER") {
            Ok(ref s) if !s.is_empty() => {
                s.clone()
            }
            _ => {
                // For backward compatibility
                env::var("RUSTUP_DIST_ROOT")
                    .ok()
                    .and_then(utils::if_not_empty)
                    .map_or(Cow::Borrowed(dist::DEFAULT_DIST_ROOT), Cow::Owned)
                    .as_ref()
                    .trim_right_matches("/dist")
                    .to_owned()
            }
        };

        let notify_clone = notify_handler.clone();
        let temp_cfg = temp::Cfg::new(multirust_dir.join("tmp"),
                                      dist_root_server.as_str(),
                                      Box::new(move |n| {
                                          (notify_clone)(n.into())
                                      }));
        let dist_root = dist_root_server.clone() + "/dist";

        Ok(Cfg {
            multirust_dir: multirust_dir,
            settings_file: settings_file,
            toolchains_dir: toolchains_dir,
            update_hash_dir: update_hash_dir,
            temp_cfg: temp_cfg,
            gpg_key: gpg_key,
            notify_handler: notify_handler,
            env_override: env_override,
            dist_root_url: dist_root,
            dist_root_server: dist_root_server,
        })
    }

    pub fn set_default(&self, toolchain: &str) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.default_toolchain = Some(toolchain.to_owned());
            Ok(())
        }));
        (self.notify_handler)(Notification::SetDefaultToolchain(toolchain));
        Ok(())
    }

    pub fn get_toolchain(&self, name: &str, create_parent: bool) -> Result<Toolchain> {
        if create_parent {
            try!(utils::ensure_dir_exists("toolchains",
                                          &self.toolchains_dir,
                                          &|n| (self.notify_handler)(n.into())));
        }

        Toolchain::from(self, name)
    }

    pub fn verify_toolchain(&self, name: &str) -> Result<Toolchain> {
        let toolchain = try!(self.get_toolchain(name, false));
        try!(toolchain.verify());
        Ok(toolchain)
    }

    pub fn get_hash_file(&self, toolchain: &str, create_parent: bool) -> Result<PathBuf> {
        if create_parent {
            try!(utils::ensure_dir_exists("update-hash",
                                          &self.update_hash_dir,
                                          &|n| (self.notify_handler)(n.into())));
        }

        Ok(self.update_hash_dir.join(toolchain))
    }

    pub fn which_binary(&self, path: &Path, binary: &str) -> Result<Option<PathBuf>> {

        if let Some((toolchain, _)) = try!(self.find_override_toolchain_or_default(path)) {
            Ok(Some(toolchain.binary_file(binary)))
        } else {
            Ok(None)
        }
    }

    pub fn upgrade_data(&self) -> Result<()> {

        let current_version = try!(self.settings_file.with(|s| Ok(s.version.clone())));

        if current_version == DEFAULT_METADATA_VERSION {
            (self.notify_handler)
                (Notification::MetadataUpgradeNotNeeded(&current_version));
            return Ok(());
        }

        (self.notify_handler)
            (Notification::UpgradingMetadata(&current_version, DEFAULT_METADATA_VERSION));

        match &*current_version {
            "2" => {
                // The toolchain installation format changed. Just delete them all.
                (self.notify_handler)(Notification::UpgradeRemovesToolchains);

                let dirs = try!(utils::read_dir("toolchains", &self.toolchains_dir));
                for dir in dirs {
                    let dir = try!(dir.chain_err(|| ErrorKind::UpgradeIoError));
                    try!(utils::remove_dir("toolchain", &dir.path(),
                                           &|n| (self.notify_handler)(n.into())));
                }

                // Also delete the update hashes
                let files = try!(utils::read_dir("update hashes", &self.update_hash_dir));
                for file in files {
                    let file = try!(file.chain_err(|| ErrorKind::UpgradeIoError));
                    try!(utils::remove_file("update hash", &file.path()));
                }

                self.settings_file.with_mut(|s| {
                    s.version = DEFAULT_METADATA_VERSION.to_owned();
                    Ok(())
                })
            }
            _ => Err(ErrorKind::UnknownMetadataVersion(current_version).into()),
        }
    }

    pub fn delete_data(&self) -> Result<()> {
        if utils::path_exists(&self.multirust_dir) {
            Ok(try!(utils::remove_dir("home", &self.multirust_dir,
                                      &|n| (self.notify_handler)(n.into()))))
        } else {
            Ok(())
        }
    }

    pub fn find_default(&self) -> Result<Option<Toolchain>> {
        let opt_name = try!(self.settings_file.with(|s| Ok(s.default_toolchain.clone())));

        if let Some(name) = opt_name {
            let toolchain = try!(self.verify_toolchain(&name)
                                 .chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));

            Ok(Some(toolchain))
        } else {
            Ok(None)
        }
    }

    pub fn find_override(&self, path: &Path) -> Result<Option<(Toolchain, OverrideReason)>> {
        if let Some(ref name) = self.env_override {
            let toolchain = try!(self.verify_toolchain(name).chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));

            return Ok(Some((toolchain, OverrideReason::Environment)));
        }

        let result = try!(self.settings_file.with(|s| {
            Ok(s.find_override(path, self.notify_handler.as_ref()))
        }));
        if let Some((name, reason_path)) = result {
            let toolchain = try!(self.verify_toolchain(&name).chain_err(|| ErrorKind::ToolchainNotInstalled(name.to_string())));
            return Ok(Some((toolchain, OverrideReason::OverrideDB(reason_path))));
        }

        Ok(None)
    }

    pub fn find_override_toolchain_or_default
        (&self,
         path: &Path)
         -> Result<Option<(Toolchain, Option<OverrideReason>)>> {
        Ok(if let Some((toolchain, reason)) = try!(self.find_override(path)) {
            Some((toolchain, Some(reason)))
        } else {
            try!(self.find_default()).map(|toolchain| (toolchain, None))
        })
    }

    pub fn list_toolchains(&self) -> Result<Vec<String>> {
        if utils::is_directory(&self.toolchains_dir) {
            let mut toolchains: Vec<_> = try!(utils::read_dir("toolchains", &self.toolchains_dir))
                                         .filter_map(io::Result::ok)
                                         .filter_map(|e| e.file_name().into_string().ok())
                                         .collect();

            utils::toolchain_sort(&mut toolchains);

            Ok(toolchains)
        } else {
            Ok(Vec::new())
        }
    }

    pub fn update_all_channels(&self) -> Result<Vec<(String, Result<UpdateStatus>)>> {
        let toolchains = try!(self.list_toolchains());

        // Convert the toolchain strings to Toolchain values
        let toolchains = toolchains.into_iter();
        let toolchains = toolchains.map(|n| (n.clone(), self.get_toolchain(&n, true)));

        // Filter out toolchains that don't track a release channel
        let toolchains = toolchains.filter(|&(_, ref t)| {
            t.as_ref().map(|t| t.is_tracking()).unwrap_or(false)
        });

        // Update toolchains and collect the results
        let toolchains = toolchains.map(|(n, t)| {
            let t = t.and_then(|t| {
                let t = t.install_from_dist();
                if let Err(ref e) = t {
                    (self.notify_handler)(Notification::NonFatalError(e));
                }
                t
            });

            (n, t)
        });

        Ok(toolchains.collect())
    }

    pub fn check_metadata_version(&self) -> Result<()> {
        try!(utils::assert_is_directory(&self.multirust_dir));

        self.settings_file.with(|s| {
            (self.notify_handler)(Notification::ReadMetadataVersion(&s.version));
            if s.version == DEFAULT_METADATA_VERSION {
                Ok(())
            } else {
                Err(ErrorKind::NeedMetadataUpgrade.into())
            }
        })
    }

    pub fn toolchain_for_dir(&self, path: &Path) -> Result<(Toolchain, Option<OverrideReason>)> {
        self.find_override_toolchain_or_default(path)
            .and_then(|r| r.ok_or("no default toolchain configured".into()))
    }

    pub fn create_command_for_dir(&self, path: &Path, binary: &str) -> Result<Command> {
        let (ref toolchain, _) = try!(self.toolchain_for_dir(path));

        if let Some(cmd) = try!(self.maybe_do_cargo_fallback(toolchain, binary)) {
            Ok(cmd)
        } else {
            toolchain.create_command(binary)
        }
    }

    pub fn create_command_for_toolchain(&self, toolchain: &str, binary: &str) -> Result<Command> {
        let ref toolchain = try!(self.get_toolchain(toolchain, false));

        if let Some(cmd) = try!(self.maybe_do_cargo_fallback(toolchain, binary)) {
            Ok(cmd)
        } else {
            toolchain.create_command(binary)
        }
    }

    // Custom toolchains don't have cargo, so here we detect that situation and
    // try to find a different cargo.
    fn maybe_do_cargo_fallback(&self, toolchain: &Toolchain, binary: &str) -> Result<Option<Command>> {
        if !toolchain.is_custom() {
            return Ok(None);
        }

        if binary != "cargo" && binary != "cargo.exe" {
            return Ok(None);
        }

        let cargo_path = toolchain.path().join("bin/cargo");
        let cargo_exe_path = toolchain.path().join("bin/cargo.exe");

        if cargo_path.exists() || cargo_exe_path.exists() {
            return Ok(None);
        }

        for fallback in &["nightly", "beta", "stable"] {
            let fallback = try!(self.get_toolchain(fallback, false));
            if fallback.exists() {
                let cmd = try!(fallback.create_fallback_command("cargo", toolchain));
                return Ok(Some(cmd));
            }
        }

        Ok(None)
    }

    pub fn doc_path_for_dir(&self, path: &Path, relative: &str) -> Result<PathBuf> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.doc_path(relative)
    }

    pub fn open_docs_for_dir(&self, path: &Path, relative: &str) -> Result<()> {
        let (toolchain, _) = try!(self.toolchain_for_dir(path));
        toolchain.open_docs(relative)
    }

    pub fn set_default_host_triple(&self, host_triple: &str) -> Result<()> {
        self.settings_file.with_mut(|s| {
            s.default_host_triple = Some(host_triple.to_owned());
            Ok(())
        })
    }

    pub fn get_default_host_triple(&self) -> Result<dist::TargetTriple> {
        Ok(try!(self.settings_file.with(|s| {
            Ok(s.default_host_triple.as_ref().map(|s| dist::TargetTriple::from_str(&s)))
        })).unwrap_or_else(dist::TargetTriple::from_build))
    }

    pub fn resolve_toolchain(&self, name: &str) -> Result<String> {
        if let Ok(desc) = dist::PartialToolchainDesc::from_str(name) {
            let host = try!(self.get_default_host_triple());
            Ok(desc.resolve(&host).to_string())
        } else {
            Ok(name.to_owned())
        }
    }

    pub fn set_telemetry(&self, telemetry_enabled: bool) -> Result<()> {
        match telemetry_enabled {
            true => self.enable_telemetry(),
            false => self.disable_telemetry(),
        }
    }

    fn enable_telemetry(&self) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.telemetry = TelemetryMode::On;
            Ok(())
        }));

        let _ = utils::ensure_dir_exists("telemetry", &self.multirust_dir.join("telemetry"),
                                         &|_| ());

        (self.notify_handler)(Notification::SetTelemetry("on"));

        Ok(())
    }

    fn disable_telemetry(&self) -> Result<()> {
        try!(self.settings_file.with_mut(|s| {
            s.telemetry = TelemetryMode::Off;
            Ok(())
        }));

        (self.notify_handler)(Notification::SetTelemetry("off"));

        Ok(())
    }

    pub fn telemetry_enabled(&self) -> Result<bool> {
        Ok(match try!(self.settings_file.with(|s| Ok(s.telemetry))) {
            TelemetryMode::On => true,
            TelemetryMode::Off => false,
        })
    }

    pub fn analyze_telemetry(&self) -> Result<TelemetryAnalysis> {
        let mut t = TelemetryAnalysis::new(self.multirust_dir.join("telemetry"));

        let events = try!(t.import_telemery());
        try!(t.analyze_telemetry_events(&events));

        Ok(t)
    }
}
<|endoftext|>


<commit_before>use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::{Path, PathBuf};

use rustc_ast::ast;
use rustc_ast::token::{DelimToken, TokenKind};
use rustc_errors::Diagnostic;
use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
use rustc_span::{sym, symbol::kw, Span};

use crate::attr::first_attr_value_str_by_name;
use crate::syntux::session::ParseSess;
use crate::{Config, Input};

pub(crate) type DirectoryOwnership = rustc_expand::module::DirectoryOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;

#[derive(Clone)]
pub(crate) struct Directory {
    pub(crate) path: PathBuf,
    pub(crate) ownership: DirectoryOwnership,
}

/// A parser for Rust source code.
pub(crate) struct Parser<'a> {
    parser: RawParser<'a>,
}

/// A builder for the `Parser`.
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
    config: Option<&'a Config>,
    sess: Option<&'a ParseSess>,
    input: Option<Input>,
    directory_ownership: Option<DirectoryOwnership>,
}

impl<'a> ParserBuilder<'a> {
    pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
        self.input = Some(input);
        self
    }

    pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
        self.sess = Some(sess);
        self
    }

    pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
        self.config = Some(config);
        self
    }

    pub(crate) fn directory_ownership(
        mut self,
        directory_ownership: Option<DirectoryOwnership>,
    ) -> ParserBuilder<'a> {
        self.directory_ownership = directory_ownership;
        self
    }

    pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
        let input = self.input.ok_or(ParserError::NoInput)?;

        let parser = match Self::parser(sess.inner(), input) {
            Ok(p) => p,
            Err(db) => {
                if let Some(diagnostics) = db {
                    sess.emit_diagnostics(diagnostics);
                    return Err(ParserError::ParserCreationError);
                }
                return Err(ParserError::ParsePanicError);
            }
        };

        Ok(Parser { parser })
    }

    fn parser(
        sess: &'a rustc_session::parse::ParseSess,
        input: Input,
    ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
        match input {
            Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
                new_parser_from_file(sess, file, None)
            }))
            .map_err(|_| None),
            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
                sess,
                rustc_span::FileName::Custom("stdin".to_owned()),
                text,
            )
            .map_err(|db| Some(db)),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
    NoParseSess,
    NoInput,
    ParserCreationError,
    ParseError,
    ParsePanicError,
}

impl<'a> Parser<'a> {
    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
        let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
        // On windows, the base path might have the form
        // `\\?\foo\bar` in which case it does not tolerate
        // mixed `/` and `\` separators, so canonicalize
        // `/` to `\`.
        #[cfg(windows)]
        let path_string = path_string.replace("/", "\\");

        Some(path.join(&*path_string))
    }

    pub(crate) fn parse_file_as_module(
        sess: &'a ParseSess,
        path: &Path,
        span: Span,
    ) -> Result<(ast::Mod, Vec<ast::Attribute>), ParserError> {
        let result = catch_unwind(AssertUnwindSafe(|| {
            let mut parser = new_parser_from_file(sess.inner(), &path, Some(span));
            match parser.parse_mod(&TokenKind::Eof, ast::Unsafe::No) {
                Ok(result) => Some(result),
                Err(mut e) => {
                    sess.emit_or_cancel_diagnostic(&mut e);
                    if sess.can_reset_errors() {
                        sess.reset_errors();
                    }
                    None
                }
            }
        }));
        match result {
            Ok(Some(m)) => {
                if !sess.has_errors() {
                    return Ok(m);
                }

                if sess.can_reset_errors() {
                    sess.reset_errors();
                    return Ok(m);
                }
                Err(ParserError::ParseError)
            }
            Ok(None) => Err(ParserError::ParseError),
            Err(..) if path.exists() => Err(ParserError::ParseError),
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_crate(
        config: &'a Config,
        input: Input,
        directory_ownership: Option<DirectoryOwnership>,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let krate = Parser::parse_crate_inner(config, input, directory_ownership, sess)?;
        if !sess.has_errors() {
            return Ok(krate);
        }

        if sess.can_reset_errors() {
            sess.reset_errors();
            return Ok(krate);
        }

        Err(ParserError::ParseError)
    }

    fn parse_crate_inner(
        config: &'a Config,
        input: Input,
        directory_ownership: Option<DirectoryOwnership>,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let mut parser = ParserBuilder::default()
            .config(config)
            .input(input)
            .directory_ownership(directory_ownership)
            .sess(sess)
            .build()?;
        parser.parse_crate_mod()
    }

    fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
        let mut parser = AssertUnwindSafe(&mut self.parser);

        match catch_unwind(move || parser.parse_crate_mod()) {
            Ok(Ok(k)) => Ok(k),
            Ok(Err(mut db)) => {
                db.emit();
                Err(ParserError::ParseError)
            }
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_cfg_if(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
            Ok(Ok(items)) => Ok(items),
            Ok(err @ Err(_)) => err,
            Err(..) => Err("failed to parse cfg_if!"),
        }
    }

    fn parse_cfg_if_inner(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        let token_stream = mac.args.inner_tokens();
        let mut parser =
            rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some(""));

        let mut items = vec![];
        let mut process_if_cfg = true;

        while parser.token.kind != TokenKind::Eof {
            if process_if_cfg {
                if !parser.eat_keyword(kw::If) {
                    return Err("Expected `if`");
                }
                parser
                    .parse_attribute(false)
                    .map_err(|_| "Failed to parse attributes")?;
            }

            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
                return Err("Expected an opening brace");
            }

            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
                && parser.token.kind != TokenKind::Eof
            {
                let item = match parser.parse_item() {
                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
                    Ok(None) => continue,
                    Err(mut err) => {
                        err.cancel();
                        parser.sess.span_diagnostic.reset_err_count();
                        return Err(
                            "Expected item inside cfg_if block, but failed to parse it as an item",
                        );
                    }
                };
                if let ast::ItemKind::Mod(..) = item.kind {
                    items.push(item);
                }
            }

            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
                return Err("Expected a closing brace");
            }

            if parser.eat(&TokenKind::Eof) {
                break;
            }

            if !parser.eat_keyword(kw::Else) {
                return Err("Expected `else`");
            }

            process_if_cfg = parser.token.is_keyword(kw::If);
        }

        Ok(items)
    }
}
<commit_msg>refactor: update cfg_if attr parsing<commit_after>use std::panic::{catch_unwind, AssertUnwindSafe};
use std::path::{Path, PathBuf};

use rustc_ast::ast;
use rustc_ast::token::{DelimToken, TokenKind};
use rustc_errors::Diagnostic;
use rustc_parse::{new_parser_from_file, parser::Parser as RawParser};
use rustc_span::{sym, symbol::kw, Span};

use crate::attr::first_attr_value_str_by_name;
use crate::syntux::session::ParseSess;
use crate::{Config, Input};

pub(crate) type DirectoryOwnership = rustc_expand::module::DirectoryOwnership;
pub(crate) type ModulePathSuccess = rustc_expand::module::ModulePathSuccess;

#[derive(Clone)]
pub(crate) struct Directory {
    pub(crate) path: PathBuf,
    pub(crate) ownership: DirectoryOwnership,
}

/// A parser for Rust source code.
pub(crate) struct Parser<'a> {
    parser: RawParser<'a>,
}

/// A builder for the `Parser`.
#[derive(Default)]
pub(crate) struct ParserBuilder<'a> {
    config: Option<&'a Config>,
    sess: Option<&'a ParseSess>,
    input: Option<Input>,
    directory_ownership: Option<DirectoryOwnership>,
}

impl<'a> ParserBuilder<'a> {
    pub(crate) fn input(mut self, input: Input) -> ParserBuilder<'a> {
        self.input = Some(input);
        self
    }

    pub(crate) fn sess(mut self, sess: &'a ParseSess) -> ParserBuilder<'a> {
        self.sess = Some(sess);
        self
    }

    pub(crate) fn config(mut self, config: &'a Config) -> ParserBuilder<'a> {
        self.config = Some(config);
        self
    }

    pub(crate) fn directory_ownership(
        mut self,
        directory_ownership: Option<DirectoryOwnership>,
    ) -> ParserBuilder<'a> {
        self.directory_ownership = directory_ownership;
        self
    }

    pub(crate) fn build(self) -> Result<Parser<'a>, ParserError> {
        let sess = self.sess.ok_or(ParserError::NoParseSess)?;
        let input = self.input.ok_or(ParserError::NoInput)?;

        let parser = match Self::parser(sess.inner(), input) {
            Ok(p) => p,
            Err(db) => {
                if let Some(diagnostics) = db {
                    sess.emit_diagnostics(diagnostics);
                    return Err(ParserError::ParserCreationError);
                }
                return Err(ParserError::ParsePanicError);
            }
        };

        Ok(Parser { parser })
    }

    fn parser(
        sess: &'a rustc_session::parse::ParseSess,
        input: Input,
    ) -> Result<rustc_parse::parser::Parser<'a>, Option<Vec<Diagnostic>>> {
        match input {
            Input::File(ref file) => catch_unwind(AssertUnwindSafe(move || {
                new_parser_from_file(sess, file, None)
            }))
            .map_err(|_| None),
            Input::Text(text) => rustc_parse::maybe_new_parser_from_source_str(
                sess,
                rustc_span::FileName::Custom("stdin".to_owned()),
                text,
            )
            .map_err(|db| Some(db)),
        }
    }
}

#[derive(Debug, PartialEq)]
pub(crate) enum ParserError {
    NoParseSess,
    NoInput,
    ParserCreationError,
    ParseError,
    ParsePanicError,
}

impl<'a> Parser<'a> {
    pub(crate) fn submod_path_from_attr(attrs: &[ast::Attribute], path: &Path) -> Option<PathBuf> {
        let path_string = first_attr_value_str_by_name(attrs, sym::path)?.as_str();
        // On windows, the base path might have the form
        // `\\?\foo\bar` in which case it does not tolerate
        // mixed `/` and `\` separators, so canonicalize
        // `/` to `\`.
        #[cfg(windows)]
        let path_string = path_string.replace("/", "\\");

        Some(path.join(&*path_string))
    }

    pub(crate) fn parse_file_as_module(
        sess: &'a ParseSess,
        path: &Path,
        span: Span,
    ) -> Result<(ast::Mod, Vec<ast::Attribute>), ParserError> {
        let result = catch_unwind(AssertUnwindSafe(|| {
            let mut parser = new_parser_from_file(sess.inner(), &path, Some(span));
            match parser.parse_mod(&TokenKind::Eof, ast::Unsafe::No) {
                Ok(result) => Some(result),
                Err(mut e) => {
                    sess.emit_or_cancel_diagnostic(&mut e);
                    if sess.can_reset_errors() {
                        sess.reset_errors();
                    }
                    None
                }
            }
        }));
        match result {
            Ok(Some(m)) => {
                if !sess.has_errors() {
                    return Ok(m);
                }

                if sess.can_reset_errors() {
                    sess.reset_errors();
                    return Ok(m);
                }
                Err(ParserError::ParseError)
            }
            Ok(None) => Err(ParserError::ParseError),
            Err(..) if path.exists() => Err(ParserError::ParseError),
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_crate(
        config: &'a Config,
        input: Input,
        directory_ownership: Option<DirectoryOwnership>,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let krate = Parser::parse_crate_inner(config, input, directory_ownership, sess)?;
        if !sess.has_errors() {
            return Ok(krate);
        }

        if sess.can_reset_errors() {
            sess.reset_errors();
            return Ok(krate);
        }

        Err(ParserError::ParseError)
    }

    fn parse_crate_inner(
        config: &'a Config,
        input: Input,
        directory_ownership: Option<DirectoryOwnership>,
        sess: &'a ParseSess,
    ) -> Result<ast::Crate, ParserError> {
        let mut parser = ParserBuilder::default()
            .config(config)
            .input(input)
            .directory_ownership(directory_ownership)
            .sess(sess)
            .build()?;
        parser.parse_crate_mod()
    }

    fn parse_crate_mod(&mut self) -> Result<ast::Crate, ParserError> {
        let mut parser = AssertUnwindSafe(&mut self.parser);

        match catch_unwind(move || parser.parse_crate_mod()) {
            Ok(Ok(k)) => Ok(k),
            Ok(Err(mut db)) => {
                db.emit();
                Err(ParserError::ParseError)
            }
            Err(_) => Err(ParserError::ParsePanicError),
        }
    }

    pub(crate) fn parse_cfg_if(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        match catch_unwind(AssertUnwindSafe(|| Parser::parse_cfg_if_inner(sess, mac))) {
            Ok(Ok(items)) => Ok(items),
            Ok(err @ Err(_)) => err,
            Err(..) => Err("failed to parse cfg_if!"),
        }
    }

    fn parse_cfg_if_inner(
        sess: &'a ParseSess,
        mac: &'a ast::MacCall,
    ) -> Result<Vec<ast::Item>, &'static str> {
        let token_stream = mac.args.inner_tokens();
        let mut parser =
            rustc_parse::stream_to_parser(sess.inner(), token_stream.clone(), Some(""));

        let mut items = vec![];
        let mut process_if_cfg = true;

        while parser.token.kind != TokenKind::Eof {
            if process_if_cfg {
                if !parser.eat_keyword(kw::If) {
                    return Err("Expected `if`");
                }
                // Inner attributes are not actually syntactically permitted here, but we don't
                // care about inner vs outer attributes in this position. Our purpose with this
                // special case parsing of cfg_if macros is to ensure we can correctly resolve
                // imported modules that may have a custom `path` defined.
                //
                // As such, we just need to advance the parser past the attribute and up to
                // to the opening brace.
                // See also https://github.com/rust-lang/rust/pull/79433
                parser
                    .parse_attribute(rustc_parse::parser::attr::InnerAttrPolicy::Permitted)
                    .map_err(|_| "Failed to parse attributes")?;
            }

            if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
                return Err("Expected an opening brace");
            }

            while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
                && parser.token.kind != TokenKind::Eof
            {
                let item = match parser.parse_item() {
                    Ok(Some(item_ptr)) => item_ptr.into_inner(),
                    Ok(None) => continue,
                    Err(mut err) => {
                        err.cancel();
                        parser.sess.span_diagnostic.reset_err_count();
                        return Err(
                            "Expected item inside cfg_if block, but failed to parse it as an item",
                        );
                    }
                };
                if let ast::ItemKind::Mod(..) = item.kind {
                    items.push(item);
                }
            }

            if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
                return Err("Expected a closing brace");
            }

            if parser.eat(&TokenKind::Eof) {
                break;
            }

            if !parser.eat_keyword(kw::Else) {
                return Err("Expected `else`");
            }

            process_if_cfg = parser.token.is_keyword(kw::If);
        }

        Ok(items)
    }
}
<|endoftext|>


<commit_before>//! Serde serialization support for sysinfo
//!
//! Usage example with `serde_derive`:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate serde_derive;
//! # extern crate serde_json;
//! # extern crate sysinfo;
//! # extern crate sysinfo_web;
//! # use sysinfo_web::sysinfo_serde;
//! # use sysinfo::SystemExt;
//! # fn main() {
//! #[derive(Serialize)]
//! struct Info (
//!     #[serde(serialize_with = "sysinfo_serde::serialize")]
//!     sysinfo::System
//! );
//!
//! let mut system = sysinfo::System::new();
//! system.refresh_all();
//!
//! let info = Info(system);
//! let serialized = serde_json::to_string(&info).unwrap();
//! # }
//!
//! ```

use std::collections::HashMap;
use serde::{Serialize, Serializer};
use serde::ser::SerializeMap;
use sysinfo::{System, SystemExt, Processor, ProcessorExt, Process, ProcessExt,
              Component, ComponentExt, Disk, DiskExt, DiskType};

pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer,
          for<'a> Ser<'a, T>: Serialize
{
    Ser::new(value).serialize(serializer)
}

/// A wrapper to serialize `sysinfo` types.
pub struct Ser<'a, T: 'a>(&'a T);

impl<'a, T> Ser<'a, T>
    where Ser<'a, T>: Serialize
{
    #[inline(always)]
    pub fn new(value: &'a T) -> Self {
        Ser(value)
    }
}

impl<'a> Serialize for Ser<'a, Processor> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("name", self.0.get_name())?;
        map.serialize_entry("cpu_usage", &self.0.get_cpu_usage())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Process> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        // Do not serialize command line arguments and environment variables
        // for extra security and save up some bandwith on each request.
        //
        // This is actually breaking whole idea behind this serde serializer,
        // but I since only sysinfo-web is using this serializer, so it should be fine.
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("name", &self.0.name())?;
        //map.serialize_entry("cmd", &self.0.cmd())?;
        map.serialize_entry("exe", &self.0.exe())?;
        map.serialize_entry("pid", &self.0.pid())?;
        map.serialize_entry("parent", &self.0.parent())?;
        //map.serialize_entry("environ", &self.0.environ())?;
        map.serialize_entry("cwd", &self.0.cwd())?;
        map.serialize_entry("root", &self.0.root())?;
        map.serialize_entry("memory", &self.0.memory())?;
        map.serialize_entry("start_time", &self.0.start_time())?;
        map.serialize_entry("cpu_usage", &self.0.cpu_usage())?;
        map.serialize_entry("uid", &self.0.uid)?;
        map.serialize_entry("gid", &self.0.gid)?;
        #[cfg(target_os = "linux")]
        map.serialize_entry("tasks", &Ser::new(&self.0.tasks))?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, HashMap<i32, Process>> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        for (pid, process) in self.0.iter() {
            map.serialize_key(pid)?;
            map.serialize_value(&Ser::new(process))?;
        }
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Component> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("temperature", &self.0.get_temperature())?;
        map.serialize_entry("max", &self.0.get_max())?;
        map.serialize_entry("critical", &self.0.get_critical())?;
        map.serialize_entry("label", &self.0.get_label())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Disk> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("type",
                             &match self.0.get_type() {
                                 DiskType::HDD => "HDD".to_owned(),
                                 DiskType::SSD => "SSD".to_owned(),
                                 DiskType::Unknown(size) => format!("Unknown({})", size),
                             })?;
        map.serialize_entry("name", self.0.get_name().to_str().unwrap())?;
        map.serialize_entry("file_system", ::std::str::from_utf8(self.0.get_file_system()).unwrap())?;
        map.serialize_entry("mount_point", self.0.get_mount_point())?;
        map.serialize_entry("total_space", &self.0.get_total_space())?;
        map.serialize_entry("available_space", &self.0.get_available_space())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, System> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("process_list", &Ser::new(self.0.get_process_list()))?;
        map.serialize_entry("processor_list",
                             &self.0
                                 .get_processor_list()
                                 .iter()
                                 .map(|p| Ser::new(p))
                                 .collect::<Vec<Ser<Processor>>>())?;
        map.serialize_entry("total_memory", &self.0.get_total_memory())?;
        map.serialize_entry("free_memory", &self.0.get_free_memory())?;
        map.serialize_entry("used_memory", &self.0.get_used_memory())?;
        map.serialize_entry("total_swap", &self.0.get_total_swap())?;
        map.serialize_entry("free_swap", &self.0.get_free_swap())?;
        map.serialize_entry("used_swap", &self.0.get_used_swap())?;
        map.serialize_entry("components_list",
                             &self.0
                                 .get_components_list()
                                 .iter()
                                 .map(|c| Ser::new(c))
                                 .collect::<Vec<Ser<Component>>>())?;
        map.serialize_entry("disks",
                             &self.0
                                 .get_disks()
                                 .iter()
                                 .map(|c| Ser::new(c))
                                 .collect::<Vec<Ser<Disk>>>())?;

        map.end()
    }
}
<commit_msg>Fix typo<commit_after>//! Serde serialization support for sysinfo
//!
//! Usage example with `serde_derive`:
//!
//! ```rust
//! # #[macro_use]
//! # extern crate serde_derive;
//! # extern crate serde_json;
//! # extern crate sysinfo;
//! # extern crate sysinfo_web;
//! # use sysinfo_web::sysinfo_serde;
//! # use sysinfo::SystemExt;
//! # fn main() {
//! #[derive(Serialize)]
//! struct Info (
//!     #[serde(serialize_with = "sysinfo_serde::serialize")]
//!     sysinfo::System
//! );
//!
//! let mut system = sysinfo::System::new();
//! system.refresh_all();
//!
//! let info = Info(system);
//! let serialized = serde_json::to_string(&info).unwrap();
//! # }
//!
//! ```

use std::collections::HashMap;
use serde::{Serialize, Serializer};
use serde::ser::SerializeMap;
use sysinfo::{System, SystemExt, Processor, ProcessorExt, Process, ProcessExt,
              Component, ComponentExt, Disk, DiskExt, DiskType};

pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer,
          for<'a> Ser<'a, T>: Serialize
{
    Ser::new(value).serialize(serializer)
}

/// A wrapper to serialize `sysinfo` types.
pub struct Ser<'a, T: 'a>(&'a T);

impl<'a, T> Ser<'a, T>
    where Ser<'a, T>: Serialize
{
    #[inline(always)]
    pub fn new(value: &'a T) -> Self {
        Ser(value)
    }
}

impl<'a> Serialize for Ser<'a, Processor> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("name", self.0.get_name())?;
        map.serialize_entry("cpu_usage", &self.0.get_cpu_usage())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Process> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        // Do not serialize command line arguments and environment variables
        // for extra security and save up some bandwith on each request.
        //
        // This is actually breaking whole idea behind this serde serializer,
        // but since only sysinfo-web is using this serializer it should be fine.
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("name", &self.0.name())?;
        //map.serialize_entry("cmd", &self.0.cmd())?;
        map.serialize_entry("exe", &self.0.exe())?;
        map.serialize_entry("pid", &self.0.pid())?;
        map.serialize_entry("parent", &self.0.parent())?;
        //map.serialize_entry("environ", &self.0.environ())?;
        map.serialize_entry("cwd", &self.0.cwd())?;
        map.serialize_entry("root", &self.0.root())?;
        map.serialize_entry("memory", &self.0.memory())?;
        map.serialize_entry("start_time", &self.0.start_time())?;
        map.serialize_entry("cpu_usage", &self.0.cpu_usage())?;
        map.serialize_entry("uid", &self.0.uid)?;
        map.serialize_entry("gid", &self.0.gid)?;
        #[cfg(target_os = "linux")]
        map.serialize_entry("tasks", &Ser::new(&self.0.tasks))?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, HashMap<i32, Process>> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        for (pid, process) in self.0.iter() {
            map.serialize_key(pid)?;
            map.serialize_value(&Ser::new(process))?;
        }
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Component> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("temperature", &self.0.get_temperature())?;
        map.serialize_entry("max", &self.0.get_max())?;
        map.serialize_entry("critical", &self.0.get_critical())?;
        map.serialize_entry("label", &self.0.get_label())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, Disk> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("type",
                             &match self.0.get_type() {
                                 DiskType::HDD => "HDD".to_owned(),
                                 DiskType::SSD => "SSD".to_owned(),
                                 DiskType::Unknown(size) => format!("Unknown({})", size),
                             })?;
        map.serialize_entry("name", self.0.get_name().to_str().unwrap())?;
        map.serialize_entry("file_system", ::std::str::from_utf8(self.0.get_file_system()).unwrap())?;
        map.serialize_entry("mount_point", self.0.get_mount_point())?;
        map.serialize_entry("total_space", &self.0.get_total_space())?;
        map.serialize_entry("available_space", &self.0.get_available_space())?;
        map.end()
    }
}

impl<'a> Serialize for Ser<'a, System> {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        let mut map = serializer.serialize_map(None)?;
        map.serialize_entry("process_list", &Ser::new(self.0.get_process_list()))?;
        map.serialize_entry("processor_list",
                             &self.0
                                 .get_processor_list()
                                 .iter()
                                 .map(|p| Ser::new(p))
                                 .collect::<Vec<Ser<Processor>>>())?;
        map.serialize_entry("total_memory", &self.0.get_total_memory())?;
        map.serialize_entry("free_memory", &self.0.get_free_memory())?;
        map.serialize_entry("used_memory", &self.0.get_used_memory())?;
        map.serialize_entry("total_swap", &self.0.get_total_swap())?;
        map.serialize_entry("free_swap", &self.0.get_free_swap())?;
        map.serialize_entry("used_swap", &self.0.get_used_swap())?;
        map.serialize_entry("components_list",
                             &self.0
                                 .get_components_list()
                                 .iter()
                                 .map(|c| Ser::new(c))
                                 .collect::<Vec<Ser<Component>>>())?;
        map.serialize_entry("disks",
                             &self.0
                                 .get_disks()
                                 .iter()
                                 .map(|c| Ser::new(c))
                                 .collect::<Vec<Ser<Disk>>>())?;

        map.end()
    }
}
<|endoftext|>


<commit_before>use super::Color;
use enum_map::{enum_map, Enum, EnumMap};
#[cfg(feature = "toml")]
use log::warn;

use std::ops::{Index, IndexMut};

// Use AHash instead of the slower SipHash
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::ABuildHasher>;

/// Color configuration for the application.
///
/// Assign each color role an actual color.
///
/// It implements `Index` and `IndexMut` to access and modify this mapping:
///
/// # Example
///
/// ```rust
/// # use cursive::theme::Palette;
/// use cursive::theme::PaletteColor::*;
/// use cursive::theme::Color::*;
/// use cursive::theme::BaseColor::*;
///
/// let mut palette = Palette::default();
///
/// assert_eq!(palette[Background], Dark(Blue));
/// palette[Shadow] = Light(Red);
/// ```
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Palette {
    basic: EnumMap<PaletteColor, Color>,
    custom: HashMap<String, PaletteNode>,
}

/// A node in the palette tree.
///
/// This describes a value attached to a custom keyword in the palette.
///
/// This can either be a color, or a nested namespace with its own mapping.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum PaletteNode {
    /// A single color.
    Color(Color),
    /// A group of values bundled in the same namespace.
    ///
    /// Namespaces can be merged in the palette with `Palette::merge`.
    Namespace(HashMap<String, PaletteNode>),
}

// Basic usage: only use basic colors
impl Index<PaletteColor> for Palette {
    type Output = Color;

    fn index(&self, palette_color: PaletteColor) -> &Color {
        &self.basic[palette_color]
    }
}

// We can alter existing color if needed (but why?...)
impl IndexMut<PaletteColor> for Palette {
    fn index_mut(&mut self, palette_color: PaletteColor) -> &mut Color {
        &mut self.basic[palette_color]
    }
}

impl Palette {
    /// Returns a custom color from this palette.
    ///
    /// Returns `None` if the given key was not found.
    pub fn custom<'a>(&'a self, key: &str) -> Option<&'a Color> {
        self.custom.get(key).and_then(|node| {
            if let PaletteNode::Color(ref color) = *node {
                Some(color)
            } else {
                None
            }
        })
    }

    /// Returns a new palette where the given namespace has been merged.
    ///
    /// All values in the namespace will override previous values.
    pub fn merge(&self, namespace: &str) -> Palette {
        let mut result = self.clone();

        if let Some(&PaletteNode::Namespace(ref palette)) =
            self.custom.get(namespace)
        {
            // Merge `result` and `palette`
            for (key, value) in palette.iter() {
                match *value {
                    PaletteNode::Color(color) => result.set_color(key, color),
                    PaletteNode::Namespace(ref map) => {
                        result.add_namespace(key, map.clone())
                    }
                }
            }
        }

        result
    }

    /// Sets the color for the given key.
    ///
    /// This will update either the basic palette or the custom values.
    pub fn set_color(&mut self, key: &str, color: Color) {
        use crate::theme::PaletteColor::*;

        match key {
            "background" => self.basic[Background] = color,
            "shadow" => self.basic[Shadow] = color,
            "view" => self.basic[View] = color,
            "primary" => self.basic[Primary] = color,
            "secondary" => self.basic[Secondary] = color,
            "tertiary" => self.basic[Tertiary] = color,
            "title_primary" => self.basic[TitlePrimary] = color,
            "title_secondary" => self.basic[TitleSecondary] = color,
            "highlight" => self.basic[Highlight] = color,
            "highlight_inactive" => self.basic[HighlightInactive] = color,
            other => {
                self.custom
                    .insert(other.to_string(), PaletteNode::Color(color));
            }
        }
    }

    /// Adds a color namespace to this palette.
    pub fn add_namespace(
        &mut self,
        key: &str,
        namespace: HashMap<String, PaletteNode>,
    ) {
        self.custom
            .insert(key.to_string(), PaletteNode::Namespace(namespace));
    }
}

/// Returns the default palette for a cursive application.
///
/// * `Background` => `Dark(Blue)`
/// * `Shadow` => `Dark(Black)`
/// * `View` => `Dark(White)`
/// * `Primary` => `Dark(Black)`
/// * `Secondary` => `Dark(Blue)`
/// * `Tertiary` => `Dark(White)`
/// * `TitlePrimary` => `Dark(Red)`
/// * `TitleSecondary` => `Dark(Yellow)`
/// * `Highlight` => `Dark(Red)`
/// * `HighlightInactive` => `Dark(Blue)`
impl Default for Palette {
    fn default() -> Palette {
        use self::PaletteColor::*;
        use crate::theme::BaseColor::*;
        use crate::theme::Color::*;

        Palette {
            basic: enum_map! {
                Background => Dark(Blue),
                Shadow => Dark(Black),
                View => Dark(White),
                Primary => Dark(Black),
                Secondary => Dark(Blue),
                Tertiary => Dark(White),
                TitlePrimary => Dark(Red),
                TitleSecondary => Light(Blue),
                Highlight => Dark(Red),
                HighlightInactive => Dark(Blue),
            },
            custom: HashMap::default(),
        }
    }
}

// Iterate over a toml
#[cfg(feature = "toml")]
fn iterate_toml<'a>(
    table: &'a toml::value::Table,
) -> impl Iterator<Item = (&'a str, PaletteNode)> + 'a {
    table.iter().flat_map(|(key, value)| {
        let node = match value {
            toml::Value::Table(table) => {
                // This should define a new namespace
                // Treat basic colors as simple string.
                // We'll convert them back in the merge method.
                let map = iterate_toml(table)
                    .map(|(key, value)| (key.to_string(), value))
                    .collect();
                // Should we only return something if it's non-empty?
                Some(PaletteNode::Namespace(map))
            }
            toml::Value::Array(colors) => {
                // This should be a list of colors - just pick the first valid one.
                colors
                    .iter()
                    .flat_map(toml::Value::as_str)
                    .flat_map(Color::parse)
                    .map(PaletteNode::Color)
                    .next()
            }
            toml::Value::String(color) => {
                // This describe a new color - easy!
                Color::parse(color).map(PaletteNode::Color)
            }
            other => {
                // Other - error?
                warn!(
                    "Found unexpected value in theme: {} = {:?}",
                    key, other
                );
                None
            }
        };

        node.map(|node| (key.as_str(), node))
    })
}

/// Fills `palette` with the colors from the given `table`.
#[cfg(feature = "toml")]
pub(crate) fn load_toml(palette: &mut Palette, table: &toml::value::Table) {
    // TODO: use serde for that?
    // Problem: toml-rs doesn't do well with Enums...

    for (key, value) in iterate_toml(table) {
        match value {
            PaletteNode::Color(color) => palette.set_color(key, color),
            PaletteNode::Namespace(map) => palette.add_namespace(key, map),
        }
    }
}

/// Color entry in a palette.
///
/// Each `PaletteColor` is used for a specific role in a default application.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Enum)]
pub enum PaletteColor {
    /// Color used for the application background.
    Background,
    /// Color used for View shadows.
    Shadow,
    /// Color used for View backgrounds.
    View,
    /// Primary color used for the text.
    Primary,
    /// Secondary color used for the text.
    Secondary,
    /// Tertiary color used for the text.
    Tertiary,
    /// Primary color used for title text.
    TitlePrimary,
    /// Secondary color used for title text.
    TitleSecondary,
    /// Color used for highlighting text.
    Highlight,
    /// Color used for highlighting inactive text.
    HighlightInactive,
}

impl PaletteColor {
    /// Given a palette, resolve `self` to a concrete color.
    pub fn resolve(self, palette: &Palette) -> Color {
        palette[self]
    }
}
<commit_msg>Change tertiary color to white instead of grey (#408)<commit_after>use super::Color;
use enum_map::{enum_map, Enum, EnumMap};
#[cfg(feature = "toml")]
use log::warn;

use std::ops::{Index, IndexMut};

// Use AHash instead of the slower SipHash
type HashMap<K, V> = std::collections::HashMap<K, V, ahash::ABuildHasher>;

/// Color configuration for the application.
///
/// Assign each color role an actual color.
///
/// It implements `Index` and `IndexMut` to access and modify this mapping:
///
/// # Example
///
/// ```rust
/// # use cursive::theme::Palette;
/// use cursive::theme::PaletteColor::*;
/// use cursive::theme::Color::*;
/// use cursive::theme::BaseColor::*;
///
/// let mut palette = Palette::default();
///
/// assert_eq!(palette[Background], Dark(Blue));
/// palette[Shadow] = Light(Red);
/// ```
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct Palette {
    basic: EnumMap<PaletteColor, Color>,
    custom: HashMap<String, PaletteNode>,
}

/// A node in the palette tree.
///
/// This describes a value attached to a custom keyword in the palette.
///
/// This can either be a color, or a nested namespace with its own mapping.
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum PaletteNode {
    /// A single color.
    Color(Color),
    /// A group of values bundled in the same namespace.
    ///
    /// Namespaces can be merged in the palette with `Palette::merge`.
    Namespace(HashMap<String, PaletteNode>),
}

// Basic usage: only use basic colors
impl Index<PaletteColor> for Palette {
    type Output = Color;

    fn index(&self, palette_color: PaletteColor) -> &Color {
        &self.basic[palette_color]
    }
}

// We can alter existing color if needed (but why?...)
impl IndexMut<PaletteColor> for Palette {
    fn index_mut(&mut self, palette_color: PaletteColor) -> &mut Color {
        &mut self.basic[palette_color]
    }
}

impl Palette {
    /// Returns a custom color from this palette.
    ///
    /// Returns `None` if the given key was not found.
    pub fn custom<'a>(&'a self, key: &str) -> Option<&'a Color> {
        self.custom.get(key).and_then(|node| {
            if let PaletteNode::Color(ref color) = *node {
                Some(color)
            } else {
                None
            }
        })
    }

    /// Returns a new palette where the given namespace has been merged.
    ///
    /// All values in the namespace will override previous values.
    pub fn merge(&self, namespace: &str) -> Palette {
        let mut result = self.clone();

        if let Some(&PaletteNode::Namespace(ref palette)) =
            self.custom.get(namespace)
        {
            // Merge `result` and `palette`
            for (key, value) in palette.iter() {
                match *value {
                    PaletteNode::Color(color) => result.set_color(key, color),
                    PaletteNode::Namespace(ref map) => {
                        result.add_namespace(key, map.clone())
                    }
                }
            }
        }

        result
    }

    /// Sets the color for the given key.
    ///
    /// This will update either the basic palette or the custom values.
    pub fn set_color(&mut self, key: &str, color: Color) {
        use crate::theme::PaletteColor::*;

        match key {
            "background" => self.basic[Background] = color,
            "shadow" => self.basic[Shadow] = color,
            "view" => self.basic[View] = color,
            "primary" => self.basic[Primary] = color,
            "secondary" => self.basic[Secondary] = color,
            "tertiary" => self.basic[Tertiary] = color,
            "title_primary" => self.basic[TitlePrimary] = color,
            "title_secondary" => self.basic[TitleSecondary] = color,
            "highlight" => self.basic[Highlight] = color,
            "highlight_inactive" => self.basic[HighlightInactive] = color,
            other => {
                self.custom
                    .insert(other.to_string(), PaletteNode::Color(color));
            }
        }
    }

    /// Adds a color namespace to this palette.
    pub fn add_namespace(
        &mut self,
        key: &str,
        namespace: HashMap<String, PaletteNode>,
    ) {
        self.custom
            .insert(key.to_string(), PaletteNode::Namespace(namespace));
    }
}

/// Returns the default palette for a cursive application.
///
/// * `Background` => `Dark(Blue)`
/// * `Shadow` => `Dark(Black)`
/// * `View` => `Dark(White)`
/// * `Primary` => `Dark(Black)`
/// * `Secondary` => `Dark(Blue)`
/// * `Tertiary` => `Light(White)`
/// * `TitlePrimary` => `Dark(Red)`
/// * `TitleSecondary` => `Dark(Yellow)`
/// * `Highlight` => `Dark(Red)`
/// * `HighlightInactive` => `Dark(Blue)`
impl Default for Palette {
    fn default() -> Palette {
        use self::PaletteColor::*;
        use crate::theme::BaseColor::*;
        use crate::theme::Color::*;

        Palette {
            basic: enum_map! {
                Background => Dark(Blue),
                Shadow => Dark(Black),
                View => Dark(White),
                Primary => Dark(Black),
                Secondary => Dark(Blue),
                Tertiary => Light(White),
                TitlePrimary => Dark(Red),
                TitleSecondary => Light(Blue),
                Highlight => Dark(Red),
                HighlightInactive => Dark(Blue),
            },
            custom: HashMap::default(),
        }
    }
}

// Iterate over a toml
#[cfg(feature = "toml")]
fn iterate_toml<'a>(
    table: &'a toml::value::Table,
) -> impl Iterator<Item = (&'a str, PaletteNode)> + 'a {
    table.iter().flat_map(|(key, value)| {
        let node = match value {
            toml::Value::Table(table) => {
                // This should define a new namespace
                // Treat basic colors as simple string.
                // We'll convert them back in the merge method.
                let map = iterate_toml(table)
                    .map(|(key, value)| (key.to_string(), value))
                    .collect();
                // Should we only return something if it's non-empty?
                Some(PaletteNode::Namespace(map))
            }
            toml::Value::Array(colors) => {
                // This should be a list of colors - just pick the first valid one.
                colors
                    .iter()
                    .flat_map(toml::Value::as_str)
                    .flat_map(Color::parse)
                    .map(PaletteNode::Color)
                    .next()
            }
            toml::Value::String(color) => {
                // This describe a new color - easy!
                Color::parse(color).map(PaletteNode::Color)
            }
            other => {
                // Other - error?
                warn!(
                    "Found unexpected value in theme: {} = {:?}",
                    key, other
                );
                None
            }
        };

        node.map(|node| (key.as_str(), node))
    })
}

/// Fills `palette` with the colors from the given `table`.
#[cfg(feature = "toml")]
pub(crate) fn load_toml(palette: &mut Palette, table: &toml::value::Table) {
    // TODO: use serde for that?
    // Problem: toml-rs doesn't do well with Enums...

    for (key, value) in iterate_toml(table) {
        match value {
            PaletteNode::Color(color) => palette.set_color(key, color),
            PaletteNode::Namespace(map) => palette.add_namespace(key, map),
        }
    }
}

/// Color entry in a palette.
///
/// Each `PaletteColor` is used for a specific role in a default application.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Enum)]
pub enum PaletteColor {
    /// Color used for the application background.
    Background,
    /// Color used for View shadows.
    Shadow,
    /// Color used for View backgrounds.
    View,
    /// Primary color used for the text.
    Primary,
    /// Secondary color used for the text.
    Secondary,
    /// Tertiary color used for the text.
    Tertiary,
    /// Primary color used for title text.
    TitlePrimary,
    /// Secondary color used for title text.
    TitleSecondary,
    /// Color used for highlighting text.
    Highlight,
    /// Color used for highlighting inactive text.
    HighlightInactive,
}

impl PaletteColor {
    /// Given a palette, resolve `self` to a concrete color.
    pub fn resolve(self, palette: &Palette) -> Color {
        palette[self]
    }
}
<|endoftext|>


<commit_before>use lib::btsf::*;

const MIN_OVERLAP : usize = 5;

pub fn pearson_correlation_coefficient(xs: &Vec<f32>, ys: &Vec<f32>) -> f64{
    let n = xs.len();

    let mut sum_xy: f64 = 0.0;
    let mut sum_xx: f64 = 0.0;
    let mut sum_yy: f64 = 0.0;
    let mut sum_x: f64 = 0.0;
    let mut sum_y: f64 = 0.0;
    for i in 0..n {
        sum_xy += (xs[i] as f64) * (ys[i] as f64);
        sum_xx += (xs[i] as f64) * (xs[i] as f64);
        sum_yy += (ys[i] as f64) * (ys[i] as f64);
        sum_x += xs[i] as f64;
        sum_y += ys[i] as f64;
    }
    let x_avg = sum_x / (n as f64);
    let y_avg = sum_y / (n as f64);

    let denom = (sum_xx - (n as f64) * (x_avg*x_avg)).sqrt() * (sum_yy - (n as f64) * (y_avg*y_avg)).sqrt();

    // this is to guard against series that are flat lines and thus might correlate with anything, added when needed with a previous formula
    // TODO this may not fire due to floating point error, it may not even be necessary anymore
    if denom == 0.0 {
        return 0.0;
    }

    let numerator = sum_xy - (sum_x * y_avg);
    let final_val = numerator / denom;

    if final_val.is_nan() {
        return 0.0;
    }

    return final_val;
}

pub fn pairinate(base: &BinaryTimeSeries, other: &BinaryTimeSeries)
                 -> Option<(Vec<f32>, Vec<f32>)>{
    if base.data.len() < MIN_OVERLAP || other.data.len() < MIN_OVERLAP { return None; }
    let (bs, os) = interpolate(&base.data[..], &other.data[..]);
    if bs.len() < MIN_OVERLAP { return None; }
    return Some((bs, os));
}

// interpolate a point from 'other' for as many points as possible in 'base'
fn interpolate(base: &[Point], other: &[Point]) -> (Vec<f32>, Vec<f32>) {
    assert!(other.len() > 0);
    let mut base_data = Vec::<f32>::new();
    let mut other_data = Vec::<f32>::new();

    // TODO: early exit on cases where there is bound to be nothing productive
    let mut search_index = 0;
    for data_point in base {
        // advance search_index as far as possible without putting it past the base data point
        // condition: not already past it or at it, not the last point, and the next point isn't past it
        while other[search_index].t < data_point.t && search_index < (other.len() - 1) && other[search_index+1].t <= data_point.t {
            search_index += 1;
        }

        if other[search_index].t == data_point.t{
            other_data.push(other[search_index].val);
            base_data.push(data_point.val);
        } else if search_index < (other.len() - 1) && other[search_index].t < data_point.t {
            assert!(other[search_index+1].t >= data_point.t);
            // We have to interpolate data
            let time_offset = data_point.t - other[search_index].t;
            let point_offset = other[search_index+1].t - other[search_index].t;
            let interpolation_ratio: f32= (time_offset as f32) / (point_offset as f32);

            let interpolated_data_point = (other[search_index].val * (1.0 - interpolation_ratio)) +
                (other[search_index+1].val * interpolation_ratio);

            other_data.push(interpolated_data_point);
            base_data.push(data_point.val);
        }
    }

    return (base_data, other_data);
}
<commit_msg>Enforce minimum number of sampled points<commit_after>use lib::btsf::*;

const MIN_OVERLAP : usize = 5;

pub fn pearson_correlation_coefficient(xs: &Vec<f32>, ys: &Vec<f32>) -> f64{
    let n = xs.len();

    let mut sum_xy: f64 = 0.0;
    let mut sum_xx: f64 = 0.0;
    let mut sum_yy: f64 = 0.0;
    let mut sum_x: f64 = 0.0;
    let mut sum_y: f64 = 0.0;
    for i in 0..n {
        sum_xy += (xs[i] as f64) * (ys[i] as f64);
        sum_xx += (xs[i] as f64) * (xs[i] as f64);
        sum_yy += (ys[i] as f64) * (ys[i] as f64);
        sum_x += xs[i] as f64;
        sum_y += ys[i] as f64;
    }
    let x_avg = sum_x / (n as f64);
    let y_avg = sum_y / (n as f64);

    let denom = (sum_xx - (n as f64) * (x_avg*x_avg)).sqrt() * (sum_yy - (n as f64) * (y_avg*y_avg)).sqrt();

    // this is to guard against series that are flat lines and thus might correlate with anything, added when needed with a previous formula
    // TODO this may not fire due to floating point error, it may not even be necessary anymore
    if denom == 0.0 {
        return 0.0;
    }

    let numerator = sum_xy - (sum_x * y_avg);
    let final_val = numerator / denom;

    if final_val.is_nan() {
        return 0.0;
    }

    return final_val;
}

pub fn pairinate(base: &BinaryTimeSeries, other: &BinaryTimeSeries)
                 -> Option<(Vec<f32>, Vec<f32>)>{
    if base.data.len() < MIN_OVERLAP || other.data.len() < MIN_OVERLAP { return None; }
    let (bs, os, sampled_other) = interpolate(&base.data[..], &other.data[..]);
    if bs.len() < MIN_OVERLAP { return None; }
    if sampled_other < MIN_OVERLAP { return None; }
    return Some((bs, os));
}

// interpolate a point from 'other' for as many points as possible in 'base'
fn interpolate(base: &[Point], other: &[Point]) -> (Vec<f32>, Vec<f32>, usize) {
    assert!(other.len() > 0);
    let mut base_data = Vec::<f32>::new();
    let mut other_data = Vec::<f32>::new();

    // TODO: early exit on cases where there is bound to be nothing productive
    let mut sampled_other : usize = 0;
    let mut search_index = 0;
    for data_point in base {
        let mut advanced_other = false;
        // advance search_index as far as possible without putting it past the base data point
        // condition: not already past it or at it, not the last point, and the next point isn't past it
        while other[search_index].t < data_point.t && search_index < (other.len() - 1) && other[search_index+1].t <= data_point.t {
            search_index += 1;
            advanced_other = true;
        }

        if other[search_index].t == data_point.t{
            other_data.push(other[search_index].val);
            base_data.push(data_point.val);
            if advanced_other { sampled_other += 1; }
        } else if search_index < (other.len() - 1) && other[search_index].t < data_point.t {
            assert!(other[search_index+1].t >= data_point.t);
            // We have to interpolate data
            let time_offset = data_point.t - other[search_index].t;
            let point_offset = other[search_index+1].t - other[search_index].t;
            let interpolation_ratio: f32= (time_offset as f32) / (point_offset as f32);

            let interpolated_data_point = (other[search_index].val * (1.0 - interpolation_ratio)) +
                (other[search_index+1].val * interpolation_ratio);

            other_data.push(interpolated_data_point);
            base_data.push(data_point.val);
            if advanced_other { sampled_other += 1; }
        }
    }

    return (base_data, other_data, sampled_other);
}
<|endoftext|>


<commit_before>extern crate num_traits;

use std::f64;
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
use std::ops::{Add, Sub, Mul, Div};
use self::num_traits::{One, Zero};

/// A struct that represents probabilities.
/// A probability is internally represented as its negative natural logarithm.
/// Probabilities greater than 1 are allowed during calculations.
#[derive(PartialOrd, Debug, Clone, Copy)]
pub enum LogProb { Exp(f64) }

impl LogProb {
    /// Creates a new `LogProb` from a given value in the interval [0,∞).
    pub fn new(value: f64) -> Result<Self, String> {
        if 0.0 <= value {
            Ok(LogProb::new_unchecked(value.ln()))
        } else {
            Err(format!("{} is not a probability, i.e. not in the interval [0,∞).", value))
        }
    }

    fn ln(&self) -> f64 {
        match self {
            &LogProb::Exp(value) => value,
        }
    }

    /// Same as `new` but without bounds check.
    #[inline]
    pub fn new_unchecked(value: f64) -> Self {
        LogProb::Exp(value.ln())
    }

    /// Probability that is represented by the given `LogProb`.
    pub fn probability(&self) -> f64 {
        self.ln().exp()
    }
}

impl Ord for LogProb {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.partial_cmp(&other) {
            Some(ordering) => ordering,
            None => {
                if self.ln().is_nan() {
                    if other.ln().is_nan() {
                        Ordering::Equal
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            }
        }
    }
}

impl PartialEq for LogProb {
    fn eq(&self, other: &Self) -> bool {
        if self.ln().is_nan() {
            if other.ln().is_nan() { true } else { false }
        } else if other.ln().is_nan() {
            false
        } else {
            (self.ln() - other.ln()).abs() <= f64::EPSILON
        }
    }
}

impl Eq for LogProb {}

impl Add for LogProb {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let (a, b) = (self.ln(), other.ln());

        let (x, y) = if a > b {
            (a, b)
        } else {
            (b, a)
        };

        LogProb::new_unchecked(x + (y - x).exp().ln_1p())
    }
}

impl Sub for LogProb {
    type Output = Self ;

    fn sub(self, other: Self) -> Self {
        match (self.ln(), other.ln()) {
            (x, y) if x >= y => LogProb::new_unchecked(x + (-(y - x).exp_m1()).ln()),
            (x, y) if x <  y => panic!("exp({}) - exp({}) is less than zero", x, y),
            _                => unreachable!(),
        }
    }
}

#[test]
fn test_arithmetic() {
    match (LogProb::new(0.5), LogProb::new(0.25), LogProb::new(0.75)) {
        (Ok(x), Ok(y), Ok(z)) => {
            assert_eq!(x + y, z); // 0.5  + 0.25 = 0.75
            assert_eq!(y + x, z); // 0.25 + 0.5  = 0.75
            assert_eq!(z - x, y); // 0.75 - 0.5  = 0.25
            assert_eq!(z - y, x); // 0.75 - 0.25 = 0.5
            assert_eq!(x * x, y); // 0.5  ⋅ 0.5  = 0.25
            assert_eq!(y / x, x); // 0.25 / 0.5  = 0.5
            assert_eq!(z / z, LogProb::one());
                                  // 0.75 / 0.75 = 1
            assert!(z > y);       // 0.75 > 0.25
        },
        _ => panic!(),
    }
}

impl Mul for LogProb {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        LogProb::new_unchecked(self.ln().add(other.ln()))
    }
}

impl Div for LogProb {
    type Output = Self;

    fn div(self, other: Self) -> Self {
        LogProb::new_unchecked(self.ln().sub(other.ln()))
    }
}

impl Zero for LogProb {
    fn zero() -> LogProb {
        LogProb::new_unchecked(f64::NEG_INFINITY)
    }

    fn is_zero(&self) -> bool {
        self.ln() == f64::NEG_INFINITY
    }
}

impl One for LogProb {
    fn one() -> LogProb {
        LogProb::new_unchecked(0.0)
    }
}

impl FromStr for LogProb {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.parse() {
            Ok(p) => LogProb::new(p),
            Err(e) => Err(e.to_string())
        }
    }
}

impl fmt::Display for LogProb {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.probability())
    }
}
<commit_msg>src/util/log_prob.rs: fixed incorrect `impl`s of Add, Sub, Mul, Div, Zero, One<commit_after>extern crate num_traits;

use std::f64;
use std::cmp::Ordering;
use std::fmt;
use std::str::FromStr;
use std::ops::{Add, Sub, Mul, Div};
use self::num_traits::{One, Zero};

/// A struct that represents probabilities.
/// A probability is internally represented as its negative natural logarithm.
/// Probabilities greater than 1 are allowed during calculations.
#[derive(PartialOrd, Debug, Clone, Copy)]
pub enum LogProb { Exp(f64) }

impl LogProb {
    /// Creates a new `LogProb` from a given value in the interval [0,∞).
    pub fn new(value: f64) -> Result<Self, String> {
        if 0.0 <= value {
            Ok(LogProb::new_unchecked(value))
        } else {
            Err(format!("{} is not a probability, i.e. not in the interval [0,∞).", value))
        }
    }

    fn ln(&self) -> f64 {
        match self {
            &LogProb::Exp(value) => value,
        }
    }

    /// Same as `new` but without bounds check.
    #[inline]
    pub fn new_unchecked(value: f64) -> Self {
        LogProb::Exp(value.ln())
    }

    /// Probability that is represented by the given `LogProb`.
    pub fn probability(&self) -> f64 {
        self.ln().exp()
    }
}

impl Ord for LogProb {
    fn cmp(&self, other: &Self) -> Ordering {
        match self.partial_cmp(&other) {
            Some(ordering) => ordering,
            None => {
                if self.ln().is_nan() {
                    if other.ln().is_nan() {
                        Ordering::Equal
                    } else {
                        Ordering::Greater
                    }
                } else {
                    Ordering::Less
                }
            }
        }
    }
}

impl PartialEq for LogProb {
    fn eq(&self, other: &Self) -> bool {
        if self.ln().is_nan() {
            if other.ln().is_nan() { true } else { false }
        } else if other.ln().is_nan() {
            false
        } else {
            self.ln() == other.ln() || (self.ln() - other.ln()).abs() <= f64::EPSILON
        }
    }
}

impl Eq for LogProb {}

impl Add for LogProb {
    type Output = Self;

    fn add(self, other: Self) -> Self {
        let (a, b) = (self.ln(), other.ln());

        let (x, y) = if a > b {
            (a, b)
        } else {
            (b, a)
        };

        LogProb::Exp(x + (y - x).exp().ln_1p())
    }
}

impl Sub for LogProb {
    type Output = Self ;

    fn sub(self, other: Self) -> Self {
        match (self.ln(), other.ln()) {
            (x, y) if x >= y => LogProb::Exp(x + (-(y - x).exp_m1()).ln()),
            (x, y) if x <  y => panic!("exp({}) - exp({}) is less than zero", x, y),
            _                => unreachable!(),
        }
    }
}

#[test]
fn test_arithmetic() {
    match (LogProb::new(0.5), LogProb::new(0.25), LogProb::new(0.75)) {
        (Ok(x), Ok(y), Ok(z)) => {
            assert_eq!(x + y, z); // 0.5  + 0.25 = 0.75
            assert_eq!(y + x, z); // 0.25 + 0.5  = 0.75
            assert_eq!(z - x, y); // 0.75 - 0.5  = 0.25
            assert_eq!(z - y, x); // 0.75 - 0.25 = 0.5
            assert_eq!(x * x, y); // 0.5  ⋅ 0.5  = 0.25
            assert_eq!(y / x, x); // 0.25 / 0.5  = 0.5
            assert_eq!(z / z, LogProb::one());
                                  // 0.75 / 0.75 = 1
            assert!(z > y);       // 0.75 > 0.25
        },
        _ => panic!(),
    }
}

impl Mul for LogProb {
    type Output = Self;

    fn mul(self, other: Self) -> Self {
        LogProb::Exp(self.ln().add(other.ln()))
    }
}

impl Div for LogProb {
    type Output = Self;

    fn div(self, other: Self) -> Self {
        LogProb::Exp(self.ln().sub(other.ln()))
    }
}

impl Zero for LogProb {
    fn zero() -> LogProb {
        LogProb::Exp(f64::NEG_INFINITY)
    }

    fn is_zero(&self) -> bool {
        self.ln() == f64::NEG_INFINITY
    }
}

impl One for LogProb {
    fn one() -> LogProb {
        LogProb::Exp(0.0)
    }
}

impl FromStr for LogProb {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s.parse() {
            Ok(p) => LogProb::new(p),
            Err(e) => Err(e.to_string())
        }
    }
}

impl fmt::Display for LogProb {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.probability())
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;

/// An *objective* defines a total ordering relation and a distance
/// metric on `solutions` in order to be able to compare any two
/// solutions according to the following two questions:
///
/// - "which solution is better" (total order)
///
/// - "how much better" (distance metric)
///
/// Objectives can be seen as a projection of a (possibly) multi-variate
/// solution value to a scalar value. There can be any number of
/// different projections (objectives) for any given solution value. Of
/// course solution values need not be multi-variate.
///
/// We use the term "solution" here, ignoring the fact that in pratice
/// we often have to evaluate the "fitness" of a solution prior of being
/// able to define any useful ordering relation or distance metric. As
/// the fitness generally is a function of the solution, this is more or
/// less an implementation detail or that of an optimization. Nothing
/// prevents you from using the fitness value here as the solution
/// value.

pub trait Objective {
    /// The solution value type that we define the objective on.
    type Solution;

    /// The output type of the distance metric.
    type Distance: Sized;

    /// An objective defines a total order between two solution values.
    fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering;

    /// An objective defines a distance metric between two solution
    /// values. Distance values can be negative, i.e. the caller is
    /// responsible for obtaining absolute values.
    fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> Self::Distance;
}

#[cfg(test)]
mod tests {
    use std::cmp::Ordering;
    use super::Objective;

    // Our multi-variate fitness/solution value
    struct Tuple(usize, usize);

    // We define three objectives
    struct Objective1;
    struct Objective2;
    struct Objective3;

    impl Objective for Objective1 {
        type Solution = Tuple;
        type Distance = f64;

        fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering {
            a.0.cmp(&b.0)
        }

        fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> Self::Distance {
            (a.0 as f64) - (b.0 as f64)
        }
    }

    impl Objective for Objective2 {
        type Solution = Tuple;
        type Distance = f64;

        fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering {
            a.1.cmp(&b.1)
        }

        fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> Self::Distance {
            (a.1 as f64) - (b.1 as f64)
        }
    }

    // Objective3 is defined on the sum of the tuple values.
    impl Objective for Objective3 {
        type Solution = Tuple;
        type Distance = f64;

        fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering {
            (a.0 + a.1).cmp(&(b.0 + b.1))
        }

        fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> Self::Distance {
            (a.0 + a.1) as f64 - (b.0 + b.1) as f64
        }
    }

    #[test]
    fn test_objectives() {
        let a = &Tuple(1, 2);
        let b = &Tuple(2, 1);
        assert_eq!(Ordering::Less, Objective1.total_order(a, b));
        assert_eq!(Ordering::Greater, Objective2.total_order(a, b));
        assert_eq!(Ordering::Equal, Objective3.total_order(a, b));

        assert_eq!(-1.0, Objective1.distance(a, b));
        assert_eq!(1.0, Objective2.distance(a, b));
        assert_eq!(0.0, Objective3.distance(a, b));
    }
}
<commit_msg>Simplify tests<commit_after>use std::cmp::Ordering;

/// An *objective* defines a total ordering relation and a distance
/// metric on `solutions` in order to be able to compare any two
/// solutions according to the following two questions:
///
/// - "which solution is better" (total order)
///
/// - "how much better" (distance metric)
///
/// Objectives can be seen as a projection of a (possibly) multi-variate
/// solution value to a scalar value. There can be any number of
/// different projections (objectives) for any given solution value. Of
/// course solution values need not be multi-variate.
///
/// We use the term "solution" here, ignoring the fact that in pratice
/// we often have to evaluate the "fitness" of a solution prior of being
/// able to define any useful ordering relation or distance metric. As
/// the fitness generally is a function of the solution, this is more or
/// less an implementation detail or that of an optimization. Nothing
/// prevents you from using the fitness value here as the solution
/// value.

pub trait Objective {
    /// The solution value type that we define the objective on.
    type Solution;

    /// The output type of the distance metric.
    type Distance: Sized;

    /// An objective defines a total order between two solution values.
    fn total_order(&self, a: &Self::Solution, b: &Self::Solution) -> Ordering;

    /// An objective defines a distance metric between two solution
    /// values. Distance values can be negative, i.e. the caller is
    /// responsible for obtaining absolute values.
    fn distance(&self, a: &Self::Solution, b: &Self::Solution) -> Self::Distance;
}

#[test]
fn test_objectives() {
    use test_helper_objective::{Objective1, Objective2, Objective3, Tuple};

    let a = &Tuple(1, 2);
    let b = &Tuple(2, 1);
    assert_eq!(Ordering::Less, Objective1.total_order(a, b));
    assert_eq!(Ordering::Greater, Objective2.total_order(a, b));
    assert_eq!(Ordering::Equal, Objective3.total_order(a, b));

    assert_eq!(-1.0, Objective1.distance(a, b));
    assert_eq!(1.0, Objective2.distance(a, b));
    assert_eq!(0.0, Objective3.distance(a, b));
}
<|endoftext|>