<commit_before>use std::{
    env, fmt, fs,
    io::{Read, Write},
    path::{Path, PathBuf},
    time::Duration,
};

use anyhow::{bail, ensure, Context, Result};
use app_dirs::{get_app_root, AppDataType};
use log::debug;
use serde_derive::{Deserialize, Serialize};
use yansi::{Color, Style};

use crate::types::PathSource;

pub const CONFIG_FILE_NAME: &str = "config.toml";
pub const MAX_CACHE_AGE: Duration = Duration::from_secs(2_592_000); // 30 days
const DEFAULT_UPDATE_INTERVAL_HOURS: u64 = MAX_CACHE_AGE.as_secs() / 3600; // 30 days

fn default_underline() -> bool {
    false
}

fn default_bold() -> bool {
    false
}

fn default_italic() -> bool {
    false
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Magenta,
    Purple, // Backwards compatibility with ansi_term (until tealdeer 1.5.0)
    Cyan,
    White,
    Ansi(u8),
    Rgb { r: u8, g: u8, b: u8 },
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Self {
        match raw_color {
            RawColor::Black => Self::Black,
            RawColor::Red => Self::Red,
            RawColor::Green => Self::Green,
            RawColor::Yellow => Self::Yellow,
            RawColor::Blue => Self::Blue,
            RawColor::Magenta | RawColor::Purple => Self::Magenta,
            RawColor::Cyan => Self::Cyan,
            RawColor::White => Self::White,
            RawColor::Ansi(num) => Self::Fixed(num),
            RawColor::Rgb { r, g, b } => Self::RGB(r, g, b),
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    #[serde(default = "default_underline")]
    pub underline: bool,
    #[serde(default = "default_bold")]
    pub bold: bool,
    #[serde(default = "default_italic")]
    pub italic: bool,
}

#[allow(clippy::derivable_impls)] // Explicitly control defaults
impl Default for RawStyle {
    fn default() -> Self {
        Self {
            foreground: None,
            background: None,
            underline: false,
            bold: false,
            italic: false,
        }
    }
}

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Self {
        let mut style = Self::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.bg(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        if raw_style.italic {
            style = style.italic();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    #[serde(default)]
    pub description: RawStyle,
    #[serde(default)]
    pub command_name: RawStyle,
    #[serde(default)]
    pub example_text: RawStyle,
    #[serde(default)]
    pub example_code: RawStyle,
    #[serde(default)]
    pub example_variable: RawStyle,
}

impl From<RawStyleConfig> for StyleConfig {
    fn from(raw_style_config: RawStyleConfig) -> Self {
        Self {
            command_name: raw_style_config.command_name.into(),
            description: raw_style_config.description.into(),
            example_text: raw_style_config.example_text.into(),
            example_code: raw_style_config.example_code.into(),
            example_variable: raw_style_config.example_variable.into(),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDisplayConfig {
    #[serde(default)]
    pub compact: bool,
    #[serde(default)]
    pub use_pager: bool,
}

impl From<RawDisplayConfig> for DisplayConfig {
    fn from(raw_display_config: RawDisplayConfig) -> Self {
        Self {
            compact: raw_display_config.compact,
            use_pager: raw_display_config.use_pager,
        }
    }
}

/// Serde doesn't support default values yet (tracking issue:
/// <https://github.com/serde-rs/serde/issues/368>), so we need to wrap
/// `DEFAULT_UPDATE_INTERVAL_HOURS` in a function to be able to use
/// `#[serde(default = ...)]`
const fn default_auto_update_interval_hours() -> u64 {
    DEFAULT_UPDATE_INTERVAL_HOURS
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawUpdatesConfig {
    #[serde(default)]
    pub auto_update: bool,
    #[serde(default = "default_auto_update_interval_hours")]
    pub auto_update_interval_hours: u64,
}

impl Default for RawUpdatesConfig {
    fn default() -> Self {
        Self {
            auto_update: false,
            auto_update_interval_hours: DEFAULT_UPDATE_INTERVAL_HOURS,
        }
    }
}

impl From<RawUpdatesConfig> for UpdatesConfig {
    fn from(raw_updates_config: RawUpdatesConfig) -> Self {
        Self {
            auto_update: raw_updates_config.auto_update,
            auto_update_interval: Duration::from_secs(
                raw_updates_config.auto_update_interval_hours * 3600,
            ),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDirectoriesConfig {
    #[serde(default)]
    pub cache_dir: Option<PathBuf>,
    #[serde(default)]
    pub custom_pages_dir: Option<PathBuf>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default)]
struct RawConfig {
    style: RawStyleConfig,
    display: RawDisplayConfig,
    updates: RawUpdatesConfig,
    directories: RawDirectoriesConfig,
}

impl RawConfig {
    fn new() -> Self {
        Self::default()
    }
}

impl Default for RawConfig {
    fn default() -> Self {
        let mut raw_config = RawConfig {
            style: RawStyleConfig::default(),
            display: RawDisplayConfig::default(),
            updates: RawUpdatesConfig::default(),
            directories: RawDirectoriesConfig::default(),
        };

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;

        raw_config
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DisplayConfig {
    pub compact: bool,
    pub use_pager: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct UpdatesConfig {
    pub auto_update: bool,
    pub auto_update_interval: Duration,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PathWithSource {
    pub path: PathBuf,
    pub source: PathSource,
}

impl PathWithSource {
    pub fn path(&self) -> &Path {
        &self.path
    }
}

impl fmt::Display for PathWithSource {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} ({})", self.path.display(), self.source)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DirectoriesConfig {
    pub cache_dir: PathWithSource,
    pub custom_pages_dir: Option<PathWithSource>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Config {
    pub style: StyleConfig,
    pub display: DisplayConfig,
    pub updates: UpdatesConfig,
    pub directories: DirectoriesConfig,
}

impl Config {
    /// Convert a `RawConfig` to a high-level `Config`.
    ///
    /// For this, some values need to be converted to other types and some
    /// defaults need to be set (sometimes based on env variables).
    fn from_raw(raw_config: RawConfig) -> Result<Self> {
        let style = raw_config.style.into();
        let display = raw_config.display.into();
        let updates = raw_config.updates.into();

        // Determine directories config. For this, we need to take some
        // additional factory into account, like env variables, or the
        // user config.
        let cache_dir_env_var = "TEALDEER_CACHE_DIR";
        let cache_dir = if let Ok(env_var) = env::var(cache_dir_env_var) {
            // For backwards compatibility reasons, the cache directory can be
            // overridden using an env variable. This is deprecated and will be
            // phased out in the future.
            eprintln!("Warning: The ${} env variable is deprecated, use the `cache_dir` option in the config file instead.", cache_dir_env_var);
            PathWithSource {
                path: PathBuf::from(env_var),
                source: PathSource::EnvVar,
            }
        } else if let Some(config_value) = raw_config.directories.cache_dir {
            // If the user explicitly configured a cache directory, use that.
            PathWithSource {
                path: config_value,
                source: PathSource::ConfigFile,
            }
        } else if let Ok(default_dir) = get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
            // Otherwise, fall back to the default user cache directory.
            PathWithSource {
                path: default_dir,
                source: PathSource::OsConvention,
            }
        } else {
            // If everything fails, give up
            bail!("Could not determine user cache directory");
        };
        let custom_pages_dir = raw_config
            .directories
            .custom_pages_dir
            .map(|path| PathWithSource {
                path,
                source: PathSource::OsConvention,
            })
            .or_else(|| {
                get_app_root(AppDataType::UserData, &crate::APP_INFO)
                    .map(|path| {
                        // Note: The `join("")` call ensures that there's a trailing slash
                        PathWithSource {
                            path: path.join("pages").join(""),
                            source: PathSource::ConfigFile,
                        }
                    })
                    .ok()
            });
        let directories = DirectoriesConfig {
            cache_dir,
            custom_pages_dir,
        };

        Ok(Self {
            style,
            display,
            updates,
            directories,
        })
    }

    pub fn load(enable_styles: bool) -> Result<Self> {
        debug!("Loading config");

        // Determine path
        let (config_file_path, _) = get_config_path().context("Could not determine config path")?;

        // Load raw config
        let raw_config: RawConfig = if config_file_path.exists() && config_file_path.is_file() {
            let mut config_file = fs::File::open(&config_file_path).with_context(|| {
                format!("Failed to open config file path at {:?}", &config_file_path)
            })?;
            let mut contents = String::new();
            config_file.read_to_string(&mut contents).with_context(|| {
                format!("Failed to read from config file at {:?}", &config_file_path)
            })?;
            toml::from_str(&contents).with_context(|| {
                format!("Failed to parse TOML config file at {:?}", config_file_path)
            })?
        } else {
            RawConfig::new()
        };

        // Convert to config
        let mut config = Self::from_raw(raw_config).context("Could not process raw config")?;

        // Potentially override styles
        if !enable_styles {
            config.style = StyleConfig {
                command_name: Style::default(),
                description: Style::default(),
                example_text: Style::default(),
                example_code: Style::default(),
                example_variable: Style::default(),
            };
        }

        Ok(config)
    }
}

/// Return the path to the config directory.
///
/// The config dir path can be overridden using the `TEALDEER_CONFIG_DIR` env
/// variable. Otherwise, the user config directory is returned.
///
/// Note that this function does not verify whether the directory at that
/// location exists, or is a directory.
pub fn get_config_dir() -> Result<(PathBuf, PathSource)> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        return Ok((PathBuf::from(value), PathSource::EnvVar));
    };

    // Otherwise, fall back to the user config directory.
    let dirs = get_app_root(AppDataType::UserConfig, &crate::APP_INFO)
        .context("Failed to determine the user config directory")?;
    Ok((dirs, PathSource::OsConvention))
}

/// Return the path to the config file.
///
/// Note that this function does not verify whether the file at that location
/// exists, or is a file.
pub fn get_config_path() -> Result<(PathBuf, PathSource)> {
    let (config_dir, source) = get_config_dir()?;
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    Ok((config_file_path, source))
}

/// Create default config file.
pub fn make_default_config() -> Result<PathBuf> {
    let (config_dir, _) = get_config_dir()?;

    // Ensure that config directory exists
    if config_dir.exists() {
        ensure!(
            config_dir.is_dir(),
            "Config directory could not be created: {} already exists but is not a directory",
            config_dir.to_string_lossy(),
        );
    } else {
        fs::create_dir_all(&config_dir).context("Could not create config directory")?;
    }

    // Ensure that a config file doesn't get overwritten
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    ensure!(
        !config_file_path.is_file(),
        "A configuration file already exists at {}, no action was taken.",
        config_file_path.to_str().unwrap()
    );

    // Create default config
    let serialized_config =
        toml::to_string(&RawConfig::new()).context("Failed to serialize default config")?;

    // Write default config
    let mut config_file =
        fs::File::create(&config_file_path).context("Could not create config file")?;
    let _wc = config_file
        .write(serialized_config.as_bytes())
        .context("Could not write to config file")?;

    Ok(config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<commit_msg>Fix path source for custom pages dir (#297)<commit_after>use std::{
    env, fmt, fs,
    io::{Read, Write},
    path::{Path, PathBuf},
    time::Duration,
};

use anyhow::{bail, ensure, Context, Result};
use app_dirs::{get_app_root, AppDataType};
use log::debug;
use serde_derive::{Deserialize, Serialize};
use yansi::{Color, Style};

use crate::types::PathSource;

pub const CONFIG_FILE_NAME: &str = "config.toml";
pub const MAX_CACHE_AGE: Duration = Duration::from_secs(2_592_000); // 30 days
const DEFAULT_UPDATE_INTERVAL_HOURS: u64 = MAX_CACHE_AGE.as_secs() / 3600; // 30 days

fn default_underline() -> bool {
    false
}

fn default_bold() -> bool {
    false
}

fn default_italic() -> bool {
    false
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
#[serde(rename_all = "lowercase")]
pub enum RawColor {
    Black,
    Red,
    Green,
    Yellow,
    Blue,
    Magenta,
    Purple, // Backwards compatibility with ansi_term (until tealdeer 1.5.0)
    Cyan,
    White,
    Ansi(u8),
    Rgb { r: u8, g: u8, b: u8 },
}

impl From<RawColor> for Color {
    fn from(raw_color: RawColor) -> Self {
        match raw_color {
            RawColor::Black => Self::Black,
            RawColor::Red => Self::Red,
            RawColor::Green => Self::Green,
            RawColor::Yellow => Self::Yellow,
            RawColor::Blue => Self::Blue,
            RawColor::Magenta | RawColor::Purple => Self::Magenta,
            RawColor::Cyan => Self::Cyan,
            RawColor::White => Self::White,
            RawColor::Ansi(num) => Self::Fixed(num),
            RawColor::Rgb { r, g, b } => Self::RGB(r, g, b),
        }
    }
}

#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyle {
    pub foreground: Option<RawColor>,
    pub background: Option<RawColor>,
    #[serde(default = "default_underline")]
    pub underline: bool,
    #[serde(default = "default_bold")]
    pub bold: bool,
    #[serde(default = "default_italic")]
    pub italic: bool,
}

#[allow(clippy::derivable_impls)] // Explicitly control defaults
impl Default for RawStyle {
    fn default() -> Self {
        Self {
            foreground: None,
            background: None,
            underline: false,
            bold: false,
            italic: false,
        }
    }
}

impl From<RawStyle> for Style {
    fn from(raw_style: RawStyle) -> Self {
        let mut style = Self::default();

        if let Some(foreground) = raw_style.foreground {
            style = style.fg(Color::from(foreground));
        }

        if let Some(background) = raw_style.background {
            style = style.bg(Color::from(background));
        }

        if raw_style.underline {
            style = style.underline();
        }

        if raw_style.bold {
            style = style.bold();
        }

        if raw_style.italic {
            style = style.italic();
        }

        style
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawStyleConfig {
    #[serde(default)]
    pub description: RawStyle,
    #[serde(default)]
    pub command_name: RawStyle,
    #[serde(default)]
    pub example_text: RawStyle,
    #[serde(default)]
    pub example_code: RawStyle,
    #[serde(default)]
    pub example_variable: RawStyle,
}

impl From<RawStyleConfig> for StyleConfig {
    fn from(raw_style_config: RawStyleConfig) -> Self {
        Self {
            command_name: raw_style_config.command_name.into(),
            description: raw_style_config.description.into(),
            example_text: raw_style_config.example_text.into(),
            example_code: raw_style_config.example_code.into(),
            example_variable: raw_style_config.example_variable.into(),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDisplayConfig {
    #[serde(default)]
    pub compact: bool,
    #[serde(default)]
    pub use_pager: bool,
}

impl From<RawDisplayConfig> for DisplayConfig {
    fn from(raw_display_config: RawDisplayConfig) -> Self {
        Self {
            compact: raw_display_config.compact,
            use_pager: raw_display_config.use_pager,
        }
    }
}

/// Serde doesn't support default values yet (tracking issue:
/// <https://github.com/serde-rs/serde/issues/368>), so we need to wrap
/// `DEFAULT_UPDATE_INTERVAL_HOURS` in a function to be able to use
/// `#[serde(default = ...)]`
const fn default_auto_update_interval_hours() -> u64 {
    DEFAULT_UPDATE_INTERVAL_HOURS
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
struct RawUpdatesConfig {
    #[serde(default)]
    pub auto_update: bool,
    #[serde(default = "default_auto_update_interval_hours")]
    pub auto_update_interval_hours: u64,
}

impl Default for RawUpdatesConfig {
    fn default() -> Self {
        Self {
            auto_update: false,
            auto_update_interval_hours: DEFAULT_UPDATE_INTERVAL_HOURS,
        }
    }
}

impl From<RawUpdatesConfig> for UpdatesConfig {
    fn from(raw_updates_config: RawUpdatesConfig) -> Self {
        Self {
            auto_update: raw_updates_config.auto_update,
            auto_update_interval: Duration::from_secs(
                raw_updates_config.auto_update_interval_hours * 3600,
            ),
        }
    }
}

#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawDirectoriesConfig {
    #[serde(default)]
    pub cache_dir: Option<PathBuf>,
    #[serde(default)]
    pub custom_pages_dir: Option<PathBuf>,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(default)]
struct RawConfig {
    style: RawStyleConfig,
    display: RawDisplayConfig,
    updates: RawUpdatesConfig,
    directories: RawDirectoriesConfig,
}

impl RawConfig {
    fn new() -> Self {
        Self::default()
    }
}

impl Default for RawConfig {
    fn default() -> Self {
        let mut raw_config = RawConfig {
            style: RawStyleConfig::default(),
            display: RawDisplayConfig::default(),
            updates: RawUpdatesConfig::default(),
            directories: RawDirectoriesConfig::default(),
        };

        // Set default config
        raw_config.style.example_text.foreground = Some(RawColor::Green);
        raw_config.style.command_name.foreground = Some(RawColor::Cyan);
        raw_config.style.example_code.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.foreground = Some(RawColor::Cyan);
        raw_config.style.example_variable.underline = true;

        raw_config
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct StyleConfig {
    pub description: Style,
    pub command_name: Style,
    pub example_text: Style,
    pub example_code: Style,
    pub example_variable: Style,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct DisplayConfig {
    pub compact: bool,
    pub use_pager: bool,
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct UpdatesConfig {
    pub auto_update: bool,
    pub auto_update_interval: Duration,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PathWithSource {
    pub path: PathBuf,
    pub source: PathSource,
}

impl PathWithSource {
    pub fn path(&self) -> &Path {
        &self.path
    }
}

impl fmt::Display for PathWithSource {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} ({})", self.path.display(), self.source)
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DirectoriesConfig {
    pub cache_dir: PathWithSource,
    pub custom_pages_dir: Option<PathWithSource>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Config {
    pub style: StyleConfig,
    pub display: DisplayConfig,
    pub updates: UpdatesConfig,
    pub directories: DirectoriesConfig,
}

impl Config {
    /// Convert a `RawConfig` to a high-level `Config`.
    ///
    /// For this, some values need to be converted to other types and some
    /// defaults need to be set (sometimes based on env variables).
    fn from_raw(raw_config: RawConfig) -> Result<Self> {
        let style = raw_config.style.into();
        let display = raw_config.display.into();
        let updates = raw_config.updates.into();

        // Determine directories config. For this, we need to take some
        // additional factory into account, like env variables, or the
        // user config.
        let cache_dir_env_var = "TEALDEER_CACHE_DIR";
        let cache_dir = if let Ok(env_var) = env::var(cache_dir_env_var) {
            // For backwards compatibility reasons, the cache directory can be
            // overridden using an env variable. This is deprecated and will be
            // phased out in the future.
            eprintln!("Warning: The ${} env variable is deprecated, use the `cache_dir` option in the config file instead.", cache_dir_env_var);
            PathWithSource {
                path: PathBuf::from(env_var),
                source: PathSource::EnvVar,
            }
        } else if let Some(config_value) = raw_config.directories.cache_dir {
            // If the user explicitly configured a cache directory, use that.
            PathWithSource {
                path: config_value,
                source: PathSource::ConfigFile,
            }
        } else if let Ok(default_dir) = get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
            // Otherwise, fall back to the default user cache directory.
            PathWithSource {
                path: default_dir,
                source: PathSource::OsConvention,
            }
        } else {
            // If everything fails, give up
            bail!("Could not determine user cache directory");
        };
        let custom_pages_dir = raw_config
            .directories
            .custom_pages_dir
            .map(|path| PathWithSource {
                path,
                source: PathSource::ConfigFile,
            })
            .or_else(|| {
                get_app_root(AppDataType::UserData, &crate::APP_INFO)
                    .map(|path| {
                        // Note: The `join("")` call ensures that there's a trailing slash
                        PathWithSource {
                            path: path.join("pages").join(""),
                            source: PathSource::OsConvention,
                        }
                    })
                    .ok()
            });
        let directories = DirectoriesConfig {
            cache_dir,
            custom_pages_dir,
        };

        Ok(Self {
            style,
            display,
            updates,
            directories,
        })
    }

    pub fn load(enable_styles: bool) -> Result<Self> {
        debug!("Loading config");

        // Determine path
        let (config_file_path, _) = get_config_path().context("Could not determine config path")?;

        // Load raw config
        let raw_config: RawConfig = if config_file_path.exists() && config_file_path.is_file() {
            let mut config_file = fs::File::open(&config_file_path).with_context(|| {
                format!("Failed to open config file path at {:?}", &config_file_path)
            })?;
            let mut contents = String::new();
            config_file.read_to_string(&mut contents).with_context(|| {
                format!("Failed to read from config file at {:?}", &config_file_path)
            })?;
            toml::from_str(&contents).with_context(|| {
                format!("Failed to parse TOML config file at {:?}", config_file_path)
            })?
        } else {
            RawConfig::new()
        };

        // Convert to config
        let mut config = Self::from_raw(raw_config).context("Could not process raw config")?;

        // Potentially override styles
        if !enable_styles {
            config.style = StyleConfig {
                command_name: Style::default(),
                description: Style::default(),
                example_text: Style::default(),
                example_code: Style::default(),
                example_variable: Style::default(),
            };
        }

        Ok(config)
    }
}

/// Return the path to the config directory.
///
/// The config dir path can be overridden using the `TEALDEER_CONFIG_DIR` env
/// variable. Otherwise, the user config directory is returned.
///
/// Note that this function does not verify whether the directory at that
/// location exists, or is a directory.
pub fn get_config_dir() -> Result<(PathBuf, PathSource)> {
    // Allow overriding the config directory by setting the
    // $TEALDEER_CONFIG_DIR env variable.
    if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
        return Ok((PathBuf::from(value), PathSource::EnvVar));
    };

    // Otherwise, fall back to the user config directory.
    let dirs = get_app_root(AppDataType::UserConfig, &crate::APP_INFO)
        .context("Failed to determine the user config directory")?;
    Ok((dirs, PathSource::OsConvention))
}

/// Return the path to the config file.
///
/// Note that this function does not verify whether the file at that location
/// exists, or is a file.
pub fn get_config_path() -> Result<(PathBuf, PathSource)> {
    let (config_dir, source) = get_config_dir()?;
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    Ok((config_file_path, source))
}

/// Create default config file.
pub fn make_default_config() -> Result<PathBuf> {
    let (config_dir, _) = get_config_dir()?;

    // Ensure that config directory exists
    if config_dir.exists() {
        ensure!(
            config_dir.is_dir(),
            "Config directory could not be created: {} already exists but is not a directory",
            config_dir.to_string_lossy(),
        );
    } else {
        fs::create_dir_all(&config_dir).context("Could not create config directory")?;
    }

    // Ensure that a config file doesn't get overwritten
    let config_file_path = config_dir.join(CONFIG_FILE_NAME);
    ensure!(
        !config_file_path.is_file(),
        "A configuration file already exists at {}, no action was taken.",
        config_file_path.to_str().unwrap()
    );

    // Create default config
    let serialized_config =
        toml::to_string(&RawConfig::new()).context("Failed to serialize default config")?;

    // Write default config
    let mut config_file =
        fs::File::create(&config_file_path).context("Could not create config file")?;
    let _wc = config_file
        .write(serialized_config.as_bytes())
        .context("Could not write to config file")?;

    Ok(config_file_path)
}

#[test]
fn test_serialize_deserialize() {
    let raw_config = RawConfig::new();
    let serialized = toml::to_string(&raw_config).unwrap();
    let deserialized: RawConfig = toml::from_str(&serialized).unwrap();
    assert_eq!(raw_config, deserialized);
}
<|endoftext|>


<commit_before>extern crate yaml_rust;

use std::error::Error;
use std::path::Path;
use util;
use self::yaml_rust::{YamlLoader, Yaml};

// I may have gotten a little carried away with these macros..

// Due to limitations of the macro system and the YAML library, we must use a generic solution
// to retrieve values dynamically.
fn yaml_to_string(yaml: &Yaml) -> Option<String> {
    use self::yaml_rust::Yaml::*;

    match *yaml {
        Real(ref string) | String(ref string) =>
            Some(string.clone()),
        Integer(num) => Some(format!("{}", num)),
        _ => None,
    }
}

macro_rules! gen_value {
    // Value type
    (v $parent:expr, $disp_name:expr, $default:expr) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
            .unwrap_or($default)
    }};

    // Value type with minimum
    (vmin $parent:expr, $disp_name:expr, [$min:expr, $default:expr]) => {{
        let result = gen_value!(v $parent, $disp_name, $default);
        if result < $min { $min } else { result }
    }};

    // Optional type
    (o $parent:expr, $disp_name:expr, $default:expr) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
    }};

    // Optional type with minimum
    (omin $parent:expr, $disp_name:expr, [$min:expr, $default:expr]) => {{
        let result = gen_value!(o $parent, $disp_name, $default);
        result.map(|v| if v < $min { $min } else { v })
    }};

    // Struct type
    ($t:ident $parent:expr, $disp_name:expr, $default:expr) => {{
        $t::parse(&$parent)
    }};
}

macro_rules! create_sub_config {
    ($yaml_name:expr, $name:ident,
        $($opt:ident $field:ident.$field_type:ty => $disp_name:expr => $default:tt,)+) => {

        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_type,)+
        }

        impl $name {
            fn new(doc: &Yaml) -> $name {
                let parent = &doc[$yaml_name];

                $name {
                    $($field: gen_value!($opt parent, $disp_name, $default),)+
                }
            }
        }
    };
}

macro_rules! try_opt {
    ($value:expr) => {{
        match $value {
            Some(v) => v,
            None    => return None,
        }
    }};
}

macro_rules! create_arr_struct {
    ($yaml_name:expr, $name:ident, $($field:ident.$field_type:ty => $disp_name:expr,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_type,)+
        }

        impl $name {
            pub fn parse(doc: &Yaml) -> Vec<$name> {
                let parent = &doc[$yaml_name];

                parent.as_vec()
                    .unwrap_or(&Vec::new())
                    .iter()
                    .filter_map(|field| {
                        Some($name {
                            $($field:
                                try_opt!(
                                    yaml_to_string(&field[$disp_name])
                                    .and_then(|s| s.parse().ok())
                                ),)+
                        })
                    })
                    .collect()
            }
        }
    };
}

create_arr_struct!("Feed Percentages", FeedPercentage,
    name.String => "Name",
    spike.f32   => "Spike",
);

create_sub_config!("Global", Global,
    vmin spike.f32                   => "Spike"                                => [0.0, 0.20],
    vmin unskewed_reset_pcnt.f32     => "Unskewed Reset Percentage"            => [0.0, 0.15],
    vmin unskewed_adjust_pcnt.f32    => "Unskewed Adjust Percentage"           => [0.0, 0.01],
    vmin low_listener_increase.f32   => "Low Listener Increase Percentage"     => [0.0, 0.005],
    vmin high_listener_dec.f32       => "High Listener Decrease Percentage"    => [0.0, 0.02],
    vmin high_listener_dec_every.f32 => "High Listener Decrease Per Listeners" => [1.0, 100.0],
    vmin update_time.f32             => "Update Time"                          => [5.0, 6.0],
    v minimum_listeners.u32          => "Minimum Listeners"                    => 15,
    o state_feeds_id.Option<u8>      => "State Feeds ID"                       => [0, None],
);

#[derive(Debug)]
pub struct Config {
    pub global:           Global,
    pub feed_percentages: Vec<FeedPercentage>,
    pub blacklist:        Vec<String>,
}

pub fn load_from_file(path: &Path) -> Result<Config, Box<Error>> {
    let doc = YamlLoader::load_from_str(&util::read_file(path)?)?;
    let doc = &doc[0]; // We don't care about multiple documents

    Ok(Config {
        global:           Global::new(&doc),
        feed_percentages: FeedPercentage::parse(&doc),
        blacklist:        Vec::new(),
    })
}<commit_msg>Improve config generation macros<commit_after>extern crate yaml_rust;

use std::error::Error;
use std::path::Path;
use util;
use self::yaml_rust::{YamlLoader, Yaml};

// I may have gotten a little carried away with these macros..

// Due to limitations of the macro system and the YAML library, we must use a generic solution
// to retrieve values dynamically.
fn yaml_to_string(yaml: &Yaml) -> Option<String> {
    use self::yaml_rust::Yaml::*;

    match *yaml {
        Real(ref string) | String(ref string) =>
            Some(string.clone()),
        Integer(num) => Some(format!("{}", num)),
        _ => None,
    }
}

macro_rules! gen_value {
    // Option
    ($parent:expr, $disp_name:expr, None) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
    }};

    // Option with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, None]) => {{
        let result = gen_value!($parent, $disp_name, None);
        result.map(|v| if v < $min { $min } else { v })
    }};

    // Value with minimum
    ($parent:expr, $disp_name:expr, [$min:expr, $default:expr]) => {{
        let result = gen_value!($parent, $disp_name, $default);
        if result < $min { $min } else { result }
    }};

    // Value
    ($parent:expr, $disp_name:expr, $default:expr) => {{
        yaml_to_string(&$parent[$disp_name])
            .and_then(|s| s.parse().ok())
            .unwrap_or($default)
    }};
}

macro_rules! create_config_section {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_t:ty => $disp_name:expr => $default:tt,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_t,)+
        }

        impl $name {
            pub fn new(doc: &Yaml) -> $name {
                let parent = &doc[$yaml_name];

                $name {
                    $($field: gen_value!(parent, $disp_name, $default),)+
                }
            }
        }
    };
}

macro_rules! try_opt {
    ($value:expr) => {{
        match $value {
            Some(v) => v,
            None    => return None,
        }
    }};
}

macro_rules! create_config_arr {
    ($yaml_name:expr, $name:ident, $($field:ident: $field_type:ty => $disp_name:expr,)+) => {
        #[derive(Debug)]
        pub struct $name {
            $(pub $field: $field_type,)+
        }

        impl $name {
            pub fn parse(doc: &Yaml) -> Vec<$name> {
                let parent = &doc[$yaml_name];

                parent.as_vec()
                    .unwrap_or(&Vec::new())
                    .iter()
                    .filter_map(|field| {
                        Some($name {
                            $($field:
                                try_opt!(
                                    yaml_to_string(&field[$disp_name])
                                    .and_then(|s| s.parse().ok())
                                ),)+
                        })
                    })
                    .collect()
            }
        }
    };
}

create_config_arr!("Feed Percentages", FeedPercentage,
    name:  String => "Name",
    spike: f32    => "Spike",
);

create_config_section!("Global", Global,
	spike:                   f32        => "Spike"                                => 0.20,
	unskewed_reset_pcnt:     f32        => "Unskewed Reset Percentage"            => [0.0, 0.15],
	unskewed_adjust_pcnt:    f32        => "Unskewed Adjust Percentage"           => [0.0, 0.01],
	low_listener_increase:   f32        => "Low Listener Increase Percentage"     => [0.0, 0.005],
	high_listener_dec:       f32        => "High Listener Decrease Percentage"    => [0.0, 0.02],
	high_listener_dec_every: f32        => "High Listener Decrease Per Listeners" => [1.0, 100.0],
	update_time:             f32        => "Update Time"                          => [5.0, 6.0],
	minimum_listeners:       u32        => "Minimum Listeners"                    => 15,
	state_feeds_id:          Option<u8> => "State Feeds ID"                       => None,
);

#[derive(Debug)]
pub struct Config {
    pub global:           Global,
    pub feed_percentages: Vec<FeedPercentage>,
    pub blacklist:        Vec<String>,
}

pub fn load_from_file(path: &Path) -> Result<Config, Box<Error>> {
    let doc = YamlLoader::load_from_str(&util::read_file(path)?)?;
    let doc = &doc[0]; // We don't care about multiple documents

    Ok(Config {
        global:           Global::new(&doc),
        feed_percentages: FeedPercentage::parse(&doc),
        blacklist:        Vec::new(),
    })
}<|endoftext|>


<commit_before>//! Various useful constants

/// Useful constants for radians.
pub trait Radians {
    /// Returns radians corresponding to 90 degrees.
    fn _90() -> Self;

    /// Returns radians corresponding to 180 degrees.
    fn _180() -> Self;

    /// Returns radians corresponding to 360 degrees.
    fn _360() -> Self;
}

impl Radians for f32 {
    #[inline(always)]
    fn _90() -> f32 {
        ::std::f32::consts::FRAC_PI_2
    }

    #[inline(always)]
    fn _180() -> f32 {
        ::std::f32::consts::PI
    }

    #[inline(always)]
    fn _360() -> f32 {
        ::std::f32::consts::PI_2
    }
}

impl Radians for f64 {
    #[inline(always)]
    fn _90() -> f64 {
        ::std::f64::consts::FRAC_PI_2
    }

    #[inline(always)]
    fn _180() -> f64 {
        ::std::f64::consts::PI
    }

    #[inline(always)]
    fn _360() -> f64 {
        ::std::f64::consts::PI_2
    }
}

<commit_msg>Add to_rad() method to Radians, make Radians a subtrait of FromPrimitive, add implementations of to_rad() and add tests<commit_after>//! Various useful constants

use std::num::FromPrimitive;

/// Useful constants for radians.
pub trait Radians : FromPrimitive {
    /// Returns radians corresponding to 90 degrees.
    fn _90() -> Self;

    /// Returns radians corresponding to 180 degrees.
    fn _180() -> Self;

    /// Returns radians corresponding to 360 degrees.
    fn _360() -> Self;

    /// Convert a value to radians, assuming the initial units
    /// are in degrees. Equivelent to:
    /// ```ignore
    ///     value * (π / 180)
    /// ```
    fn to_rad(&self) -> Self;
}

impl Radians for f32 {
    #[inline(always)]
    fn _90() -> f32 {
        ::std::f32::consts::FRAC_PI_2
    }

    #[inline(always)]
    fn _180() -> f32 {
        ::std::f32::consts::PI
    }

    #[inline(always)]
    fn _360() -> f32 {
        ::std::f32::consts::PI_2
    }

    #[inline(always)]
    fn to_rad(&self) -> Self {
        *self * (::std::f32::consts::PI / 180.0f32)
    }
}

impl Radians for f64 {
    #[inline(always)]
    fn _90() -> f64 {
        ::std::f64::consts::FRAC_PI_2
    }

    #[inline(always)]
    fn _180() -> f64 {
        ::std::f64::consts::PI
    }

    #[inline(always)]
    fn _360() -> f64 {
        ::std::f64::consts::PI_2
    }

    #[inline(always)]
    fn to_rad(&self) -> Self {
        *self * (::std::f64::consts::PI / 180.0f64)
    }
}

#[cfg(test)]
mod test {
    use super::{Radians};
    use std::num::Float;

    #[test]
    fn test_f32_deg_to_rad() {
        let degrees = 23.0f32;
        let radians = degrees.to_rad();
        assert!(Float::abs_sub(radians, 0.401425) > ::std::f32::EPSILON);
    }

    #[test]
    fn test_f64_deg_to_rad() {
        let degrees = 60.0f64;
        let radians = degrees.to_rad();
        assert!(Float::abs_sub(radians, 1.047197)  > ::std::f64::EPSILON);
    }
}

<|endoftext|>


<commit_before>use std::cmp::{Ordering};
use std::error::{Error, FromError};
use std::fmt;
use std::hash::{self, Hash, Hasher};
use std::slice::bytes::copy_memory;

use protobuf::MessageStatic;
use rustc_serialize::{self, Decodable, Decoder, Encodable, Encoder, json};
use rustc_serialize::base64::{self, ToBase64};
use sodiumoxide::crypto::hash::sha512;
use sodiumoxide::crypto::sign::ed25519::{self,
    PUBLICKEYBYTES, SECRETKEYBYTES, SIGNATUREBYTES,
    sign_detached, verify_detached};
use time::now_utc;

use error::{SimplesError, SimplesResult};

pub use sodiumoxide::crypto::hash::sha512::HASHBYTES;
pub use sodiumoxide::crypto::sign::ed25519::{SecretKey, Signature};

pub trait CryptoPrimitive {
    fn bytes(&self) -> &[u8];
}

// HashDigest:

pub struct HashDigest(pub [u8; HASHBYTES]);

pub fn hash(bytes: &[u8]) -> HashDigest {
    HashDigest(sha512::hash(bytes).0)
}

impl HashDigest {
    pub fn from_u64(mut value: u64) -> HashDigest {
        let mut proof_hash = HashDigest([0; HASHBYTES]);
        let mut index = 0us;
        while value > 0u64 {
            proof_hash.0[index] = (value & 0xffu64) as u8;
            value = value >> 8;
            index += 1;
        }
        proof_hash
    }

    pub fn from_bytes(bytes: &[u8]) -> SimplesResult<HashDigest> {
        match bytes.len() != HASHBYTES {
            true => Err(SimplesError::new(&format!(
                "Invalid length for a hash {} != {} (required).",
                bytes.len(), HASHBYTES))),
            false => {
                let mut digest = HashDigest([0; HASHBYTES]);
                copy_memory(&mut digest.0[], bytes);
                Ok(digest)
            }
        }
    }

    pub fn multiply_u8_in_place(&mut self, other: u8) {
        let mut quot = 0u16;
        for index in range(0, HASHBYTES) {
            quot = self.0[index] as u16 * other as u16 + quot;
            self.0[index] = (quot & 0x00ffu16) as u8;
            quot = quot >> 8;
        }
    }

    pub fn add_in_place(&mut self, other: &HashDigest) {
        let mut rem = 0u16;
        for index in range(0, HASHBYTES) {
            rem = self.0[index] as u16  + other.0[index] as u16 + rem;
            self.0[index] = (rem & 0x00ffu16) as u8;
            rem = rem >> 8;
        }
    }
}

impl CryptoPrimitive for HashDigest {
    fn bytes(&self) -> &[u8] { &self.0[] }
}

impl Clone for HashDigest {
    fn clone(&self) -> HashDigest { HashDigest::from_bytes(&self.0[]).unwrap() }
}

impl<C: CryptoPrimitive> PartialEq for C {
    fn eq(&self, other: &C) -> bool { self.bytes() == other.bytes() }
}

// impl<C: CryptoPrimitive> Eq for C {}

impl Eq for HashDigest {}

impl PartialOrd for HashDigest {
    fn partial_cmp(&self, other: &HashDigest) -> Option<Ordering> {
        for (byte1, byte2) in self.0.iter().rev().zip(
            other.0.iter().rev()) {
            if byte1 > byte2 {
                return Some(Ordering::Greater);
            } else if byte2 > byte1 {
                return Some(Ordering::Less);
            }
        }
        return Some(Ordering::Equal);
    }
}

impl Ord for HashDigest {
    fn cmp(&self, other: &HashDigest) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl fmt::Debug for HashDigest {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{:?}", &self.0[])
    }
}

impl fmt::Display for HashDigest {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", &self.0[].to_base64(base64::STANDARD))
    }
}

impl Encodable for HashDigest {
    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
        self.0.to_vec().encode(encoder)
    }
}

impl Decodable for HashDigest {
    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, D::Error> {
        let bytes = &try!(<Vec<u8> as Decodable>::decode(decoder))[];
        let n_bytes = bytes.len();
        match HashDigest::from_bytes(bytes) {
            Ok(digest) => Ok(digest),
            Err(err) => Err(decoder.error(err.description()))
        }
    }
}

// PublicKey:

pub struct PublicKey(pub [u8; PUBLICKEYBYTES]);

pub fn sign(secret_key: &SecretKey, message: &[u8]) -> Signature {
    sign_detached(message, secret_key)
}

pub fn verify_signature(
    public_key: &PublicKey, message: &[u8], signature: &Signature) -> bool {
    verify_detached(signature, message, &ed25519::PublicKey(public_key.0))
}

impl PublicKey {
    pub fn from_bytes(bytes: &[u8]) -> Option<PublicKey> {
        match bytes.len() != PUBLICKEYBYTES {
            true => None,
            false => {
                let mut digest = PublicKey([0; PUBLICKEYBYTES]);
                copy_memory(&mut digest.0[], bytes);
                Some(digest)
            }
        }
    }
}

impl PartialEq for PublicKey {
    fn eq(&self, other: &PublicKey) -> bool { &self.0[] == &other.0[] }
}

impl Eq for PublicKey {}

impl<H: Hasher + hash::Writer> Hash<H> for PublicKey {
    fn hash(&self, state: &mut H) { state.write(&self.0[]); }
}

// SecretKey:

pub fn gen_keypair() -> (PublicKey, SecretKey) {
    let (pk, sk) = ed25519::gen_keypair();
    (PublicKey(pk.0), sk)
}

// Utilities for crypto on protobufs:

pub fn hash_message<M: MessageStatic>(message: &M) -> HashDigest {
    hash(&message.write_to_bytes().unwrap()[])
}

pub fn sign_message<M: MessageStatic>(
    secret_key: &SecretKey, message: &M) -> Signature
{
    let msg_bytes = &message.write_to_bytes().unwrap()[];
    sign(secret_key, msg_bytes)
}

pub fn verify_signed_message<M: MessageStatic>(
    public_key: &PublicKey, message: &M, signature: &Signature) -> bool
{
    let msg_bytes: &[u8] = &message.write_to_bytes().unwrap()[];
    verify_signature(public_key, msg_bytes, signature)
}

pub fn slice_to_pk(bytes: &[u8]) -> SimplesResult<PublicKey> {
    if bytes.len() != PUBLICKEYBYTES {
        return Err(SimplesError::new(
            &format!("Invalid public key length {} != {} (requires)",
                     bytes.len(), PUBLICKEYBYTES)[]));
    }
    let mut key:[u8; PUBLICKEYBYTES] = [0; PUBLICKEYBYTES];
    for i in range(0, PUBLICKEYBYTES) {
        key[i] = bytes[i];
    }
    Ok(PublicKey(key))
}

pub fn slice_to_sk(bytes: &[u8]) -> SimplesResult<SecretKey> {
    if bytes.len() != SECRETKEYBYTES {
        return Err(SimplesError::new(
            &format!("Invalid secret key length {} != {} (requires)",
                     bytes.len(), SECRETKEYBYTES)[]));
    }
    let mut key:[u8; SECRETKEYBYTES] = [0; SECRETKEYBYTES];
    for i in range(0, SECRETKEYBYTES) {
        key[i] = bytes[i];
    }
    Ok(SecretKey(key))
}

pub fn slice_to_signature(bytes: &[u8]) -> SimplesResult<Signature> {
    if bytes.len() != SIGNATUREBYTES {
        return Err(SimplesError::new(
            &format!("Invalid signature length {} != {} (requires)",
                     bytes.len(), SIGNATUREBYTES)[]));
    }
    let mut sign:[u8; SIGNATUREBYTES] = [0; SIGNATUREBYTES];
    for i in range(0, SIGNATUREBYTES) {
        sign[i] = bytes[i];
    }
    Ok(Signature(sign))
}

// Tests:

#[test]
fn test_digest_from_u64() {
    let mut hash1 = HashDigest::from_u64(2100);
    let mut hash2 = HashDigest::from_u64(65535);

    assert!(hash1.0[0] == 52);
    assert!(hash1.0[1] == 8);
    assert!(hash2.0[0] == 255);
    assert!(hash2.0[1] == 255);
    for i in range(3, HASHBYTES) {
        assert!(hash1.0[i] == 0);
        assert!(hash2.0[i] == 0);
    }
}

#[test]
fn test_digest_multiply_u8() {
    let mut hash1 = HashDigest::from_u64(101);
    let mut hash2 = HashDigest::from_u64(256);
    hash1.multiply_u8_in_place(7);
    hash2.multiply_u8_in_place(2);
    assert!(HashDigest::from_u64(707) == hash1);
    assert!(HashDigest::from_u64(512) == hash2);
    hash1.multiply_u8_in_place(7);
    hash2.multiply_u8_in_place(4);
    assert!(HashDigest::from_u64(4949) == hash1);
    assert!(HashDigest::from_u64(2048) == hash2);
}

#[test]
fn test_digest_add() {
    let mut hash1 = HashDigest::from_u64(101);
    let mut hash2 = HashDigest::from_u64(256);
    hash1.add_in_place(&HashDigest::from_u64(100001));
    hash2.add_in_place(&HashDigest::from_u64(256));
    assert!(HashDigest::from_u64(100102) == hash1);
    assert!(HashDigest::from_u64(512) == hash2);
    hash1.add_in_place(&HashDigest::from_u64(256));
    hash2.add_in_place(&HashDigest::from_u64(1000));
    assert!(HashDigest::from_u64(100358) == hash1);
    assert!(HashDigest::from_u64(1512) == hash2);
}

// #[test]
// fn test_digest_multiply() {
//     let mut hash1 = HashDigest::from_u64(256);
//     let mut hash2 = HashDigest::from_u64(256);
//     hash1.multiply_in_place(&hash2);
//     assert!(hash1.0[0] == 0);
//     assert!(hash1.0[1] == 1);
//     for i in range(1, HASHBYTES) {
//         assert!(hash1.0[i] == 0);
//     }

//     let mut hash3 = HashDigest::from_u64(65536);
//     println!("* hash3: {:?}", hash3);
//     println!("* hash3 == hash3: {}", hash3 == hash3);
//     println!("* hash3 > hash3: {}", hash3 > hash3);
//     println!("* hash3 > hash2: {}", hash3 > hash2);
//     println!("* hash3 < hash2: {}", hash3 < hash2);
//     // hash3.multiply
// }

#[test]
fn test_digest_ord() {
    let hashes = [
        HashDigest::from_u64(0), HashDigest::from_u64(2100),
        HashDigest::from_u64(65535), HashDigest::from_u64(65536),
        HashDigest::from_u64(65536 * 100000)];

    for i in range(0, hashes.len() - 1) {
        for j in range(i + 1, hashes.len()) {
            assert!(hashes[i] < hashes[j]);
            assert!(hashes[j] > hashes[i]);
        }
    }
}

#[test]
fn test_digest_encoding() {
    let hash1 = hash(b"hello world2");
    let hash2 = HashDigest::from_u64(123456);

    let hash1_enc = json::encode(&hash1).unwrap();
    let hash2_enc = json::encode(&hash2).unwrap();

    let hash1_dec: HashDigest = json::decode(&hash1_enc[]).unwrap();
    let hash2_dec: HashDigest = json::decode(&hash2_enc[]).unwrap();

    println!("hash1 = {}", hash1_enc);
    println!("hash2 = {}", hash2_enc);
    println!("hash1_dec = {}", hash1_dec);
    println!("hash2_dec = {}", hash2_dec);

    assert!(hash1 == hash1_dec);
    assert!(hash2 == hash2_dec);
}

// #[test]
// fn test_digest() {
//     let HashDigest(target_hash_bytes) = hash(b"hello world2");
//     let target_hash = HashDigest(target_hash_bytes);
//     let now_secs = now_utc().to_timespec().sec;
//     println!("now_secs = {}", now_secs);
//     println!("target_hash = {:?}", target_hash);
//     for i in range(0, 100) {
//         let HashDigest(hash_bytes) = hash(
//             format!("{}", now_secs + i).as_bytes());
//         let hash = HashDigest(hash_bytes);
//         if hash > target_hash {
//             println!("hash({}) = {:?}", now_secs + i, hash);
//         }
//     }
// }
<commit_msg>PublicKey::from_bytes returns SimplesResult.<commit_after>use std::cmp::{Ordering};
use std::error::{Error, FromError};
use std::fmt;
use std::hash::{self, Hash, Hasher};
use std::slice::bytes::copy_memory;

use protobuf::MessageStatic;
use rustc_serialize::{self, Decodable, Decoder, Encodable, Encoder, json};
use rustc_serialize::base64::{self, ToBase64};
use sodiumoxide::crypto::hash::sha512;
use sodiumoxide::crypto::sign::ed25519::{self,
    PUBLICKEYBYTES, SECRETKEYBYTES, SIGNATUREBYTES,
    sign_detached, verify_detached};
use time::now_utc;

use error::{SimplesError, SimplesResult};

pub use sodiumoxide::crypto::hash::sha512::HASHBYTES;
pub use sodiumoxide::crypto::sign::ed25519::{SecretKey, Signature};

pub trait CryptoPrimitive {
    fn bytes(&self) -> &[u8];
}

// HashDigest:

pub struct HashDigest(pub [u8; HASHBYTES]);

pub fn hash(bytes: &[u8]) -> HashDigest {
    HashDigest(sha512::hash(bytes).0)
}

impl HashDigest {
    pub fn from_u64(mut value: u64) -> HashDigest {
        let mut proof_hash = HashDigest([0; HASHBYTES]);
        let mut index = 0us;
        while value > 0u64 {
            proof_hash.0[index] = (value & 0xffu64) as u8;
            value = value >> 8;
            index += 1;
        }
        proof_hash
    }

    pub fn from_bytes(bytes: &[u8]) -> SimplesResult<HashDigest> {
        match bytes.len() != HASHBYTES {
            true => Err(SimplesError::new(&format!(
                "Invalid length for a hash {} != {} (required).",
                bytes.len(), HASHBYTES))),
            false => {
                let mut digest = HashDigest([0; HASHBYTES]);
                copy_memory(&mut digest.0[], bytes);
                Ok(digest)
            }
        }
    }

    pub fn multiply_u8_in_place(&mut self, other: u8) {
        let mut quot = 0u16;
        for index in range(0, HASHBYTES) {
            quot = self.0[index] as u16 * other as u16 + quot;
            self.0[index] = (quot & 0x00ffu16) as u8;
            quot = quot >> 8;
        }
    }

    pub fn add_in_place(&mut self, other: &HashDigest) {
        let mut rem = 0u16;
        for index in range(0, HASHBYTES) {
            rem = self.0[index] as u16  + other.0[index] as u16 + rem;
            self.0[index] = (rem & 0x00ffu16) as u8;
            rem = rem >> 8;
        }
    }
}

impl CryptoPrimitive for HashDigest {
    fn bytes(&self) -> &[u8] { &self.0[] }
}

impl Clone for HashDigest {
    fn clone(&self) -> HashDigest { HashDigest::from_bytes(&self.0[]).unwrap() }
}

impl<C: CryptoPrimitive> PartialEq for C {
    fn eq(&self, other: &C) -> bool { self.bytes() == other.bytes() }
}

// impl<C: CryptoPrimitive> Eq for C {}

impl Eq for HashDigest {}

impl PartialOrd for HashDigest {
    fn partial_cmp(&self, other: &HashDigest) -> Option<Ordering> {
        for (byte1, byte2) in self.0.iter().rev().zip(
            other.0.iter().rev()) {
            if byte1 > byte2 {
                return Some(Ordering::Greater);
            } else if byte2 > byte1 {
                return Some(Ordering::Less);
            }
        }
        return Some(Ordering::Equal);
    }
}

impl Ord for HashDigest {
    fn cmp(&self, other: &HashDigest) -> Ordering {
        self.partial_cmp(other).unwrap()
    }
}

impl fmt::Debug for HashDigest {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{:?}", &self.0[])
    }
}

impl fmt::Display for HashDigest {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", &self.0[].to_base64(base64::STANDARD))
    }
}

impl Encodable for HashDigest {
    fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), E::Error> {
        self.0.to_vec().encode(encoder)
    }
}

impl Decodable for HashDigest {
    fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, D::Error> {
        let bytes = &try!(<Vec<u8> as Decodable>::decode(decoder))[];
        let n_bytes = bytes.len();
        match HashDigest::from_bytes(bytes) {
            Ok(digest) => Ok(digest),
            Err(err) => Err(decoder.error(err.description()))
        }
    }
}

// PublicKey:

pub struct PublicKey(pub [u8; PUBLICKEYBYTES]);

pub fn sign(secret_key: &SecretKey, message: &[u8]) -> Signature {
    sign_detached(message, secret_key)
}

pub fn verify_signature(
    public_key: &PublicKey, message: &[u8], signature: &Signature) -> bool {
    verify_detached(signature, message, &ed25519::PublicKey(public_key.0))
}

impl PublicKey {
    pub fn from_bytes(bytes: &[u8]) -> SimplesResult<PublicKey> {
        if bytes.len() != PUBLICKEYBYTES {
            Err(SimplesError::new(&format!(
                "Invalid length for a public key {} != {} (required).",
                bytes.len(), PUBLICKEYBYTES)))
        } else {
            let mut digest = PublicKey([0; PUBLICKEYBYTES]);
            copy_memory(&mut digest.0[], bytes);
            Ok(digest)
        }
    }
}

impl PartialEq for PublicKey {
    fn eq(&self, other: &PublicKey) -> bool { &self.0[] == &other.0[] }
}

impl Eq for PublicKey {}

impl<H: Hasher + hash::Writer> Hash<H> for PublicKey {
    fn hash(&self, state: &mut H) { state.write(&self.0[]); }
}

impl Clone for PublicKey {
    fn clone(&self) -> PublicKey { PublicKey::from_bytes(&self.0[]).unwrap() }
}

impl fmt::Debug for PublicKey {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{:?}", &self.0[])
    }
}

impl fmt::Display for PublicKey {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        write!(formatter, "{}", &self.0[].to_base64(base64::STANDARD))
    }
}

// SecretKey:

pub fn gen_keypair() -> (PublicKey, SecretKey) {
    let (pk, sk) = ed25519::gen_keypair();
    (PublicKey(pk.0), sk)
}

// Utilities for crypto on protobufs:

pub fn hash_message<M: MessageStatic>(message: &M) -> HashDigest {
    hash(&message.write_to_bytes().unwrap()[])
}

pub fn sign_message<M: MessageStatic>(
    secret_key: &SecretKey, message: &M) -> Signature
{
    let msg_bytes = &message.write_to_bytes().unwrap()[];
    sign(secret_key, msg_bytes)
}

pub fn verify_signed_message<M: MessageStatic>(
    public_key: &PublicKey, message: &M, signature: &Signature) -> bool
{
    let msg_bytes: &[u8] = &message.write_to_bytes().unwrap()[];
    verify_signature(public_key, msg_bytes, signature)
}

pub fn slice_to_pk(bytes: &[u8]) -> SimplesResult<PublicKey> {
    if bytes.len() != PUBLICKEYBYTES {
        return Err(SimplesError::new(
            &format!("Invalid public key length {} != {} (requires)",
                     bytes.len(), PUBLICKEYBYTES)[]));
    }
    let mut key:[u8; PUBLICKEYBYTES] = [0; PUBLICKEYBYTES];
    for i in range(0, PUBLICKEYBYTES) {
        key[i] = bytes[i];
    }
    Ok(PublicKey(key))
}

pub fn slice_to_sk(bytes: &[u8]) -> SimplesResult<SecretKey> {
    if bytes.len() != SECRETKEYBYTES {
        return Err(SimplesError::new(
            &format!("Invalid secret key length {} != {} (requires)",
                     bytes.len(), SECRETKEYBYTES)[]));
    }
    let mut key:[u8; SECRETKEYBYTES] = [0; SECRETKEYBYTES];
    for i in range(0, SECRETKEYBYTES) {
        key[i] = bytes[i];
    }
    Ok(SecretKey(key))
}

pub fn slice_to_signature(bytes: &[u8]) -> SimplesResult<Signature> {
    if bytes.len() != SIGNATUREBYTES {
        return Err(SimplesError::new(
            &format!("Invalid signature length {} != {} (requires)",
                     bytes.len(), SIGNATUREBYTES)[]));
    }
    let mut sign:[u8; SIGNATUREBYTES] = [0; SIGNATUREBYTES];
    for i in range(0, SIGNATUREBYTES) {
        sign[i] = bytes[i];
    }
    Ok(Signature(sign))
}

// Tests:

#[test]
fn test_digest_from_u64() {
    let mut hash1 = HashDigest::from_u64(2100);
    let mut hash2 = HashDigest::from_u64(65535);

    assert!(hash1.0[0] == 52);
    assert!(hash1.0[1] == 8);
    assert!(hash2.0[0] == 255);
    assert!(hash2.0[1] == 255);
    for i in range(3, HASHBYTES) {
        assert!(hash1.0[i] == 0);
        assert!(hash2.0[i] == 0);
    }
}

#[test]
fn test_digest_multiply_u8() {
    let mut hash1 = HashDigest::from_u64(101);
    let mut hash2 = HashDigest::from_u64(256);
    hash1.multiply_u8_in_place(7);
    hash2.multiply_u8_in_place(2);
    assert!(HashDigest::from_u64(707) == hash1);
    assert!(HashDigest::from_u64(512) == hash2);
    hash1.multiply_u8_in_place(7);
    hash2.multiply_u8_in_place(4);
    assert!(HashDigest::from_u64(4949) == hash1);
    assert!(HashDigest::from_u64(2048) == hash2);
}

#[test]
fn test_digest_add() {
    let mut hash1 = HashDigest::from_u64(101);
    let mut hash2 = HashDigest::from_u64(256);
    hash1.add_in_place(&HashDigest::from_u64(100001));
    hash2.add_in_place(&HashDigest::from_u64(256));
    assert!(HashDigest::from_u64(100102) == hash1);
    assert!(HashDigest::from_u64(512) == hash2);
    hash1.add_in_place(&HashDigest::from_u64(256));
    hash2.add_in_place(&HashDigest::from_u64(1000));
    assert!(HashDigest::from_u64(100358) == hash1);
    assert!(HashDigest::from_u64(1512) == hash2);
}

// #[test]
// fn test_digest_multiply() {
//     let mut hash1 = HashDigest::from_u64(256);
//     let mut hash2 = HashDigest::from_u64(256);
//     hash1.multiply_in_place(&hash2);
//     assert!(hash1.0[0] == 0);
//     assert!(hash1.0[1] == 1);
//     for i in range(1, HASHBYTES) {
//         assert!(hash1.0[i] == 0);
//     }

//     let mut hash3 = HashDigest::from_u64(65536);
//     println!("* hash3: {:?}", hash3);
//     println!("* hash3 == hash3: {}", hash3 == hash3);
//     println!("* hash3 > hash3: {}", hash3 > hash3);
//     println!("* hash3 > hash2: {}", hash3 > hash2);
//     println!("* hash3 < hash2: {}", hash3 < hash2);
//     // hash3.multiply
// }

#[test]
fn test_digest_ord() {
    let hashes = [
        HashDigest::from_u64(0), HashDigest::from_u64(2100),
        HashDigest::from_u64(65535), HashDigest::from_u64(65536),
        HashDigest::from_u64(65536 * 100000)];

    for i in range(0, hashes.len() - 1) {
        for j in range(i + 1, hashes.len()) {
            assert!(hashes[i] < hashes[j]);
            assert!(hashes[j] > hashes[i]);
        }
    }
}

#[test]
fn test_digest_encoding() {
    let hash1 = hash(b"hello world2");
    let hash2 = HashDigest::from_u64(123456);

    let hash1_enc = json::encode(&hash1).unwrap();
    let hash2_enc = json::encode(&hash2).unwrap();

    let hash1_dec: HashDigest = json::decode(&hash1_enc[]).unwrap();
    let hash2_dec: HashDigest = json::decode(&hash2_enc[]).unwrap();

    println!("hash1 = {}", hash1_enc);
    println!("hash2 = {}", hash2_enc);
    println!("hash1_dec = {}", hash1_dec);
    println!("hash2_dec = {}", hash2_dec);

    assert!(hash1 == hash1_dec);
    assert!(hash2 == hash2_dec);
}

// #[test]
// fn test_digest() {
//     let HashDigest(target_hash_bytes) = hash(b"hello world2");
//     let target_hash = HashDigest(target_hash_bytes);
//     let now_secs = now_utc().to_timespec().sec;
//     println!("now_secs = {}", now_secs);
//     println!("target_hash = {:?}", target_hash);
//     for i in range(0, 100) {
//         let HashDigest(hash_bytes) = hash(
//             format!("{}", now_secs + i).as_bytes());
//         let hash = HashDigest(hash_bytes);
//         if hash > target_hash {
//             println!("hash({}) = {:?}", now_secs + i, hash);
//         }
//     }
// }
<|endoftext|>


<commit_before>//! A module for constructing cuboid shapes.

extern crate cgmath;
extern crate glium;

use cgmath::{Angle,EuclideanVector,Matrix,Matrix3,Point3,Rotation3,SquareMatrix,Vector3,Vector4};
use errors::ShapeCreationError;
use vertex::Vertex;

/// A polygonal `Cuboid` object.
///
/// This object is constructed using a `CuboidBuilder` object.
pub struct Cuboid {
    vertices: glium::vertex::VertexBufferAny
}

/// Allows a `Cuboid` object to be passed as a source of vertices.
impl<'a> glium::vertex::IntoVerticesSource<'a> for &'a Cuboid {
    fn into_vertices_source(self) -> glium::vertex::VerticesSource<'a> {
        return self.vertices.into_vertices_source();
    }
}

/// Allows a `Cuboid` object to be passed as a source of indices.
impl<'a> Into<glium::index::IndicesSource<'a>> for &'a Cuboid {
    fn into(self) -> glium::index::IndicesSource<'a> {
        return glium::index::IndicesSource::NoIndices{
            primitives: glium::index::PrimitiveType::TrianglesList
        };
    }
}

/// Responsible for building and returning a `Cuboid` object.
///
/// By default, the cuboid is defined as a unit-cube with its centre-of-mass
/// located at the origin. This can be overriden using the transformation
/// methods on this object.
///
/// The resultant geometry is constructed to suit OpenGL defaults - assuming
/// a right-handed coordinate system, front-facing polygons are defined in
/// counter-clock-wise order. Vertex normals point in the direction of their
/// respective face (such that the cuboid appears faceted when lit). Vertex
/// texture coordinates define a planar-projection on each face.
pub struct CuboidBuilder {
    matrix: cgmath::Matrix4<f32>
}

impl Default for CuboidBuilder {
    fn default() -> Self {
        CuboidBuilder {
            matrix: cgmath::Matrix4::<f32>::identity()
        }
    }
}

impl CuboidBuilder {

    /// Create a new `CuboidBuilder` object.
    pub fn new() -> CuboidBuilder {
        Default::default()
    }

    /// Apply a scaling transformation to the shape.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn scale(mut self, x: f32, y: f32, z: f32) -> Self {
        self.matrix = cgmath::Matrix4::from_nonuniform_scale(x, y, z) * self.matrix;
        return self;
    }

    /// Apply a translation transformation to the shape.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn translate(mut self, x: f32, y:f32, z: f32) -> Self {
        self.matrix = cgmath::Matrix4::from_translation([x, y, z].into()) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the x-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_x(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_x(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the y-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_y(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_y(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the z-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_z(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_z(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Build a new `Cuboid` object.
    pub fn build<F>(self, display: &F) -> Result<Cuboid, ShapeCreationError>
    where F:glium::backend::Facade {
        let vertices = try!(glium::vertex::VertexBuffer::<Vertex>::new(
            display, &try!(self.build_vertices())
        ));

        Ok(Cuboid {
            vertices: glium::vertex::VertexBufferAny::from(vertices),
        })
    }

    /// Build the cube vertices and return them in a vector.
    ///
    /// Useful if you wish to do other things with the vertices besides constructing
    /// a `Cuboid` object (e.g. unit testing, further processing, etc).
    pub fn build_vertices(&self) -> Result<Vec<Vertex>, ShapeCreationError> {

        // Define lookup-tables used during construction of the cuboid geometry
        let index_lut = [
            0, 4, 1, 5,
            6, 2, 7, 3,
            0, 2, 4, 6,
            5, 7, 1, 3,
            2, 0, 3, 1,
            4, 6, 5, 7,
        ];
        let poly_lut = [0, 1, 2, 2, 1, 3];
        let num_sides = 6;
        let verts_per_side = 6;

        // Compute the normal transformation matrix.
        let normal_matrix = Matrix3::<f32>::from_cols(
            self.matrix.x.truncate(),
            self.matrix.y.truncate(),
            self.matrix.z.truncate()
        ).invert().unwrap_or(Matrix3::<f32>::identity()).transpose();

        // Generate cuboid vertices.
        let mut vertices = Vec::<Vertex>::with_capacity(
            verts_per_side * num_sides
        );

        for side in 0..num_sides {

            // Compute side normal.
            let mut normal = Vector3::<f32>::new(0.0, 0.0, 0.0);
            normal[ side / 2 ] = ( ( ( side % 2 ) * 2 ) as f32 ) - 1.0;

            // Build side vertices.
            for vert in 0..verts_per_side {

                let coord = index_lut[ poly_lut[ vert ] + ( side * 4 ) ];
                vertices.push(Vertex{
                    position: Point3::<f32>::from_homogeneous(self.matrix * Vector4::<f32>::new(
                        (((coord & 2) - 1) as f32) * 0.5,
                        (((coord & 1) * 2 - 1) as f32) * 0.5,
                        ((((coord >> 1) & 2) - 1) as f32) * 0.5,
                        1.0
                    )).into(),
                    normal: (normal_matrix * normal).normalize().into(),
                    texcoord: [
                        ( poly_lut[ vert ] % 2 ) as f32,
                        ( poly_lut[ vert ] / 2 ) as f32,
                    ],
                });
            }
        }

        return Ok(vertices);
    }
}
<commit_msg>Adding unit tests for cuboid builder<commit_after>//! A module for constructing cuboid shapes.

extern crate cgmath;
extern crate glium;

use cgmath::*;
use errors::ShapeCreationError;
use vertex::Vertex;

/// A polygonal `Cuboid` object.
///
/// This object is constructed using a `CuboidBuilder` object.
pub struct Cuboid {
    vertices: glium::vertex::VertexBufferAny
}

/// Allows a `Cuboid` object to be passed as a source of vertices.
impl<'a> glium::vertex::IntoVerticesSource<'a> for &'a Cuboid {
    fn into_vertices_source(self) -> glium::vertex::VerticesSource<'a> {
        return self.vertices.into_vertices_source();
    }
}

/// Allows a `Cuboid` object to be passed as a source of indices.
impl<'a> Into<glium::index::IndicesSource<'a>> for &'a Cuboid {
    fn into(self) -> glium::index::IndicesSource<'a> {
        return glium::index::IndicesSource::NoIndices{
            primitives: glium::index::PrimitiveType::TrianglesList
        };
    }
}

/// Responsible for building and returning a `Cuboid` object.
///
/// By default, the cuboid is defined as a unit-cube with its centre-of-mass
/// located at the origin. This can be overriden using the transformation
/// methods on this object.
///
/// The resultant geometry is constructed to suit OpenGL defaults - assuming
/// a right-handed coordinate system, front-facing polygons are defined in
/// counter-clock-wise order. Vertex normals point in the direction of their
/// respective face (such that the cuboid appears faceted when lit). Vertex
/// texture coordinates define a planar-projection on each face.
pub struct CuboidBuilder {
    matrix: cgmath::Matrix4<f32>
}

impl Default for CuboidBuilder {
    fn default() -> Self {
        CuboidBuilder {
            matrix: cgmath::Matrix4::<f32>::identity()
        }
    }
}

impl CuboidBuilder {

    /// Create a new `CuboidBuilder` object.
    pub fn new() -> CuboidBuilder {
        Default::default()
    }

    /// Apply a scaling transformation to the shape.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn scale(mut self, x: f32, y: f32, z: f32) -> Self {
        self.matrix = cgmath::Matrix4::from_nonuniform_scale(x, y, z) * self.matrix;
        return self;
    }

    /// Apply a translation transformation to the shape.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn translate(mut self, x: f32, y:f32, z: f32) -> Self {
        self.matrix = cgmath::Matrix4::from_translation([x, y, z].into()) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the x-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_x(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_x(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the y-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_y(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_y(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Apply a rotation transformation to the shape about the z-axis.
    ///
    /// The `scale`, `translate`, and `rotate` functions accumulate, and are
    /// not commutative. The transformation functions are intended to provide
    /// flexibility in model-space. For per-instance world-space transformations,
    /// one should prefer to share as few shapes as possible across multiple
    /// instances, and instead rely on uniform constants in the shader and/or
    /// instanced drawing.
    pub fn rotate_z(mut self, radians: f32) -> Self {
        self.matrix = cgmath::Matrix4::<f32>::from(
            cgmath::Matrix3::<f32>::from_angle_z(
                cgmath::Rad::<f32>::new(radians)
            )
        ) * self.matrix;
        return self;
    }

    /// Build a new `Cuboid` object.
    pub fn build<F>(self, display: &F) -> Result<Cuboid, ShapeCreationError>
    where F:glium::backend::Facade {
        let vertices = try!(glium::vertex::VertexBuffer::<Vertex>::new(
            display, &try!(self.build_vertices())
        ));

        Ok(Cuboid {
            vertices: glium::vertex::VertexBufferAny::from(vertices),
        })
    }

    /// Build the cube vertices and return them in a vector.
    ///
    /// Useful if you wish to do other things with the vertices besides constructing
    /// a `Cuboid` object (e.g. unit testing, further processing, etc).
    pub fn build_vertices(&self) -> Result<Vec<Vertex>, ShapeCreationError> {

        // Define lookup-tables used during construction of the cuboid geometry
        let index_lut = [
            0, 4, 1, 5,
            6, 2, 7, 3,
            0, 2, 4, 6,
            5, 7, 1, 3,
            2, 0, 3, 1,
            4, 6, 5, 7,
        ];
        let poly_lut = [0, 1, 2, 2, 1, 3];
        let num_sides = 6;
        let verts_per_side = 6;

        // Compute the normal transformation matrix.
        let normal_matrix = Matrix3::<f32>::from_cols(
            self.matrix.x.truncate(),
            self.matrix.y.truncate(),
            self.matrix.z.truncate()
        ).invert().unwrap_or(Matrix3::<f32>::identity()).transpose();

        // Generate cuboid vertices.
        let mut vertices = Vec::<Vertex>::with_capacity(
            verts_per_side * num_sides
        );

        for side in 0..num_sides {

            // Compute side normal.
            let mut normal = Vector3::<f32>::new(0.0, 0.0, 0.0);
            normal[ side / 2 ] = ( ( ( side % 2 ) * 2 ) as f32 ) - 1.0;

            // Build side vertices.
            for vert in 0..verts_per_side {

                let coord = index_lut[ poly_lut[ vert ] + ( side * 4 ) ];
                vertices.push(Vertex{
                    position: Point3::<f32>::from_homogeneous(self.matrix * Vector4::<f32>::new(
                        (((coord & 2) - 1) as f32) * 0.5,
                        (((coord & 1) * 2 - 1) as f32) * 0.5,
                        ((((coord >> 1) & 2) - 1) as f32) * 0.5,
                        1.0
                    )).into(),
                    normal: (normal_matrix * normal).normalize().into(),
                    texcoord: [
                        ( poly_lut[ vert ] % 2 ) as f32,
                        ( poly_lut[ vert ] / 2 ) as f32,
                    ],
                });
            }
        }

        return Ok(vertices);
    }
}

#[test]
pub fn ensure_default_cuboid_has_unit_dimensions() {
    let vertices = CuboidBuilder::new()
                   .build_vertices()
                   .expect("Failed to build vertices");
    for ref vertex in vertices {
        assert_eq!(vertex.position[0].abs(), 0.5);
        assert_eq!(vertex.position[1].abs(), 0.5);
        assert_eq!(vertex.position[2].abs(), 0.5);
    }
}

#[test]
pub fn ensure_default_cuboid_has_centroid_at_origin() {
    let vertices = CuboidBuilder::new()
                   .build_vertices()
                   .expect("Failed to build vertices");
    let mut sum = Vector3::<f32>::zero();
    for ref vertex in vertices {
        sum = sum + Vector3::<f32>::from(vertex.position);
    }
    assert_eq!(sum, Vector3::<f32>::zero());
}

#[test]
pub fn ensure_default_cuboid_has_outward_facing_normals() {
    let vertices = CuboidBuilder::new()
                   .scale(2.0, 2.0, 2.0)
                   .build_vertices()
                   .expect("Failed to build vertices");
    for ref vertex in vertices {
        let position = Vector3::<f32>::from(vertex.position);
        let normal = Vector3::<f32>::from(vertex.normal);
        let outside = position + normal;
        assert!(outside.x.abs() >= position.x.abs());
        assert!(outside.y.abs() >= position.y.abs());
        assert!(outside.z.abs() >= position.z.abs());
    }
}

#[test]
pub fn ensure_default_cuboid_has_uvs_in_unit_range() {
    let vertices = CuboidBuilder::new()
                   .build_vertices()
                   .expect("Failed to build vertices");
    for ref vertex in vertices {
        assert!(vertex.texcoord[0] >= 0.0);
        assert!(vertex.texcoord[1] >= 0.0);
        assert!(vertex.texcoord[0] <= 1.0);
        assert!(vertex.texcoord[1] <= 1.0);
    }
}

#[test]
pub fn ensure_default_cuboid_has_ccw_triangles() {
    let vertices = CuboidBuilder::new()
                   .build_vertices()
                   .expect("Failed to build vertices");
    for chunk in vertices.chunks(3) {
        let v0 = Vector3::<f32>::from(chunk[0].position);
        let v1 = Vector3::<f32>::from(chunk[1].position);
        let v2 = Vector3::<f32>::from(chunk[2].position);
        let e0 = v1 - v0;
        let e1 = v2 - v0;
        let n = e0.cross(e1);
        assert!(n.dot(v0) > 0.0);
        assert!(n.dot(v1) > 0.0);
        assert!(n.dot(v2) > 0.0);
    }
}

#[test]
pub fn ensure_default_cuboid_has_faceted_normals() {
    let vertices = CuboidBuilder::new()
                   .build_vertices()
                   .expect("Failed to build vertices");
    for chunk in vertices.chunks(3) {
        let v0 = Vector3::<f32>::from(chunk[0].position);
        let v1 = Vector3::<f32>::from(chunk[1].position);
        let v2 = Vector3::<f32>::from(chunk[2].position);
        let n0 = Vector3::<f32>::from(chunk[0].normal);
        let n1 = Vector3::<f32>::from(chunk[1].normal);
        let n2 = Vector3::<f32>::from(chunk[2].normal);
        let e0 = v1 - v0;
        let e1 = v2 - v0;
        let n = e0.cross(e1);
        assert_eq!(n, n0);
        assert_eq!(n, n1);
        assert_eq!(n, n2);
    }
}
<|endoftext|>


<commit_before>//! Database operations

pub use self::add_package::add_package_into_database;
pub use self::add_package::add_build_into_database;
pub use self::file::add_path_into_database;

use postgres::{Connection, SslMode};
use postgres::error::{Error, ConnectError};
use std::env;

mod add_package;
mod file;

/// Connects to database
pub fn connect_db() -> Result<Connection, ConnectError> {
    // FIXME: unwrap might not be the best here
    let db_url = env::var("CRATESFYI_DATABASE_URL")
        .expect("CRATESFYI_DATABASE_URL environment variable is not exists");
    Connection::connect(&db_url[..], SslMode::None)
}

/// Creates database tables
pub fn create_tables(conn: &Connection) -> Result<(), Error> {
    let queries = [
        "CREATE TABLE crates ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255) UNIQUE NOT NULL, \
            latest_version_id INT DEFAULT 0, \
            versions JSON DEFAULT '[]', \
            downloads_total INT DEFAULT 0, \
            github_description VARCHAR(1024) \
            github_stars INT DEFAULT 0, \
            github_forks INT DEFAULT 0, \
            github_issues INT DEFAULT 0, \
            github_last_commit TIMESTAMP \
            github_last_update TIMESTAMP \
        )",
        "CREATE TABLE releases ( \
            id SERIAL PRIMARY KEY, \
            crate_id INT NOT NULL REFERENCES crates(id), \
            version VARCHAR(100), \
            release_time TIMESTAMP, \
            dependencies JSON, \
            target_name VARCHAR(255), \
            yanked BOOL DEFAULT FALSE, \
            build_status BOOL DEFAULT FALSE, \
            rustdoc_status BOOL DEFAULT FALSE, \
            test_status BOOL DEFAULT FALSE, \
            license VARCHAR(100), \
            repository_url VARCHAR(255), \
            homepage_url VARCHAR(255), \
            description VARCHAR(1024), \
            description_long VARCHAR(51200), \
            readme VARCHAR(51200), \
            authors JSON, \
            keywords JSON, \
            have_examples BOOL DEFAULT FALSE, \
            downloads INT DEFAULT 0, \
            UNIQUE (crate_id, version) \
        )",
        "CREATE TABLE authors ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            email VARCHAR(255), \
            slug VARCHAR(255) UNIQUE NOT NULL \
        )",
        "CREATE TABLE author_rels ( \
            rid INT REFERENCES releases(id), \
            aid INT REFERENCES authors(id), \
            UNIQUE(rid, aid) \
        )",
        "CREATE TABLE keywords ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            slug VARCHAR(255) NOT NULL UNIQUE \
        )",
        "CREATE TABLE keyword_rels ( \
            rid INT REFERENCES releases(id), \
            kid INT REFERENCES keywords(id), \
            UNIQUE(rid, kid) \
        )",
        "CREATE TABLE owners ( \
            id SERIAL PRIMARY KEY, \
            login VARCHAR(255) NOT NULL UNIQUE, \
            avatar VARCHAR(255), \
            name VARCHAR(255), \
            email VARCHAR(255) \
        )",
        "CREATE TABLE owner_rels ( \
            cid INT REFERENCES releases(id), \
            oid INT REFERENCES owners(id), \
            UNIQUE(cid, oid) \
        )",
        "CREATE TABLE builds ( \
            id SERIAL, \
            rid INT NOT NULL REFERENCES releases(id), \
            rustc_version VARCHAR(100) NOT NULL, \
            cratesfyi_version VARCHAR(100) NOT NULL, \
            build_status BOOL NOT NULL, \
            build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            output TEXT \
        )",
        "CREATE TABLE queue ( \
            id SERIAL, \
            name VARCHAR(255), \
            version VARCHAR(100), \
            UNIQUE(name, version) \
        )",
        "CREATE TABLE files ( \
            path VARCHAR(255) NOT NULL PRIMARY KEY, \
            mime VARCHAR(100) NOT NULL, \
            date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            content BYTEA \
        )",
    ];

    for query in queries.into_iter() {
        try!(conn.execute(query, &[]));
    }

    Ok(())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_connect_db() {
        let conn = connect_db();
        assert!(conn.is_ok());
    }

    #[test]
    #[ignore]
    fn test_create_tables() {
        let _ = env_logger::init();
        let conn = connect_db();
        assert!(conn.is_ok());

        // FIXME: As expected this test always fails if database is already created
        let res = create_tables(&conn.unwrap());
        info!("RES: {:#?}", res);
        assert!(res.is_ok());
    }
}
<commit_msg>Fix syntax error in SQL query<commit_after>//! Database operations

pub use self::add_package::add_package_into_database;
pub use self::add_package::add_build_into_database;
pub use self::file::add_path_into_database;

use postgres::{Connection, SslMode};
use postgres::error::{Error, ConnectError};
use std::env;

mod add_package;
mod file;

/// Connects to database
pub fn connect_db() -> Result<Connection, ConnectError> {
    // FIXME: unwrap might not be the best here
    let db_url = env::var("CRATESFYI_DATABASE_URL")
        .expect("CRATESFYI_DATABASE_URL environment variable is not exists");
    Connection::connect(&db_url[..], SslMode::None)
}

/// Creates database tables
pub fn create_tables(conn: &Connection) -> Result<(), Error> {
    let queries = [
        "CREATE TABLE crates ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255) UNIQUE NOT NULL, \
            latest_version_id INT DEFAULT 0, \
            versions JSON DEFAULT '[]', \
            downloads_total INT DEFAULT 0, \
            github_description VARCHAR(1024), \
            github_stars INT DEFAULT 0, \
            github_forks INT DEFAULT 0, \
            github_issues INT DEFAULT 0, \
            github_last_commit TIMESTAMP, \
            github_last_update TIMESTAMP \
        )",
        "CREATE TABLE releases ( \
            id SERIAL PRIMARY KEY, \
            crate_id INT NOT NULL REFERENCES crates(id), \
            version VARCHAR(100), \
            release_time TIMESTAMP, \
            dependencies JSON, \
            target_name VARCHAR(255), \
            yanked BOOL DEFAULT FALSE, \
            build_status BOOL DEFAULT FALSE, \
            rustdoc_status BOOL DEFAULT FALSE, \
            test_status BOOL DEFAULT FALSE, \
            license VARCHAR(100), \
            repository_url VARCHAR(255), \
            homepage_url VARCHAR(255), \
            description VARCHAR(1024), \
            description_long VARCHAR(51200), \
            readme VARCHAR(51200), \
            authors JSON, \
            keywords JSON, \
            have_examples BOOL DEFAULT FALSE, \
            downloads INT DEFAULT 0, \
            UNIQUE (crate_id, version) \
        )",
        "CREATE TABLE authors ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            email VARCHAR(255), \
            slug VARCHAR(255) UNIQUE NOT NULL \
        )",
        "CREATE TABLE author_rels ( \
            rid INT REFERENCES releases(id), \
            aid INT REFERENCES authors(id), \
            UNIQUE(rid, aid) \
        )",
        "CREATE TABLE keywords ( \
            id SERIAL PRIMARY KEY, \
            name VARCHAR(255), \
            slug VARCHAR(255) NOT NULL UNIQUE \
        )",
        "CREATE TABLE keyword_rels ( \
            rid INT REFERENCES releases(id), \
            kid INT REFERENCES keywords(id), \
            UNIQUE(rid, kid) \
        )",
        "CREATE TABLE owners ( \
            id SERIAL PRIMARY KEY, \
            login VARCHAR(255) NOT NULL UNIQUE, \
            avatar VARCHAR(255), \
            name VARCHAR(255), \
            email VARCHAR(255) \
        )",
        "CREATE TABLE owner_rels ( \
            cid INT REFERENCES releases(id), \
            oid INT REFERENCES owners(id), \
            UNIQUE(cid, oid) \
        )",
        "CREATE TABLE builds ( \
            id SERIAL, \
            rid INT NOT NULL REFERENCES releases(id), \
            rustc_version VARCHAR(100) NOT NULL, \
            cratesfyi_version VARCHAR(100) NOT NULL, \
            build_status BOOL NOT NULL, \
            build_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            output TEXT \
        )",
        "CREATE TABLE queue ( \
            id SERIAL, \
            name VARCHAR(255), \
            version VARCHAR(100), \
            UNIQUE(name, version) \
        )",
        "CREATE TABLE files ( \
            path VARCHAR(255) NOT NULL PRIMARY KEY, \
            mime VARCHAR(100) NOT NULL, \
            date_added TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            date_updated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, \
            content BYTEA \
        )",
    ];

    for query in queries.into_iter() {
        try!(conn.execute(query, &[]));
    }

    Ok(())
}

#[cfg(test)]
mod test {
    extern crate env_logger;
    use super::*;

    #[test]
    #[ignore]
    fn test_connect_db() {
        let conn = connect_db();
        assert!(conn.is_ok());
    }

    #[test]
    #[ignore]
    fn test_create_tables() {
        let _ = env_logger::init();
        let conn = connect_db();
        assert!(conn.is_ok());

        // FIXME: As expected this test always fails if database is already created
        let res = create_tables(&conn.unwrap());
        info!("RES: {:#?}", res);
        assert!(res.is_ok());
    }
}
<|endoftext|>