<commit_before>use crate::util;
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use validator::Validate;

#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, Validate)]
pub struct Config {
    #[serde(default)]
    #[validate]
    pub logging: crate::logging::config::Config,

    #[serde(default)]
    #[validate]
    pub serve: crate::serve::config::Config,

    #[serde(default)]
    #[validate]
    pub plugins: crate::plugins::config::Config,

    #[serde(default)]
    #[validate]
    pub upgrade: crate::upgrade::config::Config,
}

const CONFIG_FILE: &str = "config.toml";

/// Get the path of the configuration file
#[tracing::instrument]
fn path() -> Result<PathBuf> {
    #[cfg(not(test))]
    return Ok(util::dirs::config(true)?.join(CONFIG_FILE));

    // When running tests, avoid messing with users existing config
    #[cfg(test)]
    {
        let path = std::env::temp_dir()
            .join("stencila")
            .join("test")
            .join(CONFIG_FILE);
        fs::create_dir_all(path.parent().unwrap())?;
        Ok(path)
    }
}

/// Read a config from the configuration file
#[tracing::instrument]
pub fn read() -> Result<Config> {
    let config_file = path()?;

    let content = if config_file.exists() {
        match fs::read_to_string(config_file.clone()) {
            Ok(content) => content,
            // If there was a problem reading the config file warn the user
            // but return an empty string.
            Err(error) => {
                tracing::warn!("Error while reading config file: {}", error.to_string());
                String::new()
            }
        }
    } else {
        // If there is no config file just return an empty string
        String::new()
    };

    let config = match toml::from_str(content.as_str()) {
        Ok(config) => config,
        // If there was a problem reading the config file. e.g syntax error, a change
        // in the enum variants for an option, then warn the user and return the
        // defaults instead. This avoids a "corrupt" config file from preventing the
        // user from doing anything.
        Err(error) => {
            tracing::warn!(
                "Error while parsing config file; use `stencila config reset all` or edit {} to fix; defaults will be used: {}",
                config_file.display(),
                error.to_string()
            );
            Config {
                ..Default::default()
            }
        }
    };

    if let Err(errors) = config.validate() {
        tracing::error!(
            "Invalid config file; use `stencila config set`, `stencila config reset` or edit {} to fix.\n\n{}",
            config_file.display(),
            serde_json::to_string_pretty(&errors)?
        )
    }

    Ok(config)
}

/// Write a config to the configuration file
#[tracing::instrument]
fn write(config: &Config) -> Result<()> {
    let config_file = path()?;
    let mut file = fs::File::create(config_file)?;
    file.write_all(toml::to_string(&config)?.as_bytes())?;
    Ok(())
}

/// Ensure that a string is a valid JSON pointer
///
/// Replaces dots (`.`) with slashes (`/`) and ensures a
/// leading slash.
#[tracing::instrument]
pub fn json_pointer(pointer: &str) -> String {
    let pointer = pointer.replace(".", "/");
    if pointer.starts_with('/') {
        pointer
    } else {
        format!("/{}", pointer)
    }
}

/// Display a config property
#[tracing::instrument]
pub fn display(config: &Config, pointer: Option<String>) -> Result<String> {
    match pointer {
        None => Ok(toml::to_string(config)?),
        Some(pointer) => {
            let config = serde_json::to_value(config)?;
            if let Some(part) = config.pointer(json_pointer(&pointer).as_str()) {
                let json = serde_json::to_string(part)?;
                let part: toml::Value = serde_json::from_str(&json)?;
                let toml = toml::to_string(&part)?;
                Ok(toml)
            } else {
                bail!("No configuration value at pointer: {}", pointer)
            }
        }
    }
}

/// Set a config property
#[tracing::instrument]
pub fn set(config: &Config, pointer: &str, value: &str) -> Result<Config> {
    let mut config = serde_json::to_value(config)?;
    if let Some(property) = config.pointer_mut(json_pointer(&pointer).as_str()) {
        let value = match serde_json::from_str(&value) {
            Ok(value) => value,
            Err(_) => serde_json::Value::String(value.into()),
        };
        *property = value;
    } else {
        bail!("No configuration value at pointer: {}", pointer)
    };

    let config: Config = serde_json::from_value(config)?;
    if let Err(errors) = config.validate() {
        bail!(
            "Invalid configuration value/s:\n\n{}",
            serde_json::to_string_pretty(&errors)?
        )
    }
    Ok(config)
}

/// Reset a config property
#[tracing::instrument]
pub fn reset(config: &Config, property: &str) -> Result<Config> {
    Ok(match property {
        "all" => Default::default(),
        "logging" => Config {
            logging: Default::default(),
            ..config.clone()
        },
        "plugins" => Config {
            plugins: Default::default(),
            ..config.clone()
        },
        "serve" => Config {
            serve: Default::default(),
            ..config.clone()
        },
        "upgrade" => Config {
            upgrade: Default::default(),
            ..config.clone()
        },
        _ => bail!("No top level configuration property named: {}", property),
    })
}

/// CLI options for the `config` command
#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

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

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Get(Get),
        Set(Set),
        Reset(Reset),

        #[structopt(
            about = "Get the directories used for config, cache etc",
            setting = structopt::clap::AppSettings::ColoredHelp
        )]
        Dirs,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get configuration properties",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Get {
        /// A pointer to a config property e.g. `upgrade.auto`
        pub pointer: Option<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Set configuration properties",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Set {
        /// A pointer to a config property e.g. `upgrade.auto`
        pub pointer: String,

        /// The value to set the property to
        pub value: String,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Reset configuration properties to their defaults",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Reset {
        /// The config property to reset. Use 'all' to reset the entire config.
        pub property: String,
    }

    pub fn run(args: Args, config: &Config) -> Result<Config> {
        let Args { action } = args;
        match action {
            Action::Get(action) => {
                let Get { pointer } = action;
                println!("{}", super::display(config, pointer)?);
                Ok(config.clone())
            }
            Action::Set(action) => {
                let Set { pointer, value } = action;
                let config = super::set(config, &pointer, &value)?;
                write(&config)?;
                Ok(config)
            }
            Action::Reset(action) => {
                let Reset { property } = action;
                let config = super::reset(config, &property)?;
                write(&config)?;
                Ok(config)
            }
            Action::Dirs => {
                let config_dir = util::dirs::config(false)?.display().to_string();
                let logs_dir = util::dirs::logs(false)?.display().to_string();
                let plugins_dir = util::dirs::plugins(false)?.display().to_string();
                println!(
                    "config: {}\nlogs: {}\nplugins: {}",
                    config_dir, logs_dir, plugins_dir
                );
                Ok(config.clone())
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use super::*;

    #[test]
    fn test_path() -> Result<()> {
        let path = path()?;
        assert!(path.starts_with(std::env::temp_dir()));
        Ok(())
    }

    #[test]
    fn test_json_pointer() {
        assert_eq!(json_pointer("a"), "/a");
        assert_eq!(json_pointer("a/b"), "/a/b");
        assert_eq!(json_pointer("/a.b"), "/a/b");
        assert_eq!(json_pointer("a.b.c"), "/a/b/c");
    }

    #[test]
    fn test_display() -> Result<()> {
        let config = Config {
            ..Default::default()
        };

        let all = display(&config, None)?;
        assert!(all.contains("[serve]\n"));
        assert!(all.contains("[upgrade]\n"));

        let serve = display(&config, Some("serve".to_string()))?;
        assert!(!serve.contains("[serve]\n"));
        assert!(serve.contains("url = "));

        let upgrade = display(&config, Some("upgrade".to_string()))?;
        assert!(!upgrade.contains("[upgrade]\n"));
        assert!(upgrade.contains("auto = "));

        assert_eq!(
            display(&config, Some("foo.bar".to_string()))
                .unwrap_err()
                .to_string(),
            "No configuration value at pointer: foo.bar"
        );

        Ok(())
    }

    #[test]
    fn test_set() -> Result<()> {
        let config = Config {
            ..Default::default()
        };

        let result = set(&config, "upgrade.auto", "off")?;
        assert_eq!(result.upgrade.auto, "off");

        let result = set(&config, "upgrade.verbose", "true")?;
        assert_eq!(result.upgrade.verbose, true);

        assert_eq!(
            set(&config, "foo.bar", "baz").unwrap_err().to_string(),
            "No configuration value at pointer: foo.bar"
        );

        Ok(())
    }

    #[test]
    fn test_reset() -> Result<()> {
        let config = serde_json::from_value(json!({
            "upgrade": {
                "auto": "off",
                "verbose": true,
                "confirm": true,
            }
        }))?;

        let default = Config {
            ..Default::default()
        };

        let result = reset(&config, "upgrade")?;
        assert_eq!(result.upgrade.auto, default.upgrade.auto);
        assert_eq!(result.upgrade.verbose, default.upgrade.verbose);

        let result = reset(&config, "all")?;
        assert_eq!(result, default);

        assert_eq!(
            reset(&config, "foo").unwrap_err().to_string(),
            "No top level configuration property named: foo"
        );

        Ok(())
    }

    #[test]
    fn test_cli() -> Result<()> {
        use super::cli::*;

        let config = Config {
            ..Default::default()
        };

        run(
            Args {
                action: Action::Get(Get { pointer: None }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Set(Set {
                    pointer: "upgrade.confirm".to_string(),
                    value: "true".to_string(),
                }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Reset(Reset {
                    property: "upgrade".to_string(),
                }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Dirs,
            },
            &config,
        )?;

        Ok(())
    }
}
<commit_msg>feat(Config): Expose validate function<commit_after>use crate::util;
use anyhow::{bail, Result};
use serde::{Deserialize, Serialize};
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use validator::Validate;

#[derive(Debug, Default, PartialEq, Clone, Deserialize, Serialize, Validate)]
pub struct Config {
    #[serde(default)]
    #[validate]
    pub logging: crate::logging::config::Config,

    #[serde(default)]
    #[validate]
    pub serve: crate::serve::config::Config,

    #[serde(default)]
    #[validate]
    pub plugins: crate::plugins::config::Config,

    #[serde(default)]
    #[validate]
    pub upgrade: crate::upgrade::config::Config,
}

const CONFIG_FILE: &str = "config.toml";

/// Get the path of the configuration file
#[tracing::instrument]
fn path() -> Result<PathBuf> {
    #[cfg(not(test))]
    return Ok(util::dirs::config(true)?.join(CONFIG_FILE));

    // When running tests, avoid messing with users existing config
    #[cfg(test)]
    {
        let path = std::env::temp_dir()
            .join("stencila")
            .join("test")
            .join(CONFIG_FILE);
        fs::create_dir_all(path.parent().unwrap())?;
        Ok(path)
    }
}

/// Read a config from the configuration file
#[tracing::instrument]
pub fn read() -> Result<Config> {
    let config_file = path()?;

    let content = if config_file.exists() {
        match fs::read_to_string(config_file.clone()) {
            Ok(content) => content,
            // If there was a problem reading the config file warn the user
            // but return an empty string.
            Err(error) => {
                tracing::warn!("Error while reading config file: {}", error.to_string());
                String::new()
            }
        }
    } else {
        // If there is no config file just return an empty string
        String::new()
    };

    let config = match toml::from_str(content.as_str()) {
        Ok(config) => config,
        // If there was a problem reading the config file. e.g syntax error, a change
        // in the enum variants for an option, then warn the user and return the
        // defaults instead. This avoids a "corrupt" config file from preventing the
        // user from doing anything.
        Err(error) => {
            tracing::warn!(
                "Error while parsing config file; use `stencila config reset all` or edit {} to fix; defaults will be used: {}",
                config_file.display(),
                error.to_string()
            );
            Config {
                ..Default::default()
            }
        }
    };

    if let Err(errors) = config.validate() {
        tracing::error!(
            "Invalid config file; use `stencila config set`, `stencila config reset` or edit {} to fix.\n\n{}",
            config_file.display(),
            serde_json::to_string_pretty(&errors)?
        )
    }

    Ok(config)
}

/// Write a config to the configuration file
#[tracing::instrument]
pub fn write(config: &Config) -> Result<()> {
    let config_file = path()?;

    let mut file = fs::File::create(config_file)?;
    file.write_all(toml::to_string(&config)?.as_bytes())?;
    Ok(())
}

/// Ensure that a string is a valid JSON pointer
///
/// Replaces dots (`.`) with slashes (`/`) and ensures a
/// leading slash.
#[tracing::instrument]
pub fn json_pointer(pointer: &str) -> String {
    let pointer = pointer.replace(".", "/");
    if pointer.starts_with('/') {
        pointer
    } else {
        format!("/{}", pointer)
    }
}

/// Display a config property
#[tracing::instrument]
pub fn display(config: &Config, pointer: Option<String>) -> Result<String> {
    match pointer {
        None => Ok(toml::to_string(config)?),
        Some(pointer) => {
            let config = serde_json::to_value(config)?;
            if let Some(part) = config.pointer(json_pointer(&pointer).as_str()) {
                let json = serde_json::to_string(part)?;
                let part: toml::Value = serde_json::from_str(&json)?;
                let toml = toml::to_string(&part)?;
                Ok(toml)
            } else {
                bail!("No configuration value at pointer: {}", pointer)
            }
        }
    }
}

/// Validate a configuration
#[tracing::instrument]
pub fn validate(config: &Config) -> Result<()> {
    if let Err(errors) = config.validate() {
        bail!(
            "Invalid configuration value/s:\n\n{}",
            serde_json::to_string_pretty(&errors)?
        )
    } else {
        Ok(())
    }
}

/// Set a config property
#[tracing::instrument]
pub fn set(config: &Config, pointer: &str, value: &str) -> Result<Config> {
    let mut config = serde_json::to_value(config)?;
    if let Some(property) = config.pointer_mut(json_pointer(&pointer).as_str()) {
        let value = match serde_json::from_str(&value) {
            Ok(value) => value,
            Err(_) => serde_json::Value::String(value.into()),
        };
        *property = value;
    } else {
        bail!("No configuration value at pointer: {}", pointer)
    };

    let config: Config = serde_json::from_value(config)?;
    validate(&config)?;
    Ok(config)
}

/// Reset a config property
#[tracing::instrument]
pub fn reset(config: &Config, property: &str) -> Result<Config> {
    Ok(match property {
        "all" => Default::default(),
        "logging" => Config {
            logging: Default::default(),
            ..config.clone()
        },
        "plugins" => Config {
            plugins: Default::default(),
            ..config.clone()
        },
        "serve" => Config {
            serve: Default::default(),
            ..config.clone()
        },
        "upgrade" => Config {
            upgrade: Default::default(),
            ..config.clone()
        },
        _ => bail!("No top level configuration property named: {}", property),
    })
}

/// CLI options for the `config` command
#[cfg(feature = "cli")]
pub mod cli {
    use super::*;
    use structopt::StructOpt;

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

    #[derive(Debug, StructOpt)]
    #[structopt(
        setting = structopt::clap::AppSettings::DeriveDisplayOrder
    )]
    pub enum Action {
        Get(Get),
        Set(Set),
        Reset(Reset),

        #[structopt(
            about = "Get the directories used for config, cache etc",
            setting = structopt::clap::AppSettings::ColoredHelp
        )]
        Dirs,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Get configuration properties",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Get {
        /// A pointer to a config property e.g. `upgrade.auto`
        pub pointer: Option<String>,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Set configuration properties",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Set {
        /// A pointer to a config property e.g. `upgrade.auto`
        pub pointer: String,

        /// The value to set the property to
        pub value: String,
    }

    #[derive(Debug, StructOpt)]
    #[structopt(
        about = "Reset configuration properties to their defaults",
        setting = structopt::clap::AppSettings::ColoredHelp
    )]
    pub struct Reset {
        /// The config property to reset. Use 'all' to reset the entire config.
        pub property: String,
    }

    pub fn run(args: Args, config: &Config) -> Result<Config> {
        let Args { action } = args;
        match action {
            Action::Get(action) => {
                let Get { pointer } = action;
                println!("{}", super::display(config, pointer)?);
                Ok(config.clone())
            }
            Action::Set(action) => {
                let Set { pointer, value } = action;
                let config = super::set(config, &pointer, &value)?;
                write(&config)?;
                Ok(config)
            }
            Action::Reset(action) => {
                let Reset { property } = action;
                let config = super::reset(config, &property)?;
                write(&config)?;
                Ok(config)
            }
            Action::Dirs => {
                let config_dir = util::dirs::config(false)?.display().to_string();
                let logs_dir = util::dirs::logs(false)?.display().to_string();
                let plugins_dir = util::dirs::plugins(false)?.display().to_string();
                println!(
                    "config: {}\nlogs: {}\nplugins: {}",
                    config_dir, logs_dir, plugins_dir
                );
                Ok(config.clone())
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use super::*;

    #[test]
    fn test_path() -> Result<()> {
        let path = path()?;
        assert!(path.starts_with(std::env::temp_dir()));
        Ok(())
    }

    #[test]
    fn test_json_pointer() {
        assert_eq!(json_pointer("a"), "/a");
        assert_eq!(json_pointer("a/b"), "/a/b");
        assert_eq!(json_pointer("/a.b"), "/a/b");
        assert_eq!(json_pointer("a.b.c"), "/a/b/c");
    }

    #[test]
    fn test_display() -> Result<()> {
        let config = Config {
            ..Default::default()
        };

        let all = display(&config, None)?;
        assert!(all.contains("[serve]\n"));
        assert!(all.contains("[upgrade]\n"));

        let serve = display(&config, Some("serve".to_string()))?;
        assert!(!serve.contains("[serve]\n"));
        assert!(serve.contains("url = "));

        let upgrade = display(&config, Some("upgrade".to_string()))?;
        assert!(!upgrade.contains("[upgrade]\n"));
        assert!(upgrade.contains("auto = "));

        assert_eq!(
            display(&config, Some("foo.bar".to_string()))
                .unwrap_err()
                .to_string(),
            "No configuration value at pointer: foo.bar"
        );

        Ok(())
    }

    #[test]
    fn test_set() -> Result<()> {
        let config = Config {
            ..Default::default()
        };

        let result = set(&config, "upgrade.auto", "off")?;
        assert_eq!(result.upgrade.auto, "off");

        let result = set(&config, "upgrade.verbose", "true")?;
        assert_eq!(result.upgrade.verbose, true);

        assert_eq!(
            set(&config, "foo.bar", "baz").unwrap_err().to_string(),
            "No configuration value at pointer: foo.bar"
        );

        Ok(())
    }

    #[test]
    fn test_reset() -> Result<()> {
        let config = serde_json::from_value(json!({
            "upgrade": {
                "auto": "off",
                "verbose": true,
                "confirm": true,
            }
        }))?;

        let default = Config {
            ..Default::default()
        };

        let result = reset(&config, "upgrade")?;
        assert_eq!(result.upgrade.auto, default.upgrade.auto);
        assert_eq!(result.upgrade.verbose, default.upgrade.verbose);

        let result = reset(&config, "all")?;
        assert_eq!(result, default);

        assert_eq!(
            reset(&config, "foo").unwrap_err().to_string(),
            "No top level configuration property named: foo"
        );

        Ok(())
    }

    #[test]
    fn test_cli() -> Result<()> {
        use super::cli::*;

        let config = Config {
            ..Default::default()
        };

        run(
            Args {
                action: Action::Get(Get { pointer: None }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Set(Set {
                    pointer: "upgrade.confirm".to_string(),
                    value: "true".to_string(),
                }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Reset(Reset {
                    property: "upgrade".to_string(),
                }),
            },
            &config,
        )?;

        run(
            Args {
                action: Action::Dirs,
            },
            &config,
        )?;

        Ok(())
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use ::io::Write;
use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
            }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_ISR(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_IRR(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct PICs (PIC, PIC);

impl PICs {
    const fn new() -> Self {
        PICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    fn initialize(&mut self) {
        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = unsafe { (self.0.data_port.in8(), self.1.data_port.in8()) };

        // Send both PICs the 'initialize' command.
        self.0.initialize();
        self.1.initialize();

        // Each PIC then expects us to send it the following:
        self.0.data_port
            .write(&[ self.0.offset     // 1. the PIC's new vector offset
                    , 0x04              // 2. number to configure PIC cascading
                    , Command::Mode8086 as u8 // 3. command for 8086 mode
                    , saved_mask1       // 4. finally, the mask we saved earlier
                    ]).expect("Could not write to PIC 0 data port!");

        self.1.data_port
            .write(&[ self.1.offset
                    , 0x02              // PIC2 gets 0x02 to set it as follower
                    , Command::Mode8086 as u8
                    , saved_mask2
                    ]).expect("Could not write to PIC 1 data port!");
    }
}

impl IRQHandler for PICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<PICs> = Mutex::new(PICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub fn initialize() {
    PICS.lock()
        .initialize()
}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<commit_msg>Some changes to PICs Apparently we need to wait after each data port write<commit_after>//
//  SOS: the Stupid Operating System
//  by Hawk Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015 Hawk Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! Support for the 8259 Programmable Interrupt Controller.
//!
//! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests
//! and feeds them to the CPU in order. An x86 system typically has two PICs,
//! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15
//! unique IRQs, because one interrupt on the leader PIC is linked to interrupts
//! on the follower PIC.
//!
//! Mistakes were made, and Intel boneheadedly decided that it was a good idea
//! to map the vector offset of PIC1 to 8, so that it maps to interrupts
//! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers
//! used by CPU exceptions. Therefore, we must remap the PIC vectors so that
//! PIC1 starts at 32 and PIC2 at 40.

use ::io::Write;
use super::super::Port;
use spin::Mutex;
use core::mem::transmute;

/// Starting offset for PIC1
const OFFSET: u8 = 0x20;

/// Commands to send to the PIC
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
enum Command { Mode8086 = 0x01
             , Init     = 0x11
             , EndIRQ   = 0x20
             , ReadIRR  = 0x0a
             , ReadISR  = 0x0b
             }

/// List of IRQs on the x86.
///
/// See here for more info:
/// https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller
#[repr(u8)]
#[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)]
pub enum IRQ { /// System timer IRQ
               Timer        = 0 + OFFSET
             , /// PS/2 keyboard controller
               PS2Keyboard  = 1 + OFFSET
             , /// PIC2 cascade IRQ
               Cascade      = 2 + OFFSET
             , /// COM2 serial port
               COM2         = 3 + OFFSET
             , /// COM1 serial port
               COM1         = 4 + OFFSET
             , /// Line printer 2
               LPT2         = 5 + OFFSET
             , /// Floppy disc controller
               Floppy       = 6 + OFFSET
             , /// Line printer 1
               LPT1         = 7 + OFFSET
             , /// CMOS clock
               RTCTimer     = 8 + OFFSET
             , /// PS/2 mouse controller
               PS2Mouse     = 12 + OFFSET
             , /// Floating-point Coprocessor
               FPU          = 13 + OFFSET
             , /// ATA channel 1
               PrimaryATA   = 14 + OFFSET
             , /// ATA channel 2
               SecondaryATA = 15 + OFFSET
             }

struct PIC {
    /// The base offset to which interrupts on this PIC are mapped
    offset: u8
  , /// The port on the CPU that sends commands to this PIC.
    command_port: Port
  , /// The port that sends and recieves data from the PIC
    data_port: Port
}

impl PIC {

    const fn leader() -> PIC {
        unsafe {
            PIC { offset: OFFSET
                , command_port: Port::new(0x20)
                , data_port: Port::new(0x21)
                }
        }
    }

    const fn follower() -> PIC {
        unsafe {
            PIC { offset: OFFSET + 8
                , command_port: Port::new(0xA0)
                , data_port: Port::new(0xA1)
                }
        }
    }

    #[inline]
    fn is_leader(&self) -> bool {
        self.offset == OFFSET
    }

    #[inline]
    fn send_command(&self, command: Command) {
        unsafe {
            self.command_port
                .out8(command as u8)
        }
    }

    #[inline]
    fn send_data(&self, data: u8) {
        unsafe {
            self.data_port
                .out8(data)
        }
    }

    #[inline]
    fn initialize(&self) {
        self.send_command(Command::Init)
    }

    #[inline]
    fn read_ISR(&self) -> u8 {
        self.send_command(Command::ReadISR);
        unsafe { self.data_port.in8() }
    }

    #[inline]
    fn read_IRR(&self) -> u8 {
        self.send_command(Command::ReadIRR);
        unsafe { self.data_port.in8() }
    }

}

trait IRQHandler {
    /// Returns whether or not this handler handles the given IRQ
    fn handles(&self, irq: IRQ) -> bool;
    /// End an interrupt request
    fn end_interrupt(&self, irq: IRQ);
}

impl IRQHandler for PIC {

    fn handles(&self, irq: IRQ) -> bool {
        self.offset <= (irq as u8) && (irq as u8) < self.offset + 8
    }

    fn end_interrupt(&self, irq: IRQ) {
        self.send_command(Command::EndIRQ)
    }
}

/// A pair of PICs in cascade mode.
///
/// This is the standard configuration on all modern x86 systems.
struct PICs (PIC, PIC);

impl PICs {
    const fn new() -> Self {
        PICs (PIC::leader(), PIC::follower())
    }

    /// Initialize the system's PICs.
    fn initialize(&mut self) {
        let mut wait_port = unsafe { Port::new(0x80) };
        let mut wait = || unsafe { wait_port.out8(0); };
        // helper macro to avoid writing repetitive code
        macro_rules! send {
            (pic0 => $data:expr) => {
                self.0.send_data($data)
                wait();
            };
            (pic1 => $data:expr) => {
                self.1.send_data($data)
                wait();
            };
        }

        // Read the default interrupt masks from PIC1 and PIC2
        let (saved_mask1, saved_mask2)
            = unsafe { (self.0.data_port.in8(), self.1.data_port.in8()) };

        // Send both PICs the 'initialize' command.
        self.0.initialize(); wait();
        self.1.initialize(); wait();

        // Each PIC then expects us to send it the following:
        // 1. the PIC's new vector offset
        send!(pic0 => self.0.offset);
        send!(pic1 => self.1.offset);
        // 2. number to configure PIC cascading
        send!(pic0 => 0x04);
        send!(pic1 => 0x02);
        // 3. command for 8086 mode
        send!(pic0 => Command::Mode8086 as u8);
        send!(pic1 => Command::Mode8086 as u8);
        // 4. finally, the mask we saved earlier
        send!(pic0 => saved_mask1);
        send!(pic1 => saved_mask2);
    }
}

impl IRQHandler for PICs {

    fn handles(&self, irq: IRQ) -> bool {
        self.0.handles(irq) ||
        self.1.handles(irq)
    }

    fn end_interrupt(&self, irq: IRQ) {
        if self.1.handles(irq) {
            self.1.end_interrupt(irq);
        }
        self.0.end_interrupt(irq);
    }

}

/// Global PIC instance and mutex
static PICS: Mutex<PICs> = Mutex::new(PICs::new());

/// Initialize the system's Programmable Interrupt Controller
pub fn initialize() {
    PICS.lock()
        .initialize()
}

/// If an interrupt is being handled by the PICs, end that interrupt.
///
/// This is called by the interrupt handler at the end of all interrupts.
/// If the interrupt is not a PIC interrupt, it silently does nothing.
pub unsafe fn end_pic_interrupt(interrupt_id: u8) {
    let pics = PICS.lock();
    let irq: IRQ = transmute(interrupt_id);

    if pics.handles(irq) {
        pics.end_interrupt(irq)
    }
}
<|endoftext|>


<commit_before>use std::fmt::Display;
use std::str::FromStr;

use clap::builder::PossibleValue;
use clap::ValueEnum;

use crate::shells;
use crate::Generator;

/// Shell with auto-generated completion script available.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
    /// Bourne Again SHell (bash)
    Bash,
    /// Elvish shell
    Elvish,
    /// Friendly Interactive SHell (fish)
    Fish,
    /// PowerShell
    PowerShell,
    /// Z SHell (zsh)
    Zsh,
}

impl Display for Shell {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_possible_value()
            .expect("no values are skipped")
            .get_name()
            .fmt(f)
    }
}

impl FromStr for Shell {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        for variant in Self::value_variants() {
            if variant.to_possible_value().unwrap().matches(s, false) {
                return Ok(*variant);
            }
        }
        Err(format!("Invalid variant: {}", s))
    }
}

// Hand-rolled so it can work even when `derive` feature is disabled
impl ValueEnum for Shell {
    fn value_variants<'a>() -> &'a [Self] {
        &[
            Shell::Bash,
            Shell::Elvish,
            Shell::Fish,
            Shell::PowerShell,
            Shell::Zsh,
        ]
    }

    fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
        Some(match self {
            Shell::Bash => PossibleValue::new("bash"),
            Shell::Elvish => PossibleValue::new("elvish"),
            Shell::Fish => PossibleValue::new("fish"),
            Shell::PowerShell => PossibleValue::new("powershell"),
            Shell::Zsh => PossibleValue::new("zsh"),
        })
    }
}

impl Generator for Shell {
    fn file_name(&self, name: &str) -> String {
        match self {
            Shell::Bash => shells::Bash.file_name(name),
            Shell::Elvish => shells::Elvish.file_name(name),
            Shell::Fish => shells::Fish.file_name(name),
            Shell::PowerShell => shells::PowerShell.file_name(name),
            Shell::Zsh => shells::Zsh.file_name(name),
        }
    }

    fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
        match self {
            Shell::Bash => shells::Bash.generate(cmd, buf),
            Shell::Elvish => shells::Elvish.generate(cmd, buf),
            Shell::Fish => shells::Fish.generate(cmd, buf),
            Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
            Shell::Zsh => shells::Zsh.generate(cmd, buf),
        }
    }
}
<commit_msg>feat(complete): Support getting shell from env<commit_after>use std::fmt::Display;
use std::path::Path;
use std::str::FromStr;

use clap::builder::PossibleValue;
use clap::ValueEnum;

use crate::shells;
use crate::Generator;

/// Shell with auto-generated completion script available.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
#[non_exhaustive]
pub enum Shell {
    /// Bourne Again SHell (bash)
    Bash,
    /// Elvish shell
    Elvish,
    /// Friendly Interactive SHell (fish)
    Fish,
    /// PowerShell
    PowerShell,
    /// Z SHell (zsh)
    Zsh,
}

impl Display for Shell {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        self.to_possible_value()
            .expect("no values are skipped")
            .get_name()
            .fmt(f)
    }
}

impl FromStr for Shell {
    type Err = String;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        for variant in Self::value_variants() {
            if variant.to_possible_value().unwrap().matches(s, false) {
                return Ok(*variant);
            }
        }
        Err(format!("Invalid variant: {}", s))
    }
}

// Hand-rolled so it can work even when `derive` feature is disabled
impl ValueEnum for Shell {
    fn value_variants<'a>() -> &'a [Self] {
        &[
            Shell::Bash,
            Shell::Elvish,
            Shell::Fish,
            Shell::PowerShell,
            Shell::Zsh,
        ]
    }

    fn to_possible_value<'a>(&self) -> Option<PossibleValue> {
        Some(match self {
            Shell::Bash => PossibleValue::new("bash"),
            Shell::Elvish => PossibleValue::new("elvish"),
            Shell::Fish => PossibleValue::new("fish"),
            Shell::PowerShell => PossibleValue::new("powershell"),
            Shell::Zsh => PossibleValue::new("zsh"),
        })
    }
}

impl Generator for Shell {
    fn file_name(&self, name: &str) -> String {
        match self {
            Shell::Bash => shells::Bash.file_name(name),
            Shell::Elvish => shells::Elvish.file_name(name),
            Shell::Fish => shells::Fish.file_name(name),
            Shell::PowerShell => shells::PowerShell.file_name(name),
            Shell::Zsh => shells::Zsh.file_name(name),
        }
    }

    fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) {
        match self {
            Shell::Bash => shells::Bash.generate(cmd, buf),
            Shell::Elvish => shells::Elvish.generate(cmd, buf),
            Shell::Fish => shells::Fish.generate(cmd, buf),
            Shell::PowerShell => shells::PowerShell.generate(cmd, buf),
            Shell::Zsh => shells::Zsh.generate(cmd, buf),
        }
    }
}

impl Shell {
    /// Parse a shell from a path to the executable for the shell
    ///
    /// # Examples
    ///
    /// ```
    /// use clap_complete::shells::Shell;
    ///
    /// assert_eq!(Shell::from_shell_path("/bin/bash"), Some(Shell::Bash));
    /// assert_eq!(Shell::from_shell_path("/usr/bin/zsh"), Some(Shell::Zsh));
    /// assert_eq!(Shell::from_shell_path("/opt/my_custom_shell"), None);
    /// ```
    pub fn from_shell_path<P: AsRef<Path>>(path: P) -> Option<Shell> {
        parse_shell_from_path(path.as_ref())
    }

    /// Determine the user's current shell from the environment
    ///
    /// This will read the SHELL environment variable and try to determine which shell is in use
    /// from that.
    ///
    /// If SHELL is not set, then on windows, it will default to powershell, and on
    /// other OSes it will return `None`.
    ///
    /// If SHELL is set, but contains a value that doesn't correspond to one of the supported shell
    /// types, then return `None`.
    ///
    /// # Example:
    ///
    /// ```no_run
    /// # use clap::Command;
    /// use clap_complete::{generate, shells::Shell};
    /// # fn build_cli() -> Command {
    /// #     Command::new("compl")
    /// # }
    /// let mut cmd = build_cli();
    /// generate(Shell::from_env().unwrap_or(Shell::Bash), &mut cmd, "myapp", &mut std::io::stdout());
    /// ```
    pub fn from_env() -> Option<Shell> {
        if let Some(env_shell) = std::env::var_os("SHELL") {
            Shell::from_shell_path(env_shell)
        } else if cfg!(windows) {
            Some(Shell::PowerShell)
        } else {
            None
        }
    }
}

// use a separate function to avoid having to monomorphize the entire function due
// to from_shell_path being generic
fn parse_shell_from_path(path: &Path) -> Option<Shell> {
    let name = path.file_stem()?.to_str()?;
    match name {
        "bash" => Some(Shell::Bash),
        "zsh" => Some(Shell::Zsh),
        "fish" => Some(Shell::Fish),
        "elvish" => Some(Shell::Elvish),
        "powershell" | "powershell_ise" => Some(Shell::PowerShell),
        _ => None,
    }
}
<|endoftext|>


<commit_before>use super::write_file;
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use std::{fs, str};

const BINDING_CC_TEMPLATE: &'static str = include_str!("./templates/binding.cc");
const BINDING_GYP_TEMPLATE: &'static str = include_str!("./templates/binding.gyp");
const INDEX_JS_TEMPLATE: &'static str = include_str!("./templates/index.js");
const LIB_RS_TEMPLATE: &'static str = include_str!("./templates/lib.rs");
const BUILD_RS_TEMPLATE: &'static str = include_str!("./templates/build.rs");
const CARGO_TOML_TEMPLATE: &'static str = include_str!("./templates/cargo.toml");
const PACKAGE_JSON_TEMPLATE: &'static str = include_str!("./templates/package.json");
const PARSER_NAME_PLACEHOLDER: &'static str = "PARSER_NAME";
const CLI_VERSION_PLACEHOLDER: &'static str = "CLI_VERSION";
const CLI_VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn generate_binding_files(repo_path: &Path, language_name: &str) -> Result<()> {
    let bindings_dir = repo_path.join("bindings");

    let dashed_language_name = language_name.replace("_", "-");
    let dashed_language_name = dashed_language_name.as_str();

    // Generate rust bindings if needed.
    let rust_binding_dir = bindings_dir.join("rust");
    create_path(&rust_binding_dir, |path| create_dir(path))?;

    create_path(&rust_binding_dir.join("lib.rs").to_owned(), |path| {
        generate_file(path, LIB_RS_TEMPLATE, language_name)
    })?;

    create_path(&rust_binding_dir.join("build.rs").to_owned(), |path| {
        generate_file(path, BUILD_RS_TEMPLATE, language_name)
    })?;

    create_path(&repo_path.join("Cargo.toml").to_owned(), |path| {
        generate_file(path, CARGO_TOML_TEMPLATE, dashed_language_name)
    })?;

    // Generate node bindings
    let node_binding_dir = bindings_dir.join("node");
    let node_binding_dir_created = create_path(&node_binding_dir, |path| create_dir(path))?;

    create_path(&node_binding_dir.join("index.js").to_owned(), |path| {
        generate_file(path, INDEX_JS_TEMPLATE, language_name)
    })?;

    create_path(&node_binding_dir.join("binding.cc").to_owned(), |path| {
        generate_file(path, BINDING_CC_TEMPLATE, language_name)
    })?;

    if node_binding_dir_created {
        // Create binding.gyp, or update it with new binding path.
        let binding_gyp_path = repo_path.join("binding.gyp");
        if binding_gyp_path.exists() {
            eprintln!("Updating binding.gyp with new binding path");

            let binding_gyp = fs::read_to_string(&binding_gyp_path)
                .with_context(|| "Failed to read binding.gyp")?;
            let binding_gyp = binding_gyp.replace("src/binding.cc", "bindings/node/binding.cc");
            write_file(&binding_gyp_path, binding_gyp)?;
        } else {
            generate_file(&binding_gyp_path, BINDING_GYP_TEMPLATE, language_name)?;
        }
    }

    // Create package.json, or update it with new binding path.
    let package_json_path = repo_path.join("package.json");
    if package_json_path.exists() {
        let package_json_str = fs::read_to_string(&package_json_path)
            .with_context(|| "Failed to read package.json")?;
        let mut package_json =
            serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(&package_json_str)
                .with_context(|| "Failed to parse package.json")?;
        let package_json_main = package_json.get("main");
        let package_json_needs_update = package_json_main.map_or(true, |v| {
            let main_string = v.as_str();
            main_string == Some("index.js") || main_string == Some("./index.js")
        });
        if package_json_needs_update {
            eprintln!("Updating package.json with new binding path");

            package_json.insert(
                "main".to_string(),
                serde_json::Value::String("bindings/node".to_string()),
            );
            let mut package_json_str = serde_json::to_string_pretty(&package_json)?;
            package_json_str.push('\n');
            write_file(&package_json_path, package_json_str)?;
        }
    } else {
        generate_file(
            &package_json_path,
            PACKAGE_JSON_TEMPLATE,
            dashed_language_name,
        )?;
    }

    // Remove files from old node binding paths.
    let old_index_js_path = repo_path.join("index.js");
    let old_binding_cc_path = repo_path.join("src").join("binding.cc");
    if old_index_js_path.exists() {
        fs::remove_file(old_index_js_path).ok();
    }
    if old_binding_cc_path.exists() {
        fs::remove_file(old_binding_cc_path).ok();
    }

    Ok(())
}

fn generate_file(path: &Path, template: &str, language_name: &str) -> Result<()> {
    write_file(
        path,
        template
            .replace(PARSER_NAME_PLACEHOLDER, language_name)
            .replace(CLI_VERSION_PLACEHOLDER, CLI_VERSION),
    )
}

fn create_dir(path: &Path) -> Result<()> {
    fs::create_dir_all(&path)
        .with_context(|| format!("Failed to create {:?}", path.to_string_lossy()))
}

fn create_path<F>(path: &PathBuf, action: F) -> Result<bool>
where
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    }
    Ok(false)
}
<commit_msg>Add create_path_else fn to handle creation or modification<commit_after>use super::write_file;
use anyhow::{Context, Result};
use std::path::{Path, PathBuf};
use std::{fs, str};

const BINDING_CC_TEMPLATE: &'static str = include_str!("./templates/binding.cc");
const BINDING_GYP_TEMPLATE: &'static str = include_str!("./templates/binding.gyp");
const INDEX_JS_TEMPLATE: &'static str = include_str!("./templates/index.js");
const LIB_RS_TEMPLATE: &'static str = include_str!("./templates/lib.rs");
const BUILD_RS_TEMPLATE: &'static str = include_str!("./templates/build.rs");
const CARGO_TOML_TEMPLATE: &'static str = include_str!("./templates/cargo.toml");
const PACKAGE_JSON_TEMPLATE: &'static str = include_str!("./templates/package.json");
const PARSER_NAME_PLACEHOLDER: &'static str = "PARSER_NAME";
const CLI_VERSION_PLACEHOLDER: &'static str = "CLI_VERSION";
const CLI_VERSION: &'static str = env!("CARGO_PKG_VERSION");

pub fn generate_binding_files(repo_path: &Path, language_name: &str) -> Result<()> {
    let bindings_dir = repo_path.join("bindings");

    let dashed_language_name = language_name.replace("_", "-");
    let dashed_language_name = dashed_language_name.as_str();

    // Generate rust bindings if needed.
    let rust_binding_dir = bindings_dir.join("rust");
    create_path(&rust_binding_dir, |path| create_dir(path))?;

    create_path(&rust_binding_dir.join("lib.rs").to_owned(), |path| {
        generate_file(path, LIB_RS_TEMPLATE, language_name)
    })?;

    create_path(&rust_binding_dir.join("build.rs").to_owned(), |path| {
        generate_file(path, BUILD_RS_TEMPLATE, language_name)
    })?;

    create_path(&repo_path.join("Cargo.toml").to_owned(), |path| {
        generate_file(path, CARGO_TOML_TEMPLATE, dashed_language_name)
    })?;

    // Generate node bindings
    let node_binding_dir = bindings_dir.join("node");
    create_path(&node_binding_dir, |path| create_dir(path))?;

    create_path(&node_binding_dir.join("index.js").to_owned(), |path| {
        generate_file(path, INDEX_JS_TEMPLATE, language_name)
    })?;

    create_path(&node_binding_dir.join("binding.cc").to_owned(), |path| {
        generate_file(path, BINDING_CC_TEMPLATE, language_name)
    })?;

    // Create binding.gyp, or update it with new binding path.
    let binding_gyp_path = repo_path.join("binding.gyp");
    create_path_else(
        &binding_gyp_path,
        |path| generate_file(path, BINDING_GYP_TEMPLATE, language_name),
        |path| {
            eprintln!("Updating binding.gyp with new binding path");
            let binding_gyp =
                fs::read_to_string(path).with_context(|| "Failed to read binding.gyp")?;
            let binding_gyp = binding_gyp.replace("src/binding.cc", "bindings/node/binding.cc");
            write_file(path, binding_gyp)
        },
    )?;

    // Create package.json, or update it with new binding path.
    let package_json_path = repo_path.join("package.json");
    create_path_else(
        &package_json_path,
        |path| generate_file(path, PACKAGE_JSON_TEMPLATE, dashed_language_name),
        |path| {
            let package_json_str =
                fs::read_to_string(path).with_context(|| "Failed to read package.json")?;
            let mut package_json =
                serde_json::from_str::<serde_json::Map<String, serde_json::Value>>(
                    &package_json_str,
                )
                .with_context(|| "Failed to parse package.json")?;
            let package_json_main = package_json.get("main");
            let package_json_needs_update = package_json_main.map_or(true, |v| {
                let main_string = v.as_str();
                main_string == Some("index.js") || main_string == Some("./index.js")
            });
            if package_json_needs_update {
                eprintln!("Updating package.json with new binding path");

                package_json.insert(
                    "main".to_string(),
                    serde_json::Value::String("bindings/node".to_string()),
                );
                let mut package_json_str = serde_json::to_string_pretty(&package_json)?;
                package_json_str.push('\n');
                write_file(path, package_json_str)?;
            }
            Ok(())
        },
    )?;

    // Remove files from old node binding paths.
    let old_index_js_path = repo_path.join("index.js");
    let old_binding_cc_path = repo_path.join("src").join("binding.cc");
    if old_index_js_path.exists() {
        fs::remove_file(old_index_js_path).ok();
    }
    if old_binding_cc_path.exists() {
        fs::remove_file(old_binding_cc_path).ok();
    }

    Ok(())
}

fn generate_file(path: &Path, template: &str, language_name: &str) -> Result<()> {
    write_file(
        path,
        template
            .replace(PARSER_NAME_PLACEHOLDER, language_name)
            .replace(CLI_VERSION_PLACEHOLDER, CLI_VERSION),
    )
}

fn create_dir(path: &Path) -> Result<()> {
    fs::create_dir_all(&path)
        .with_context(|| format!("Failed to create {:?}", path.to_string_lossy()))
}

fn create_path<F>(path: &PathBuf, action: F) -> Result<bool>
where
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    }
    Ok(false)
}

fn create_path_else<T, F>(path: &PathBuf, action: T, else_action: F) -> Result<bool>
where
    T: Fn(&PathBuf) -> Result<()>,
    F: Fn(&PathBuf) -> Result<()>,
{
    if !path.exists() {
        action(path)?;
        return Ok(true);
    } else {
        else_action(path)?;
    }
    Ok(false)
}
<|endoftext|>


<commit_before>mod memory;
mod scheduler;
mod video;

pub use memory::GbaMemory;

use arm::Cpu;
use scheduler::Scheduler;
pub use video::{GbaVideo, SCREEN_HEIGHT, SCREEN_PIXEL_COUNT, SCREEN_WIDTH};

pub struct Gba {
    mem: GbaMemory,
    cpu: Cpu,
    video: GbaVideo,
    scheduler: Scheduler,
}

impl Gba {
    pub fn new() -> Gba {
        let scheduler = Scheduler::default();

        Gba {
            mem: GbaMemory::new(),
            cpu: Cpu::uninitialized(arm::Isa::Arm, arm::CpuMode::System),
            video: GbaVideo::new(scheduler.clone()),
            scheduler,
        }
    }

    pub fn reset(&mut self) {
        self.mem.init();
        self.video.init(&mut self.mem);
        self.cpu.branch(0x08000000, &mut self.mem);
    }

    pub fn set_gamepak(&mut self, cart: Vec<u8>) {
        self.mem.set_gamepak(cart);
    }

    pub fn frame(&mut self) {
        // wait until we are out of VBLANK
        while self.mem.ioregs.vblank() {
            self.step();
        }

        // Wait until the end of the frame (enter VBLANK)
        while !self.mem.ioregs.vblank() {
            self.step();
        }
    }

    pub fn step(&mut self) {
        let mut cycles = self.cpu.step(&mut self.mem);

        while let Some((event, next_cycles)) = self.scheduler.advance(cycles) {
            cycles = next_cycles;
            (event)(self, cycles);
        }
    }

    pub fn video(&self) -> &GbaVideo {
        &self.video
    }
}

impl Default for Gba {
    fn default() -> Self {
        Self::new()
    }
}

// Send should be safe to implement for the GBA because we never leak the RC's
// that are used by the GBA and its other parts.
unsafe impl Send for Gba {}
<commit_msg>Expose some things from the GBA.<commit_after>pub mod memory;
mod scheduler;
mod video;

pub use memory::GbaMemory;

use arm::Cpu;
use scheduler::Scheduler;
pub use video::{GbaVideo, SCREEN_HEIGHT, SCREEN_PIXEL_COUNT, SCREEN_WIDTH};

pub struct Gba {
    mem: GbaMemory,
    cpu: Cpu,
    video: GbaVideo,
    scheduler: Scheduler,
}

impl Gba {
    pub fn new() -> Gba {
        let scheduler = Scheduler::default();

        Gba {
            mem: GbaMemory::new(),
            cpu: Cpu::uninitialized(arm::Isa::Arm, arm::CpuMode::System),
            video: GbaVideo::new(scheduler.clone()),
            scheduler,
        }
    }

    pub fn reset(&mut self) {
        self.mem.init();
        self.video.init(&mut self.mem);
        self.cpu.branch(0x08000000, &mut self.mem);
    }

    pub fn set_gamepak(&mut self, cart: Vec<u8>) {
        self.mem.set_gamepak(cart);
    }

    pub fn frame(&mut self) {
        // wait until we are out of VBLANK
        while self.mem.ioregs.vblank() {
            self.step();
        }

        // Wait until the end of the frame (enter VBLANK)
        while !self.mem.ioregs.vblank() {
            self.step();
        }
    }

    pub fn step(&mut self) {
        let mut cycles = self.cpu.step(&mut self.mem);

        while let Some((event, next_cycles)) = self.scheduler.advance(cycles) {
            cycles = next_cycles;
            (event)(self, cycles);
        }
    }

    pub fn video(&self) -> &GbaVideo {
        &self.video
    }

    pub fn cpu(&self) -> &Cpu {
        &self.cpu
    }
}

impl Default for Gba {
    fn default() -> Self {
        Self::new()
    }
}

// Send should be safe to implement for the GBA because we never leak the RC's
// that are used by the GBA and its other parts.
unsafe impl Send for Gba {}
<|endoftext|>


<commit_before>use proc_macro::TokenStream;
use quote::quote;
use syn::{export::Span, parse_macro_input, Ident, ItemFn};

pub fn slot(attrs: TokenStream, input: TokenStream) -> TokenStream {
    let slot_type = parse_macro_input!(attrs as Ident);
    let input = parse_macro_input!(input as ItemFn);

    let args = input.sig.inputs.len();
    let arg_names = (0..(args - 1))
        .map(|i| Ident::new(&format!("arg{}", i), Span::call_site()))
        .collect::<Vec<_>>();

    let fn_name = &input.sig.ident;
    let slot_name = Ident::new(&format!("slot_{}", fn_name.to_string()), Span::call_site());
    let vis = &input.vis;

    let expanded = quote! {
        #input

        #vis unsafe fn #slot_name(self: &std::rc::Rc<Self>) -> qt_core::QBox<#slot_type> {
            let this = Rc::clone(&self);
            #slot_type::new(self.main_widget(), move |#(#arg_names),*| {
                this.#fn_name(#(#arg_names),*);
            })
        }
    };

    TokenStream::from(expanded)
}
<commit_msg>use StaticUpcast and Weak in slot macro<commit_after>use proc_macro::TokenStream;
use quote::quote;
use syn::{export::Span, parse_macro_input, Ident, ItemFn};

pub fn slot(attrs: TokenStream, input: TokenStream) -> TokenStream {
    let slot_type = parse_macro_input!(attrs as Ident);
    let input = parse_macro_input!(input as ItemFn);

    let args = input.sig.inputs.len();
    let arg_names = (0..(args - 1))
        .map(|i| Ident::new(&format!("arg{}", i), Span::call_site()))
        .collect::<Vec<_>>();

    let fn_name = &input.sig.ident;
    let slot_name = Ident::new(&format!("slot_{}", fn_name.to_string()), Span::call_site());
    let vis = &input.vis;

    let expanded = quote! {
        #input

        #vis unsafe fn #slot_name(self: &::std::rc::Rc<Self>) -> ::qt_core::QBox<#slot_type> {
            let this = ::std::rc::Rc::downgrade(&self);
            let parent = ::cpp_core::Ptr::from_raw(&**self);
            #slot_type::new(parent, move |#(#arg_names),*| {
                if let Some(this) = ::std::rc::Weak::upgrade(&this) {
                    this.#fn_name(#(#arg_names),*);
                }
            })
        }
    };

    TokenStream::from(expanded)
}
<|endoftext|>


<commit_before>use libc::{c_double, c_void};
use std::cmp::{Ordering};
use std::sync::{Once, ONCE_INIT};

static EXACTINIT: Once = ONCE_INIT;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point2D {
    x: c_double,
    y: c_double,
}

impl Point2D {
    pub fn new(x: c_double, y: c_double) -> Point2D {
        Point2D { x: x, y: y }
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point3D {
    x: c_double,
    y: c_double,
    z: c_double,
}

impl Point3D {
    pub fn new(x: c_double, y: c_double, z: c_double) -> Point3D {
        Point3D { x: x, y: y, z: z }
    }
}

#[link(name = "predicates")]
extern "C" {
    fn exactinit() -> c_void;
    fn orient2d(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D) -> c_double;
    fn incircle(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D, pd: *const Point2D) -> c_double;
    fn orient3d(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D) -> c_double;
    fn insphere(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D, pe: *const Point3D) -> c_double;
}

fn init_predicates() {
    EXACTINIT.call_once(|| {
        unsafe { exactinit() };
    });
}

#[derive(Debug, Eq, PartialEq)]
pub enum InCircleLocation {
    Inside,
    Outside,
    On
}

#[derive(Debug, Eq, PartialEq)]
pub enum Orientation {
    Positive,
    Negative
}

impl Orientation {
    pub fn to_f64_multiplier(&self) -> f64 {
        match *self {
            Orientation::Positive => {  1.0 },
            Orientation::Negative => { -1.0 }
        }
    }
}

pub trait InCircleTestable<P> {
    fn in_circle_test(&self, point: &P) -> Option<InCircleLocation>;
}

pub trait Orientable {
    fn orientation(&self) -> Option<Orientation>;
}

pub struct Triangle<P> {
    p1: P,
    p2: P,
    p3: P,
}

impl<P> Triangle<P> {
    pub fn new(p1: P, p2: P, p3: P) -> Triangle<P> {
        Triangle {
            p1: p1,
            p2: p2,
            p3: p3,
        }
    }
}

pub struct Tetrahedron<P> {
    p1: P,
    p2: P,
    p3: P,
    p4: P,
}

impl<P> Tetrahedron<P> {
    pub fn new(p1: P, p2: P, p3: P, p4: P) -> Tetrahedron<P> {
        Tetrahedron {
            p1: p1,
            p2: p2,
            p3: p3,
            p4: p4,
        }
    }
}

fn det_to_in_circle_location(det: f64) -> Option<InCircleLocation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(InCircleLocation::Inside) }
        Some(Ordering::Less) => { Some(InCircleLocation::Outside) }
        Some(Ordering::Equal) => { Some(InCircleLocation::On) }
        None => { None }
    }
}

fn det_to_orientation(det: f64) -> Option<Orientation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(Orientation::Positive) }
        Some(Ordering::Less) => { Some(Orientation::Negative) }
        _ => { None }
    }
}

impl Orientable for Triangle<Point2D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient2d(&self.p1, &self.p2, &self.p3) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point2D> for Triangle<Point2D> {
    fn in_circle_test(&self, point: &Point2D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { incircle(&self.p1, &self.p2, &self.p3, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

impl Orientable for Tetrahedron<Point3D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient3d(&self.p1, &self.p2, &self.p3, &self.p4) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point3D> for Tetrahedron<Point3D> {
    fn in_circle_test(&self, point: &Point3D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { insphere(&self.p1, &self.p2, &self.p3, &self.p4, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

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

    use std::f64;
    use std::f64::consts;
    use rand;
    use quickcheck::{TestResult, quickcheck, QuickCheck, StdGen};

    #[test]
    fn in_circle_2d() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);
        let d_inside = Point2D::new(0.5, 0.5);
        let d_outside = Point2D::new(2.0, 0.0);
        let d_on = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

    #[test]
    fn in_circle_2d_triangle_constructed_from_circle() {
        fn in_circle_2d_triangle_constructed_from_circle(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};
            fn to_pnt(center: (f64, f64), radius: f64, angle: f64) -> Point2D {
                let x = center.0 + radius * angle.cos();
                let y = center.1 + radius * angle.sin();
                Point2D::new(x, y)
            }
            let p1 = to_pnt(center, radius, angles.0);
            let p2 = to_pnt(center, radius, angles.1);
            let p3 = to_pnt(center, radius, angles.2);
            let p_center = Point2D::new(center.0, center.1);

            let triangle = Triangle::new(p1, p2, p3);

            TestResult::from_bool(triangle.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        quickcheck(in_circle_2d_triangle_constructed_from_circle as fn(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult)
    }

    #[test]
    fn in_circle_2d_tetrahedron_constructed_from_sphere() {
        const DOMAIN: usize = 100;
        fn in_circle_2d_tetrahedron_constructed_from_sphere(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};
            fn to_pnt(center: (f64, f64, f64), radius: f64, angle_inputs: (f64, f64)) -> Point3D {
                let domain_f64 = DOMAIN as f64;
                let u = 0.5 * (angle_inputs.0 / domain_f64 + 1.0); // transform to random variable on [0, 1]
                let v = angle_inputs.1 / domain_f64; // transform to random variable on [-1, 1]
                let theta = 2.0 * consts::PI * u;
                let phi = v.acos();
                let x = center.0 + radius * theta.cos() * phi.sin();
                let y = center.1 + radius * theta.sin() * phi.sin();
                let z = center.2 + radius * phi.cos();
                Point3D::new(x, y, z)
            }
            let p1 = to_pnt(center, radius, angle_inputs.0);
            let p2 = to_pnt(center, radius, angle_inputs.1);
            let p3 = to_pnt(center, radius, angle_inputs.2);
            let p4 = to_pnt(center, radius, angle_inputs.3);
            let p_center = Point3D::new(center.0, center.1, center.2);

            let tetrahedron = Tetrahedron::new(p1, p2, p3, p4);

            TestResult::from_bool(tetrahedron.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        QuickCheck::new().gen(StdGen::new(rand::thread_rng(), DOMAIN)).quickcheck(
            in_circle_2d_tetrahedron_constructed_from_sphere as fn(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult
        )
    }

    #[test]
    fn in_circle_2d_nan_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NAN);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_nan_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NAN);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NEG_INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NEG_INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_3d() {
        let p1 = Point3D::new(-1.0,  1.0, -1.0);
        let p2 = Point3D::new( 1.0,  1.0, -1.0);
        let p3 = Point3D::new( 0.0, -1.0, -1.0);
        let p4 = Point3D::new( 0.0,  0.0,  1.0);
        let d_inside = Point3D::new(0.0, 0.0, 0.0);
        let d_outside = Point3D::new(10.0, 10.0, 10.0);
        let d_on = Point3D::new(0.0, 0.0, 1.0);

        let t = Tetrahedron::new(p1, p2, p3, p4);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

}
<commit_msg>Add a few comments<commit_after>use libc::{c_double, c_void};
use std::cmp::{Ordering};
use std::sync::{Once, ONCE_INIT};

static EXACTINIT: Once = ONCE_INIT;

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point2D {
    x: c_double,
    y: c_double,
}

impl Point2D {
    pub fn new(x: c_double, y: c_double) -> Point2D {
        Point2D { x: x, y: y }
    }
}

#[repr(C)]
#[derive(Debug, Clone, PartialEq)]
pub struct Point3D {
    x: c_double,
    y: c_double,
    z: c_double,
}

impl Point3D {
    pub fn new(x: c_double, y: c_double, z: c_double) -> Point3D {
        Point3D { x: x, y: y, z: z }
    }
}

#[link(name = "predicates")]
extern "C" {
    fn exactinit() -> c_void;
    fn orient2d(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D) -> c_double;
    fn incircle(pa: *const Point2D, pb: *const Point2D, pc: *const Point2D, pd: *const Point2D) -> c_double;
    fn orient3d(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D) -> c_double;
    fn insphere(pa: *const Point3D, pb: *const Point3D, pc: *const Point3D, pd: *const Point3D, pe: *const Point3D) -> c_double;
}

fn init_predicates() {
    EXACTINIT.call_once(|| {
        unsafe { exactinit() };
    });
}

#[derive(Debug, Eq, PartialEq)]
pub enum InCircleLocation {
    Inside,
    Outside,
    On
}

#[derive(Debug, Eq, PartialEq)]
pub enum Orientation {
    Positive,
    Negative
}

impl Orientation {
    pub fn to_f64_multiplier(&self) -> f64 {
        match *self {
            Orientation::Positive => {  1.0 },
            Orientation::Negative => { -1.0 }
        }
    }
}

pub trait InCircleTestable<P> {
    fn in_circle_test(&self, point: &P) -> Option<InCircleLocation>;
}

pub trait Orientable {
    fn orientation(&self) -> Option<Orientation>;
}

pub struct Triangle<P> {
    p1: P,
    p2: P,
    p3: P,
}

impl<P> Triangle<P> {
    pub fn new(p1: P, p2: P, p3: P) -> Triangle<P> {
        Triangle {
            p1: p1,
            p2: p2,
            p3: p3,
        }
    }
}

pub struct Tetrahedron<P> {
    p1: P,
    p2: P,
    p3: P,
    p4: P,
}

impl<P> Tetrahedron<P> {
    pub fn new(p1: P, p2: P, p3: P, p4: P) -> Tetrahedron<P> {
        Tetrahedron {
            p1: p1,
            p2: p2,
            p3: p3,
            p4: p4,
        }
    }
}

fn det_to_in_circle_location(det: f64) -> Option<InCircleLocation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(InCircleLocation::Inside) }
        Some(Ordering::Less) => { Some(InCircleLocation::Outside) }
        Some(Ordering::Equal) => { Some(InCircleLocation::On) }
        None => { None }
    }
}

fn det_to_orientation(det: f64) -> Option<Orientation> {
    match 0.0.partial_cmp(&det) {
        Some(Ordering::Greater) => { Some(Orientation::Positive) }
        Some(Ordering::Less) => { Some(Orientation::Negative) }
        _ => { None }
    }
}

impl Orientable for Triangle<Point2D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient2d(&self.p1, &self.p2, &self.p3) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point2D> for Triangle<Point2D> {
    fn in_circle_test(&self, point: &Point2D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { incircle(&self.p1, &self.p2, &self.p3, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

impl Orientable for Tetrahedron<Point3D> {
    fn orientation(&self) -> Option<Orientation> {
        init_predicates();

        let orientation_det = unsafe { orient3d(&self.p1, &self.p2, &self.p3, &self.p4) };

        det_to_orientation(orientation_det)
    }
}

impl InCircleTestable<Point3D> for Tetrahedron<Point3D> {
    fn in_circle_test(&self, point: &Point3D) -> Option<InCircleLocation> {
        init_predicates();

        let orienation_multiplier: f64 = match self.orientation() {
            Some(p) => { p.to_f64_multiplier() },
            None => { return None; }
        };

        let incircle_det = unsafe { insphere(&self.p1, &self.p2, &self.p3, &self.p4, point) };

        det_to_in_circle_location(orienation_multiplier * incircle_det)
    }
}

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

    use std::f64;
    use std::f64::consts;
    use rand;
    use quickcheck::{TestResult, quickcheck, QuickCheck, StdGen};

    #[test]
    fn in_circle_2d() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);
        let d_inside = Point2D::new(0.5, 0.5);
        let d_outside = Point2D::new(2.0, 0.0);
        let d_on = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

    #[test]
    fn in_circle_2d_triangle_constructed_from_circle() {
        fn in_circle_2d_triangle_constructed_from_circle(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};
            fn to_pnt(center: (f64, f64), radius: f64, angle: f64) -> Point2D {
                let x = center.0 + radius * angle.cos();
                let y = center.1 + radius * angle.sin();
                Point2D::new(x, y)
            }
            let p1 = to_pnt(center, radius, angles.0);
            let p2 = to_pnt(center, radius, angles.1);
            let p3 = to_pnt(center, radius, angles.2);
            let p_center = Point2D::new(center.0, center.1);

            let triangle = Triangle::new(p1, p2, p3);

            TestResult::from_bool(triangle.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        quickcheck(in_circle_2d_triangle_constructed_from_circle as fn(center: (f64, f64), radius: f64, angles: (f64, f64, f64)) -> TestResult)
    }

    #[test]
    fn in_circle_2d_tetrahedron_constructed_from_sphere() {
        const DOMAIN: usize = 100;
        fn in_circle_2d_tetrahedron_constructed_from_sphere(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult {
            if radius <= 0.0 { return TestResult::discard()};

            /// Generate a point on the sphere given by the "spherical" input coordinates.
            /// angle_inputs should be on the interval [-DOMAIN, DOMAIN], as converting to real
            /// spherical coordinates is also done by this function.
            /// See e.g. http://mathworld.wolfram.com/SpherePointPicking.html how uniform sampling
            /// on a sphere works.
            fn to_pnt(center: (f64, f64, f64), radius: f64, angle_inputs: (f64, f64)) -> Point3D {
                let domain_f64 = DOMAIN as f64;

                // Transform input to random variable on [0, 1]
                let u = 0.5 * (angle_inputs.0 / domain_f64 + 1.0);
                // Transform other input to random variable on [-1, 1]
                let v = angle_inputs.1 / domain_f64;

                let theta = 2.0 * consts::PI * u;
                let phi = v.acos();

                let x = center.0 + radius * theta.cos() * phi.sin();
                let y = center.1 + radius * theta.sin() * phi.sin();
                let z = center.2 + radius * phi.cos();

                Point3D::new(x, y, z)
            }

            let p1 = to_pnt(center, radius, angle_inputs.0);
            let p2 = to_pnt(center, radius, angle_inputs.1);
            let p3 = to_pnt(center, radius, angle_inputs.2);
            let p4 = to_pnt(center, radius, angle_inputs.3);
            let p_center = Point3D::new(center.0, center.1, center.2);

            let tetrahedron = Tetrahedron::new(p1, p2, p3, p4);

            TestResult::from_bool(tetrahedron.in_circle_test(&p_center) == Some(InCircleLocation::Inside))
        }
        QuickCheck::new().gen(StdGen::new(rand::thread_rng(), DOMAIN)).quickcheck(
            in_circle_2d_tetrahedron_constructed_from_sphere as fn(center: (f64, f64, f64), radius: f64, angle_inputs: ((f64, f64), (f64, f64), (f64, f64), (f64, f64))) -> TestResult
        )
    }

    #[test]
    fn in_circle_2d_nan_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NAN);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_nan_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NAN);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_test_point() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, 1.0);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, f64::NEG_INFINITY);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_2d_neg_infinity_in_triangle() {
        let p1 = Point2D::new(0.0, 0.0);
        let p2 = Point2D::new(0.0, f64::NEG_INFINITY);
        let p3 = Point2D::new(1.0, 1.0);

        let d = Point2D::new(1.0, 1.0);

        let t = Triangle::new(p1, p2, p3);

        assert_eq!(t.in_circle_test(&d), None);
    }

    #[test]
    fn in_circle_3d() {
        let p1 = Point3D::new(-1.0,  1.0, -1.0);
        let p2 = Point3D::new( 1.0,  1.0, -1.0);
        let p3 = Point3D::new( 0.0, -1.0, -1.0);
        let p4 = Point3D::new( 0.0,  0.0,  1.0);
        let d_inside = Point3D::new(0.0, 0.0, 0.0);
        let d_outside = Point3D::new(10.0, 10.0, 10.0);
        let d_on = Point3D::new(0.0, 0.0, 1.0);

        let t = Tetrahedron::new(p1, p2, p3, p4);

        assert_eq!(t.in_circle_test(&d_inside), Some(InCircleLocation::Inside));
        assert_eq!(t.in_circle_test(&d_outside), Some(InCircleLocation::Outside));
        assert_eq!(t.in_circle_test(&d_on), Some(InCircleLocation::On));
    }

}
<|endoftext|>