<commit_before>use crate::config::PgpPublicKey;
use crate::dist::dist::TargetTriple;
use crate::dist::manifest::Component;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::notify::NotificationLevel;
use std::fmt::{self, Display};
use std::path::Path;

#[derive(Debug)]
pub enum Notification<'a> {
    Utils(crate::utils::Notification<'a>),
    Temp(temp::Notification<'a>),

    Extracting(&'a Path, &'a Path),
    ComponentAlreadyInstalled(&'a str),
    CantReadUpdateHash(&'a Path),
    NoUpdateHash(&'a Path),
    ChecksumValid(&'a str),
    SignatureValid(&'a str, &'a PgpPublicKey),
    FileAlreadyDownloaded,
    CachedFileChecksumFailed,
    RollingBack,
    ExtensionNotInstalled(&'a str),
    NonFatalError(&'a Error),
    MissingInstalledComponent(&'a str),
    DownloadingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    InstallingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    RemovingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    RemovingOldComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    DownloadingManifest(&'a str),
    DownloadedManifest(&'a str, Option<&'a str>),
    DownloadingLegacyManifest,
    SkippingNightlyMissingComponent(&'a [Component]),
    ForcingUnavailableComponent(&'a str),
    ManifestChecksumFailedHack,
    ComponentUnavailable(&'a str, Option<&'a TargetTriple>),
    StrayHash(&'a Path),
    SignatureInvalid(&'a str),
    RetryingDownload(&'a str),
}

impl<'a> From<crate::utils::Notification<'a>> for Notification<'a> {
    fn from(n: crate::utils::Notification<'a>) -> Self {
        Notification::Utils(n)
    }
}

impl<'a> From<temp::Notification<'a>> for Notification<'a> {
    fn from(n: temp::Notification<'a>) -> Self {
        Notification::Temp(n)
    }
}

impl<'a> Notification<'a> {
    pub fn level(&self) -> NotificationLevel {
        use self::Notification::*;
        match self {
            Temp(n) => n.level(),
            Utils(n) => n.level(),
            ChecksumValid(_)
            | SignatureValid(_, _)
            | NoUpdateHash(_)
            | FileAlreadyDownloaded
            | DownloadingLegacyManifest => NotificationLevel::Verbose,
            Extracting(_, _)
            | DownloadingComponent(_, _, _)
            | InstallingComponent(_, _, _)
            | RemovingComponent(_, _, _)
            | RemovingOldComponent(_, _, _)
            | ComponentAlreadyInstalled(_)
            | ManifestChecksumFailedHack
            | RollingBack
            | DownloadingManifest(_)
            | SkippingNightlyMissingComponent(_)
            | RetryingDownload(_)
            | DownloadedManifest(_, _) => NotificationLevel::Info,
            CantReadUpdateHash(_)
            | ExtensionNotInstalled(_)
            | MissingInstalledComponent(_)
            | CachedFileChecksumFailed
            | ComponentUnavailable(_, _)
            | ForcingUnavailableComponent(_)
            | StrayHash(_) => NotificationLevel::Warn,
            NonFatalError(_) => NotificationLevel::Error,
            SignatureInvalid(_) => NotificationLevel::Warn,
        }
    }
}

impl<'a> Display for Notification<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
        use self::Notification::*;
        match self {
            Temp(n) => n.fmt(f),
            Utils(n) => n.fmt(f),
            Extracting(_, _) => write!(f, "extracting..."),
            ComponentAlreadyInstalled(c) => write!(f, "component {} is up to date", c),
            CantReadUpdateHash(path) => write!(
                f,
                "can't read update hash file: '{}', can't skip update...",
                path.display()
            ),
            NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()),
            ChecksumValid(_) => write!(f, "checksum passed"),
            SignatureValid(url, key) => (|| {
                writeln!(f, "Good signature from on {} from:", url)?;
                for l in key.show_key().map_err(|_| std::fmt::Error)?.iter() {
                    writeln!(f, "{}", l)?;
                }
                Ok(())
            })(),
            FileAlreadyDownloaded => write!(f, "reusing previously downloaded file"),
            CachedFileChecksumFailed => write!(f, "bad checksum for cached download"),
            RollingBack => write!(f, "rolling back changes"),
            ExtensionNotInstalled(c) => write!(f, "extension '{}' was not installed", c),
            NonFatalError(e) => write!(f, "{}", e),
            MissingInstalledComponent(c) => {
                write!(f, "during uninstall component {} was not found", c)
            }
            DownloadingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "downloading component '{}'", c)
                } else {
                    write!(f, "downloading component '{}' for '{}'", c, t.unwrap())
                }
            }
            InstallingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "installing component '{}'", c)
                } else {
                    write!(f, "installing component '{}' for '{}'", c, t.unwrap())
                }
            }
            RemovingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "removing component '{}'", c)
                } else {
                    write!(f, "removing component '{}' for '{}'", c, t.unwrap())
                }
            }
            RemovingOldComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "removing previous version of component '{}'", c)
                } else {
                    write!(
                        f,
                        "removing previous version of component '{}' for '{}'",
                        c,
                        t.unwrap()
                    )
                }
            }
            DownloadingManifest(t) => write!(f, "syncing channel updates for '{}'", t),
            DownloadedManifest(date, Some(version)) => {
                write!(f, "latest update on {}, rust version {}", date, version)
            }
            DownloadedManifest(date, None) => {
                write!(f, "latest update on {}, no rust version", date)
            }
            DownloadingLegacyManifest => write!(f, "manifest not found. trying legacy manifest"),
            ManifestChecksumFailedHack => {
                write!(f, "update not yet available, sorry! try again later")
            }
            ComponentUnavailable(pkg, toolchain) => {
                if let Some(tc) = toolchain {
                    write!(f, "component '{}' is not available on target '{}'", pkg, tc)
                } else {
                    write!(f, "component '{}' is not available", pkg)
                }
            }
            StrayHash(path) => write!(
                f,
                "removing stray hash found at '{}' in order to continue",
                path.display()
            ),
            SkippingNightlyMissingComponent(components) => write!(
                f,
                "skipping nightly which is missing installed component '{}'",
                components[0].short_name_in_manifest()
            ),
            ForcingUnavailableComponent(component) => {
                write!(f, "Force-skipping unavailable component '{}'", component)
            }
            SignatureInvalid(url) => write!(f, "Signature verification failed for '{}'", url),
            RetryingDownload(url) => write!(f, "Retrying download for '{}'", url),
        }
    }
}
<commit_msg>notifications: list all missing components when skipping nightlies<commit_after>use crate::config::PgpPublicKey;
use crate::dist::dist::TargetTriple;
use crate::dist::manifest::Component;
use crate::dist::temp;
use crate::errors::*;
use crate::utils::notify::NotificationLevel;
use std::fmt::{self, Display};
use std::path::Path;

#[derive(Debug)]
pub enum Notification<'a> {
    Utils(crate::utils::Notification<'a>),
    Temp(temp::Notification<'a>),

    Extracting(&'a Path, &'a Path),
    ComponentAlreadyInstalled(&'a str),
    CantReadUpdateHash(&'a Path),
    NoUpdateHash(&'a Path),
    ChecksumValid(&'a str),
    SignatureValid(&'a str, &'a PgpPublicKey),
    FileAlreadyDownloaded,
    CachedFileChecksumFailed,
    RollingBack,
    ExtensionNotInstalled(&'a str),
    NonFatalError(&'a Error),
    MissingInstalledComponent(&'a str),
    DownloadingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    InstallingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    RemovingComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    RemovingOldComponent(&'a str, &'a TargetTriple, Option<&'a TargetTriple>),
    DownloadingManifest(&'a str),
    DownloadedManifest(&'a str, Option<&'a str>),
    DownloadingLegacyManifest,
    SkippingNightlyMissingComponent(&'a [Component]),
    ForcingUnavailableComponent(&'a str),
    ManifestChecksumFailedHack,
    ComponentUnavailable(&'a str, Option<&'a TargetTriple>),
    StrayHash(&'a Path),
    SignatureInvalid(&'a str),
    RetryingDownload(&'a str),
}

impl<'a> From<crate::utils::Notification<'a>> for Notification<'a> {
    fn from(n: crate::utils::Notification<'a>) -> Self {
        Notification::Utils(n)
    }
}

impl<'a> From<temp::Notification<'a>> for Notification<'a> {
    fn from(n: temp::Notification<'a>) -> Self {
        Notification::Temp(n)
    }
}

impl<'a> Notification<'a> {
    pub fn level(&self) -> NotificationLevel {
        use self::Notification::*;
        match self {
            Temp(n) => n.level(),
            Utils(n) => n.level(),
            ChecksumValid(_)
            | SignatureValid(_, _)
            | NoUpdateHash(_)
            | FileAlreadyDownloaded
            | DownloadingLegacyManifest => NotificationLevel::Verbose,
            Extracting(_, _)
            | DownloadingComponent(_, _, _)
            | InstallingComponent(_, _, _)
            | RemovingComponent(_, _, _)
            | RemovingOldComponent(_, _, _)
            | ComponentAlreadyInstalled(_)
            | ManifestChecksumFailedHack
            | RollingBack
            | DownloadingManifest(_)
            | SkippingNightlyMissingComponent(_)
            | RetryingDownload(_)
            | DownloadedManifest(_, _) => NotificationLevel::Info,
            CantReadUpdateHash(_)
            | ExtensionNotInstalled(_)
            | MissingInstalledComponent(_)
            | CachedFileChecksumFailed
            | ComponentUnavailable(_, _)
            | ForcingUnavailableComponent(_)
            | StrayHash(_) => NotificationLevel::Warn,
            NonFatalError(_) => NotificationLevel::Error,
            SignatureInvalid(_) => NotificationLevel::Warn,
        }
    }
}

impl<'a> Display for Notification<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> std::result::Result<(), fmt::Error> {
        use self::Notification::*;
        match self {
            Temp(n) => n.fmt(f),
            Utils(n) => n.fmt(f),
            Extracting(_, _) => write!(f, "extracting..."),
            ComponentAlreadyInstalled(c) => write!(f, "component {} is up to date", c),
            CantReadUpdateHash(path) => write!(
                f,
                "can't read update hash file: '{}', can't skip update...",
                path.display()
            ),
            NoUpdateHash(path) => write!(f, "no update hash at: '{}'", path.display()),
            ChecksumValid(_) => write!(f, "checksum passed"),
            SignatureValid(url, key) => (|| {
                writeln!(f, "Good signature from on {} from:", url)?;
                for l in key.show_key().map_err(|_| std::fmt::Error)?.iter() {
                    writeln!(f, "{}", l)?;
                }
                Ok(())
            })(),
            FileAlreadyDownloaded => write!(f, "reusing previously downloaded file"),
            CachedFileChecksumFailed => write!(f, "bad checksum for cached download"),
            RollingBack => write!(f, "rolling back changes"),
            ExtensionNotInstalled(c) => write!(f, "extension '{}' was not installed", c),
            NonFatalError(e) => write!(f, "{}", e),
            MissingInstalledComponent(c) => {
                write!(f, "during uninstall component {} was not found", c)
            }
            DownloadingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "downloading component '{}'", c)
                } else {
                    write!(f, "downloading component '{}' for '{}'", c, t.unwrap())
                }
            }
            InstallingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "installing component '{}'", c)
                } else {
                    write!(f, "installing component '{}' for '{}'", c, t.unwrap())
                }
            }
            RemovingComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "removing component '{}'", c)
                } else {
                    write!(f, "removing component '{}' for '{}'", c, t.unwrap())
                }
            }
            RemovingOldComponent(c, h, t) => {
                if Some(h) == t.as_ref() || t.is_none() {
                    write!(f, "removing previous version of component '{}'", c)
                } else {
                    write!(
                        f,
                        "removing previous version of component '{}' for '{}'",
                        c,
                        t.unwrap()
                    )
                }
            }
            DownloadingManifest(t) => write!(f, "syncing channel updates for '{}'", t),
            DownloadedManifest(date, Some(version)) => {
                write!(f, "latest update on {}, rust version {}", date, version)
            }
            DownloadedManifest(date, None) => {
                write!(f, "latest update on {}, no rust version", date)
            }
            DownloadingLegacyManifest => write!(f, "manifest not found. trying legacy manifest"),
            ManifestChecksumFailedHack => {
                write!(f, "update not yet available, sorry! try again later")
            }
            ComponentUnavailable(pkg, toolchain) => {
                if let Some(tc) = toolchain {
                    write!(f, "component '{}' is not available on target '{}'", pkg, tc)
                } else {
                    write!(f, "component '{}' is not available", pkg)
                }
            }
            StrayHash(path) => write!(
                f,
                "removing stray hash found at '{}' in order to continue",
                path.display()
            ),
            SkippingNightlyMissingComponent(components) => write!(
                f,
                "skipping nightly which is missing installed component{} '{}'",
                if components.len() > 1 { "s" } else { "" },
                components
                    .iter()
                    .map(|component| component.short_name_in_manifest().to_owned())
                    .collect::<Vec<_>>()
                    .join("', '")
            ),
            ForcingUnavailableComponent(component) => {
                write!(f, "Force-skipping unavailable component '{}'", component)
            }
            SignatureInvalid(url) => write!(f, "Signature verification failed for '{}'", url),
            RetryingDownload(url) => write!(f, "Retrying download for '{}'", url),
        }
    }
}
<|endoftext|>


<commit_before>

use std::{env, fmt};
use std::path::PathBuf;

#[derive(Clone)]
pub struct DocBuilderOptions {
    pub keep_build_directory: bool,
    pub prefix: PathBuf,
    pub destination: PathBuf,
    pub chroot_path: PathBuf,
    pub chroot_user: String,
    pub container_name: String,
    pub crates_io_index_path: PathBuf,
    pub sources_path: PathBuf,
    pub skip_if_exists: bool,
    pub skip_if_log_exists: bool,
    pub skip_oldest_versions: bool,
    pub build_only_latest_version: bool,
    pub debug: bool,
}

// This error only occurs if check_dirs fails
pub enum DocBuilderPathError {
    DestinationPathNotExists,
    ChrootPathNotExists,
    BuildDirectoryNotExists,
    CratesIoIndexPathNotExists,
    LogsPathNotExists,
}

impl fmt::Debug for DocBuilderPathError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            DocBuilderPathError::DestinationPathNotExists =>
                write!(f, "Destination path not exists"),
            DocBuilderPathError::ChrootPathNotExists =>
                write!(f, "Chroot path not exists"),
            DocBuilderPathError::BuildDirectoryNotExists =>
                write!(f, "Build directory path not exists"),
            DocBuilderPathError::CratesIoIndexPathNotExists =>
                write!(f, "crates.io-index path not exists"),
            DocBuilderPathError::LogsPathNotExists =>
                write!(f, "Logs path not exists"),
        }
    }
}

impl Default for DocBuilderOptions {
    fn default() -> DocBuilderOptions {

        let cwd = env::current_dir().unwrap();

        let (prefix, destination, chroot_path, crates_io_index_path, sources_path) =
            generate_paths(cwd);

        DocBuilderOptions {
            prefix: prefix,
            destination: destination,
            chroot_path: chroot_path,
            crates_io_index_path: crates_io_index_path,
            sources_path: sources_path,

            chroot_user: "cratesfyi".to_string(),
            container_name: "cratesfyi-container".to_string(),

            keep_build_directory: false,
            skip_if_exists: false,
            skip_if_log_exists: false,
            skip_oldest_versions: false,
            build_only_latest_version: false,
            debug: false,
        }
    }
}

impl fmt::Debug for DocBuilderOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "DocBuilderOptions {{ destination: {:?}, chroot_path: {:?}, \
                crates_io_index_path: {:?}, \
                sources_path: {:?}, chroot_user: {:?}, \
                keep_build_directory: {:?}, skip_if_exists: {:?}, \
                skip_if_log_exists: {:?}, debug: {:?} }}",
                self.destination,
                self.chroot_path,
                self.crates_io_index_path,
                self.sources_path,
                self.chroot_user,
                self.keep_build_directory,
                self.skip_if_exists,
                self.skip_if_log_exists,
                self.debug)
    }
}

impl DocBuilderOptions {

    /// Creates new DocBuilderOptions from prefix
    pub fn from_prefix(prefix: PathBuf) -> DocBuilderOptions {
        let (prefix, destination, chroot_path, crates_io_index_path, sources_path) =
            generate_paths(prefix);
        DocBuilderOptions {
            prefix: prefix,
            destination: destination,
            chroot_path: chroot_path,
            crates_io_index_path: crates_io_index_path,
            sources_path: sources_path,

            .. Default::default()
        }
    }

    pub fn check_paths(&self) -> Result<(), DocBuilderPathError> {
        if !self.destination.exists() {
            return Err(DocBuilderPathError::DestinationPathNotExists)
        }
        if !self.chroot_path.exists() {
            return Err(DocBuilderPathError::ChrootPathNotExists)
        }
        if !self.crates_io_index_path.exists() {
            return Err(DocBuilderPathError::CratesIoIndexPathNotExists)
        }
        if !self.crates_io_index_path.exists() {
            return Err(DocBuilderPathError::LogsPathNotExists)
        }
        Ok(())
    }

}

fn generate_paths(prefix: PathBuf) -> (PathBuf, PathBuf, PathBuf, PathBuf, PathBuf) {

    let destination = PathBuf::from(&prefix).join("documentations");
    let chroot_path = PathBuf::from(&prefix).join("cratesfyi-container/rootfs");
    let crates_io_index_path = PathBuf::from(&prefix).join("crates.io-index");
    let sources_path = PathBuf::from(&prefix).join("sources");

    (prefix, destination, chroot_path, crates_io_index_path, sources_path)
}
<commit_msg>Remove DocBuilderPathError<commit_after>

use std::{env, fmt};
use std::path::PathBuf;
use errors::*;

#[derive(Clone)]
pub struct DocBuilderOptions {
    pub keep_build_directory: bool,
    pub prefix: PathBuf,
    pub destination: PathBuf,
    pub chroot_path: PathBuf,
    pub chroot_user: String,
    pub container_name: String,
    pub crates_io_index_path: PathBuf,
    pub sources_path: PathBuf,
    pub skip_if_exists: bool,
    pub skip_if_log_exists: bool,
    pub skip_oldest_versions: bool,
    pub build_only_latest_version: bool,
    pub debug: bool,
}

impl Default for DocBuilderOptions {
    fn default() -> DocBuilderOptions {

        let cwd = env::current_dir().unwrap();

        let (prefix, destination, chroot_path, crates_io_index_path, sources_path) =
            generate_paths(cwd);

        DocBuilderOptions {
            prefix: prefix,
            destination: destination,
            chroot_path: chroot_path,
            crates_io_index_path: crates_io_index_path,
            sources_path: sources_path,

            chroot_user: "cratesfyi".to_string(),
            container_name: "cratesfyi-container".to_string(),

            keep_build_directory: false,
            skip_if_exists: false,
            skip_if_log_exists: false,
            skip_oldest_versions: false,
            build_only_latest_version: false,
            debug: false,
        }
    }
}

impl fmt::Debug for DocBuilderOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "DocBuilderOptions {{ destination: {:?}, chroot_path: {:?}, \
                crates_io_index_path: {:?}, \
                sources_path: {:?}, chroot_user: {:?}, \
                keep_build_directory: {:?}, skip_if_exists: {:?}, \
                skip_if_log_exists: {:?}, debug: {:?} }}",
                self.destination,
                self.chroot_path,
                self.crates_io_index_path,
                self.sources_path,
                self.chroot_user,
                self.keep_build_directory,
                self.skip_if_exists,
                self.skip_if_log_exists,
                self.debug)
    }
}

impl DocBuilderOptions {

    /// Creates new DocBuilderOptions from prefix
    pub fn from_prefix(prefix: PathBuf) -> DocBuilderOptions {
        let (prefix, destination, chroot_path, crates_io_index_path, sources_path) =
            generate_paths(prefix);
        DocBuilderOptions {
            prefix: prefix,
            destination: destination,
            chroot_path: chroot_path,
            crates_io_index_path: crates_io_index_path,
            sources_path: sources_path,

            .. Default::default()
        }
    }

    pub fn check_paths(&self) -> Result<()> {
        if !self.destination.exists() {
            return Err("Destination path not exists".into())
        }
        if !self.chroot_path.exists() {
            return Err("Chroot path not exists".into())
        }
        if !self.crates_io_index_path.exists() {
            return Err("crates.io-index path not exists".into())
        }
        if !self.crates_io_index_path.exists() {
            return Err("Logs path not exists".into())
        }
        Ok(())
    }

}

fn generate_paths(prefix: PathBuf) -> (PathBuf, PathBuf, PathBuf, PathBuf, PathBuf) {

    let destination = PathBuf::from(&prefix).join("documentations");
    let chroot_path = PathBuf::from(&prefix).join("cratesfyi-container/rootfs");
    let crates_io_index_path = PathBuf::from(&prefix).join("crates.io-index");
    let sources_path = PathBuf::from(&prefix).join("sources");

    (prefix, destination, chroot_path, crates_io_index_path, sources_path)
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;
type TypeError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.into_iter().zip(args2.into_iter()) {
                let (a1, a2) = iter;

                // TODO: should try to avoid the clone here
                match unify(a1, a2, env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            let body_ty = w(body, env, inst);

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            env.insert(name, expr_ty);

            w(body, env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, env, inst)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, env, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  env)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(1)], Box::new(
                       Type::Lambda(vec![Type::Unbound(2)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<commit_msg>create child environments for let/lambda<commit_after>use std::collections::HashMap;

type FreeVariable = u64;
type VariableName<'a> = &'a str;

/// AST node expression
#[derive(Copy, Clone, Debug, PartialEq)]
enum Expression<'a> {
    Number(f64),
    Boolean(bool),

    /// simple variable
    Variable(VariableName<'a>),

    /// fn (x, y, z) -> expr
    Lambda(&'a [VariableName<'a>], &'a Expression<'a>),

    /// let x = expr in expr
    Let(VariableName<'a>, &'a Expression<'a>, &'a Expression<'a>),

    /// some_fn (x, y, z)
    FnCall(&'a Expression<'a>, &'a [ &'a Expression<'a>])
}

#[derive(Clone, Debug, PartialEq)]
enum Type {
    Number,
    Boolean,
    Unbound(FreeVariable),
    // FIXME: can I avoid having this be boxed and vec'd?
    Lambda(Vec<Type>, Box<Type>),
}

type TypeEnv<'a> = HashMap<VariableName<'a>, Type>;

/// Bind previously unbound type variables to a specified type.
fn bind_type<'a>(env: &mut TypeEnv<'a>, var: FreeVariable, ty: Type) -> Type {
    println!("substitute unbound({}) for {:?}", var, ty);

    for val in env.values_mut() {
        if *val == Type::Unbound(var) {
            *val = ty.clone();
        }
    }

    ty
}

#[derive(Clone, Copy)]
struct VariableGen { current: FreeVariable }
impl VariableGen {
    fn next(&mut self) -> FreeVariable { self.current += 1; self.current }
}

type UnificationError = &'static str;
type TypeError = &'static str;

/// modifies the environment to unify the given types (or returns false if not possible)
fn unify<'a>(a: Type, b: Type, env: &mut TypeEnv<'a>) -> Result<Type, UnificationError> {
    match (a, b) {
        // Easy. Literals match with themselves
        (Type::Number, Type::Number) => Ok(Type::Number),
        (Type::Boolean, Type::Boolean) => Ok(Type::Boolean),

        (Type::Unbound(n), ty) | (ty, Type::Unbound(n)) => Ok(bind_type(env, n, ty)),

        (Type::Lambda(args1, body1), Type::Lambda(args2, body2)) => {
            if args1.len() != args2.len() {
                return Err("lambdas take differing numbers of arguments")
            }

            let mut unified_args = Vec::with_capacity(args1.len());

            for iter in args1.into_iter().zip(args2.into_iter()) {
                match unify(iter.0, iter.1, env) {
                    Ok(ty) => unified_args.push(ty),
                    err => return err
                }
            }

            match unify(*body1, *body2, env) {
                Ok(body_ty) => Ok(Type::Lambda(unified_args, Box::new(body_ty))),
                err => err
            }
        }

        _ => Err("could not unify types")
    }
}

/// Run algorithm W on expr with given type env, yielding the resolved
/// type of the expression, or the type error encountered.
fn w<'a>(expr: &Expression<'a>, env: &mut TypeEnv<'a>, inst: &mut VariableGen) -> Type {
    match *expr {
        // simplest case: literals have immediately defined types.
        Expression::Number(_) => Type::Number,
        Expression::Boolean(_) => Type::Boolean,

        // variables we need to look up inside our type environment
        Expression::Variable(name) => match env.get(name) {
            Some(typ) => typ.clone(),
            None => panic!("undefined variable, {}", name)
        },

        Expression::Lambda(args, body) => {
            // Any parameters or variables defined inside the lambda are scoped
            // only to the lambda.
            let mut child_env = env.clone();

            let args_ty = args
                .into_iter()
                .map(|arg| {
                    let ty = inst.next();
                    child_env.insert(arg, Type::Unbound(ty));
                    Type::Unbound(ty)
                })
                .collect::<Vec<Type>>();

            let body_ty = w(body, &mut child_env, inst);

            Type::Lambda(args_ty, Box::new(body_ty))
        },

        // TODO: need an occurs check here.
        Expression::Let(name, expr, body) => {
            let expr_ty = w(expr, env, inst);
            let mut child_env = env.clone();

            child_env.insert(name, expr_ty);

            w(body, &mut child_env, inst)
        },

        // 1. ensure that the callable in this function call is truly callable.
        // 2. ensure that the number and type of args match
        // 3. return type of body of lambda
        Expression::FnCall(callable, args) => {
            let type_args = args
                .iter()
                .map(|arg| {
                    w(arg, env, inst)
                })
                .collect::<Vec<Type>>();

            // Try to unify the lambda we are calling with the way we are calling it.
            unify(w(callable, env, inst),
                  Type::Lambda(type_args, Box::new(Type::Unbound(inst.next()))),
                  env)
                .map(move |ty|{
                    match ty {
                        Type::Lambda(_, return_type) => *return_type,
                        _ => panic!("BUG: expected unify to return lambda, not {:?}", ty)
                    }
                })
                .or(Err("could not unify lambda types"))
                // FIXME: this should eventually return a result anyway.
                .unwrap()
        }
    }
}

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

    fn infer(exp: Expression) -> Type {
        let mut inst = VariableGen { current: 0 };
        let mut env = HashMap::new();

        w(&exp, &mut env, &mut inst)
    }

    #[test]
    #[should_panic]
    fn undefined_variable() {
        let var = Expression::Variable("x");

        // FIXME: until we return a result type this will panic.
        infer(var);
    }

    #[test]
    fn literal_inference() {
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(true);

        assert_eq!(infer(num), Type::Number);
        assert_eq!(infer(boolean), Type::Boolean);
    }

    #[test]
    fn lambda_expression_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        assert_eq!(infer(lambda), Type::Lambda(vec![Type::Unbound(1)], Box::new(Type::Number)));

        // \x -> \y -> 123.0
        let lambdalambda = Expression::Lambda(&args, &lambda);
        assert_eq!(infer(lambdalambda),
                   Type::Lambda(vec![Type::Unbound(1)], Box::new(
                       Type::Lambda(vec![Type::Unbound(2)], Box::new(Type::Number)))));
    }

    #[test]
    fn let_inference() {
        let boolean = Expression::Boolean(false);
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");

        // let x = false in x
        let let_ = Expression::Let("x", &boolean, &var_x);

        assert_eq!(infer(let_), Type::Boolean);

        // let y = (let x = false in x) in y
        let nested_let = Expression::Let("y", &let_, &var_y);
        assert_eq!(infer(nested_let), Type::Boolean);
    }

    #[test]
    fn fn_call_inference() {
        let args = ["x"];
        let num = Expression::Number(123.0);
        let boolean = Expression::Boolean(false);

        // \x -> 123.0
        let lambda = Expression::Lambda(&args, &num);

        let app_args = [&boolean];
        let application = Expression::FnCall(&lambda, &app_args);

        assert_eq!(infer(application), Type::Number);

        // let
        //    x = (\a -> a)
        // in
        //    let y = (\a -> a)
        //    in
        //       x (y)
        // ===> \a -> a
        let var_a = Expression::Variable("a");
        let var_x = Expression::Variable("x");
        let var_y = Expression::Variable("y");
        let args_a = ["a"];
        let args_y = [&var_y];
        let lambda_x = Expression::Lambda(&args_a, &var_a);
        let lambda_y = Expression::Lambda(&args_a, &var_a);

        let fncall = Expression::FnCall(&var_x, &args_y);

        let let_y = Expression::Let("y", &lambda_y, &fncall);
        let let_x = Expression::Let("x", &lambda_x, &let_y);

        assert_eq!(infer(let_x), Type::Lambda(vec![Type::Unbound(3)], Box::new(Type::Unbound(3))))
    }

    #[test]
    fn test_unify() {
        let mut env = HashMap::new();

        assert_eq!(unify(Type::Number, Type::Number, &mut env), Ok(Type::Number));
        assert!(unify(Type::Number, Type::Boolean, &mut env).is_err());
        assert_eq!(unify(Type::Unbound(1), Type::Number, &mut env), Ok(Type::Number));

        assert_eq!(unify(Type::Lambda(vec![Type::Unbound(1), Type::Unbound(2)], Box::new(Type::Number)),
                         Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number)),
                         &mut env),
                   Ok(Type::Lambda(vec![Type::Unbound(3), Type::Unbound(4)], Box::new(Type::Number))));
    }
}

fn main() {
    println!("patience.");
}
<|endoftext|>


<commit_before>use crate::graphics::*;

use mint;

type Vec3 = na::Vector3<f32>;

/// A struct containing all the necessary info for drawing a [`Drawable`](trait.Drawable.html).
///
/// This struct implements the `Default` trait, so to set only some parameter
/// you can just do:
///
/// ```rust
/// # use ggez::*;
/// # use ggez::graphics::*;
/// # fn t<P>(ctx: &mut Context, drawable: &P) where P: Drawable {
/// let my_dest = nalgebra::Point2::new(13.0, 37.0);
/// graphics::draw(ctx, drawable, DrawParam::default().dest(my_dest) );
/// # }
/// ```
///
/// As a shortcut, it also implements `From` for a variety of tuple types.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DrawParam {
    /// A portion of the drawable to clip, as a fraction of the whole image.
    /// Defaults to the whole image `(0,0 to 1,1)` if omitted.
    pub src: Rect,
    /// The position to draw the graphic expressed as a `Point2`.
    pub dest: mint::Point2<f32>,
    /// The orientation of the graphic in radians.
    pub rotation: f32,
    /// The x/y scale factors expressed as a `Vector2`.
    pub scale: mint::Vector2<f32>,
    /// An offset from the center for transform operations like scale/rotation,
    /// with `0,0` meaning the origin and `1,1` meaning the opposite corner from the origin.
    /// By default these operations are done from the top-left corner, so to rotate something
    /// from the center specify `Point2::new(0.5, 0.5)` here.
    pub offset: mint::Point2<f32>,
    /// A color to draw the target with.
    /// Default: white.
    pub color: Color,
}

impl Default for DrawParam {
    fn default() -> Self {
        DrawParam {
            src: Rect::one(),
            dest: mint::Point2 { x: 0.0, y: 0.0 },
            rotation: 0.0,
            scale: mint::Vector2 { x: 1.0, y: 1.0 },
            offset: mint::Point2 { x: 0.0, y: 0.0 },
            color: WHITE,
        }
    }
}

impl DrawParam {
    /// Create a new DrawParam with default values.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the source rect
    pub fn src(mut self, src: Rect) -> Self {
        self.src = src;
        self
    }

    /// Set the dest point
    pub fn dest<P>(mut self, dest: P) -> Self
    where
        P: Into<mint::Point2<f32>>,
    {
        let p: mint::Point2<f32> = dest.into();
        self.dest = p;
        self
    }

    /// Set the drawable color.  This will be blended with whatever
    /// color the drawn object already is.
    pub fn color(mut self, color: Color) -> Self {
        self.color = color;
        self
    }

    /// Set the rotation of the drawable.
    pub fn rotation(mut self, rotation: f32) -> Self {
        self.rotation = rotation;
        self
    }

    /// Set the scaling factors of the drawable.
    pub fn scale<V>(mut self, scale: V) -> Self
    where
        V: Into<mint::Vector2<f32>>,
    {
        let p: mint::Vector2<f32> = scale.into();
        self.scale = p;
        self
    }

    /// Set the transformation offset of the drawable.
    pub fn offset<P>(mut self, offset: P) -> Self
    where
        P: Into<mint::Point2<f32>>,
    {
        let p: mint::Point2<f32> = offset.into();
        self.offset = p;
        self
    }

    /// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single matrix.
    fn to_na_matrix(&self) -> Matrix4 {
        // Calculate a matrix equivalent to doing this:
        //  let translate = Matrix4::new_translation(&Vec3::new(self.dest.x, self.dest.y, 0.0));
        //  let offset = Matrix4::new_translation(&Vec3::new(self.offset.x, self.offset.y, 0.0));
        //  let offset_inverse =
        //      Matrix4::new_translation(&Vec3::new(-self.offset.x, -self.offset.y, 0.0));
        //  let axis_angle = Vec3::z() * self.rotation;
        //  let rotation = Matrix4::new_rotation(axis_angle);
        //  let scale = Matrix4::new_nonuniform_scaling(&Vec3::new(self.scale.x, self.scale.y, 1.0));
        let cosr = self.rotation.cos();
        let sinr = self.rotation.sin();
        let m00 = cosr * self.scale.x;
        let m01 = -sinr * self.scale.y;
        let m10 = sinr * self.scale.x;
        let m11 = cosr * self.scale.y;
        let m03 = self.offset.x * (1.0 - m00) - self.offset.y * m01 + self.dest.x;
        let m13 = self.offset.y * (1.0 - m11) - self.offset.x * m10 + self.dest.y;
        Matrix4::new(m00, m01, 0.0, m03,  m10, m11, 0.0, m13,  0.0, 0.0, 1.0, 0.0,  0.0, 0.0, 0.0, 1.0)
    }

    /// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single
    ///matrix.  Because of this it only contains the transform part (rotation/scale/etc),
    /// with no src/dest/color info.
    pub fn to_matrix(&self) -> mint::ColumnMatrix4<f32> {
        self.to_na_matrix().into()
    }
}

/// Create a `DrawParam` from a location.
/// Note that this takes a single-element tuple.
/// It's a little weird but keeps the trait implementations
/// from clashing.
impl<P> From<(P,)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from(location: (P,)) -> Self {
        DrawParam::new().dest(location.0)
    }
}

/// Create a `DrawParam` from a location and color
impl<P> From<(P, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, color): (P, Color)) -> Self {
        DrawParam::new().dest(location).color(color)
    }
}

/// Create a `DrawParam` from a location, rotation and color
impl<P> From<(P, f32, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, rotation, color): (P, f32, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .color(color)
    }
}

/// Create a `DrawParam` from a location, rotation, offset and color
impl<P> From<(P, f32, P, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, rotation, offset, color): (P, f32, P, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .offset(offset)
            .color(color)
    }
}

/// Create a `DrawParam` from a location, rotation, offset, scale and color
impl<P, V> From<(P, f32, P, V, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
    V: Into<mint::Vector2<f32>>,
{
    fn from((location, rotation, offset, scale, color): (P, f32, P, V, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .offset(offset)
            .scale(scale)
            .color(color)
    }
}

/// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single matrix.
/// This is a lot less useful for doing transformations than I'd hoped; basically, we sometimes
/// have to modify parameters of a `DrawParam` based *on* the parameters of a `DrawParam`, for
/// instance when scaling images so that they are in units of pixels.  This makes it really
/// hard to extract scale and rotation and such, so meh.
///
/// It's still useful for a couple internal things though, so it's kept around.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct DrawTransform {
    /// The transform matrix for the DrawParams
    pub matrix: Matrix4,
    /// A portion of the drawable to clip, as a fraction of the whole image.
    /// Defaults to the whole image (1.0) if omitted.
    pub src: Rect,
    /// A color to draw the target with.
    /// Default: white.
    pub color: Color,
}

impl Default for DrawTransform {
    fn default() -> Self {
        DrawTransform {
            matrix: na::one(),
            src: Rect::one(),
            color: WHITE,
        }
    }
}

impl From<DrawParam> for DrawTransform {
    fn from(param: DrawParam) -> Self {
        let transform = param.to_matrix();
        DrawTransform {
            src: param.src,
            color: param.color,
            matrix: transform.into(),
        }
    }
}

impl DrawTransform {
    pub(crate) fn to_instance_properties(&self, srgb: bool) -> InstanceProperties {
        let mat: [[f32; 4]; 4] = self.matrix.into();
        let color: [f32; 4] = if srgb {
            let linear_color: types::LinearColor = self.color.into();
            linear_color.into()
        } else {
            self.color.into()
        };
        InstanceProperties {
            src: self.src.into(),
            col1: mat[0],
            col2: mat[1],
            col3: mat[2],
            col4: mat[3],
            color,
        }
    }
}
<commit_msg>Nit: missed final step in comment calculation<commit_after>use crate::graphics::*;

use mint;

type Vec3 = na::Vector3<f32>;

/// A struct containing all the necessary info for drawing a [`Drawable`](trait.Drawable.html).
///
/// This struct implements the `Default` trait, so to set only some parameter
/// you can just do:
///
/// ```rust
/// # use ggez::*;
/// # use ggez::graphics::*;
/// # fn t<P>(ctx: &mut Context, drawable: &P) where P: Drawable {
/// let my_dest = nalgebra::Point2::new(13.0, 37.0);
/// graphics::draw(ctx, drawable, DrawParam::default().dest(my_dest) );
/// # }
/// ```
///
/// As a shortcut, it also implements `From` for a variety of tuple types.
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct DrawParam {
    /// A portion of the drawable to clip, as a fraction of the whole image.
    /// Defaults to the whole image `(0,0 to 1,1)` if omitted.
    pub src: Rect,
    /// The position to draw the graphic expressed as a `Point2`.
    pub dest: mint::Point2<f32>,
    /// The orientation of the graphic in radians.
    pub rotation: f32,
    /// The x/y scale factors expressed as a `Vector2`.
    pub scale: mint::Vector2<f32>,
    /// An offset from the center for transform operations like scale/rotation,
    /// with `0,0` meaning the origin and `1,1` meaning the opposite corner from the origin.
    /// By default these operations are done from the top-left corner, so to rotate something
    /// from the center specify `Point2::new(0.5, 0.5)` here.
    pub offset: mint::Point2<f32>,
    /// A color to draw the target with.
    /// Default: white.
    pub color: Color,
}

impl Default for DrawParam {
    fn default() -> Self {
        DrawParam {
            src: Rect::one(),
            dest: mint::Point2 { x: 0.0, y: 0.0 },
            rotation: 0.0,
            scale: mint::Vector2 { x: 1.0, y: 1.0 },
            offset: mint::Point2 { x: 0.0, y: 0.0 },
            color: WHITE,
        }
    }
}

impl DrawParam {
    /// Create a new DrawParam with default values.
    pub fn new() -> Self {
        Self::default()
    }

    /// Set the source rect
    pub fn src(mut self, src: Rect) -> Self {
        self.src = src;
        self
    }

    /// Set the dest point
    pub fn dest<P>(mut self, dest: P) -> Self
    where
        P: Into<mint::Point2<f32>>,
    {
        let p: mint::Point2<f32> = dest.into();
        self.dest = p;
        self
    }

    /// Set the drawable color.  This will be blended with whatever
    /// color the drawn object already is.
    pub fn color(mut self, color: Color) -> Self {
        self.color = color;
        self
    }

    /// Set the rotation of the drawable.
    pub fn rotation(mut self, rotation: f32) -> Self {
        self.rotation = rotation;
        self
    }

    /// Set the scaling factors of the drawable.
    pub fn scale<V>(mut self, scale: V) -> Self
    where
        V: Into<mint::Vector2<f32>>,
    {
        let p: mint::Vector2<f32> = scale.into();
        self.scale = p;
        self
    }

    /// Set the transformation offset of the drawable.
    pub fn offset<P>(mut self, offset: P) -> Self
    where
        P: Into<mint::Point2<f32>>,
    {
        let p: mint::Point2<f32> = offset.into();
        self.offset = p;
        self
    }

    /// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single matrix.
    fn to_na_matrix(&self) -> Matrix4 {
        // Calculate a matrix equivalent to doing this:
        //  let translate = Matrix4::new_translation(&Vec3::new(self.dest.x, self.dest.y, 0.0));
        //  let offset = Matrix4::new_translation(&Vec3::new(self.offset.x, self.offset.y, 0.0));
        //  let offset_inverse =
        //      Matrix4::new_translation(&Vec3::new(-self.offset.x, -self.offset.y, 0.0));
        //  let axis_angle = Vec3::z() * self.rotation;
        //  let rotation = Matrix4::new_rotation(axis_angle);
        //  let scale = Matrix4::new_nonuniform_scaling(&Vec3::new(self.scale.x, self.scale.y, 1.0));
        //  translate * offset * rotation * scale * offset_inverse
        let cosr = self.rotation.cos();
        let sinr = self.rotation.sin();
        let m00 = cosr * self.scale.x;
        let m01 = -sinr * self.scale.y;
        let m10 = sinr * self.scale.x;
        let m11 = cosr * self.scale.y;
        let m03 = self.offset.x * (1.0 - m00) - self.offset.y * m01 + self.dest.x;
        let m13 = self.offset.y * (1.0 - m11) - self.offset.x * m10 + self.dest.y;
        Matrix4::new(m00, m01, 0.0, m03,  m10, m11, 0.0, m13,  0.0, 0.0, 1.0, 0.0,  0.0, 0.0, 0.0, 1.0)
    }

    /// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single
    ///matrix.  Because of this it only contains the transform part (rotation/scale/etc),
    /// with no src/dest/color info.
    pub fn to_matrix(&self) -> mint::ColumnMatrix4<f32> {
        self.to_na_matrix().into()
    }
}

/// Create a `DrawParam` from a location.
/// Note that this takes a single-element tuple.
/// It's a little weird but keeps the trait implementations
/// from clashing.
impl<P> From<(P,)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from(location: (P,)) -> Self {
        DrawParam::new().dest(location.0)
    }
}

/// Create a `DrawParam` from a location and color
impl<P> From<(P, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, color): (P, Color)) -> Self {
        DrawParam::new().dest(location).color(color)
    }
}

/// Create a `DrawParam` from a location, rotation and color
impl<P> From<(P, f32, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, rotation, color): (P, f32, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .color(color)
    }
}

/// Create a `DrawParam` from a location, rotation, offset and color
impl<P> From<(P, f32, P, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
{
    fn from((location, rotation, offset, color): (P, f32, P, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .offset(offset)
            .color(color)
    }
}

/// Create a `DrawParam` from a location, rotation, offset, scale and color
impl<P, V> From<(P, f32, P, V, Color)> for DrawParam
where
    P: Into<mint::Point2<f32>>,
    V: Into<mint::Vector2<f32>>,
{
    fn from((location, rotation, offset, scale, color): (P, f32, P, V, Color)) -> Self {
        DrawParam::new()
            .dest(location)
            .rotation(rotation)
            .offset(offset)
            .scale(scale)
            .color(color)
    }
}

/// A [`DrawParam`](struct.DrawParam.html) that has been crunched down to a single matrix.
/// This is a lot less useful for doing transformations than I'd hoped; basically, we sometimes
/// have to modify parameters of a `DrawParam` based *on* the parameters of a `DrawParam`, for
/// instance when scaling images so that they are in units of pixels.  This makes it really
/// hard to extract scale and rotation and such, so meh.
///
/// It's still useful for a couple internal things though, so it's kept around.
#[derive(Debug, Copy, Clone, PartialEq)]
pub(crate) struct DrawTransform {
    /// The transform matrix for the DrawParams
    pub matrix: Matrix4,
    /// A portion of the drawable to clip, as a fraction of the whole image.
    /// Defaults to the whole image (1.0) if omitted.
    pub src: Rect,
    /// A color to draw the target with.
    /// Default: white.
    pub color: Color,
}

impl Default for DrawTransform {
    fn default() -> Self {
        DrawTransform {
            matrix: na::one(),
            src: Rect::one(),
            color: WHITE,
        }
    }
}

impl From<DrawParam> for DrawTransform {
    fn from(param: DrawParam) -> Self {
        let transform = param.to_matrix();
        DrawTransform {
            src: param.src,
            color: param.color,
            matrix: transform.into(),
        }
    }
}

impl DrawTransform {
    pub(crate) fn to_instance_properties(&self, srgb: bool) -> InstanceProperties {
        let mat: [[f32; 4]; 4] = self.matrix.into();
        let color: [f32; 4] = if srgb {
            let linear_color: types::LinearColor = self.color.into();
            linear_color.into()
        } else {
            self.color.into()
        };
        InstanceProperties {
            src: self.src.into(),
            col1: mat[0],
            col2: mat[1],
            col3: mat[2],
            col4: mat[3],
            color,
        }
    }
}
<|endoftext|>


<commit_before>//! Long name wrappers of fitsio functions

pub use fitsio_sys::*;
#[cfg(feature = "default")]
use libc::{c_char, c_double, c_int, c_long, c_void};
#[cfg(feature = "bindgen")]
use std::os::raw::{c_char, c_double, c_int, c_long, c_void};

pub unsafe fn fits_close_file(fptr: *mut fitsfile, status: *mut c_int) -> c_int {
    ffclos(fptr, status)
}

pub unsafe fn fits_copy_hdu(
    infptr: *mut fitsfile,
    outfptr: *mut fitsfile,
    morekeys: c_int,
    status: *mut c_int,
) -> c_int {
    ffcopy(infptr, outfptr, morekeys, status)
}

pub unsafe fn fits_create_img(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffcrim(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_create_tbl(
    fptr: *mut fitsfile,
    tbltype: c_int,
    naxis2: LONGLONG,
    tfields: c_int,
    ttype: *mut *mut c_char,
    tform: *mut *mut c_char,
    tunit: *mut *mut c_char,
    extname: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffcrtb(
        fptr,
        tbltype,
        naxis2,
        tfields,
        ttype,
        tform,
        tunit,
        extname,
        status,
    )
}

pub unsafe fn fits_delete_col(fptr: *mut fitsfile, numcol: c_int, status: *mut c_int) -> c_int {
    ffdcol(fptr, numcol, status)
}

pub unsafe fn fits_delete_hdu(
    fptr: *mut fitsfile,
    hdutype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffdhdu(fptr, hdutype, status)
}

pub unsafe fn fits_file_mode(
    fptr: *mut fitsfile,
    filemode: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffflmd(fptr, filemode, status)
}

pub unsafe fn fits_get_bcolparms(
    fptr: *mut fitsfile,
    colnum: c_int,
    ttype: *mut c_char,
    tunit: *mut c_char,
    dtype: *mut c_char,
    repeat: *mut c_long,
    tscal: *mut c_double,
    tzero: *mut c_double,
    tnull: *mut c_long,
    tdisp: *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffgbcl(
        fptr,
        colnum,
        ttype,
        tunit,
        dtype,
        repeat,
        tscal,
        tzero,
        tnull,
        tdisp,
        status,
    )
}

pub unsafe fn fits_get_col_display_width(
    fptr: *mut fitsfile,
    colnum: c_int,
    width: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcdw(fptr, colnum, width, status)
}

pub unsafe fn fits_get_colnum(
    fptr: *mut fitsfile,
    casesen: c_int,
    templt: *mut c_char,
    colnum: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcno(fptr, casesen, templt, colnum, status)
}

pub unsafe fn fits_read_col_str(
    fptr: *mut fitsfile,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    nulval: *mut c_char,
    array: *mut *mut c_char,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcvs(
        fptr,
        colnum,
        firstrow,
        firstelem,
        nelem,
        nulval,
        array,
        anynul,
        status,
    )
}

pub unsafe fn fits_get_hdu_num(fptr: *mut fitsfile, chdunum: *mut c_int) -> c_int {
    ffghdn(fptr, chdunum)
}

pub unsafe fn fits_get_hdu_type(
    fptr: *mut fitsfile,
    exttype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffghdt(fptr, exttype, status)
}

pub unsafe fn fits_get_img_dim(
    fptr: *mut fitsfile,
    naxis: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgidm(fptr, naxis, status)
}

pub unsafe fn fits_get_img_equivtype(
    fptr: *mut fitsfile,
    imgtype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgiet(fptr, imgtype, status)
}

pub unsafe fn fits_get_img_size(
    fptr: *mut fitsfile,
    nlen: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffgisz(fptr, nlen, naxes, status)
}

pub unsafe fn fits_read_key_str(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: *mut c_char,
    comm: *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffgkys(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_get_num_cols(
    fptr: *mut fitsfile,
    ncols: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgncl(fptr, ncols, status)
}

pub unsafe fn fits_get_num_rows(
    fptr: *mut fitsfile,
    nrows: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffgnrw(fptr, nrows, status)
}

pub unsafe fn fits_read_img(
    fptr: *mut fitsfile,
    datatype: c_int,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    nulval: *mut c_void,
    array: *mut c_void,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgpv(
        fptr,
        datatype,
        firstelem,
        nelem,
        nulval,
        array,
        anynul,
        status,
    )
}

pub unsafe fn fits_read_subset(
    fptr: *mut fitsfile,
    datatype: c_int,
    blc: *mut c_long,
    trc: *mut c_long,
    inc: *mut c_long,
    nulval: *mut c_void,
    array: *mut c_void,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgsv(fptr, datatype, blc, trc, inc, nulval, array, anynul, status)
}

pub unsafe fn fits_insert_col(
    fptr: *mut fitsfile,
    numcol: c_int,
    ttype: *mut c_char,
    tform: *mut c_char,
    status: *mut c_int,
) -> c_int {
    fficol(fptr, numcol, ttype, tform, status)
}

pub unsafe fn fits_movabs_hdu(
    fptr: *mut fitsfile,
    hdunum: c_int,
    exttype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffmahd(fptr, hdunum, exttype, status)
}

pub unsafe fn fits_movnam_hdu(
    fptr: *mut fitsfile,
    exttype: c_int,
    hduname: *mut c_char,
    hduvers: c_int,
    status: *mut c_int,
) -> c_int {
    ffmnhd(fptr, exttype, hduname, hduvers, status)
}

pub unsafe fn fits_create_file(
    fptr: *mut *mut fitsfile,
    filename: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffinit(fptr, filename, status)
}

pub unsafe fn fits_write_col(
    fptr: *mut fitsfile,
    datatype: c_int,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffpcl(
        fptr,
        datatype,
        colnum,
        firstrow,
        firstelem,
        nelem,
        array,
        status,
    )
}

pub unsafe fn fits_write_col_str(
    fptr: *mut fitsfile,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffpcls(fptr, colnum, firstrow, firstelem, nelem, array, status)
}

pub unsafe fn fits_write_imghdr(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffphps(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_write_key_lng(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: LONGLONG,
    comm: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffpkyj(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_write_key_str(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: *const c_char,
    comm: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffpkys(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_write_img(
    fptr: *mut fitsfile,
    datatype: c_int,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffppr(fptr, datatype, firstelem, nelem, array, status)
}

pub unsafe fn fits_write_subset(
    fptr: *mut fitsfile,
    datatype: c_int,
    fpixel: *mut c_long,
    lpixel: *mut c_long,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffpss(fptr, datatype, fpixel, lpixel, array, status)
}

pub unsafe fn fits_resize_img(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffrsim(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_get_num_hdus(
    fptr: *mut fitsfile,
    nhdu: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffthdu(fptr, nhdu, status)
}

pub unsafe fn fits_open_file(
    fptr: *mut *mut fitsfile,
    filename: *const c_char,
    iomode: c_int,
    status: *mut c_int,
) -> c_int {
    ffopen(fptr, filename, iomode, status)
}
<commit_msg>Include specific short fits functions<commit_after>//! Long name wrappers of fitsio functions

pub use fitsio_sys::{ffclos, ffcopy, ffcrim, ffcrtb, ffdcol, ffdhdu, ffflmd, ffgbcl, ffgcdw,
                     ffgcno, ffgcvd, ffgcve, ffgcvj, ffgcvk, ffgcvs, ffgcvuj, ffgcvuk, ffghdn,
                     ffghdt, ffgidm, ffgiet, ffgisz, ffgkyd, ffgkye, ffgkyj, ffgkyl, ffgkys,
                     ffgncl, ffgnrw, ffgpv, ffgsv, fficol, ffinit, ffmahd, ffmnhd, ffopen, ffpcl,
                     ffpcls, ffphps, ffpkyd, ffpkye, ffpkyj, ffpkys, ffppr, ffpss, ffrsim, ffthdu,
                     fitsfile, LONGLONG};
#[cfg(feature = "default")]
use libc::{c_char, c_double, c_int, c_long, c_void};
#[cfg(feature = "bindgen")]
use std::os::raw::{c_char, c_double, c_int, c_long, c_void};

pub unsafe fn fits_close_file(fptr: *mut fitsfile, status: *mut c_int) -> c_int {
    ffclos(fptr, status)
}

pub unsafe fn fits_copy_hdu(
    infptr: *mut fitsfile,
    outfptr: *mut fitsfile,
    morekeys: c_int,
    status: *mut c_int,
) -> c_int {
    ffcopy(infptr, outfptr, morekeys, status)
}

pub unsafe fn fits_create_img(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffcrim(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_create_tbl(
    fptr: *mut fitsfile,
    tbltype: c_int,
    naxis2: LONGLONG,
    tfields: c_int,
    ttype: *mut *mut c_char,
    tform: *mut *mut c_char,
    tunit: *mut *mut c_char,
    extname: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffcrtb(
        fptr,
        tbltype,
        naxis2,
        tfields,
        ttype,
        tform,
        tunit,
        extname,
        status,
    )
}

pub unsafe fn fits_delete_col(fptr: *mut fitsfile, numcol: c_int, status: *mut c_int) -> c_int {
    ffdcol(fptr, numcol, status)
}

pub unsafe fn fits_delete_hdu(
    fptr: *mut fitsfile,
    hdutype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffdhdu(fptr, hdutype, status)
}

pub unsafe fn fits_file_mode(
    fptr: *mut fitsfile,
    filemode: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffflmd(fptr, filemode, status)
}

pub unsafe fn fits_get_bcolparms(
    fptr: *mut fitsfile,
    colnum: c_int,
    ttype: *mut c_char,
    tunit: *mut c_char,
    dtype: *mut c_char,
    repeat: *mut c_long,
    tscal: *mut c_double,
    tzero: *mut c_double,
    tnull: *mut c_long,
    tdisp: *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffgbcl(
        fptr,
        colnum,
        ttype,
        tunit,
        dtype,
        repeat,
        tscal,
        tzero,
        tnull,
        tdisp,
        status,
    )
}

pub unsafe fn fits_get_col_display_width(
    fptr: *mut fitsfile,
    colnum: c_int,
    width: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcdw(fptr, colnum, width, status)
}

pub unsafe fn fits_get_colnum(
    fptr: *mut fitsfile,
    casesen: c_int,
    templt: *mut c_char,
    colnum: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcno(fptr, casesen, templt, colnum, status)
}

pub unsafe fn fits_read_col_str(
    fptr: *mut fitsfile,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    nulval: *mut c_char,
    array: *mut *mut c_char,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgcvs(
        fptr,
        colnum,
        firstrow,
        firstelem,
        nelem,
        nulval,
        array,
        anynul,
        status,
    )
}

pub unsafe fn fits_get_hdu_num(fptr: *mut fitsfile, chdunum: *mut c_int) -> c_int {
    ffghdn(fptr, chdunum)
}

pub unsafe fn fits_get_hdu_type(
    fptr: *mut fitsfile,
    exttype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffghdt(fptr, exttype, status)
}

pub unsafe fn fits_get_img_dim(
    fptr: *mut fitsfile,
    naxis: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgidm(fptr, naxis, status)
}

pub unsafe fn fits_get_img_equivtype(
    fptr: *mut fitsfile,
    imgtype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgiet(fptr, imgtype, status)
}

pub unsafe fn fits_get_img_size(
    fptr: *mut fitsfile,
    nlen: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffgisz(fptr, nlen, naxes, status)
}

pub unsafe fn fits_read_key_str(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: *mut c_char,
    comm: *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffgkys(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_get_num_cols(
    fptr: *mut fitsfile,
    ncols: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgncl(fptr, ncols, status)
}

pub unsafe fn fits_get_num_rows(
    fptr: *mut fitsfile,
    nrows: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffgnrw(fptr, nrows, status)
}

pub unsafe fn fits_read_img(
    fptr: *mut fitsfile,
    datatype: c_int,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    nulval: *mut c_void,
    array: *mut c_void,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgpv(
        fptr,
        datatype,
        firstelem,
        nelem,
        nulval,
        array,
        anynul,
        status,
    )
}

pub unsafe fn fits_read_subset(
    fptr: *mut fitsfile,
    datatype: c_int,
    blc: *mut c_long,
    trc: *mut c_long,
    inc: *mut c_long,
    nulval: *mut c_void,
    array: *mut c_void,
    anynul: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffgsv(fptr, datatype, blc, trc, inc, nulval, array, anynul, status)
}

pub unsafe fn fits_insert_col(
    fptr: *mut fitsfile,
    numcol: c_int,
    ttype: *mut c_char,
    tform: *mut c_char,
    status: *mut c_int,
) -> c_int {
    fficol(fptr, numcol, ttype, tform, status)
}

pub unsafe fn fits_movabs_hdu(
    fptr: *mut fitsfile,
    hdunum: c_int,
    exttype: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffmahd(fptr, hdunum, exttype, status)
}

pub unsafe fn fits_movnam_hdu(
    fptr: *mut fitsfile,
    exttype: c_int,
    hduname: *mut c_char,
    hduvers: c_int,
    status: *mut c_int,
) -> c_int {
    ffmnhd(fptr, exttype, hduname, hduvers, status)
}

pub unsafe fn fits_create_file(
    fptr: *mut *mut fitsfile,
    filename: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffinit(fptr, filename, status)
}

pub unsafe fn fits_write_col(
    fptr: *mut fitsfile,
    datatype: c_int,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffpcl(
        fptr,
        datatype,
        colnum,
        firstrow,
        firstelem,
        nelem,
        array,
        status,
    )
}

pub unsafe fn fits_write_col_str(
    fptr: *mut fitsfile,
    colnum: c_int,
    firstrow: LONGLONG,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut *mut c_char,
    status: *mut c_int,
) -> c_int {
    ffpcls(fptr, colnum, firstrow, firstelem, nelem, array, status)
}

pub unsafe fn fits_write_imghdr(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffphps(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_write_key_lng(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: LONGLONG,
    comm: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffpkyj(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_write_key_str(
    fptr: *mut fitsfile,
    keyname: *const c_char,
    value: *const c_char,
    comm: *const c_char,
    status: *mut c_int,
) -> c_int {
    ffpkys(fptr, keyname, value, comm, status)
}

pub unsafe fn fits_write_img(
    fptr: *mut fitsfile,
    datatype: c_int,
    firstelem: LONGLONG,
    nelem: LONGLONG,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffppr(fptr, datatype, firstelem, nelem, array, status)
}

pub unsafe fn fits_write_subset(
    fptr: *mut fitsfile,
    datatype: c_int,
    fpixel: *mut c_long,
    lpixel: *mut c_long,
    array: *mut c_void,
    status: *mut c_int,
) -> c_int {
    ffpss(fptr, datatype, fpixel, lpixel, array, status)
}

pub unsafe fn fits_resize_img(
    fptr: *mut fitsfile,
    bitpix: c_int,
    naxis: c_int,
    naxes: *mut c_long,
    status: *mut c_int,
) -> c_int {
    ffrsim(fptr, bitpix, naxis, naxes, status)
}

pub unsafe fn fits_get_num_hdus(
    fptr: *mut fitsfile,
    nhdu: *mut c_int,
    status: *mut c_int,
) -> c_int {
    ffthdu(fptr, nhdu, status)
}

pub unsafe fn fits_open_file(
    fptr: *mut *mut fitsfile,
    filename: *const c_char,
    iomode: c_int,
    status: *mut c_int,
) -> c_int {
    ffopen(fptr, filename, iomode, status)
}
<|endoftext|>


<commit_before>extern crate cairo;

pub struct Segment {
    is_degenerate: bool, /* If true, only (p1x, p1y) are valid.  If false, all are valid */
    p1x: f64, p1y: f64,
    p2x: f64, p2y: f64,
    p3x: f64, p3y: f64,
    p4x: f64, p4y: f64
}

enum SegmentState {
    Start,
    End
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. segment.is_degenerate = TRUE => the segment is actually a single point (segment.p1x, segment.p1y)
 *
 * 2. segment.is_degenerate = FALSE => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (p1x, p1y)
 *       P2 = (p2x, p2y)
 *       P3 = (p3x, p3y)
 *       P4 = (p4x, p4y)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut has_first_segment : bool;
    let mut segment_num : usize;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    has_first_segment = false;
    segment_num = 0;
    segments = Vec::new ();
    state = SegmentState::End;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                if has_first_segment {
                    segment_num += 1;
                } else {
                    has_first_segment = true;
                }

                cur_x = x;
                cur_y = y;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                let seg = Segment {
                    is_degenerate: true,
                    p1x: cur_x,
                    p1y: cur_y,
                    p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0 // these are set in the next iteration
                };

                segments.push (seg);

                state = SegmentState::Start;
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                match state {
                    SegmentState::Start => {
                        segments[segment_num].is_degenerate = false;
                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        segment_num += 1;

                        let seg = Segment {
                            is_degenerate: false,
                            p1x: last_x,
                            p1y: last_y,
                            p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0  // these are set below
                        };

                        segments.push (seg);
                    }
                }

                segments[segment_num].p2x = cur_x;
                segments[segment_num].p2y = cur_y;

                segments[segment_num].p3x = last_x;
                segments[segment_num].p3y = last_y;

                segments[segment_num].p4x = cur_x;
                segments[segment_num].p4y = cur_y;
            },

            cairo::PathSegment::CurveTo ((p2x, p2y), (p3x, p3y), (p4x, p4y)) => {
                cur_x = p4x;
                cur_y = p4y;

                match state {
                    SegmentState::Start => {
                        segments[segment_num as usize].is_degenerate = false;
                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        segment_num += 1;

                        let seg = Segment {
                            is_degenerate: false,
                            p1x: last_x,
                            p1y: last_y,
                            p2x: 0.0, p2y: 0.0, p3x: 0.0, p3y: 0.0, p4x: 0.0, p4y: 0.0 // these are set below
                        };

                        segments.push (seg);
                    }
                }

                segments[segment_num].p2x = p2x;
                segments[segment_num].p2y = p2y;

                segments[segment_num].p3x = p3x;
                segments[segment_num].p3y = p3y;

                segments[segment_num].p4x = cur_x;
                segments[segment_num].p4y = cur_y;

                /* Fix the tangents for when the middle control points coincide with their respective endpoints */

                if double_equals (segments[segment_num].p2x, segments[segment_num].p1x)
                    && double_equals (segments[segment_num].p2y, segments[segment_num].p1y) {
                    segments[segment_num].p2x = segments[segment_num].p3x;
                    segments[segment_num].p2y = segments[segment_num].p3y;
                }

                if double_equals (segments[segment_num].p3x, segments[segment_num].p4x)
                    && double_equals (segments[segment_num].p3y, segments[segment_num].p4y) {
                    segments[segment_num].p3x = segments[segment_num].p2x;
                    segments[segment_num].p3y = segments[segment_num].p2y;
                }
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                match state {
                    SegmentState::Start => {
                        segments[segment_num].is_degenerate = false;

                        segments[segment_num].p2x = cur_x;
                        segments[segment_num].p2y = cur_y;

                        segments[segment_num].p3x = last_x;
                        segments[segment_num].p3y = last_y;

                        segments[segment_num].p4x = cur_x;
                        segments[segment_num].p4y = cur_y;

                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        /* nothing; closepath after moveto (or a single lone closepath) does nothing */
                    }
                }
            }
        }
    }

    segments
}

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

    fn setup_open_path () -> cairo::Path {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let path = setup_open_path ();
        let segments = path_to_segments (path);

        for (index, seg) in segments.iter ().enumerate () {
            match index {
                0 => {
                    assert_eq! (seg.is_degenerate, false);
                    assert_eq! ((seg.p1x, seg.p1y), (10.0, 10.0));
                    assert_eq! ((seg.p2x, seg.p2y), (20.0, 10.0));
                    assert_eq! ((seg.p3x, seg.p3y), (10.0, 10.0));
                    assert_eq! ((seg.p4x, seg.p4y), (20.0, 10.0));
                },

                1 => {
                    assert_eq! (seg.is_degenerate, false);
                    assert_eq! ((seg.p1x, seg.p1y), (20.0, 10.0));
                    assert_eq! ((seg.p2x, seg.p2y), (20.0, 20.0));
                    assert_eq! ((seg.p3x, seg.p3y), (20.0, 10.0));
                    assert_eq! ((seg.p4x, seg.p4y), (20.0, 20.0));
                },

                _ => { unreachable! (); }
            }
        }
    }
}
<commit_msg>marker.rs: Rename p1x/p1y, etc. to x1/y1, etc.<commit_after>extern crate cairo;

pub struct Segment {
    is_degenerate: bool, /* If true, only (x1, y1) are valid.  If false, all are valid */
    x1: f64, y1: f64,
    x2: f64, y2: f64,
    x3: f64, y3: f64,
    x4: f64, y4: f64
}

enum SegmentState {
    Start,
    End
}

/* This converts a cairo_path_t into a list of curveto-like segments.  Each segment can be:
 *
 * 1. segment.is_degenerate = TRUE => the segment is actually a single point (segment.x1, segment.y1)
 *
 * 2. segment.is_degenerate = FALSE => either a lineto or a curveto (or the effective lineto that results from a closepath).
 *    We have the following points:
 *       P1 = (x1, y1)
 *       P2 = (x2, y2)
 *       P3 = (x3, y3)
 *       P4 = (x4, y4)
 *
 *    The start and end points are P1 and P4, respectively.
 *    The tangent at the start point is given by the vector (P2 - P1).
 *    The tangent at the end point is given by the vector (P4 - P3).
 *    The tangents also work if the segment refers to a lineto (they will both just point in the same direction).
 */

const EPSILON: f64 = 1e-10;

fn double_equals (a: f64, b: f64) -> bool {
    (a - b).abs () < EPSILON
}

pub fn path_to_segments (path: cairo::Path) -> Vec<Segment> {
    let mut last_x: f64;
    let mut last_y: f64;
    let mut cur_x: f64;
    let mut cur_y: f64;
    let mut subpath_start_x: f64;
    let mut subpath_start_y: f64;
    let mut has_first_segment : bool;
    let mut segment_num : usize;
    let mut segments: Vec<Segment>;
    let mut state: SegmentState;

    cur_x = 0.0;
    cur_y = 0.0;
    subpath_start_x = 0.0;
    subpath_start_y = 0.0;

    has_first_segment = false;
    segment_num = 0;
    segments = Vec::new ();
    state = SegmentState::End;

    for cairo_segment in path.iter () {
        last_x = cur_x;
        last_y = cur_y;

        match cairo_segment {
            cairo::PathSegment::MoveTo ((x, y)) => {
                if has_first_segment {
                    segment_num += 1;
                } else {
                    has_first_segment = true;
                }

                cur_x = x;
                cur_y = y;

                subpath_start_x = cur_x;
                subpath_start_y = cur_y;

                let seg = Segment {
                    is_degenerate: true,
                    x1: cur_x,
                    y1: cur_y,
                    x2: 0.0, y2: 0.0, x3: 0.0, y3: 0.0, x4: 0.0, y4: 0.0 // these are set in the next iteration
                };

                segments.push (seg);

                state = SegmentState::Start;
            },

            cairo::PathSegment::LineTo ((x, y)) => {
                cur_x = x;
                cur_y = y;

                match state {
                    SegmentState::Start => {
                        segments[segment_num].is_degenerate = false;
                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        segment_num += 1;

                        let seg = Segment {
                            is_degenerate: false,
                            x1: last_x,
                            y1: last_y,
                            x2: 0.0, y2: 0.0, x3: 0.0, y3: 0.0, x4: 0.0, y4: 0.0  // these are set below
                        };

                        segments.push (seg);
                    }
                }

                segments[segment_num].x2 = cur_x;
                segments[segment_num].y2 = cur_y;

                segments[segment_num].x3 = last_x;
                segments[segment_num].y3 = last_y;

                segments[segment_num].x4 = cur_x;
                segments[segment_num].y4 = cur_y;
            },

            cairo::PathSegment::CurveTo ((x2, y2), (x3, y3), (x4, y4)) => {
                cur_x = x4;
                cur_y = y4;

                match state {
                    SegmentState::Start => {
                        segments[segment_num as usize].is_degenerate = false;
                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        segment_num += 1;

                        let seg = Segment {
                            is_degenerate: false,
                            x1: last_x,
                            y1: last_y,
                            x2: 0.0, y2: 0.0, x3: 0.0, y3: 0.0, x4: 0.0, y4: 0.0 // these are set below
                        };

                        segments.push (seg);
                    }
                }

                segments[segment_num].x2 = x2;
                segments[segment_num].y2 = y2;

                segments[segment_num].x3 = x3;
                segments[segment_num].y3 = y3;

                segments[segment_num].x4 = cur_x;
                segments[segment_num].y4 = cur_y;

                /* Fix the tangents for when the middle control points coincide with their respective endpoints */

                if double_equals (segments[segment_num].x2, segments[segment_num].x1)
                    && double_equals (segments[segment_num].y2, segments[segment_num].y1) {
                    segments[segment_num].x2 = segments[segment_num].x3;
                    segments[segment_num].y2 = segments[segment_num].y3;
                }

                if double_equals (segments[segment_num].x3, segments[segment_num].x4)
                    && double_equals (segments[segment_num].y3, segments[segment_num].y4) {
                    segments[segment_num].x3 = segments[segment_num].x2;
                    segments[segment_num].y3 = segments[segment_num].y2;
                }
            }

            cairo::PathSegment::ClosePath => {
                cur_x = subpath_start_x;
                cur_y = subpath_start_y;

                match state {
                    SegmentState::Start => {
                        segments[segment_num].is_degenerate = false;

                        segments[segment_num].x2 = cur_x;
                        segments[segment_num].y2 = cur_y;

                        segments[segment_num].x3 = last_x;
                        segments[segment_num].y3 = last_y;

                        segments[segment_num].x4 = cur_x;
                        segments[segment_num].y4 = cur_y;

                        state = SegmentState::End;
                    },

                    SegmentState::End => {
                        /* nothing; closepath after moveto (or a single lone closepath) does nothing */
                    }
                }
            }
        }
    }

    segments
}

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

    fn setup_open_path () -> cairo::Path {
        let surf = cairo::ImageSurface::create (cairo::Format::Rgb24, 1, 1);
        let cr = cairo::Context::new (&surf);

        cr.move_to (10.0, 10.0);
        cr.line_to (20.0, 10.0);
        cr.line_to (20.0, 20.0);

        let path = cr.copy_path ();
        path
    }

    #[test]
    fn path_to_segments_handles_open_path () {
        let path = setup_open_path ();
        let segments = path_to_segments (path);

        for (index, seg) in segments.iter ().enumerate () {
            match index {
                0 => {
                    assert_eq! (seg.is_degenerate, false);
                    assert_eq! ((seg.x1, seg.y1), (10.0, 10.0));
                    assert_eq! ((seg.x2, seg.y2), (20.0, 10.0));
                    assert_eq! ((seg.x3, seg.y3), (10.0, 10.0));
                    assert_eq! ((seg.x4, seg.y4), (20.0, 10.0));
                },

                1 => {
                    assert_eq! (seg.is_degenerate, false);
                    assert_eq! ((seg.x1, seg.y1), (20.0, 10.0));
                    assert_eq! ((seg.x2, seg.y2), (20.0, 20.0));
                    assert_eq! ((seg.x3, seg.y3), (20.0, 10.0));
                    assert_eq! ((seg.x4, seg.y4), (20.0, 20.0));
                },

                _ => { unreachable! (); }
            }
        }
    }
}
<|endoftext|>