<commit_before>use crate::util::dirs;
use anyhow::{anyhow, bail, Result};
use futures::StreamExt;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::thread;
use std::{collections::HashMap, process::Command};
use strum::{Display, EnumString, EnumVariantNames};

#[derive(Debug, Display, EnumString, EnumVariantNames, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum Kind {
    #[cfg(any(feature = "plugins-docker"))]
    Docker,
    #[cfg(any(feature = "plugins-binary"))]
    Binary,
    #[cfg(any(feature = "plugins-package"))]
    Package,
}

/// Split a plugin spec (ie. alias/name and, optionally, version) into alias and version
pub fn spec_to_alias_version(spec: &str) -> (String, String) {
    if spec.contains('@') {
        let parts: Vec<&str> = spec.split('@').collect();
        (parts[0].into(), parts[1].into())
    } else {
        (spec.into(), "latest".into())
    }
}

/// Resolve a plugin alias to a plugin name
///
/// If the provided string is a registered alias then returns the corresponding
/// plugin name, otherwise assumes the string is a plugin name
pub fn alias_to_name(alias: &str, aliases: &HashMap<String, String>) -> String {
    match aliases.get(alias) {
        Some(name) => name,
        None => alias,
    }
    .into()
}

/// Get the path to the manifest file of a plugin
pub fn manifest_path(name: &str) -> Result<PathBuf> {
    Ok(dirs::plugins(false)?.join(format!("{}.json", name)))
}

/// Get the manifest for an installed plugin
pub fn manifest(name: &str) -> Result<String> {
    // TODO: deserialize JSON into a manifest object
    match fs::read_to_string(manifest_path(name)?) {
        Ok(json) => Ok(json),
        Err(_) => bail!("Plugin '{}' is not installed", name),
    }
}

/// Test whether a plugin is installed
pub fn is_installed(name: &str) -> Result<bool> {
    Ok(manifest_path(name)?.exists())
}

/// List installed manifests
pub fn list() -> Result<Vec<String>> {
    // TODO: Return a Vec<Manifest>
    let dir = dirs::plugins(true)?;
    let mut plugins: Vec<String> = vec![];
    for entry in fs::read_dir(dir)? {
        let entry = entry?;
        let path = entry.path();
        if let Some(ext) = path.extension() {
            if ext == "json" {
                let name = path.file_stem().unwrap().to_string_lossy().to_string();
                plugins.push(name)
            }
        }
    }
    Ok(plugins)
}

/// Add a plugin as a pulled Docker image
///
/// For this to succeed must be able to connect to the local
/// Docker server and be able to pull an image with corresponding
/// name.
#[cfg(any(feature = "plugins-docker"))]
pub async fn install_docker(name: &str, version: &str) -> Result<()> {
    let docker = bollard::Docker::connect_with_local_defaults()?;

    let image = if name.contains('/') {
        name.to_string()
    } else {
        format!("stencila/{}", name)
    };
    let image = format!("{}:{}", image, version);

    // Pull the image (by creating an image from it)
    let mut stream = docker.create_image(
        Some(bollard::image::CreateImageOptions {
            from_image: image.clone(),
            ..Default::default()
        }),
        None,
        None,
    );
    while let Some(item) = stream.next().await {
        match item {
            Ok(info) => {
                if let Some(error) = info.error {
                    bail!("{}", error)
                } else if let Some(status) = info.status {
                    // TODO display of status and progress could be improved and displayed
                    // per id (layer) as in docker CLI
                    // println!("{:?} {:?} {:?}", info.id, info.progress, info.progress_detail);
                    if let Some(progress) = info.progress {
                        tracing::info!("{} {}", status, progress)
                    } else {
                        tracing::info!("{}", status)
                    }
                }
            }
            Err(error) => match error {
                bollard::errors::Error::DockerResponseNotFoundError { .. } => {
                    bail!("Unable to find Docker image '{}'", image)
                }
                _ => bail!("{}", error),
            },
        }
    }

    // Create a container to obtain manifest from
    let container_name: String = rand::thread_rng()
        .sample_iter(&rand::distributions::Alphanumeric)
        .take(24)
        .map(char::from)
        .collect();
    let response = docker
        .create_container(
            Some(bollard::container::CreateContainerOptions {
                name: &container_name,
            }),
            bollard::container::Config {
                image: Some(image.clone()),
                cmd: Some(vec!["manifest".to_string()]),
                ..Default::default()
            },
        )
        .await?;
    if response.warnings.len() > 0 {
        for warning in response.warnings {
            tracing::warn!("When creating container: {}", warning);
        }
    }

    // Start the container
    docker
        .start_container(
            &container_name,
            None::<bollard::container::StartContainerOptions<String>>,
        )
        .await?;

    // Capture the container output into `manifest`
    let mut stream = docker.logs::<String>(
        &container_name,
        Some(bollard::container::LogsOptions {
            follow: true,
            stderr: true,
            stdout: true,
            ..Default::default()
        }),
    );
    let mut stdout = vec![];
    let mut stderr = vec![];
    while let Some(output) = stream.next().await {
        if let Ok(output) = output {
            match output {
                bollard::container::LogOutput::StdOut { message } => {
                    stdout.extend_from_slice(&message)
                }
                bollard::container::LogOutput::StdErr { message } => {
                    stderr.extend_from_slice(&message)
                }
                _ => {}
            }
        }
    }

    if stderr.len() > 0 {
        tracing::warn!("{}", std::str::from_utf8(&stderr)?);
    }

    let manifest = if stdout.len() > 0 {
        match std::str::from_utf8(&stdout) {
            Ok(stdout) => stdout,
            Err(error) => bail!("Error converting stream to UTF8: {}", error),
        }
    } else {
        bail!(
            "No output from Docker container manifest command; is {} a Stencila plugin?",
            image
        )
    };

    // TODO: deserialize manifest and then serialize to file
    fs::write(manifest_path(name)?, manifest)?;

    Ok(())
}

/// Add a plugin as a downloaded binary
#[cfg(any(feature = "plugins-binary"))]
pub fn install_binary(name: &str, version: &str) -> Result<()> {
    let (owner, name) = if name.contains('/') {
        let parts: Vec<&str> = name.split('/').collect();
        (parts[0], parts[1])
    } else {
        ("stencila", name)
    };

    // Get the current version from the manifest (if any)
    let current_version = if let Ok(_manifest) = manifest(name) {
        // TODO extract version from manifest
        "0.1.0"
    } else {
        // Not yet installed, so artificially low version
        "0.0.0"
    };

    // Create the directory where the binary will be downloaded to
    let install_dir = dirs::plugins(false)?.join(name);
    fs::create_dir_all(&install_dir)?;
    let install_path = install_dir.join(name);

    let mut builder = self_update::backends::github::Update::configure();
    builder
        .repo_owner(owner)
        .repo_name(name)
        .bin_name(name)
        .current_version(current_version)
        .bin_install_path(&install_path)
        .show_output(true)
        .show_download_progress(true);
    if version != "latest" {
        builder.target_version_tag(format!("v{}", version).as_str());
    }

    // The download has to be done in another thread because it spawns
    // a new tokio runtime
    thread::spawn(move || -> Result<()> {
        if let Err(error) = builder.build()?.update() {
            match error {
                self_update::errors::Error::Network(message) => {
                    if message.contains("404") {
                        bail!("Could not find repository or corresponding release in repository")
                    } else {
                        bail!(message)
                    }
                }
                _ => bail!(error.to_string()),
            }
        } else {
            Ok(())
        }
    })
    .join()
    .map_err(|_| anyhow!("Error joining thread"))??;

    // Get and store manifest
    let manifest = Command::new(&install_path).arg("manifest").output()?.stdout;
    let manifest = std::str::from_utf8(&manifest)?;
    // TODO: deserialize manifest and then serialize to file
    fs::write(manifest_path(name)?, manifest)?;

    Ok(())
}

/// Add a plugin as a programing language package
#[cfg(any(feature = "plugins-package"))]
pub fn install_package(name: &str, version: &str) -> Result<()> {
    // TODO
    bail!(
        "Unable to add plugin '{}@{}' as programming language package",
        name,
        version
    )
}

/// Add a plugin
pub async fn add(plugin: &str, kinds: &Vec<Kind>, aliases: &HashMap<String, String>) -> Result<()> {
    let (alias, version) = spec_to_alias_version(plugin);
    let name = alias_to_name(&alias, aliases);

    if is_installed(&name)? {
        bail!(
            "Plugin '{}' is already installed, consider using `stencila plugins upgrade {}`",
            name,
            name
        );
    }

    for kind in kinds {
        let result = match kind {
            Kind::Docker => install_docker(&name, &version).await,
            Kind::Binary => install_binary(&name, &version),
            Kind::Package => install_package(&name, &version),
        };
        match result {
            // Success, so just return now
            Ok(_) => return Ok(()),
            // Error, keep trying other kinds, or if there is
            // only one kind, return that error.
            Err(error) => {
                if kinds.len() == 1 {
                    return Err(error);
                }
            }
        }
    }

    bail!(
        "Unable to install plugin '{}', tried kinds {:?}",
        plugin,
        kinds
    )
}

/// Add a list of plugins
pub async fn add_list(
    plugins: Vec<String>,
    kinds: Vec<Kind>,
    aliases: HashMap<String, String>,
) -> Result<()> {
    for plugin in plugins {
        match add(&plugin, &kinds, &aliases).await {
            Ok(_) => tracing::info!("Added plugin {}", plugin),
            Err(error) => tracing::error!("{}", error),
        }
    }
    Ok(())
}

/// Remove a plugin
pub fn remove(plugin: &str, aliases: &HashMap<String, String>) -> Result<()> {
    let (alias, ..) = spec_to_alias_version(plugin);
    let name = alias_to_name(&alias, aliases);

    let file = manifest_path(&name)?;
    if file.exists() {
        fs::remove_file(file)?;
    }

    Ok(())
}

/// Remove a list of plugins
pub fn remove_list(plugins: Vec<String>, aliases: HashMap<String, String>) -> Result<()> {
    for plugin in plugins {
        match remove(&plugin, &aliases) {
            Ok(_) => tracing::info!("Removed plugin {}", plugin),
            Err(error) => tracing::error!("{}", error),
        }
    }
    Ok(())
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use validator::Validate;

    #[derive(Debug, PartialEq, Deserialize, Serialize, Validate)]
    pub struct Config {
        #[serde(default = "default_kinds")]
        pub kinds: Vec<Kind>,

        #[serde(default = "default_aliases")]
        pub aliases: HashMap<String, String>,
    }

    /// Default configuration
    ///
    /// These values are used when `config.toml` does not
    /// contain any config for `upgrade`.
    impl Default for Config {
        fn default() -> Self {
            Config {
                kinds: default_kinds(),
                aliases: default_aliases(),
            }
        }
    }

    /// Get the default value for `kinds`
    pub fn default_kinds() -> Vec<Kind> {
        vec![Kind::Docker, Kind::Binary, Kind::Package]
    }

    /// Get the default value for `aliases`
    pub fn default_aliases() -> HashMap<String, String> {
        let mut aliases = HashMap::new();
        aliases.insert("javascript".to_string(), "jesta".to_string());
        aliases.insert("js".to_string(), "jesta".to_string());
        aliases
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage plugins",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub struct Args {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List,
        Show(Show),
        Add(Add),
        Remove(Remove),
        Upgrade(Upgrade),
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Show the manifest of a plugin")]
    pub struct Show {
        /// The name of the plugin to show
        #[structopt()]
        pub plugin: String,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Add one or more plugins")]
    pub struct Add {
        /// Attempt to add plugins as Docker image
        #[structopt(short, long)]
        pub docker: bool,

        /// Attempt to add plugins as binary
        #[structopt(short, long)]
        pub binary: bool,

        /// Attempt to add plugins as language package
        #[structopt(short, long)]
        pub package: bool,

        /// The names or aliases of plugins to add
        #[structopt(required = true, multiple = true)]
        pub plugins: Vec<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Remove one or more plugins")]
    pub struct Remove {
        /// The names or aliases of plugins to remove
        #[structopt(required = true, multiple = true)]
        pub plugins: Vec<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Upgrade plugins")]
    pub struct Upgrade {
        /// The names or aliases of plugins to upgrade
        /// (omit to upgrade all plugins)
        #[structopt(multiple = true)]
        pub plugins: Vec<String>,
    }

    pub async fn plugins(args: Args) -> Result<()> {
        let Args { action } = args;
        let config::Config { kinds, aliases } = crate::config::get()?.plugins;

        match action {
            Action::List => {
                let plugins = list()?;
                println!("{:?}", plugins);
                Ok(())
            }
            Action::Show(action) => {
                let Show { plugin } = action;

                let manifest = manifest(&plugin)?;
                println!("{}", manifest);
                Ok(())
            }
            Action::Add(action) => {
                let Add {
                    docker,
                    binary,
                    package,
                    plugins,
                } = action;

                let mut kinds_local = vec![];
                if docker {
                    kinds_local.push(Kind::Docker)
                }
                if binary {
                    kinds_local.push(Kind::Binary)
                }
                if package {
                    kinds_local.push(Kind::Package)
                }
                if kinds_local.len() == 0 {
                    kinds_local = kinds
                }

                add_list(plugins, kinds_local, aliases).await
            }
            Action::Remove(action) => {
                let Remove { plugins } = action;

                remove_list(plugins, aliases)
            }
            Action::Upgrade(action) => {
                let Upgrade { plugins } = action;

                let plugins = if plugins.len() == 0 { list()? } else { plugins };

                // Note: Currently, `upgrade` is just an alias for `add`
                // and does not warn user if plugin is not yet installed.
                add_list(plugins, kinds, aliases).await
            }
        }
    }
}
<commit_msg>chore(Plugins): Linting<commit_after>use crate::util::dirs;
use anyhow::{anyhow, bail, Result};
use futures::StreamExt;
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::fs;
use std::path::PathBuf;
use std::thread;
use std::{collections::HashMap, process::Command};
use strum::{Display, EnumString, EnumVariantNames};

#[derive(Debug, Display, EnumString, EnumVariantNames, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
#[strum(serialize_all = "lowercase")]
pub enum Kind {
    #[cfg(any(feature = "plugins-docker"))]
    Docker,
    #[cfg(any(feature = "plugins-binary"))]
    Binary,
    #[cfg(any(feature = "plugins-package"))]
    Package,
}

/// Split a plugin spec (ie. alias/name and, optionally, version) into alias and version
pub fn spec_to_alias_version(spec: &str) -> (String, String) {
    if spec.contains('@') {
        let parts: Vec<&str> = spec.split('@').collect();
        (parts[0].into(), parts[1].into())
    } else {
        (spec.into(), "latest".into())
    }
}

/// Resolve a plugin alias to a plugin name
///
/// If the provided string is a registered alias then returns the corresponding
/// plugin name, otherwise assumes the string is a plugin name
pub fn alias_to_name(alias: &str, aliases: &HashMap<String, String>) -> String {
    match aliases.get(alias) {
        Some(name) => name,
        None => alias,
    }
    .into()
}

/// Get the path to the manifest file of a plugin
pub fn manifest_path(name: &str) -> Result<PathBuf> {
    Ok(dirs::plugins(false)?.join(format!("{}.json", name)))
}

/// Get the manifest for an installed plugin
pub fn manifest(name: &str) -> Result<String> {
    // TODO: deserialize JSON into a manifest object
    match fs::read_to_string(manifest_path(name)?) {
        Ok(json) => Ok(json),
        Err(_) => bail!("Plugin '{}' is not installed", name),
    }
}

/// Test whether a plugin is installed
pub fn is_installed(name: &str) -> Result<bool> {
    Ok(manifest_path(name)?.exists())
}

/// List installed manifests
pub fn list() -> Result<Vec<String>> {
    // TODO: Return a Vec<Manifest>
    let dir = dirs::plugins(true)?;
    let mut plugins: Vec<String> = vec![];
    for entry in fs::read_dir(dir)? {
        let entry = entry?;
        let path = entry.path();
        if let Some(ext) = path.extension() {
            if ext == "json" {
                let name = path.file_stem().unwrap().to_string_lossy().to_string();
                plugins.push(name)
            }
        }
    }
    Ok(plugins)
}

/// Add a plugin as a pulled Docker image
///
/// For this to succeed must be able to connect to the local
/// Docker server and be able to pull an image with corresponding
/// name.
#[cfg(any(feature = "plugins-docker"))]
pub async fn install_docker(name: &str, version: &str) -> Result<()> {
    let docker = bollard::Docker::connect_with_local_defaults()?;

    let image = if name.contains('/') {
        name.to_string()
    } else {
        format!("stencila/{}", name)
    };
    let image = format!("{}:{}", image, version);

    // Pull the image (by creating an image from it)
    let mut stream = docker.create_image(
        Some(bollard::image::CreateImageOptions {
            from_image: image.clone(),
            ..Default::default()
        }),
        None,
        None,
    );
    while let Some(item) = stream.next().await {
        match item {
            Ok(info) => {
                if let Some(error) = info.error {
                    bail!("{}", error)
                } else if let Some(status) = info.status {
                    // TODO display of status and progress could be improved and displayed
                    // per id (layer) as in docker CLI
                    // println!("{:?} {:?} {:?}", info.id, info.progress, info.progress_detail);
                    if let Some(progress) = info.progress {
                        tracing::info!("{} {}", status, progress)
                    } else {
                        tracing::info!("{}", status)
                    }
                }
            }
            Err(error) => match error {
                bollard::errors::Error::DockerResponseNotFoundError { .. } => {
                    bail!("Unable to find Docker image '{}'", image)
                }
                _ => bail!("{}", error),
            },
        }
    }

    // Create a container to obtain manifest from
    let container_name: String = rand::thread_rng()
        .sample_iter(&rand::distributions::Alphanumeric)
        .take(24)
        .map(char::from)
        .collect();
    let response = docker
        .create_container(
            Some(bollard::container::CreateContainerOptions {
                name: &container_name,
            }),
            bollard::container::Config {
                image: Some(image.clone()),
                cmd: Some(vec!["manifest".to_string()]),
                ..Default::default()
            },
        )
        .await?;
    if !response.warnings.is_empty() {
        for warning in response.warnings {
            tracing::warn!("When creating container: {}", warning);
        }
    }

    // Start the container
    docker
        .start_container(
            &container_name,
            None::<bollard::container::StartContainerOptions<String>>,
        )
        .await?;

    // Capture the container output into `manifest`
    let mut stream = docker.logs::<String>(
        &container_name,
        Some(bollard::container::LogsOptions {
            follow: true,
            stderr: true,
            stdout: true,
            ..Default::default()
        }),
    );
    let mut stdout = vec![];
    let mut stderr = vec![];
    while let Some(output) = stream.next().await {
        if let Ok(output) = output {
            match output {
                bollard::container::LogOutput::StdOut { message } => {
                    stdout.extend_from_slice(&message)
                }
                bollard::container::LogOutput::StdErr { message } => {
                    stderr.extend_from_slice(&message)
                }
                _ => {}
            }
        }
    }

    if !stderr.is_empty() {
        tracing::warn!("{}", std::str::from_utf8(&stderr)?);
    }

    let manifest = if !stdout.is_empty() {
        match std::str::from_utf8(&stdout) {
            Ok(stdout) => stdout,
            Err(error) => bail!("Error converting stream to UTF8: {}", error),
        }
    } else {
        bail!(
            "No output from Docker container manifest command; is {} a Stencila plugin?",
            image
        )
    };

    // TODO: deserialize manifest and then serialize to file
    fs::write(manifest_path(name)?, manifest)?;

    Ok(())
}

/// Add a plugin as a downloaded binary
#[cfg(any(feature = "plugins-binary"))]
pub fn install_binary(name: &str, version: &str) -> Result<()> {
    let (owner, name) = if name.contains('/') {
        let parts: Vec<&str> = name.split('/').collect();
        (parts[0], parts[1])
    } else {
        ("stencila", name)
    };

    // Get the current version from the manifest (if any)
    let current_version = if let Ok(_manifest) = manifest(name) {
        // TODO extract version from manifest
        "0.1.0"
    } else {
        // Not yet installed, so artificially low version
        "0.0.0"
    };

    // Create the directory where the binary will be downloaded to
    let install_dir = dirs::plugins(false)?.join(name);
    fs::create_dir_all(&install_dir)?;
    let install_path = install_dir.join(name);

    let mut builder = self_update::backends::github::Update::configure();
    builder
        .repo_owner(owner)
        .repo_name(name)
        .bin_name(name)
        .current_version(current_version)
        .bin_install_path(&install_path)
        .show_output(true)
        .show_download_progress(true);
    if version != "latest" {
        builder.target_version_tag(format!("v{}", version).as_str());
    }

    // The download has to be done in another thread because it spawns
    // a new tokio runtime
    thread::spawn(move || -> Result<()> {
        if let Err(error) = builder.build()?.update() {
            match error {
                self_update::errors::Error::Network(message) => {
                    if message.contains("404") {
                        bail!("Could not find repository or corresponding release in repository")
                    } else {
                        bail!(message)
                    }
                }
                _ => bail!(error.to_string()),
            }
        } else {
            Ok(())
        }
    })
    .join()
    .map_err(|_| anyhow!("Error joining thread"))??;

    // Get and store manifest
    let manifest = Command::new(&install_path).arg("manifest").output()?.stdout;
    let manifest = std::str::from_utf8(&manifest)?;
    // TODO: deserialize manifest and then serialize to file
    fs::write(manifest_path(name)?, manifest)?;

    Ok(())
}

/// Add a plugin as a programing language package
#[cfg(any(feature = "plugins-package"))]
pub fn install_package(name: &str, version: &str) -> Result<()> {
    // TODO
    bail!(
        "Unable to add plugin '{}@{}' as programming language package",
        name,
        version
    )
}

/// Add a plugin
pub async fn add(plugin: &str, kinds: &[Kind], aliases: &HashMap<String, String>) -> Result<()> {
    let (alias, version) = spec_to_alias_version(plugin);
    let name = alias_to_name(&alias, aliases);

    if is_installed(&name)? {
        bail!(
            "Plugin '{}' is already installed, consider using `stencila plugins upgrade {}`",
            name,
            name
        );
    }

    for kind in kinds {
        let result = match kind {
            Kind::Docker => install_docker(&name, &version).await,
            Kind::Binary => install_binary(&name, &version),
            Kind::Package => install_package(&name, &version),
        };
        match result {
            // Success, so just return now
            Ok(_) => return Ok(()),
            // Error, keep trying other kinds, or if there is
            // only one kind, return that error.
            Err(error) => {
                if kinds.len() == 1 {
                    return Err(error);
                }
            }
        }
    }

    bail!(
        "Unable to install plugin '{}', tried kinds {:?}",
        plugin,
        kinds
    )
}

/// Add a list of plugins
pub async fn add_list(
    plugins: Vec<String>,
    kinds: Vec<Kind>,
    aliases: HashMap<String, String>,
) -> Result<()> {
    for plugin in plugins {
        match add(&plugin, &kinds, &aliases).await {
            Ok(_) => tracing::info!("Added plugin {}", plugin),
            Err(error) => tracing::error!("{}", error),
        }
    }
    Ok(())
}

/// Remove a plugin
pub fn remove(plugin: &str, aliases: &HashMap<String, String>) -> Result<()> {
    let (alias, ..) = spec_to_alias_version(plugin);
    let name = alias_to_name(&alias, aliases);

    let file = manifest_path(&name)?;
    if file.exists() {
        fs::remove_file(file)?;
    }

    Ok(())
}

/// Remove a list of plugins
pub fn remove_list(plugins: Vec<String>, aliases: HashMap<String, String>) -> Result<()> {
    for plugin in plugins {
        match remove(&plugin, &aliases) {
            Ok(_) => tracing::info!("Removed plugin {}", plugin),
            Err(error) => tracing::error!("{}", error),
        }
    }
    Ok(())
}

#[cfg(feature = "config")]
pub mod config {
    use super::*;
    use validator::Validate;

    #[derive(Debug, PartialEq, Deserialize, Serialize, Validate)]
    pub struct Config {
        #[serde(default = "default_kinds")]
        pub kinds: Vec<Kind>,

        #[serde(default = "default_aliases")]
        pub aliases: HashMap<String, String>,
    }

    /// Default configuration
    ///
    /// These values are used when `config.toml` does not
    /// contain any config for `upgrade`.
    impl Default for Config {
        fn default() -> Self {
            Config {
                kinds: default_kinds(),
                aliases: default_aliases(),
            }
        }
    }

    /// Get the default value for `kinds`
    pub fn default_kinds() -> Vec<Kind> {
        vec![Kind::Docker, Kind::Binary, Kind::Package]
    }

    /// Get the default value for `aliases`
    pub fn default_aliases() -> HashMap<String, String> {
        let mut aliases = HashMap::new();
        aliases.insert("javascript".to_string(), "jesta".to_string());
        aliases.insert("js".to_string(), "jesta".to_string());
        aliases
    }
}

#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Manage plugins",
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub struct Args {
        #[structopt(subcommand)]
        pub action: Action,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        List,
        Show(Show),
        Add(Add),
        Remove(Remove),
        Upgrade(Upgrade),
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Show the manifest of a plugin")]
    pub struct Show {
        /// The name of the plugin to show
        #[structopt()]
        pub plugin: String,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Add one or more plugins")]
    pub struct Add {
        /// Attempt to add plugins as Docker image
        #[structopt(short, long)]
        pub docker: bool,

        /// Attempt to add plugins as binary
        #[structopt(short, long)]
        pub binary: bool,

        /// Attempt to add plugins as language package
        #[structopt(short, long)]
        pub package: bool,

        /// The names or aliases of plugins to add
        #[structopt(required = true, multiple = true)]
        pub plugins: Vec<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Remove one or more plugins")]
    pub struct Remove {
        /// The names or aliases of plugins to remove
        #[structopt(required = true, multiple = true)]
        pub plugins: Vec<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(about = "Upgrade plugins")]
    pub struct Upgrade {
        /// The names or aliases of plugins to upgrade
        /// (omit to upgrade all plugins)
        #[structopt(multiple = true)]
        pub plugins: Vec<String>,
    }

    pub async fn plugins(args: Args) -> Result<()> {
        let Args { action } = args;
        let config::Config { kinds, aliases } = crate::config::get()?.plugins;

        match action {
            Action::List => {
                let plugins = list()?;
                println!("{:?}", plugins);
                Ok(())
            }
            Action::Show(action) => {
                let Show { plugin } = action;

                let manifest = manifest(&plugin)?;
                println!("{}", manifest);
                Ok(())
            }
            Action::Add(action) => {
                let Add {
                    docker,
                    binary,
                    package,
                    plugins,
                } = action;

                let mut kinds_local = vec![];
                if docker {
                    kinds_local.push(Kind::Docker)
                }
                if binary {
                    kinds_local.push(Kind::Binary)
                }
                if package {
                    kinds_local.push(Kind::Package)
                }
                if kinds_local.is_empty() {
                    kinds_local = kinds
                }

                add_list(plugins, kinds_local, aliases).await
            }
            Action::Remove(action) => {
                let Remove { plugins } = action;

                remove_list(plugins, aliases)
            }
            Action::Upgrade(action) => {
                let Upgrade { plugins } = action;

                let plugins = if plugins.is_empty() { list()? } else { plugins };

                // Note: Currently, `upgrade` is just an alias for `add`
                // and does not warn user if plugin is not yet installed.
                add_list(plugins, kinds, aliases).await
            }
        }
    }
}
<|endoftext|>


<commit_before>fn main() {
    // testing(5, vec![(3, 1)]);
    // testing(20, vec![(6, 2)]);
    // testing(9001, vec![(4501, 2250)]);
    // testing(9004, vec![(2252, 1125)]);
    // testing(9005, vec![(4503, 2251), (903, 449)]);
    // testing(90005, vec![(45003, 22501), (9003, 4499), (981, 467), (309, 37)]);
    // testing(90002, vec![]);

    println!(" {} -- {:?}", 9000, solequa(9000));
    println!(" {} -- {:?}", 9001, solequa(9001));
    println!(" {} -- {:?}", 9002, solequa(9002));
    println!(" {} -- {:?}", 9003, solequa(9003));
    println!(" {} -- {:?}", 9004, solequa(9004));
    println!(" {} -- {:?}", 9005, solequa(9005));
    println!(" {} -- {:?}", 9006, solequa(9006));
    println!(" {} -- {:?}", 9007, solequa(9007));
    println!(" {} -- {:?}", 9008, solequa(9008));
    println!(" {} -- {:?}", 9009, solequa(9009));

}

fn q_sqrt( n:f64 ) -> u64 {
	let mut i: u64;
	let mut x2: f64;
    let mut y: f64;
	let threehalfs: f64 = 1.5;

	x2 = n * 0.5;
	y  = n;
	i  = y as u64;                       // evil floating point bit level hacking
	i  = 0x5f3759df - ( i >> 1 );               // what the fuck?
	y  = i as f64;
	y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//	y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

	y as u64
}

fn solequa(n: u64) -> Vec<(u64, u64)> {
    let mut ret = Vec::new();
    let mut x: u64;
    let mut y: u64;

    x = (n as f64).sqrt() as u64;

    println!("?{} -- x{}", 1/q_sqrt(n as f64), x);
    let xlimit = n / 2 + 1;

    if n % 2 == 0 {
        if x % 2 != 0 {
            x += 1;
        }
    } else {
        if x % 2 == 0 {
            x += 1;
        }
    }

    while x <= xlimit {
        let fx = x as f64;

        y = (( fx * fx - n as f64 ).sqrt() as f64 / 2.0) as u64;

        let res = (fx - 2.0 * y as f64) * (fx + 2.0 * y as f64);

        if res as u64 == n {
            ret.insert(0, (x, y));
        }

        x += 2;
    }

    ret
}

fn testing(n: u64, exp: Vec<(u64, u64)>) -> () {
    assert_eq!(solequa(n), exp)
}

#[test]
fn basics_solequa() {
    testing(20, vec![(6, 2)]);
    testing(900000, vec![(112502, 56249), (56254, 28123), (37506, 18747), (22510, 11245), (18762, 9369),
    (12518, 6241), (11270, 5615), (7530, 3735), (6286, 3107), (4550, 2225), (3810, 1845), (2590, 1205),
    (2350, 1075), (1650, 675), (1430, 535), (1150, 325), (1050, 225), (950, 25)]);

    testing(9001, vec![(4501, 2250)]);
    testing(9000001, vec![(4500001, 2250000), (73801, 36870)]);
    testing(90000001, vec![(45000001, 22500000), (6428575, 3214284), (3461545, 1730766), (494551, 247230)]);

    testing(90002, vec![]);

    testing(9004, vec![(2252, 1125)]);
    testing(9000004, vec![(2250002, 1125000), (173090, 86532), (132370, 66168), (10402, 4980)]);
    testing(90000004, vec![(22500002, 11250000), (252898, 126360), (93602, 46560), (22498, 10200)]);

    testing(9005, vec![(4503, 2251), (903, 449)]);
    testing(90005, vec![(45003, 22501), (9003, 4499), (981, 467), (309, 37)]);
    testing(5, vec![(3, 1)]);
    testing(9000005, vec![(4500003, 2250001), (900003, 449999), (642861, 321427), (155187, 77579),
    (128589, 64277), (31107, 15481), (22269, 11033), (4941, 1963)]);

    testing(9009, vec![(4505, 2252), (1503, 750), (647, 320), (505, 248), (415, 202), (353, 170), (225, 102),
    (153, 60), (135, 48), (103, 20), (97, 10), (95, 2)]);

    testing(900000009, vec![(450000005, 225000002), (150000003, 75000000), (50000005, 24999998), (26470597, 13235290), (8823555, 4411752), (2941253, 1470550)]);
}

<commit_msg>cleanup<commit_after>fn main() {
    // testing(5, vec![(3, 1)]);
    // testing(20, vec![(6, 2)]);
    // testing(9001, vec![(4501, 2250)]);
    // testing(9004, vec![(2252, 1125)]);
    // testing(9005, vec![(4503, 2251), (903, 449)]);
    // testing(90005, vec![(45003, 22501), (9003, 4499), (981, 467), (309, 37)]);
    // testing(90002, vec![]);

    println!(" {} -- {:?}", 9000, solequa(9000));
    println!(" {} -- {:?}", 9001, solequa(9001));
    println!(" {} -- {:?}", 9002, solequa(9002));
    println!(" {} -- {:?}", 9003, solequa(9003));
    println!(" {} -- {:?}", 9004, solequa(9004));
    println!(" {} -- {:?}", 9005, solequa(9005));
    println!(" {} -- {:?}", 9006, solequa(9006));
    println!(" {} -- {:?}", 9007, solequa(9007));
    println!(" {} -- {:?}", 9008, solequa(9008));
    println!(" {} -- {:?}", 9009, solequa(9009));

}

fn solequa(n: u64) -> Vec<(u64, u64)> {
    let mut ret = Vec::new();
    let mut x: u64;
    let mut y: u64;

    x = (n as f64).sqrt() as u64;

    let xlimit = n / 2 + 1;

    if n % 2 == 0 {
        if x % 2 != 0 {
            x += 1;
        }
    } else {
        if x % 2 == 0 {
            x += 1;
        }
    }

    while x <= xlimit {
        let fx = x as f64;

        y = (( fx * fx - n as f64 ).sqrt() as f64 / 2.0) as u64;

        let res = (fx - 2.0 * y as f64) * (fx + 2.0 * y as f64);

        if res as u64 == n {
            ret.insert(0, (x, y));
        }

        x += 2;
    }

    ret
}

fn testing(n: u64, exp: Vec<(u64, u64)>) -> () {
    assert_eq!(solequa(n), exp)
}

#[test]
fn basics_solequa() {
    testing(20, vec![(6, 2)]);
    testing(900000, vec![(112502, 56249), (56254, 28123), (37506, 18747), (22510, 11245), (18762, 9369),
    (12518, 6241), (11270, 5615), (7530, 3735), (6286, 3107), (4550, 2225), (3810, 1845), (2590, 1205),
    (2350, 1075), (1650, 675), (1430, 535), (1150, 325), (1050, 225), (950, 25)]);

    testing(9001, vec![(4501, 2250)]);
    testing(9000001, vec![(4500001, 2250000), (73801, 36870)]);
    testing(90000001, vec![(45000001, 22500000), (6428575, 3214284), (3461545, 1730766), (494551, 247230)]);

    testing(90002, vec![]);

    testing(9004, vec![(2252, 1125)]);
    testing(9000004, vec![(2250002, 1125000), (173090, 86532), (132370, 66168), (10402, 4980)]);
    testing(90000004, vec![(22500002, 11250000), (252898, 126360), (93602, 46560), (22498, 10200)]);

    testing(9005, vec![(4503, 2251), (903, 449)]);
    testing(90005, vec![(45003, 22501), (9003, 4499), (981, 467), (309, 37)]);
    testing(5, vec![(3, 1)]);
    testing(9000005, vec![(4500003, 2250001), (900003, 449999), (642861, 321427), (155187, 77579),
    (128589, 64277), (31107, 15481), (22269, 11033), (4941, 1963)]);

    testing(9009, vec![(4505, 2252), (1503, 750), (647, 320), (505, 248), (415, 202), (353, 170), (225, 102),
    (153, 60), (135, 48), (103, 20), (97, 10), (95, 2)]);

    testing(900000009, vec![(450000005, 225000002), (150000003, 75000000), (50000005, 24999998), (26470597, 13235290), (8823555, 4411752), (2941253, 1470550)]);
}

<|endoftext|>


<commit_before>
use mouros_rust_bindings::mailbox::{Mailbox, MailboxRaw};
use mouros_rust_bindings::CVoid;
use mouros_rust_bindings::tasks;

use bindings::messages;
use bindings::constants;
use bindings::message_dispatcher;

use core::ptr;

const NUM_CHANNELS: usize = 1;

enum SpinnerEvent<'a> {
    SetPlan(&'a messages::spin_plan_data),
    GetPlan,
    SetState(&'a messages::spin_state_set_data),
    GetState,
}

#[derive(PartialEq, Eq)]
enum ChannelState {
    Stopped,
    Running,
    Spindown,
}

#[derive(Default, Clone, Copy)]
struct PlanLeg {
    duration_msecs: u32,
    target_pct: f32,
}

struct Channel {
    id: u8,
    state: ChannelState,
    plan: [PlanLeg; constants::MAX_SPIN_PLAN_LEGS as usize],
    plan_len: usize,
    elapsed_time_msecs: u32,
    output_val_pct: f32,
}

impl Default for Channel {
    fn default() -> Channel {
        Channel {
            id: 0,
            state: ChannelState::Stopped,
            plan: [PlanLeg::default(); constants::MAX_SPIN_PLAN_LEGS as usize],
            plan_len: 0,
            elapsed_time_msecs: 0,
            output_val_pct: 0f32,
        }
    }
}

impl Channel {
    fn send_event(&mut self, trans_id: u32, event: SpinnerEvent) {
        match self.state {
            ChannelState::Stopped => {
                match event {
                    SpinnerEvent::SetPlan(data) => {
                        self.set_channel_plan(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::SetState(data) => {
                        self.set_channel_state(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                }
            }
            ChannelState::Running => {
                match event {
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::SetState(data) => {
                        self.set_channel_state(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                    _ => {
                        self.send_return_val(trans_id, 1);
                    }
                }
            }
            ChannelState::Spindown => {
                match event {
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                    _ => {
                        self.send_return_val(trans_id, 1);
                    }
                }
            }
        }
    }

    fn send_spin_plan_reply(&self, trans_id: u32) {
        let mut msg = if let Ok(msg) =
            messages::MessageWrapper::new(trans_id, messages::MsgTypes::SPIN_PLAN_REPLY) {
            msg
        } else {
            return;
        };

        if let messages::Message::SpinPlanReply(ref mut data) = *msg {
            data.channel_num = self.id;
            data.spin_plan_leg_count = self.plan_len as u32;
            for leg_idx in 0..self.plan_len {
                data.plan_legs[leg_idx].duration_msecs = self.plan[leg_idx].duration_msecs;
                data.plan_legs[leg_idx].target_pct = self.plan[leg_idx].target_pct;
            }
        } else {
            return;
        }

        message_dispatcher::send_message(msg);
    }

    fn send_spin_state_reply(&self, trans_id: u32) {
        let mut msg = if let Ok(msg) =
            messages::MessageWrapper::new(trans_id, messages::MsgTypes::SPIN_STATE_REPLY) {
            msg
        } else {
            return;
        };

        if let messages::Message::SpinStateReply(ref mut data) = *msg {
            data.channel_num = self.id;
            data.state = match self.state {
                ChannelState::Stopped => messages::SpinStates::STOPPED,
                ChannelState::Running => messages::SpinStates::RUNNING,
                ChannelState::Spindown => messages::SpinStates::SPINNING_DOWN,
            };
            data.plan_time_elapsed_msecs = self.elapsed_time_msecs;
            data.output_val_pct = self.output_val_pct;

        } else {
            return;
        }

        message_dispatcher::send_message(msg);
    }

    fn send_return_val(&self, trans_id: u32, ret_val: i32) {
        let mut msg = if let Ok(msg) = messages::MessageWrapper::new(trans_id,
                                                                     messages::MsgTypes::RET_VAL) {
            msg
        } else {
            return;
        };

        if let messages::Message::ReturnValue(ref mut data) = *msg {
            data.ret_val = ret_val;
        } else {
            return;
        }

        message_dispatcher::send_message(msg);
    }

    fn set_channel_state(&mut self, data: &messages::spin_state_set_data) {
        critical!({
            if self.state == ChannelState::Running && data.state == messages::SpinStates::STOPPED {
                self.state = ChannelState::Spindown;
            } else if self.state == ChannelState::Stopped &&
                      data.state == messages::SpinStates::RUNNING {
                self.state = ChannelState::Running;
            }
        });
    }

    fn set_channel_plan(&mut self, data: &messages::spin_plan_data) {
        critical!({
            if self.state != ChannelState::Stopped {
                return;
            }
        });

        self.plan_len = data.spin_plan_leg_count as usize;
        for leg_idx in 0..self.plan_len {
            self.plan[leg_idx].duration_msecs = data.plan_legs[leg_idx].duration_msecs;
            self.plan[leg_idx].target_pct = data.plan_legs[leg_idx].target_pct;
        }
    }
}

pub struct SpinnerContext {
    channels: [Channel; NUM_CHANNELS],
    msg_buf: Mailbox<*mut messages::message>,
}

static mut MSG_BUF_MEM: [*mut messages::message; 30] = [0 as *mut messages::message; 30];
static mut SPINNER_CONTEXT: Option<SpinnerContext> = None;

#[no_mangle]
pub static mut SPINNER_MSG_QUEUE_PTR: *const MailboxRaw = 0 as *const MailboxRaw;

#[no_mangle]
pub extern "C" fn spinner_get_context() -> *mut CVoid {
    unsafe {
        if SPINNER_CONTEXT.is_none() {
            let mut ctx = SpinnerContext {
                channels: [Channel::default(); NUM_CHANNELS],
                msg_buf: Mailbox::new(&mut MSG_BUF_MEM),
            };

            for (id, ch) in ctx.channels.iter_mut().enumerate() {
                ch.id = id as u8;
            }

            SPINNER_CONTEXT = Some(ctx);
            SPINNER_MSG_QUEUE_PTR = SPINNER_CONTEXT.as_mut().unwrap().msg_buf.get_raw_mb_struct();
        }

        SPINNER_CONTEXT.as_mut().unwrap() as *mut SpinnerContext as *mut CVoid
    }
}

#[no_mangle]
pub extern "C" fn spinner_comm_loop(context_ptr: *mut CVoid) {

    let context: &mut SpinnerContext = unsafe {
        if context_ptr != ptr::null_mut() {
            &mut *(context_ptr as *mut SpinnerContext)
        } else {
            panic!();
        }
    };

    if let Some(msg_ptr) = context.msg_buf.read() {
        if let Ok((trans_id, msg)) = messages::MessageWrapper::from_raw(msg_ptr) {
            match *msg {
                messages::Message::SetSpinPlan(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        context.channels[ch_num].send_event(trans_id, SpinnerEvent::SetPlan(data));
                    }
                }
                messages::Message::GetSpinPlan(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        context.channels[ch_num].send_event(trans_id, SpinnerEvent::GetPlan);
                    }
                }
                messages::Message::SetSpinState(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        context.channels[ch_num].send_event(trans_id, SpinnerEvent::SetState(data));
                    }
                }
                messages::Message::GetSpinState(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        context.channels[ch_num].send_event(trans_id, SpinnerEvent::GetState);
                    }
                }
                _ => {}
            }
        }

        return;
    }

    tasks::sleep(50);
}
<commit_msg>Reworked Spinner code to use the new version of messages (#4 and #6)<commit_after>
use mouros_rust_bindings::mailbox::{Mailbox, MailboxRaw};
use mouros_rust_bindings::CVoid;
use mouros_rust_bindings::tasks;

use bindings::constants;
use bindings::message_dispatcher;

use core::ptr;
use core::convert::TryFrom;

use bindings::message_dispatcher::{MessageWrapper, MemManagement};

mod MsgTypes {
    pub const SET_PLAN: u32 = 0;
    pub const GET_PLAN: u32 = 1;
    pub const PLAN_REPLY: u32 = 2;
    pub const SET_STATE: u32 = 3;
    pub const GET_STATE: u32 = 4;
    pub const STATE_REPLY: u32 = 5;
    pub const RET_VAL: u32 = 6;
}

#[repr(C)]
pub struct spin_plan_data {
    pub channel_num: u8,
    pub spin_plan_leg_count: u32,
    pub plan_legs: [pwm_leg; 10usize],
}

#[repr(C)]
pub struct pwm_leg {
    pub duration_msecs: u32,
    pub target_pct: f32,
}

#[repr(C)]
pub struct spin_channel {
    pub channel_num: u8,
}

#[repr(C)]
pub struct ret_val {
    pub ret_val: i32,
}

mod SpinStates {
    pub const STOPPED: u32 = 0;
    pub const RUNNING: u32 = 1;
    pub const SPINNING_DOWN: u32 = 2;
}

#[repr(C)]
pub struct spin_state_set_data {
    pub channel_num: u8,
    pub state: u32,
}

#[repr(C)]
pub struct spin_state_data {
    pub channel_num: u8,
    pub state: u32,
    pub plan_time_elapsed_msecs: u32,
    pub output_val_pct: f32,
}

pub enum Message<'a> {
    SetSpinPlan(&'a mut spin_plan_data),
    GetSpinPlan(&'a mut spin_channel),
    SpinPlanReply(&'a mut spin_plan_data),
    SetSpinState(&'a mut spin_state_set_data),
    GetSpinState(&'a mut spin_channel),
    SpinStateReply(&'a mut spin_state_data),
    ReturnValue(&'a mut ret_val),
}

impl<'a> message_dispatcher::MemManagement for Message<'a> {
    fn alloc(msg_type: u32) -> *mut message_dispatcher::message {
        unsafe { get_alloc_fn()(msg_type) }
    }

    fn free(msg_ptr: *mut message_dispatcher::message) {
        unsafe { get_free_fn()(msg_ptr) }
    }
}

impl<'a> TryFrom<*mut message_dispatcher::message> for Message<'a> {
    type Err = ();

    fn try_from(raw_msg_ptr: *mut message_dispatcher::message) -> Result<Self, Self::Err> {
        unsafe {
            if raw_msg_ptr.is_null() || (*raw_msg_ptr).data.is_null() {
                return Err(());
            }

            match (*raw_msg_ptr).msg_type {
                MsgTypes::SET_PLAN => {
                    Ok(Message::SetSpinPlan(&mut *((*raw_msg_ptr).data as *mut spin_plan_data)))
                }
                MsgTypes::GET_PLAN => {
                    Ok(Message::GetSpinPlan(&mut *((*raw_msg_ptr).data as *mut spin_channel)))
                }
                MsgTypes::PLAN_REPLY => {
                    Ok(Message::SpinPlanReply(&mut *((*raw_msg_ptr).data as *mut spin_plan_data)))
                }
                MsgTypes::SET_STATE => {
                    Ok(Message::SetSpinState(&mut *((*raw_msg_ptr).data as
                                                    *mut spin_state_set_data)))
                }
                MsgTypes::GET_STATE => {
                    Ok(Message::GetSpinState(&mut *((*raw_msg_ptr).data as *mut spin_channel)))
                }
                MsgTypes::STATE_REPLY => {
                    Ok(Message::SpinStateReply(&mut *((*raw_msg_ptr).data as *mut spin_state_data)))
                }
                MsgTypes::RET_VAL => {
                    Ok(Message::ReturnValue(&mut *((*raw_msg_ptr).data as *mut ret_val)))
                }
                _ => {
                    Self::free(raw_msg_ptr);
                    Err(())
                }
            }
        }
    }
}

const NUM_CHANNELS: usize = 1;

enum SpinnerEvent<'a> {
    SetPlan(&'a spin_plan_data),
    GetPlan,
    SetState(&'a spin_state_set_data),
    GetState,
}

#[derive(PartialEq, Eq)]
enum ChannelState {
    Stopped,
    Running,
    Spindown,
}

#[derive(Default, Clone, Copy)]
struct PlanLeg {
    duration_msecs: u32,
    target_pct: f32,
}

struct Channel {
    id: u8,
    state: ChannelState,
    plan: [PlanLeg; constants::MAX_SPIN_PLAN_LEGS as usize],
    plan_len: usize,
    elapsed_time_msecs: u32,
    output_val_pct: f32,
}

impl Default for Channel {
    fn default() -> Channel {
        Channel {
            id: 0,
            state: ChannelState::Stopped,
            plan: [PlanLeg::default(); constants::MAX_SPIN_PLAN_LEGS as usize],
            plan_len: 0,
            elapsed_time_msecs: 0,
            output_val_pct: 0f32,
        }
    }
}

impl Channel {
    fn send_event(&mut self, trans_id: u32, event: SpinnerEvent) {
        match self.state {
            ChannelState::Stopped => {
                match event {
                    SpinnerEvent::SetPlan(data) => {
                        self.set_channel_plan(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::SetState(data) => {
                        self.set_channel_state(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                }
            }
            ChannelState::Running => {
                match event {
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::SetState(data) => {
                        self.set_channel_state(data);
                        self.send_return_val(trans_id, 0);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                    _ => {
                        self.send_return_val(trans_id, 1);
                    }
                }
            }
            ChannelState::Spindown => {
                match event {
                    SpinnerEvent::GetPlan => {
                        self.send_spin_plan_reply(trans_id);
                    }
                    SpinnerEvent::GetState => {
                        self.send_spin_state_reply(trans_id);
                    }
                    _ => {
                        self.send_return_val(trans_id, 1);
                    }
                }
            }
        }
    }

    fn send_spin_plan_reply(&self, trans_id: u32) {
        let mut msg = if let Ok(msg) = MessageWrapper::new(trans_id, MsgTypes::PLAN_REPLY) {
            msg
        } else {
            return;
        };

        if let Message::SpinPlanReply(ref mut data) = *msg {
            data.channel_num = self.id;
            data.spin_plan_leg_count = self.plan_len as u32;
            for leg_idx in 0..self.plan_len {
                data.plan_legs[leg_idx].duration_msecs = self.plan[leg_idx].duration_msecs;
                data.plan_legs[leg_idx].target_pct = self.plan[leg_idx].target_pct;
            }
        } else {
            return;
        }

        get_outgoing_queue().write(msg.into());
    }

    fn send_spin_state_reply(&self, trans_id: u32) {
        let mut msg = if let Ok(msg) = MessageWrapper::new(trans_id, MsgTypes::STATE_REPLY) {
            msg
        } else {
            return;
        };

        if let Message::SpinStateReply(ref mut data) = *msg {
            data.channel_num = self.id;
            data.state = match self.state {
                ChannelState::Stopped => SpinStates::STOPPED,
                ChannelState::Running => SpinStates::RUNNING,
                ChannelState::Spindown => SpinStates::SPINNING_DOWN,
            };
            data.plan_time_elapsed_msecs = self.elapsed_time_msecs;
            data.output_val_pct = self.output_val_pct;

        } else {
            return;
        }

        get_outgoing_queue().write(msg.into());
    }

    fn send_return_val(&self, trans_id: u32, ret_val: i32) {
        let mut msg = if let Ok(msg) = MessageWrapper::new(trans_id, MsgTypes::RET_VAL) {
            msg
        } else {
            return;
        };

        if let Message::ReturnValue(ref mut data) = *msg {
            data.ret_val = ret_val;
        } else {
            return;
        }

        get_outgoing_queue().write(msg.into());
    }

    fn set_channel_state(&mut self, data: &spin_state_set_data) {
        critical!({
                      if self.state == ChannelState::Running && data.state == SpinStates::STOPPED {
                          self.state = ChannelState::Spindown;
                      } else if self.state == ChannelState::Stopped &&
                      data.state == SpinStates::RUNNING {
                self.state = ChannelState::Running;
            }
                  });
    }

    fn set_channel_plan(&mut self, data: &spin_plan_data) {
        critical!({
                      if self.state != ChannelState::Stopped {
                          return;
                      }
                  });

        self.plan_len = data.spin_plan_leg_count as usize;
        for leg_idx in 0..self.plan_len {
            self.plan[leg_idx].duration_msecs = data.plan_legs[leg_idx].duration_msecs;
            self.plan[leg_idx].target_pct = data.plan_legs[leg_idx].target_pct;
        }
    }
}

struct SpinnerContext {
    channels: [Channel; NUM_CHANNELS],
    subsystem_conf: &'static message_dispatcher::subsystem_message_conf,
}

static mut SPINNER_CTX: Option<SpinnerContext> = None;

fn get_channels() -> &'static mut [Channel] {
    unsafe {
        match SPINNER_CTX {
            Some(ref mut ctx) => &mut ctx.channels,
            None => panic!(),
        }
    }
}

fn get_incoming_queue() -> &'static mut Mailbox<*mut message_dispatcher::message> {
    unsafe {
        match SPINNER_CTX {
            Some(ref ctx) => &mut *ctx.subsystem_conf.incoming_msg_queue,
            None => panic!(),
        }
    }
}

fn get_outgoing_queue() -> &'static mut Mailbox<*mut message_dispatcher::message> {
    unsafe {
        match SPINNER_CTX {
            Some(ref ctx) => &mut *ctx.subsystem_conf.outgoing_msg_queue,
            None => panic!(),
        }
    }
}

fn get_outgoing_error_queue() -> &'static mut Mailbox<i32> {
    unsafe {
        match SPINNER_CTX {
            Some(ref ctx) => &mut *ctx.subsystem_conf.outgoing_err_queue,
            None => panic!(),
        }
    }
}

fn get_alloc_fn() -> unsafe extern "C" fn(msg_type_id: u32) -> *mut message_dispatcher::message {
    unsafe {
        match SPINNER_CTX {
            Some(ref ctx) => ctx.subsystem_conf.alloc_message.unwrap(),
            None => panic!(),
        }
    }
}

fn get_free_fn() -> unsafe extern "C" fn(msg: *mut message_dispatcher::message) {
    unsafe {
        match SPINNER_CTX {
            Some(ref ctx) => ctx.subsystem_conf.free_message.unwrap(),
            None => panic!(),
        }
    }
}

#[no_mangle]
pub extern "C" fn spinner_rust_init(conf: *mut message_dispatcher::subsystem_message_conf) {
    unsafe {
        if conf.is_null() || (*conf).incoming_msg_queue.is_null() ||
           (*conf).outgoing_msg_queue.is_null() || (*conf).outgoing_err_queue.is_null() ||
           (*conf).alloc_message.is_some() || (*conf).free_message.is_some() {
            panic!();
        }

        SPINNER_CTX = Some(SpinnerContext {
                               channels: [Channel::default(); NUM_CHANNELS],
                               subsystem_conf: &*conf,
                           });
    }
}

#[no_mangle]
pub extern "C" fn spinner_comm_loop(_params: *mut CVoid) {

    let channels = get_channels();
    let in_queue = get_incoming_queue();

    if let Some(msg_ptr) = in_queue.read() {
        if let Ok(msg) = MessageWrapper::try_from(msg_ptr) {

            match *msg {
                Message::SetSpinPlan(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        channels[ch_num].send_event(msg.get_transaction_id(),
                                                    SpinnerEvent::SetPlan(data));
                    }
                }
                Message::GetSpinPlan(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        channels[ch_num].send_event(msg.get_transaction_id(),
                                                    SpinnerEvent::GetPlan);
                    }
                }
                Message::SetSpinState(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        channels[ch_num].send_event(msg.get_transaction_id(),
                                                    SpinnerEvent::SetState(data));
                    }
                }
                Message::GetSpinState(ref data) => {
                    let ch_num = data.channel_num as usize;
                    if ch_num < NUM_CHANNELS {
                        channels[ch_num].send_event(msg.get_transaction_id(),
                                                    SpinnerEvent::GetState);
                    }
                }
                _ => {}
            }
        }

        return;
    }

    tasks::sleep(50);
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::{CStr, CString};
use std::fmt;
use super::address::SBAddress;
use super::block::SBBlock;
use super::compileunit::SBCompileUnit;
use super::expressionoptions::SBExpressionOptions;
use super::function::SBFunction;
use super::lineentry::SBLineEntry;
use super::module::SBModule;
use super::stream::SBStream;
use super::symbol::SBSymbol;
use super::symbolcontext::SBSymbolContext;
use super::thread::SBThread;
use super::value::SBValue;
use super::valuelist::SBValueList;
use super::variablesoptions::SBVariablesOptions;
use super::lldb_addr_t;
use sys;

/// One of the stack frames associated with a thread.
pub struct SBFrame {
    /// The underlying raw `SBFrameRef`.
    pub raw: sys::SBFrameRef,
}

impl SBFrame {
    /// Construct a new `SBFrame`.
    pub fn wrap(raw: sys::SBFrameRef) -> SBFrame {
        SBFrame { raw: raw }
    }

    /// Construct a new `Some(SBFrame)` or `None`.
    pub fn maybe_wrap(raw: sys::SBFrameRef) -> Option<SBFrame> {
        if unsafe { sys::SBFrameIsValid(raw) != 0 } {
            Some(SBFrame { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBFrame` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBFrameIsValid(self.raw) != 0 }
    }

    /// The zero-based stack frame index for this frame.
    ///
    /// This can be used to locate adjacent frames in the
    /// thread's stack frames.
    pub fn frame_id(&self) -> u32 {
        unsafe { sys::SBFrameGetFrameID(self.raw) }
    }

    /// Get the Canonical Frame Address for this stack frame.
    ///
    /// This is the DWARF standard's definition of a CFA, a
    /// stack address that remains constant throughout the
    /// lifetime of the function.
    pub fn cfa(&self) -> Option<lldb_addr_t> {
        let cfa = unsafe { sys::SBFrameGetCFA(self.raw) };
        if cfa != u64::max_value() {
            Some(cfa)
        } else {
            None
        }
    }

    /// The program counter (PC) as an unsigned integer.
    pub fn pc(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetPC(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_pc(&self, new_pc: lldb_addr_t) -> bool {
        unsafe { sys::SBFrameSetPC(self.raw, new_pc) != 0 }
    }

    /// The stack pointer address as an unsigned integer.
    pub fn sp(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetSP(self.raw) }
    }

    /// The frame pointer address as an unsigned integer.
    pub fn fp(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetFP(self.raw) }
    }

    /// The program counter (PC) as a section offset address (`SBAddress`).
    pub fn pc_address(&self) -> SBAddress {
        SBAddress::wrap(unsafe { sys::SBFrameGetPCAddress(self.raw) })
    }

    /// The symbol context for this frame's current pc value.
    ///
    /// The frame maintains this symbol context and adds information to
    /// it as needed. This helps avoid repeated lookups of the same
    /// information.
    ///
    /// * `resolve_scope`: Flags that specify what type of symbol context
    ///   is needed by the caller. These flags have constants starting
    ///   with `SYMBOL_CONTEXT_ITEM_`.
    pub fn symbol_context(&self, resolve_scope: u32) -> SBSymbolContext {
        SBSymbolContext::wrap(unsafe { sys::SBFrameGetSymbolContext(self.raw, resolve_scope) })
    }

    /// The `SBModule` for this stack frame.
    pub fn module(&self) -> SBModule {
        SBModule::wrap(unsafe { sys::SBFrameGetModule(self.raw) })
    }

    /// The `SBCompileUnit` for this stack frame.
    pub fn compile_unit(&self) -> SBCompileUnit {
        SBCompileUnit::wrap(unsafe { sys::SBFrameGetCompileUnit(self.raw) })
    }

    /// The `SBFunction` for this stack frame.
    pub fn function(&self) -> SBFunction {
        SBFunction::wrap(unsafe { sys::SBFrameGetFunction(self.raw) })
    }

    /// The `SBSymbol` for this stack frame.
    pub fn symbol(&self) -> SBSymbol {
        SBSymbol::wrap(unsafe { sys::SBFrameGetSymbol(self.raw) })
    }

    /// Get the deepest block that contains the frame PC.
    pub fn block(&self) -> SBBlock {
        SBBlock::wrap(unsafe { sys::SBFrameGetBlock(self.raw) })
    }

    /// Get the appropriate function name for this frame. Inlined functions in
    /// LLDB are represented by blocks that have inlined function information, so
    /// just looking at the `SBFunction` or `SBSymbol` for a frame isn't enough.
    /// This function will return the appropriate function, symbol or inlined
    /// function name for the frame.
    ///
    /// This function returns:
    ///
    /// * the name of the inlined function (if there is one)
    /// * the name of the concrete function (if there is one)
    /// * the name of the symbol (if there is one)
    /// * NULL
    ///
    /// See also `is_inlined`.
    pub fn function_name(&self) -> Option<&str> {
        unsafe {
            match CStr::from_ptr(sys::SBFrameGetFunctionName(self.raw)).to_str() {
                Ok(s) => Some(s),
                _ => None,
            }
        }
    }

    #[allow(missing_docs)]
    pub fn display_function_name(&self) -> Option<&str> {
        unsafe {
            match CStr::from_ptr(sys::SBFrameGetDisplayFunctionName(self.raw)).to_str() {
                Ok(s) => Some(s),
                _ => None,
            }
        }
    }

    /// Return `true` if this frame represents an inlined function.
    pub fn is_inlined(&self) -> bool {
        unsafe { sys::SBFrameIsInlined(self.raw) != 0 }
    }

    /// Evaluate an expression within the context of this frame.
    pub fn evaluate_expression(&self, expression: &str, options: &SBExpressionOptions) -> SBValue {
        let expression = CString::new(expression).unwrap();
        SBValue::wrap(unsafe {
                          sys::SBFrameEvaluateExpression(self.raw, expression.as_ptr(), options.raw)
                      })
    }

    /// Gets the lexical block that defines the stack frame. Another way to think
    /// of this is it will return the block that contains all of the variables
    /// for a stack frame. Inlined functions are represented as `SBBlock` objects
    /// that have inlined function information: the name of the inlined function,
    /// where it was called from. The block that is returned will be the first
    /// block at or above the block for the PC (`SBFrame::block()`) that defines
    /// the scope of the frame. When a function contains no inlined functions,
    /// this will be the top most lexical block that defines the function.
    /// When a function has inlined functions and the PC is currently
    /// in one of those inlined functions, this method will return the inlined
    /// block that defines this frame. If the PC isn't currently in an inlined
    /// function, the lexical block that defines the function is returned.
    pub fn frame_block(&self) -> SBBlock {
        SBBlock::wrap(unsafe { sys::SBFrameGetFrameBlock(self.raw) })
    }

    /// The line table entry (`SBLineEntry`) for this stack frame.
    pub fn line_entry(&self) -> Option<SBLineEntry> {
        SBLineEntry::maybe_wrap(unsafe { sys::SBFrameGetLineEntry(self.raw) })
    }

    /// The thread that is executing this stack frame.
    pub fn thread(&self) -> SBThread {
        SBThread::wrap(unsafe { sys::SBFrameGetThread(self.raw) })
    }

    /// The disassembly of this function, presented as a string.
    pub fn disassemble(&self) -> &str {
        unsafe {
            match CStr::from_ptr(sys::SBFrameDisassemble(self.raw)).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    #[allow(missing_docs)]
    pub fn clear(&self) {
        unsafe { sys::SBFrameClear(self.raw) }
    }

    /// The values for variables matching the specified options.
    pub fn variables(&self, options: &SBVariablesOptions) -> SBValueList {
        SBValueList::wrap(unsafe { sys::SBFrameGetVariables(self.raw, options.raw) })
    }

    /// The values for all variables in this stack frame.
    pub fn all_variables(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(true);
        options.set_include_locals(true);
        options.set_include_statics(true);
        options.set_in_scope_only(true);
        self.variables(&options)
    }

    /// The values for the argument variables in this stack frame.
    pub fn arguments(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(true);
        options.set_include_locals(false);
        options.set_include_statics(false);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the local variables in this stack frame.
    pub fn locals(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(false);
        options.set_include_locals(true);
        options.set_include_statics(false);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the static variables in this stack frame.
    pub fn statics(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(false);
        options.set_include_locals(false);
        options.set_include_statics(true);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the CPU registers for this stack frame.
    pub fn registers(&self) -> SBValueList {
        SBValueList::wrap(unsafe { sys::SBFrameGetRegisters(self.raw) })
    }

    /// The value for a particular register, if present.
    pub fn find_register(&self, name: &str) -> Option<SBValue> {
        let name = CString::new(name).unwrap();
        SBValue::maybe_wrap(unsafe { sys::SBFrameFindRegister(self.raw, name.as_ptr()) })
    }

    /// The parent frame that invoked this frame, if available.
    pub fn parent_frame(&self) -> Option<SBFrame> {
        let thread = self.thread();
        let parent_idx = self.frame_id() + 1;
        if parent_idx < unsafe { sys::SBThreadGetNumFrames(thread.raw) } {
            SBFrame::maybe_wrap(unsafe { sys::SBThreadGetFrameAtIndex(thread.raw, parent_idx) })
        } else {
            None
        }
    }
}

impl fmt::Debug for SBFrame {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBFrameGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBFrame {{ {} }}", stream.data())
    }
}

impl Drop for SBFrame {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBFrame(self.raw) };
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBFrame: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm): This should be u32
    field frame_id() -> i64 {
        self.frame_id() as i64
    }

    // TODO(bm) This should be u64
    field cfa() -> Option<i64> {
        self.cfa().map(|i| i as i64)
    }

    // TODO(bm) This should be u64
    field pc() -> i64 {
        self.pc() as i64
    }

    // TODO(bm) This should be u64
    field sp() -> i64 {
        self.sp() as i64
    }

    // TODO(bm) This should be u64
    field fp() -> i64 {
        self.fp() as i64
    }

    field pc_address() -> SBAddress {
        self.pc_address()
    }

    field module() -> SBModule {
        self.module()
    }

    field compile_unit() -> SBCompileUnit {
        self.compile_unit()
    }

    field function() -> SBFunction {
        self.function()
    }

    field symbol() -> SBSymbol {
        self.symbol()
    }

    field block() -> SBBlock {
        self.block()
    }

    field function_name() -> Option<&str> {
        self.function_name()
    }

    field display_function_name() -> Option<&str> {
        self.display_function_name()
    }

    field is_inlined() -> bool {
        self.is_inlined()
    }

    field frame_block() -> SBBlock {
        self.frame_block()
    }

    field line_entry() -> Option<SBLineEntry> {
        self.line_entry()
    }

    field thread() -> SBThread {
        self.thread()
    }
});
<commit_msg>Don't wrap SBFrame::clear.<commit_after>// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::ffi::{CStr, CString};
use std::fmt;
use super::address::SBAddress;
use super::block::SBBlock;
use super::compileunit::SBCompileUnit;
use super::expressionoptions::SBExpressionOptions;
use super::function::SBFunction;
use super::lineentry::SBLineEntry;
use super::module::SBModule;
use super::stream::SBStream;
use super::symbol::SBSymbol;
use super::symbolcontext::SBSymbolContext;
use super::thread::SBThread;
use super::value::SBValue;
use super::valuelist::SBValueList;
use super::variablesoptions::SBVariablesOptions;
use super::lldb_addr_t;
use sys;

/// One of the stack frames associated with a thread.
pub struct SBFrame {
    /// The underlying raw `SBFrameRef`.
    pub raw: sys::SBFrameRef,
}

impl SBFrame {
    /// Construct a new `SBFrame`.
    pub fn wrap(raw: sys::SBFrameRef) -> SBFrame {
        SBFrame { raw: raw }
    }

    /// Construct a new `Some(SBFrame)` or `None`.
    pub fn maybe_wrap(raw: sys::SBFrameRef) -> Option<SBFrame> {
        if unsafe { sys::SBFrameIsValid(raw) != 0 } {
            Some(SBFrame { raw: raw })
        } else {
            None
        }
    }

    /// Check whether or not this is a valid `SBFrame` value.
    pub fn is_valid(&self) -> bool {
        unsafe { sys::SBFrameIsValid(self.raw) != 0 }
    }

    /// The zero-based stack frame index for this frame.
    ///
    /// This can be used to locate adjacent frames in the
    /// thread's stack frames.
    pub fn frame_id(&self) -> u32 {
        unsafe { sys::SBFrameGetFrameID(self.raw) }
    }

    /// Get the Canonical Frame Address for this stack frame.
    ///
    /// This is the DWARF standard's definition of a CFA, a
    /// stack address that remains constant throughout the
    /// lifetime of the function.
    pub fn cfa(&self) -> Option<lldb_addr_t> {
        let cfa = unsafe { sys::SBFrameGetCFA(self.raw) };
        if cfa != u64::max_value() {
            Some(cfa)
        } else {
            None
        }
    }

    /// The program counter (PC) as an unsigned integer.
    pub fn pc(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetPC(self.raw) }
    }

    #[allow(missing_docs)]
    pub fn set_pc(&self, new_pc: lldb_addr_t) -> bool {
        unsafe { sys::SBFrameSetPC(self.raw, new_pc) != 0 }
    }

    /// The stack pointer address as an unsigned integer.
    pub fn sp(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetSP(self.raw) }
    }

    /// The frame pointer address as an unsigned integer.
    pub fn fp(&self) -> lldb_addr_t {
        unsafe { sys::SBFrameGetFP(self.raw) }
    }

    /// The program counter (PC) as a section offset address (`SBAddress`).
    pub fn pc_address(&self) -> SBAddress {
        SBAddress::wrap(unsafe { sys::SBFrameGetPCAddress(self.raw) })
    }

    /// The symbol context for this frame's current pc value.
    ///
    /// The frame maintains this symbol context and adds information to
    /// it as needed. This helps avoid repeated lookups of the same
    /// information.
    ///
    /// * `resolve_scope`: Flags that specify what type of symbol context
    ///   is needed by the caller. These flags have constants starting
    ///   with `SYMBOL_CONTEXT_ITEM_`.
    pub fn symbol_context(&self, resolve_scope: u32) -> SBSymbolContext {
        SBSymbolContext::wrap(unsafe { sys::SBFrameGetSymbolContext(self.raw, resolve_scope) })
    }

    /// The `SBModule` for this stack frame.
    pub fn module(&self) -> SBModule {
        SBModule::wrap(unsafe { sys::SBFrameGetModule(self.raw) })
    }

    /// The `SBCompileUnit` for this stack frame.
    pub fn compile_unit(&self) -> SBCompileUnit {
        SBCompileUnit::wrap(unsafe { sys::SBFrameGetCompileUnit(self.raw) })
    }

    /// The `SBFunction` for this stack frame.
    pub fn function(&self) -> SBFunction {
        SBFunction::wrap(unsafe { sys::SBFrameGetFunction(self.raw) })
    }

    /// The `SBSymbol` for this stack frame.
    pub fn symbol(&self) -> SBSymbol {
        SBSymbol::wrap(unsafe { sys::SBFrameGetSymbol(self.raw) })
    }

    /// Get the deepest block that contains the frame PC.
    pub fn block(&self) -> SBBlock {
        SBBlock::wrap(unsafe { sys::SBFrameGetBlock(self.raw) })
    }

    /// Get the appropriate function name for this frame. Inlined functions in
    /// LLDB are represented by blocks that have inlined function information, so
    /// just looking at the `SBFunction` or `SBSymbol` for a frame isn't enough.
    /// This function will return the appropriate function, symbol or inlined
    /// function name for the frame.
    ///
    /// This function returns:
    ///
    /// * the name of the inlined function (if there is one)
    /// * the name of the concrete function (if there is one)
    /// * the name of the symbol (if there is one)
    /// * NULL
    ///
    /// See also `is_inlined`.
    pub fn function_name(&self) -> Option<&str> {
        unsafe {
            match CStr::from_ptr(sys::SBFrameGetFunctionName(self.raw)).to_str() {
                Ok(s) => Some(s),
                _ => None,
            }
        }
    }

    #[allow(missing_docs)]
    pub fn display_function_name(&self) -> Option<&str> {
        unsafe {
            match CStr::from_ptr(sys::SBFrameGetDisplayFunctionName(self.raw)).to_str() {
                Ok(s) => Some(s),
                _ => None,
            }
        }
    }

    /// Return `true` if this frame represents an inlined function.
    pub fn is_inlined(&self) -> bool {
        unsafe { sys::SBFrameIsInlined(self.raw) != 0 }
    }

    /// Evaluate an expression within the context of this frame.
    pub fn evaluate_expression(&self, expression: &str, options: &SBExpressionOptions) -> SBValue {
        let expression = CString::new(expression).unwrap();
        SBValue::wrap(unsafe {
                          sys::SBFrameEvaluateExpression(self.raw, expression.as_ptr(), options.raw)
                      })
    }

    /// Gets the lexical block that defines the stack frame. Another way to think
    /// of this is it will return the block that contains all of the variables
    /// for a stack frame. Inlined functions are represented as `SBBlock` objects
    /// that have inlined function information: the name of the inlined function,
    /// where it was called from. The block that is returned will be the first
    /// block at or above the block for the PC (`SBFrame::block()`) that defines
    /// the scope of the frame. When a function contains no inlined functions,
    /// this will be the top most lexical block that defines the function.
    /// When a function has inlined functions and the PC is currently
    /// in one of those inlined functions, this method will return the inlined
    /// block that defines this frame. If the PC isn't currently in an inlined
    /// function, the lexical block that defines the function is returned.
    pub fn frame_block(&self) -> SBBlock {
        SBBlock::wrap(unsafe { sys::SBFrameGetFrameBlock(self.raw) })
    }

    /// The line table entry (`SBLineEntry`) for this stack frame.
    pub fn line_entry(&self) -> Option<SBLineEntry> {
        SBLineEntry::maybe_wrap(unsafe { sys::SBFrameGetLineEntry(self.raw) })
    }

    /// The thread that is executing this stack frame.
    pub fn thread(&self) -> SBThread {
        SBThread::wrap(unsafe { sys::SBFrameGetThread(self.raw) })
    }

    /// The disassembly of this function, presented as a string.
    pub fn disassemble(&self) -> &str {
        unsafe {
            match CStr::from_ptr(sys::SBFrameDisassemble(self.raw)).to_str() {
                Ok(s) => s,
                _ => panic!("Invalid string?"),
            }
        }
    }

    /// The values for variables matching the specified options.
    pub fn variables(&self, options: &SBVariablesOptions) -> SBValueList {
        SBValueList::wrap(unsafe { sys::SBFrameGetVariables(self.raw, options.raw) })
    }

    /// The values for all variables in this stack frame.
    pub fn all_variables(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(true);
        options.set_include_locals(true);
        options.set_include_statics(true);
        options.set_in_scope_only(true);
        self.variables(&options)
    }

    /// The values for the argument variables in this stack frame.
    pub fn arguments(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(true);
        options.set_include_locals(false);
        options.set_include_statics(false);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the local variables in this stack frame.
    pub fn locals(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(false);
        options.set_include_locals(true);
        options.set_include_statics(false);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the static variables in this stack frame.
    pub fn statics(&self) -> SBValueList {
        let options = SBVariablesOptions::new();
        options.set_include_arguments(false);
        options.set_include_locals(false);
        options.set_include_statics(true);
        options.set_in_scope_only(false);
        self.variables(&options)
    }

    /// The values for the CPU registers for this stack frame.
    pub fn registers(&self) -> SBValueList {
        SBValueList::wrap(unsafe { sys::SBFrameGetRegisters(self.raw) })
    }

    /// The value for a particular register, if present.
    pub fn find_register(&self, name: &str) -> Option<SBValue> {
        let name = CString::new(name).unwrap();
        SBValue::maybe_wrap(unsafe { sys::SBFrameFindRegister(self.raw, name.as_ptr()) })
    }

    /// The parent frame that invoked this frame, if available.
    pub fn parent_frame(&self) -> Option<SBFrame> {
        let thread = self.thread();
        let parent_idx = self.frame_id() + 1;
        if parent_idx < unsafe { sys::SBThreadGetNumFrames(thread.raw) } {
            SBFrame::maybe_wrap(unsafe { sys::SBThreadGetFrameAtIndex(thread.raw, parent_idx) })
        } else {
            None
        }
    }
}

impl fmt::Debug for SBFrame {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        let stream = SBStream::new();
        unsafe { sys::SBFrameGetDescription(self.raw, stream.raw) };
        write!(fmt, "SBFrame {{ {} }}", stream.data())
    }
}

impl Drop for SBFrame {
    fn drop(&mut self) {
        unsafe { sys::DisposeSBFrame(self.raw) };
    }
}

#[cfg(feature = "graphql")]
graphql_object!(SBFrame: super::debugger::SBDebugger | &self | {
    field is_valid() -> bool {
        self.is_valid()
    }

    // TODO(bm): This should be u32
    field frame_id() -> i64 {
        self.frame_id() as i64
    }

    // TODO(bm) This should be u64
    field cfa() -> Option<i64> {
        self.cfa().map(|i| i as i64)
    }

    // TODO(bm) This should be u64
    field pc() -> i64 {
        self.pc() as i64
    }

    // TODO(bm) This should be u64
    field sp() -> i64 {
        self.sp() as i64
    }

    // TODO(bm) This should be u64
    field fp() -> i64 {
        self.fp() as i64
    }

    field pc_address() -> SBAddress {
        self.pc_address()
    }

    field module() -> SBModule {
        self.module()
    }

    field compile_unit() -> SBCompileUnit {
        self.compile_unit()
    }

    field function() -> SBFunction {
        self.function()
    }

    field symbol() -> SBSymbol {
        self.symbol()
    }

    field block() -> SBBlock {
        self.block()
    }

    field function_name() -> Option<&str> {
        self.function_name()
    }

    field display_function_name() -> Option<&str> {
        self.display_function_name()
    }

    field is_inlined() -> bool {
        self.is_inlined()
    }

    field frame_block() -> SBBlock {
        self.frame_block()
    }

    field line_entry() -> Option<SBLineEntry> {
        self.line_entry()
    }

    field thread() -> SBThread {
        self.thread()
    }
});
<|endoftext|>


<commit_before>use std::fmt;
use std::io::{self, Write};

use config::Config;
use dep::ResolvedDep;
use error::CliResult;

pub type Nd = usize;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Ed(pub Nd, pub Nd);

impl Ed {
    pub fn label<W: Write>(&self, w: &mut W, dg: &DepGraph) -> io::Result<()> {
        use dep::DepKind::{Optional, Dev, Build};
        let parent = dg.get(self.0).unwrap().kind();
        let child = dg.get(self.1).unwrap().kind();

        match (parent, child) {
            (Build, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.build_lines),
            (Build, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Build, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Dev, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            _               => writeln!(w, "[label=\"\"];")
        }
    }
}

impl fmt::Display for Ed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let &Ed(il, ir) = self;
        write!(f, "N{} -> N{}", il, ir)
    }
}

#[derive(Debug)]
pub struct DepGraph<'c, 'o>
    where 'o: 'c
{
    pub nodes: Vec<ResolvedDep>,
    pub edges: Vec<Ed>,
    cfg: &'c Config<'o>,
}

impl<'c, 'o> DepGraph<'c, 'o> {
    pub fn new(cfg: &'c Config<'o>) -> Self {
        DepGraph {
            nodes: vec![],
            edges: vec![],
            cfg: cfg,
        }
    }

    pub fn add_child(&mut self, parent: usize, dep_name: &str, dep_ver: &str) -> usize {
        let idr = self.find_or_add(dep_name, dep_ver);
        self.edges.push(Ed(parent, idr));
        idr
    }

    pub fn get(&self, id: usize) -> Option<&ResolvedDep> {
        if id < self.nodes.len() {
            return Some(&self.nodes[id]);
        }
        None
    }

    pub fn remove(&mut self, id: usize) {
        debugln!("remove; name={}; index={}", name, id);
        self.nodes.remove(id);
        // Remove edges of the removed node.
        self.edges = self.edges.iter()
            .filter(|e| !(e.0 == id || e.1 == id))
            .cloned()
            .collect();
        self.shift_edges_after_node(id);
    }

    fn shift_edges_after_node(&mut self, id: usize) {
        enum Side {
            Left,
            Right,
        }
        let mut to_upd = vec![];
        for c in id..self.nodes.len() {
            for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == c { to_upd.push((eid, Side::Left, c-1)); }
                if idr == c { to_upd.push((eid, Side::Right, c-1)); }
            }
        }
        for (eid, side, new) in to_upd {
            match side {
                Side::Left => self.edges[eid].0 = new,
                Side::Right => self.edges[eid].1 = new,
            }
        }
    }

    pub fn remove_orphans(&mut self) {
        let len = self.nodes.len();
        self.edges.retain(|&Ed(idl,idr)| idl < len && idr < len);
        debugln!("remove_orphans; nodes={:?}", self.nodes);
        loop {
            let mut removed = false;
            let mut used = vec![false; self.nodes.len()];
            used[0] = true;
            for &Ed(_, idr) in &self.edges {
                debugln!("remove_orphans; idr={}", idr);
                used[idr] = true;
            }
            debugln!("remove_orphans; unsued_nodes={:?}", used);

            for (id, &u) in used.iter().enumerate() {
                if !u {
                    debugln!("remove_orphans; removing={}", id);
                    self.nodes.remove(id);

                    // Remove edges originating from the removed node
                    self.edges.retain(|&Ed(origin,_)| origin != id);
                    // Adjust edges to match the new node indexes
                    for edge in self.edges.iter_mut() {
                        if edge.0 > id {
                            edge.0 -= 1;
                        }
                        if edge.1 > id {
                            edge.1 -= 1;
                        }
                    }
                    removed = true;
                    break;
                }
            }
            if !removed {
                break;
            }
        }
    }

    fn remove_self_pointing(&mut self) {
        loop {
            let mut found = false;
            let mut self_p = vec![false; self.edges.len()];
            for (eid ,&Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == idr {
                    found = true;
                    self_p[eid] = true;
                    break;
                }
            }
            debugln!("remove_self_pointing; self_pointing={:?}", self_p);

            for (id, &u) in self_p.iter().enumerate() {
                if u {
                    debugln!("remove_self_pointing; removing={}", id);
                    self.edges.remove(id);
                    break;
                }
            }
            if !found {
                break;
            }
        }
    }

    pub fn find_or_add(&mut self, name: &str, ver: &str) -> usize {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == name && d.ver == ver {
                return i;
            }
        }
        self.nodes.push(ResolvedDep::new(name.to_owned(), ver.to_owned()));
        self.nodes.len() - 1
    }

    pub fn render_to<W: Write>(mut self, output: &mut W) -> CliResult<()> {
        debugln!("exec=render_to;");
        self.edges.sort();
        self.edges.dedup();
        self.remove_orphans();
        self.remove_self_pointing();
        debugln!("dg={:#?}", self);
        try!(writeln!(output, "{}", "digraph dependencies {"));
        for (i, dep) in self.nodes.iter().enumerate() {
            try!(write!(output, "\tN{}", i));
            try!(dep.label(output, self.cfg));
        }
        for ed in &self.edges {
            try!(write!(output, "\t{}", ed));
            try!(ed.label(output, &self));
        }
        try!(writeln!(output, "{}", "}"));
        Ok(())
    }

}
<commit_msg>Fix build error with --features debug<commit_after>use std::fmt;
use std::io::{self, Write};

use config::Config;
use dep::ResolvedDep;
use error::CliResult;

pub type Nd = usize;

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)]
pub struct Ed(pub Nd, pub Nd);

impl Ed {
    pub fn label<W: Write>(&self, w: &mut W, dg: &DepGraph) -> io::Result<()> {
        use dep::DepKind::{Optional, Dev, Build};
        let parent = dg.get(self.0).unwrap().kind();
        let child = dg.get(self.1).unwrap().kind();

        match (parent, child) {
            (Build, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.build_lines),
            (Build, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Build, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Optional, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.optional_lines),
            (Dev, Build) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Dev) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            (Dev, Optional) => writeln!(w, "[label=\"\"{}];", dg.cfg.dev_lines),
            _               => writeln!(w, "[label=\"\"];")
        }
    }
}

impl fmt::Display for Ed {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let &Ed(il, ir) = self;
        write!(f, "N{} -> N{}", il, ir)
    }
}

#[derive(Debug)]
pub struct DepGraph<'c, 'o>
    where 'o: 'c
{
    pub nodes: Vec<ResolvedDep>,
    pub edges: Vec<Ed>,
    cfg: &'c Config<'o>,
}

impl<'c, 'o> DepGraph<'c, 'o> {
    pub fn new(cfg: &'c Config<'o>) -> Self {
        DepGraph {
            nodes: vec![],
            edges: vec![],
            cfg: cfg,
        }
    }

    pub fn add_child(&mut self, parent: usize, dep_name: &str, dep_ver: &str) -> usize {
        let idr = self.find_or_add(dep_name, dep_ver);
        self.edges.push(Ed(parent, idr));
        idr
    }

    pub fn get(&self, id: usize) -> Option<&ResolvedDep> {
        if id < self.nodes.len() {
            return Some(&self.nodes[id]);
        }
        None
    }

    pub fn remove(&mut self, id: usize) {
        debugln!("remove; index={}", id);
        self.nodes.remove(id);
        // Remove edges of the removed node.
        self.edges = self.edges.iter()
            .filter(|e| !(e.0 == id || e.1 == id))
            .cloned()
            .collect();
        self.shift_edges_after_node(id);
    }

    fn shift_edges_after_node(&mut self, id: usize) {
        enum Side {
            Left,
            Right,
        }
        let mut to_upd = vec![];
        for c in id..self.nodes.len() {
            for (eid, &Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == c { to_upd.push((eid, Side::Left, c-1)); }
                if idr == c { to_upd.push((eid, Side::Right, c-1)); }
            }
        }
        for (eid, side, new) in to_upd {
            match side {
                Side::Left => self.edges[eid].0 = new,
                Side::Right => self.edges[eid].1 = new,
            }
        }
    }

    pub fn remove_orphans(&mut self) {
        let len = self.nodes.len();
        self.edges.retain(|&Ed(idl,idr)| idl < len && idr < len);
        debugln!("remove_orphans; nodes={:?}", self.nodes);
        loop {
            let mut removed = false;
            let mut used = vec![false; self.nodes.len()];
            used[0] = true;
            for &Ed(_, idr) in &self.edges {
                debugln!("remove_orphans; idr={}", idr);
                used[idr] = true;
            }
            debugln!("remove_orphans; unsued_nodes={:?}", used);

            for (id, &u) in used.iter().enumerate() {
                if !u {
                    debugln!("remove_orphans; removing={}", id);
                    self.nodes.remove(id);

                    // Remove edges originating from the removed node
                    self.edges.retain(|&Ed(origin,_)| origin != id);
                    // Adjust edges to match the new node indexes
                    for edge in self.edges.iter_mut() {
                        if edge.0 > id {
                            edge.0 -= 1;
                        }
                        if edge.1 > id {
                            edge.1 -= 1;
                        }
                    }
                    removed = true;
                    break;
                }
            }
            if !removed {
                break;
            }
        }
    }

    fn remove_self_pointing(&mut self) {
        loop {
            let mut found = false;
            let mut self_p = vec![false; self.edges.len()];
            for (eid ,&Ed(idl, idr)) in self.edges.iter().enumerate() {
                if idl == idr {
                    found = true;
                    self_p[eid] = true;
                    break;
                }
            }
            debugln!("remove_self_pointing; self_pointing={:?}", self_p);

            for (id, &u) in self_p.iter().enumerate() {
                if u {
                    debugln!("remove_self_pointing; removing={}", id);
                    self.edges.remove(id);
                    break;
                }
            }
            if !found {
                break;
            }
        }
    }

    pub fn find_or_add(&mut self, name: &str, ver: &str) -> usize {
        for (i, d) in self.nodes.iter().enumerate() {
            if d.name == name && d.ver == ver {
                return i;
            }
        }
        self.nodes.push(ResolvedDep::new(name.to_owned(), ver.to_owned()));
        self.nodes.len() - 1
    }

    pub fn render_to<W: Write>(mut self, output: &mut W) -> CliResult<()> {
        debugln!("exec=render_to;");
        self.edges.sort();
        self.edges.dedup();
        self.remove_orphans();
        self.remove_self_pointing();
        debugln!("dg={:#?}", self);
        try!(writeln!(output, "{}", "digraph dependencies {"));
        for (i, dep) in self.nodes.iter().enumerate() {
            try!(write!(output, "\tN{}", i));
            try!(dep.label(output, self.cfg));
        }
        for ed in &self.edges {
            try!(write!(output, "\t{}", ed));
            try!(ed.label(output, &self));
        }
        try!(writeln!(output, "{}", "}"));
        Ok(())
    }

}
<|endoftext|>