<commit_before>#![allow(unknown_lints)]

use crate::core::{TargetKind, Workspace};
use crate::ops::CompileOptions;
use anyhow::Error;
use std::fmt;
use std::path::PathBuf;
use std::process::{ExitStatus, Output};
use std::str;

pub type CargoResult<T> = anyhow::Result<T>;

// TODO: should delete this trait and just use `with_context` instead
pub trait CargoResultExt<T, E> {
    fn chain_err<F, D>(self, f: F) -> CargoResult<T>
    where
        F: FnOnce() -> D,
        D: fmt::Display + Send + Sync + 'static;
}

impl<T, E> CargoResultExt<T, E> for Result<T, E>
where
    E: Into<Error>,
{
    fn chain_err<F, D>(self, f: F) -> CargoResult<T>
    where
        F: FnOnce() -> D,
        D: fmt::Display + Send + Sync + 'static,
    {
        self.map_err(|e| e.into().context(f()))
    }
}

#[derive(Debug)]
pub struct HttpNot200 {
    pub code: u32,
    pub url: String,
}

impl fmt::Display for HttpNot200 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "failed to get 200 response from `{}`, got {}",
            self.url, self.code
        )
    }
}

impl std::error::Error for HttpNot200 {}

// =============================================================================
// Verbose error

/// An error wrapper for errors that should only be displayed with `--verbose`.
///
/// This should only be used in rare cases. When emitting this error, you
/// should have a normal error higher up the error-cause chain (like "could
/// not compile `foo`"), so at least *something* gets printed without
/// `--verbose`.
pub struct VerboseError {
    inner: Error,
}

impl VerboseError {
    pub fn new(inner: Error) -> VerboseError {
        VerboseError { inner }
    }
}

impl std::error::Error for VerboseError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.inner.source()
    }
}

impl fmt::Debug for VerboseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

impl fmt::Display for VerboseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

// =============================================================================
// Internal error

/// An unexpected, internal error.
///
/// This should only be used for unexpected errors. It prints a message asking
/// the user to file a bug report.
pub struct InternalError {
    inner: Error,
}

impl InternalError {
    pub fn new(inner: Error) -> InternalError {
        InternalError { inner }
    }
}

impl std::error::Error for InternalError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.inner.source()
    }
}

impl fmt::Debug for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

impl fmt::Display for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

// =============================================================================
// Manifest error

/// Error wrapper related to a particular manifest and providing it's path.
///
/// This error adds no displayable info of it's own.
pub struct ManifestError {
    cause: Error,
    manifest: PathBuf,
}

impl ManifestError {
    pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
        Self {
            cause: cause.into(),
            manifest,
        }
    }

    pub fn manifest_path(&self) -> &PathBuf {
        &self.manifest
    }

    /// Returns an iterator over the `ManifestError` chain of causes.
    ///
    /// So if this error was not caused by another `ManifestError` this will be empty.
    pub fn manifest_causes(&self) -> ManifestCauses<'_> {
        ManifestCauses { current: self }
    }
}

impl std::error::Error for ManifestError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ManifestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

/// An iterator over the `ManifestError` chain of causes.
pub struct ManifestCauses<'a> {
    current: &'a ManifestError,
}

impl<'a> Iterator for ManifestCauses<'a> {
    type Item = &'a ManifestError;

    fn next(&mut self) -> Option<Self::Item> {
        self.current = self.current.cause.downcast_ref()?;
        Some(self.current)
    }
}

impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}

// =============================================================================
// Process errors
#[derive(Debug)]
pub struct ProcessError {
    /// A detailed description to show to the user why the process failed.
    pub desc: String,
    /// The exit status of the process.
    ///
    /// This can be `None` if the process failed to launch (like process not found).
    pub exit: Option<ExitStatus>,
    /// The output from the process.
    ///
    /// This can be `None` if the process failed to launch, or the output was not captured.
    pub output: Option<Output>,
}

impl fmt::Display for ProcessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.desc.fmt(f)
    }
}

impl std::error::Error for ProcessError {}

// =============================================================================
// Cargo test errors.

/// Error when testcases fail
#[derive(Debug)]
pub struct CargoTestError {
    pub test: Test,
    pub desc: String,
    pub exit: Option<ExitStatus>,
    pub causes: Vec<ProcessError>,
}

impl fmt::Display for CargoTestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.desc.fmt(f)
    }
}

impl std::error::Error for CargoTestError {}

#[derive(Debug)]
pub enum Test {
    Multiple,
    Doc,
    UnitTest {
        kind: TargetKind,
        name: String,
        pkg_name: String,
    },
}

impl CargoTestError {
    pub fn new(test: Test, errors: Vec<ProcessError>) -> Self {
        if errors.is_empty() {
            panic!("Cannot create CargoTestError from empty Vec")
        }
        let desc = errors
            .iter()
            .map(|error| error.desc.clone())
            .collect::<Vec<String>>()
            .join("\n");
        CargoTestError {
            test,
            desc,
            exit: errors[0].exit,
            causes: errors,
        }
    }

    pub fn hint(&self, ws: &Workspace<'_>, opts: &CompileOptions) -> String {
        match self.test {
            Test::UnitTest {
                ref kind,
                ref name,
                ref pkg_name,
            } => {
                let pkg_info = if opts.spec.needs_spec_flag(ws) {
                    format!("-p {} ", pkg_name)
                } else {
                    String::new()
                };

                match *kind {
                    TargetKind::Bench => {
                        format!("test failed, to rerun pass '{}--bench {}'", pkg_info, name)
                    }
                    TargetKind::Bin => {
                        format!("test failed, to rerun pass '{}--bin {}'", pkg_info, name)
                    }
                    TargetKind::Lib(_) => format!("test failed, to rerun pass '{}--lib'", pkg_info),
                    TargetKind::Test => {
                        format!("test failed, to rerun pass '{}--test {}'", pkg_info, name)
                    }
                    TargetKind::ExampleBin | TargetKind::ExampleLib(_) => {
                        format!("test failed, to rerun pass '{}--example {}", pkg_info, name)
                    }
                    _ => "test failed.".into(),
                }
            }
            Test::Doc => "test failed, to rerun pass '--doc'".into(),
            _ => "test failed.".into(),
        }
    }
}

// =============================================================================
// CLI errors

pub type CliResult = Result<(), CliError>;

#[derive(Debug)]
/// The CLI error is the error type used at Cargo's CLI-layer.
///
/// All errors from the lib side of Cargo will get wrapped with this error.
/// Other errors (such as command-line argument validation) will create this
/// directly.
pub struct CliError {
    /// The error to display. This can be `None` in rare cases to exit with a
    /// code without displaying a message. For example `cargo run -q` where
    /// the resulting process exits with a nonzero code (on Windows), or an
    /// external subcommand that exits nonzero (we assume it printed its own
    /// message).
    pub error: Option<anyhow::Error>,
    /// The process exit code.
    pub exit_code: i32,
}

impl CliError {
    pub fn new(error: anyhow::Error, code: i32) -> CliError {
        CliError {
            error: Some(error),
            exit_code: code,
        }
    }

    pub fn code(code: i32) -> CliError {
        CliError {
            error: None,
            exit_code: code,
        }
    }
}

impl From<anyhow::Error> for CliError {
    fn from(err: anyhow::Error) -> CliError {
        CliError::new(err, 101)
    }
}

impl From<clap::Error> for CliError {
    fn from(err: clap::Error) -> CliError {
        let code = if err.use_stderr() { 1 } else { 0 };
        CliError::new(err.into(), code)
    }
}

// =============================================================================
// Construction helpers

/// Creates a new process error.
///
/// `status` can be `None` if the process did not launch.
/// `output` can be `None` if the process did not launch, or output was not captured.
pub fn process_error(
    msg: &str,
    status: Option<ExitStatus>,
    output: Option<&Output>,
) -> ProcessError {
    let exit = match status {
        Some(s) => status_to_string(s),
        None => "never executed".to_string(),
    };
    let mut desc = format!("{} ({})", &msg, exit);

    if let Some(out) = output {
        match str::from_utf8(&out.stdout) {
            Ok(s) if !s.trim().is_empty() => {
                desc.push_str("\n--- stdout\n");
                desc.push_str(s);
            }
            Ok(..) | Err(..) => {}
        }
        match str::from_utf8(&out.stderr) {
            Ok(s) if !s.trim().is_empty() => {
                desc.push_str("\n--- stderr\n");
                desc.push_str(s);
            }
            Ok(..) | Err(..) => {}
        }
    }

    return ProcessError {
        desc,
        exit: status,
        output: output.cloned(),
    };

    #[cfg(unix)]
    fn status_to_string(status: ExitStatus) -> String {
        use std::os::unix::process::*;

        if let Some(signal) = status.signal() {
            let name = match signal as libc::c_int {
                libc::SIGABRT => ", SIGABRT: process abort signal",
                libc::SIGALRM => ", SIGALRM: alarm clock",
                libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
                libc::SIGHUP => ", SIGHUP: hangup",
                libc::SIGILL => ", SIGILL: illegal instruction",
                libc::SIGINT => ", SIGINT: terminal interrupt signal",
                libc::SIGKILL => ", SIGKILL: kill",
                libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
                libc::SIGQUIT => ", SIGQUIT: terminal quite signal",
                libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
                libc::SIGTERM => ", SIGTERM: termination signal",
                libc::SIGBUS => ", SIGBUS: access to undefined memory",
                #[cfg(not(target_os = "haiku"))]
                libc::SIGSYS => ", SIGSYS: bad system call",
                libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
                _ => "",
            };
            format!("signal: {}{}", signal, name)
        } else {
            status.to_string()
        }
    }

    #[cfg(windows)]
    fn status_to_string(status: ExitStatus) -> String {
        use winapi::shared::minwindef::DWORD;
        use winapi::um::winnt::*;

        let mut base = status.to_string();
        let extra = match status.code().unwrap() as DWORD {
            STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION",
            STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR",
            STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
            STATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
            STATUS_NO_MEMORY => "STATUS_NO_MEMORY",
            STATUS_ILLEGAL_INSTRUCTION => "STATUS_ILLEGAL_INSTRUCTION",
            STATUS_NONCONTINUABLE_EXCEPTION => "STATUS_NONCONTINUABLE_EXCEPTION",
            STATUS_INVALID_DISPOSITION => "STATUS_INVALID_DISPOSITION",
            STATUS_ARRAY_BOUNDS_EXCEEDED => "STATUS_ARRAY_BOUNDS_EXCEEDED",
            STATUS_FLOAT_DENORMAL_OPERAND => "STATUS_FLOAT_DENORMAL_OPERAND",
            STATUS_FLOAT_DIVIDE_BY_ZERO => "STATUS_FLOAT_DIVIDE_BY_ZERO",
            STATUS_FLOAT_INEXACT_RESULT => "STATUS_FLOAT_INEXACT_RESULT",
            STATUS_FLOAT_INVALID_OPERATION => "STATUS_FLOAT_INVALID_OPERATION",
            STATUS_FLOAT_OVERFLOW => "STATUS_FLOAT_OVERFLOW",
            STATUS_FLOAT_STACK_CHECK => "STATUS_FLOAT_STACK_CHECK",
            STATUS_FLOAT_UNDERFLOW => "STATUS_FLOAT_UNDERFLOW",
            STATUS_INTEGER_DIVIDE_BY_ZERO => "STATUS_INTEGER_DIVIDE_BY_ZERO",
            STATUS_INTEGER_OVERFLOW => "STATUS_INTEGER_OVERFLOW",
            STATUS_PRIVILEGED_INSTRUCTION => "STATUS_PRIVILEGED_INSTRUCTION",
            STATUS_STACK_OVERFLOW => "STATUS_STACK_OVERFLOW",
            STATUS_DLL_NOT_FOUND => "STATUS_DLL_NOT_FOUND",
            STATUS_ORDINAL_NOT_FOUND => "STATUS_ORDINAL_NOT_FOUND",
            STATUS_ENTRYPOINT_NOT_FOUND => "STATUS_ENTRYPOINT_NOT_FOUND",
            STATUS_CONTROL_C_EXIT => "STATUS_CONTROL_C_EXIT",
            STATUS_DLL_INIT_FAILED => "STATUS_DLL_INIT_FAILED",
            STATUS_FLOAT_MULTIPLE_FAULTS => "STATUS_FLOAT_MULTIPLE_FAULTS",
            STATUS_FLOAT_MULTIPLE_TRAPS => "STATUS_FLOAT_MULTIPLE_TRAPS",
            STATUS_REG_NAT_CONSUMPTION => "STATUS_REG_NAT_CONSUMPTION",
            STATUS_HEAP_CORRUPTION => "STATUS_HEAP_CORRUPTION",
            STATUS_STACK_BUFFER_OVERRUN => "STATUS_STACK_BUFFER_OVERRUN",
            STATUS_ASSERTION_FAILURE => "STATUS_ASSERTION_FAILURE",
            _ => return base,
        };
        base.push_str(", ");
        base.push_str(extra);
        base
    }
}

pub fn is_simple_exit_code(code: i32) -> bool {
    // Typical unix exit codes are 0 to 127.
    // Windows doesn't have anything "typical", and is a
    // 32-bit number (which appears signed here, but is really
    // unsigned). However, most of the interesting NTSTATUS
    // codes are very large. This is just a rough
    // approximation of which codes are "normal" and which
    // ones are abnormal termination.
    code >= 0 && code <= 127
}

pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
    InternalError::new(anyhow::format_err!("{}", error)).into()
}
<commit_msg>Auto merge of #8615 - davepacheco:sigquit-typo, r=ehuss<commit_after>#![allow(unknown_lints)]

use crate::core::{TargetKind, Workspace};
use crate::ops::CompileOptions;
use anyhow::Error;
use std::fmt;
use std::path::PathBuf;
use std::process::{ExitStatus, Output};
use std::str;

pub type CargoResult<T> = anyhow::Result<T>;

// TODO: should delete this trait and just use `with_context` instead
pub trait CargoResultExt<T, E> {
    fn chain_err<F, D>(self, f: F) -> CargoResult<T>
    where
        F: FnOnce() -> D,
        D: fmt::Display + Send + Sync + 'static;
}

impl<T, E> CargoResultExt<T, E> for Result<T, E>
where
    E: Into<Error>,
{
    fn chain_err<F, D>(self, f: F) -> CargoResult<T>
    where
        F: FnOnce() -> D,
        D: fmt::Display + Send + Sync + 'static,
    {
        self.map_err(|e| e.into().context(f()))
    }
}

#[derive(Debug)]
pub struct HttpNot200 {
    pub code: u32,
    pub url: String,
}

impl fmt::Display for HttpNot200 {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(
            f,
            "failed to get 200 response from `{}`, got {}",
            self.url, self.code
        )
    }
}

impl std::error::Error for HttpNot200 {}

// =============================================================================
// Verbose error

/// An error wrapper for errors that should only be displayed with `--verbose`.
///
/// This should only be used in rare cases. When emitting this error, you
/// should have a normal error higher up the error-cause chain (like "could
/// not compile `foo`"), so at least *something* gets printed without
/// `--verbose`.
pub struct VerboseError {
    inner: Error,
}

impl VerboseError {
    pub fn new(inner: Error) -> VerboseError {
        VerboseError { inner }
    }
}

impl std::error::Error for VerboseError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.inner.source()
    }
}

impl fmt::Debug for VerboseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

impl fmt::Display for VerboseError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

// =============================================================================
// Internal error

/// An unexpected, internal error.
///
/// This should only be used for unexpected errors. It prints a message asking
/// the user to file a bug report.
pub struct InternalError {
    inner: Error,
}

impl InternalError {
    pub fn new(inner: Error) -> InternalError {
        InternalError { inner }
    }
}

impl std::error::Error for InternalError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.inner.source()
    }
}

impl fmt::Debug for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

impl fmt::Display for InternalError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.inner.fmt(f)
    }
}

// =============================================================================
// Manifest error

/// Error wrapper related to a particular manifest and providing it's path.
///
/// This error adds no displayable info of it's own.
pub struct ManifestError {
    cause: Error,
    manifest: PathBuf,
}

impl ManifestError {
    pub fn new<E: Into<Error>>(cause: E, manifest: PathBuf) -> Self {
        Self {
            cause: cause.into(),
            manifest,
        }
    }

    pub fn manifest_path(&self) -> &PathBuf {
        &self.manifest
    }

    /// Returns an iterator over the `ManifestError` chain of causes.
    ///
    /// So if this error was not caused by another `ManifestError` this will be empty.
    pub fn manifest_causes(&self) -> ManifestCauses<'_> {
        ManifestCauses { current: self }
    }
}

impl std::error::Error for ManifestError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        self.cause.source()
    }
}

impl fmt::Debug for ManifestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

impl fmt::Display for ManifestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.cause.fmt(f)
    }
}

/// An iterator over the `ManifestError` chain of causes.
pub struct ManifestCauses<'a> {
    current: &'a ManifestError,
}

impl<'a> Iterator for ManifestCauses<'a> {
    type Item = &'a ManifestError;

    fn next(&mut self) -> Option<Self::Item> {
        self.current = self.current.cause.downcast_ref()?;
        Some(self.current)
    }
}

impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}

// =============================================================================
// Process errors
#[derive(Debug)]
pub struct ProcessError {
    /// A detailed description to show to the user why the process failed.
    pub desc: String,
    /// The exit status of the process.
    ///
    /// This can be `None` if the process failed to launch (like process not found).
    pub exit: Option<ExitStatus>,
    /// The output from the process.
    ///
    /// This can be `None` if the process failed to launch, or the output was not captured.
    pub output: Option<Output>,
}

impl fmt::Display for ProcessError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.desc.fmt(f)
    }
}

impl std::error::Error for ProcessError {}

// =============================================================================
// Cargo test errors.

/// Error when testcases fail
#[derive(Debug)]
pub struct CargoTestError {
    pub test: Test,
    pub desc: String,
    pub exit: Option<ExitStatus>,
    pub causes: Vec<ProcessError>,
}

impl fmt::Display for CargoTestError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.desc.fmt(f)
    }
}

impl std::error::Error for CargoTestError {}

#[derive(Debug)]
pub enum Test {
    Multiple,
    Doc,
    UnitTest {
        kind: TargetKind,
        name: String,
        pkg_name: String,
    },
}

impl CargoTestError {
    pub fn new(test: Test, errors: Vec<ProcessError>) -> Self {
        if errors.is_empty() {
            panic!("Cannot create CargoTestError from empty Vec")
        }
        let desc = errors
            .iter()
            .map(|error| error.desc.clone())
            .collect::<Vec<String>>()
            .join("\n");
        CargoTestError {
            test,
            desc,
            exit: errors[0].exit,
            causes: errors,
        }
    }

    pub fn hint(&self, ws: &Workspace<'_>, opts: &CompileOptions) -> String {
        match self.test {
            Test::UnitTest {
                ref kind,
                ref name,
                ref pkg_name,
            } => {
                let pkg_info = if opts.spec.needs_spec_flag(ws) {
                    format!("-p {} ", pkg_name)
                } else {
                    String::new()
                };

                match *kind {
                    TargetKind::Bench => {
                        format!("test failed, to rerun pass '{}--bench {}'", pkg_info, name)
                    }
                    TargetKind::Bin => {
                        format!("test failed, to rerun pass '{}--bin {}'", pkg_info, name)
                    }
                    TargetKind::Lib(_) => format!("test failed, to rerun pass '{}--lib'", pkg_info),
                    TargetKind::Test => {
                        format!("test failed, to rerun pass '{}--test {}'", pkg_info, name)
                    }
                    TargetKind::ExampleBin | TargetKind::ExampleLib(_) => {
                        format!("test failed, to rerun pass '{}--example {}", pkg_info, name)
                    }
                    _ => "test failed.".into(),
                }
            }
            Test::Doc => "test failed, to rerun pass '--doc'".into(),
            _ => "test failed.".into(),
        }
    }
}

// =============================================================================
// CLI errors

pub type CliResult = Result<(), CliError>;

#[derive(Debug)]
/// The CLI error is the error type used at Cargo's CLI-layer.
///
/// All errors from the lib side of Cargo will get wrapped with this error.
/// Other errors (such as command-line argument validation) will create this
/// directly.
pub struct CliError {
    /// The error to display. This can be `None` in rare cases to exit with a
    /// code without displaying a message. For example `cargo run -q` where
    /// the resulting process exits with a nonzero code (on Windows), or an
    /// external subcommand that exits nonzero (we assume it printed its own
    /// message).
    pub error: Option<anyhow::Error>,
    /// The process exit code.
    pub exit_code: i32,
}

impl CliError {
    pub fn new(error: anyhow::Error, code: i32) -> CliError {
        CliError {
            error: Some(error),
            exit_code: code,
        }
    }

    pub fn code(code: i32) -> CliError {
        CliError {
            error: None,
            exit_code: code,
        }
    }
}

impl From<anyhow::Error> for CliError {
    fn from(err: anyhow::Error) -> CliError {
        CliError::new(err, 101)
    }
}

impl From<clap::Error> for CliError {
    fn from(err: clap::Error) -> CliError {
        let code = if err.use_stderr() { 1 } else { 0 };
        CliError::new(err.into(), code)
    }
}

// =============================================================================
// Construction helpers

/// Creates a new process error.
///
/// `status` can be `None` if the process did not launch.
/// `output` can be `None` if the process did not launch, or output was not captured.
pub fn process_error(
    msg: &str,
    status: Option<ExitStatus>,
    output: Option<&Output>,
) -> ProcessError {
    let exit = match status {
        Some(s) => status_to_string(s),
        None => "never executed".to_string(),
    };
    let mut desc = format!("{} ({})", &msg, exit);

    if let Some(out) = output {
        match str::from_utf8(&out.stdout) {
            Ok(s) if !s.trim().is_empty() => {
                desc.push_str("\n--- stdout\n");
                desc.push_str(s);
            }
            Ok(..) | Err(..) => {}
        }
        match str::from_utf8(&out.stderr) {
            Ok(s) if !s.trim().is_empty() => {
                desc.push_str("\n--- stderr\n");
                desc.push_str(s);
            }
            Ok(..) | Err(..) => {}
        }
    }

    return ProcessError {
        desc,
        exit: status,
        output: output.cloned(),
    };

    #[cfg(unix)]
    fn status_to_string(status: ExitStatus) -> String {
        use std::os::unix::process::*;

        if let Some(signal) = status.signal() {
            let name = match signal as libc::c_int {
                libc::SIGABRT => ", SIGABRT: process abort signal",
                libc::SIGALRM => ", SIGALRM: alarm clock",
                libc::SIGFPE => ", SIGFPE: erroneous arithmetic operation",
                libc::SIGHUP => ", SIGHUP: hangup",
                libc::SIGILL => ", SIGILL: illegal instruction",
                libc::SIGINT => ", SIGINT: terminal interrupt signal",
                libc::SIGKILL => ", SIGKILL: kill",
                libc::SIGPIPE => ", SIGPIPE: write on a pipe with no one to read",
                libc::SIGQUIT => ", SIGQUIT: terminal quit signal",
                libc::SIGSEGV => ", SIGSEGV: invalid memory reference",
                libc::SIGTERM => ", SIGTERM: termination signal",
                libc::SIGBUS => ", SIGBUS: access to undefined memory",
                #[cfg(not(target_os = "haiku"))]
                libc::SIGSYS => ", SIGSYS: bad system call",
                libc::SIGTRAP => ", SIGTRAP: trace/breakpoint trap",
                _ => "",
            };
            format!("signal: {}{}", signal, name)
        } else {
            status.to_string()
        }
    }

    #[cfg(windows)]
    fn status_to_string(status: ExitStatus) -> String {
        use winapi::shared::minwindef::DWORD;
        use winapi::um::winnt::*;

        let mut base = status.to_string();
        let extra = match status.code().unwrap() as DWORD {
            STATUS_ACCESS_VIOLATION => "STATUS_ACCESS_VIOLATION",
            STATUS_IN_PAGE_ERROR => "STATUS_IN_PAGE_ERROR",
            STATUS_INVALID_HANDLE => "STATUS_INVALID_HANDLE",
            STATUS_INVALID_PARAMETER => "STATUS_INVALID_PARAMETER",
            STATUS_NO_MEMORY => "STATUS_NO_MEMORY",
            STATUS_ILLEGAL_INSTRUCTION => "STATUS_ILLEGAL_INSTRUCTION",
            STATUS_NONCONTINUABLE_EXCEPTION => "STATUS_NONCONTINUABLE_EXCEPTION",
            STATUS_INVALID_DISPOSITION => "STATUS_INVALID_DISPOSITION",
            STATUS_ARRAY_BOUNDS_EXCEEDED => "STATUS_ARRAY_BOUNDS_EXCEEDED",
            STATUS_FLOAT_DENORMAL_OPERAND => "STATUS_FLOAT_DENORMAL_OPERAND",
            STATUS_FLOAT_DIVIDE_BY_ZERO => "STATUS_FLOAT_DIVIDE_BY_ZERO",
            STATUS_FLOAT_INEXACT_RESULT => "STATUS_FLOAT_INEXACT_RESULT",
            STATUS_FLOAT_INVALID_OPERATION => "STATUS_FLOAT_INVALID_OPERATION",
            STATUS_FLOAT_OVERFLOW => "STATUS_FLOAT_OVERFLOW",
            STATUS_FLOAT_STACK_CHECK => "STATUS_FLOAT_STACK_CHECK",
            STATUS_FLOAT_UNDERFLOW => "STATUS_FLOAT_UNDERFLOW",
            STATUS_INTEGER_DIVIDE_BY_ZERO => "STATUS_INTEGER_DIVIDE_BY_ZERO",
            STATUS_INTEGER_OVERFLOW => "STATUS_INTEGER_OVERFLOW",
            STATUS_PRIVILEGED_INSTRUCTION => "STATUS_PRIVILEGED_INSTRUCTION",
            STATUS_STACK_OVERFLOW => "STATUS_STACK_OVERFLOW",
            STATUS_DLL_NOT_FOUND => "STATUS_DLL_NOT_FOUND",
            STATUS_ORDINAL_NOT_FOUND => "STATUS_ORDINAL_NOT_FOUND",
            STATUS_ENTRYPOINT_NOT_FOUND => "STATUS_ENTRYPOINT_NOT_FOUND",
            STATUS_CONTROL_C_EXIT => "STATUS_CONTROL_C_EXIT",
            STATUS_DLL_INIT_FAILED => "STATUS_DLL_INIT_FAILED",
            STATUS_FLOAT_MULTIPLE_FAULTS => "STATUS_FLOAT_MULTIPLE_FAULTS",
            STATUS_FLOAT_MULTIPLE_TRAPS => "STATUS_FLOAT_MULTIPLE_TRAPS",
            STATUS_REG_NAT_CONSUMPTION => "STATUS_REG_NAT_CONSUMPTION",
            STATUS_HEAP_CORRUPTION => "STATUS_HEAP_CORRUPTION",
            STATUS_STACK_BUFFER_OVERRUN => "STATUS_STACK_BUFFER_OVERRUN",
            STATUS_ASSERTION_FAILURE => "STATUS_ASSERTION_FAILURE",
            _ => return base,
        };
        base.push_str(", ");
        base.push_str(extra);
        base
    }
}

pub fn is_simple_exit_code(code: i32) -> bool {
    // Typical unix exit codes are 0 to 127.
    // Windows doesn't have anything "typical", and is a
    // 32-bit number (which appears signed here, but is really
    // unsigned). However, most of the interesting NTSTATUS
    // codes are very large. This is just a rough
    // approximation of which codes are "normal" and which
    // ones are abnormal termination.
    code >= 0 && code <= 127
}

pub fn internal<S: fmt::Display>(error: S) -> anyhow::Error {
    InternalError::new(anyhow::format_err!("{}", error)).into()
}
<|endoftext|>


<commit_before>use std::cmp;
use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod negra;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

pub fn to_term<K: Clone + cmp::Ord, V: Clone, A, B>(tree_map: &BTreeMap<K, PMCFGRule<A, V, B>>) -> BTreeMap<K, Composition<V>> {
    let mut term_map = BTreeMap::new();

    for (address, rule) in tree_map {
        let &PMCFGRule { head: _, tail: _, ref composition, weight: _ } = rule;
        term_map.insert(address.clone(), composition.clone());
    }

    term_map
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};

    #[test]
    fn test_evaluate() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(1, 0), Var(0, 1), Var(1, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0, 0], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0, 0, 0], Composition::from(vec![
            vec![],
            vec![]
        ]));
        term_map.insert(vec![0, 0, 1], Composition::from(vec![
            vec![T('a')]
        ]));
        term_map.insert(vec![0, 0, 2], Composition::from(vec![
            vec![T('c')]
        ]));
        term_map.insert(vec![0, 1], Composition::from(vec![
            vec![T('a')]
        ]));
        term_map.insert(vec![0, 2], Composition::from(vec![
            vec![T('c')]
        ]));
        term_map.insert(vec![1], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![1, 0], Composition::from(vec![
            vec![],
            vec![]
        ]));
        term_map.insert(vec![1, 1], Composition::from(vec![
            vec![T('b')]
        ]));
        term_map.insert(vec![1, 2], Composition::from(vec![
            vec![T('d')]
        ]));

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![T('a')]
        ]));

        evaluate(&term_map);
    }

    #[test]
    fn test_to_term() {
        let compos0 = Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]);
        let compos1 = Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]);
        let compos2 = Composition::from(vec![
            vec![],
            vec![]
        ]);

        let mut tree_map = BTreeMap::new();
        tree_map.insert(vec![], PMCFGRule {
            head: 0,
            tail: vec![],
            composition: compos0.clone(),
            weight: 0.5
        });
        tree_map.insert(vec![0], PMCFGRule {
            head: 1,
            tail: vec![],
            composition: compos1.clone(),
            weight: 1.5
        });
        tree_map.insert(vec![0, 1], PMCFGRule {
            head: 2,
            tail: vec![],
            composition: compos2.clone(),
            weight: 2.5
        });

        let mut term_map = BTreeMap::new();
        term_map.insert(vec![], compos0);
        term_map.insert(vec![0], compos1);
        term_map.insert(vec![0, 1], compos2);

        assert_eq!(term_map, to_term(&tree_map));
    }
}
<commit_msg>src/pmcfg/mod.rs: to_term() now returns heads of PMCFG rules<commit_after>use std::cmp;
use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod negra;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

pub fn to_term<K: Clone + cmp::Ord, H: Clone, T: Clone, W>(tree_map: &BTreeMap<K, PMCFGRule<H, T, W>>) -> (BTreeMap<K, Composition<T>>, BTreeMap<K, H>) {
    let mut term_map = BTreeMap::new();
    let mut head_map = BTreeMap::new();

    for (address, rule) in tree_map {
        let &PMCFGRule { ref head, tail: _, ref composition, weight: _ } = rule;
        term_map.insert(address.clone(), composition.clone());
        head_map.insert(address.clone(), head.clone());
    }

    (term_map, head_map)
}

#[cfg(test)]
mod tests {
    use super::*;
    use self::VarT::{Var, T};

    #[test]
    fn test_evaluate() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(1, 0), Var(0, 1), Var(1, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0, 0], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0, 0, 0], Composition::from(vec![
            vec![],
            vec![]
        ]));
        term_map.insert(vec![0, 0, 1], Composition::from(vec![
            vec![T('a')]
        ]));
        term_map.insert(vec![0, 0, 2], Composition::from(vec![
            vec![T('c')]
        ]));
        term_map.insert(vec![0, 1], Composition::from(vec![
            vec![T('a')]
        ]));
        term_map.insert(vec![0, 2], Composition::from(vec![
            vec![T('c')]
        ]));
        term_map.insert(vec![1], Composition::from(vec![
            vec![Var(1, 0), Var(0, 0)],
            vec![Var(2, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![1, 0], Composition::from(vec![
            vec![],
            vec![]
        ]));
        term_map.insert(vec![1, 1], Composition::from(vec![
            vec![T('b')]
        ]));
        term_map.insert(vec![1, 2], Composition::from(vec![
            vec![T('d')]
        ]));

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
            vec![Var(0, 0), Var(0, 1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
            vec![T('a')]
        ]));

        evaluate(&term_map);
    }

    #[test]
    fn test_to_term() {
        let compos0 = Composition::from(vec![
            vec![Var(0, 0), T("a"), Var(0, 1), T("b")]
        ]);
        let compos1 = Composition::from(vec![
            vec![Var(1, 0)],
            vec![T("c")]
        ]);
        let compos2 = Composition::from(vec![
            vec![],
            vec![]
        ]);

        let mut tree_map = BTreeMap::new();
        tree_map.insert(vec![], PMCFGRule {
            head: 0,
            tail: vec![],
            composition: compos0.clone(),
            weight: 0.5
        });
        tree_map.insert(vec![0], PMCFGRule {
            head: 1,
            tail: vec![],
            composition: compos1.clone(),
            weight: 1.5
        });
        tree_map.insert(vec![0, 1], PMCFGRule {
            head: 2,
            tail: vec![],
            composition: compos2.clone(),
            weight: 2.5
        });

        let mut term_map = BTreeMap::new();
        term_map.insert(vec![], compos0);
        term_map.insert(vec![0], compos1);
        term_map.insert(vec![0, 1], compos2);

        assert_eq!(term_map, to_term(&tree_map));
    }
}
<|endoftext|>


<commit_before>//! Draw rectangle
//!
//! This module contains the definintion of a rectangle with possibly rounded
//! corners. It contains the code to draw the rectangle and defines properties
//! like color and shape. The rectangle dimensions and location are specified by
//! `types::Rectangle`.
//!
//! To draw a square with side 10 and top left corner in (0, 0), do the
//! following:
//! ```
//! let rectangle = Rectangle::new(color::BLACK);
//! let dims = square(0.0, 0.0, 10.0);
//! rectangle.draw(dims, &draw_state::Default::default(), transform, g);
//! ```

use types::{ Color, Radius, Resolution };
use { types, triangulation, Graphics, DrawState };
use math::{ Matrix2d, Scalar };

pub use math::margin_rectangle as margin;

/// Use x, y, half-width, half-height
pub fn centered(rect: types::Rectangle) -> types::Rectangle {
    [rect[0] - rect[2], rect[1] - rect[3], 2.0 * rect[2], 2.0 * rect[3]]
}

/// Create `types::Rectangle` for a square with a center in (`x`, `y`) and side
/// `2 * radius`.
pub fn centered_square(
    x: Scalar,
    y: Scalar,
    radius: Scalar
) -> types::Rectangle {
    [x - radius, y - radius, 2.0 * radius, 2.0 * radius]
}

/// Create `types::Rectangle` for a square with a top-left corner in (`x`, `y`)
/// and side `size`.
pub fn square(
    x: Scalar,
    y: Scalar,
    size: Scalar
) -> types::Rectangle {
    [x, y, size, size]
}

/// The shape of the rectangle corners
#[derive(Copy, Clone)]
pub enum Shape {
    /// Square corners
    Square,
    /// Round corners, with resolution per corner.
    Round(Radius, Resolution),
    /// Bevel corners
    Bevel(Radius),
}

/// The border of the rectangle
#[derive(Copy, Clone)]
pub struct Border {
    /// The color of the border
    pub color: Color,
    /// The radius of the border. The half-width of the line by which border is
    /// drawn.
    pub radius: Radius,
}

/// A filled rectangle
#[derive(Copy, Clone)]
pub struct Rectangle {
    /// The rectangle color
    pub color: Color,
    /// The roundness of the rectangle
    pub shape: Shape,
    /// The border
    pub border: Option<Border>,
}

impl Rectangle {
    /// Creates a new rectangle.
    pub fn new(color: Color) -> Rectangle {
        Rectangle {
            color: color,
            shape: Shape::Square,
            border: None,
        }
    }

    /// Creates a new rectangle with rounded corners.
    pub fn new_round(
        color: Color,
        round_radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: color,
            shape: Shape::Round(round_radius, 32),
            border: None
        }
    }

    /// Creates a new rectangle border.
    pub fn new_border(
        color: Color,
        radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: [0.0; 4],
            shape: Shape::Square,
            border: Some(Border {
                    color: color,
                    radius: radius
                })
        }
    }

    /// Creates a new rectangle border with rounded corners.
    pub fn new_round_border(
        color: Color,
        round_radius: Radius,
        border_radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: [0.0; 4],
            shape: Shape::Round(round_radius, 32),
            border: Some(Border {
                    color: color,
                    radius: border_radius
                })
        }
    }

    /// Sets color.
    pub fn color(mut self, value: Color) -> Self {
        self.color = value;
        self
    }

    /// Sets shape of the corners.
    pub fn shape(mut self, value: Shape) -> Self {
        self.shape = value;
        self
    }

    /// Sets border properties.
    pub fn border(mut self, value: Border) -> Self {
        self.border = Some(value);
        self
    }

    /// Sets optional border.
    pub fn maybe_border(mut self, value: Option<Border>) -> Self {
        self.border = value;
        self
    }

    /// Draws the rectangle using the default method.
    ///
    /// `rectangle` defines the rectangle's location and dimensions,
    /// `draw_state` draw state, `draw_state::Default::default()` can be used
    /// as a default, `transform` is the transformation matrix, `g` is the
    /// `Graphics` implementation, that is used to actually draw the rectangle.s
    #[inline(always)]
    pub fn draw<R: Into<types::Rectangle>, G>(
        &self,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        g.rectangle(self, rectangle, draw_state, transform);
    }

    /// Draws the rectangle using triangulation.
    ///
    /// This is the default implementation of draw() that will be used if `G`
    /// does not redefine `Graphics::rectangle()`.
    pub fn draw_tri<R: Into<types::Rectangle>, G>(
        &self,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        let rectangle = rectangle.into();
        if self.color[3] != 0.0 {
            match self.shape {
                Shape::Square => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                            f(&triangulation::rect_tri_list_xy(
                                transform,
                                rectangle
                            )),
                    );
                }
                Shape::Round(round_radius, resolution) => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                    triangulation::with_round_rectangle_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        round_radius,
                        |vertices| f(vertices)
                    ));
                }
                Shape::Bevel(bevel_radius) => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                    triangulation::with_round_rectangle_tri_list(
                        2,
                        transform,
                        rectangle,
                        bevel_radius,
                        |vertices| f(vertices)
                    ));
                }
            }
        }

        if let Some(Border { color, radius: border_radius }) = self.border {
            if color[3] == 0.0 { return; }
            match self.shape {
                Shape::Square => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f| f(
                            &triangulation::rect_border_tri_list_xy(
                                transform, rectangle, border_radius),
                        )
                    );
                }
                Shape::Round(round_radius, resolution) => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f|
                    triangulation::with_round_rectangle_border_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        round_radius,
                        border_radius,
                        |vertices| f(vertices)
                    ));
                }
                Shape::Bevel(bevel_radius) => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f|
                    triangulation::with_round_rectangle_border_tri_list(
                        2,
                        transform,
                        rectangle,
                        bevel_radius,
                        border_radius,
                        |vertices| f(vertices)
                    ));
                }
            }
        }
    }
}

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

    #[test]
    fn test_rectangle() {
        let _rectangle = Rectangle::new([1.0; 4])
            .color([0.0; 4])
            .shape(Shape::Round(10.0, 32))
            .border(Border { color: [0.0; 4], radius: 4.0 });
    }
}
<commit_msg>Add a function to create a rectangle by opposite corners.<commit_after>//! Draw rectangle
//!
//! This module contains the definintion of a rectangle with possibly rounded
//! corners. It contains the code to draw the rectangle and defines properties
//! like color and shape. The rectangle dimensions and location are specified by
//! `types::Rectangle`.
//!
//! To draw a square with side 10 and top left corner in (0, 0), do the
//! following:
//! ```
//! let rectangle = Rectangle::new(color::BLACK);
//! let dims = square(0.0, 0.0, 10.0);
//! rectangle.draw(dims, &draw_state::Default::default(), transform, g);
//! ```

use types::{ Color, Radius, Resolution };
use { types, triangulation, Graphics, DrawState };
use math::{ Matrix2d, Scalar };

pub use math::margin_rectangle as margin;

/// Create `types::Rectangle` by the two opposit corners.
///
/// The corners are in (x0, y0) and (x1, y1).
pub fn rectangle_by_corners(
    x0: Scalar,
    y0: Scalar,
    x1: Scalar,
    y1: Scalar
) -> types::Rectangle {
    let (xmin, w) = if x0 <= x1 {
        (x0, x1 - x0)
    } else {
        (x1, x0 - x1)
    };

    let (ymin, h) = if y0 <= y1 {
        (y0, y1 - y0)
    } else {
        (y1, y0 - y1)
    };

    [xmin, ymin, w, h]
}

/// Use x, y, half-width, half-height
pub fn centered(rect: types::Rectangle) -> types::Rectangle {
    [rect[0] - rect[2], rect[1] - rect[3], 2.0 * rect[2], 2.0 * rect[3]]
}

/// Create `types::Rectangle` for a square with a center in (`x`, `y`) and side
/// `2 * radius`.
pub fn centered_square(
    x: Scalar,
    y: Scalar,
    radius: Scalar
) -> types::Rectangle {
    [x - radius, y - radius, 2.0 * radius, 2.0 * radius]
}

/// Create `types::Rectangle` for a square with a top-left corner in (`x`, `y`)
/// and side `size`.
pub fn square(
    x: Scalar,
    y: Scalar,
    size: Scalar
) -> types::Rectangle {
    [x, y, size, size]
}

/// The shape of the rectangle corners
#[derive(Copy, Clone)]
pub enum Shape {
    /// Square corners
    Square,
    /// Round corners, with resolution per corner.
    Round(Radius, Resolution),
    /// Bevel corners
    Bevel(Radius),
}

/// The border of the rectangle
#[derive(Copy, Clone)]
pub struct Border {
    /// The color of the border
    pub color: Color,
    /// The radius of the border. The half-width of the line by which border is
    /// drawn.
    pub radius: Radius,
}

/// A filled rectangle
#[derive(Copy, Clone)]
pub struct Rectangle {
    /// The rectangle color
    pub color: Color,
    /// The roundness of the rectangle
    pub shape: Shape,
    /// The border
    pub border: Option<Border>,
}

impl Rectangle {
    /// Creates a new rectangle.
    pub fn new(color: Color) -> Rectangle {
        Rectangle {
            color: color,
            shape: Shape::Square,
            border: None,
        }
    }

    /// Creates a new rectangle with rounded corners.
    pub fn new_round(
        color: Color,
        round_radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: color,
            shape: Shape::Round(round_radius, 32),
            border: None
        }
    }

    /// Creates a new rectangle border.
    pub fn new_border(
        color: Color,
        radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: [0.0; 4],
            shape: Shape::Square,
            border: Some(Border {
                    color: color,
                    radius: radius
                })
        }
    }

    /// Creates a new rectangle border with rounded corners.
    pub fn new_round_border(
        color: Color,
        round_radius: Radius,
        border_radius: Radius
    ) -> Rectangle {
        Rectangle {
            color: [0.0; 4],
            shape: Shape::Round(round_radius, 32),
            border: Some(Border {
                    color: color,
                    radius: border_radius
                })
        }
    }

    /// Sets color.
    pub fn color(mut self, value: Color) -> Self {
        self.color = value;
        self
    }

    /// Sets shape of the corners.
    pub fn shape(mut self, value: Shape) -> Self {
        self.shape = value;
        self
    }

    /// Sets border properties.
    pub fn border(mut self, value: Border) -> Self {
        self.border = Some(value);
        self
    }

    /// Sets optional border.
    pub fn maybe_border(mut self, value: Option<Border>) -> Self {
        self.border = value;
        self
    }

    /// Draws the rectangle using the default method.
    ///
    /// `rectangle` defines the rectangle's location and dimensions,
    /// `draw_state` draw state, `draw_state::Default::default()` can be used
    /// as a default, `transform` is the transformation matrix, `g` is the
    /// `Graphics` implementation, that is used to actually draw the rectangle.s
    #[inline(always)]
    pub fn draw<R: Into<types::Rectangle>, G>(
        &self,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        g.rectangle(self, rectangle, draw_state, transform);
    }

    /// Draws the rectangle using triangulation.
    ///
    /// This is the default implementation of draw() that will be used if `G`
    /// does not redefine `Graphics::rectangle()`.
    pub fn draw_tri<R: Into<types::Rectangle>, G>(
        &self,
        rectangle: R,
        draw_state: &DrawState,
        transform: Matrix2d,
        g: &mut G
    )
        where G: Graphics
    {
        let rectangle = rectangle.into();
        if self.color[3] != 0.0 {
            match self.shape {
                Shape::Square => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                            f(&triangulation::rect_tri_list_xy(
                                transform,
                                rectangle
                            )),
                    );
                }
                Shape::Round(round_radius, resolution) => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                    triangulation::with_round_rectangle_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        round_radius,
                        |vertices| f(vertices)
                    ));
                }
                Shape::Bevel(bevel_radius) => {
                    g.tri_list(
                        draw_state,
                        &self.color,
                        |f|
                    triangulation::with_round_rectangle_tri_list(
                        2,
                        transform,
                        rectangle,
                        bevel_radius,
                        |vertices| f(vertices)
                    ));
                }
            }
        }

        if let Some(Border { color, radius: border_radius }) = self.border {
            if color[3] == 0.0 { return; }
            match self.shape {
                Shape::Square => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f| f(
                            &triangulation::rect_border_tri_list_xy(
                                transform, rectangle, border_radius),
                        )
                    );
                }
                Shape::Round(round_radius, resolution) => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f|
                    triangulation::with_round_rectangle_border_tri_list(
                        resolution,
                        transform,
                        rectangle,
                        round_radius,
                        border_radius,
                        |vertices| f(vertices)
                    ));
                }
                Shape::Bevel(bevel_radius) => {
                    g.tri_list(
                        draw_state,
                        &color,
                        |f|
                    triangulation::with_round_rectangle_border_tri_list(
                        2,
                        transform,
                        rectangle,
                        bevel_radius,
                        border_radius,
                        |vertices| f(vertices)
                    ));
                }
            }
        }
    }
}

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

    #[test]
    fn test_rectangle() {
        let _rectangle = Rectangle::new([1.0; 4])
            .color([0.0; 4])
            .shape(Shape::Round(10.0, 32))
            .border(Border { color: [0.0; 4], radius: 4.0 });
    }

    #[test]
    fn test_rectangle_by_corners() {
        assert_eq!(rectangle_by_corners(1.0, -1.0, 2.0, 3.0),
                   [1.0, -1.0, 1.0, 4.0]);
        assert_eq!(rectangle_by_corners(2.0, 3.0, 1.0, -1.0),
                   [1.0, -1.0, 1.0, 4.0]);
    }
}
<|endoftext|>


<commit_before>use petgraph::graph::IndexType;
use sdl2;
use sdl2::event::Event;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use sdl2::surface::{Surface, SurfaceRef};

use sdl;
use squaregrid::{GridDirection, SquareGrid};

// impl AsRef for &SurfaceRef {
//     fn as_ref(&self) ->
// }

pub fn render_square_grid<GridIndexType>(grid: &mut SquareGrid<GridIndexType>)
    where GridIndexType: IndexType
{
    // For a non-text based view of a maze we need a GUI window if
    // we want to see anything live.
    // To just write out an image we only need an sdl surface
    // to draw on and write out as a PNG?
    // Renderer seems to require a window...though the window
    // could be hidden I guess?
    // - sdl_context -> video_subsystem -> window_builder
    // - window_builder options:
    //   - *hidden*, fullscreen, opengl enabled, borderless, resizable
    //
    // sdl2_image provides:
    // trait SaveSurface for Surface (save as PNG only)
    // trait LoadSurface for Surface (load any texture)
    // trait LoadTexture for Renderer
    //
    // sdl2 provides a Surface accessor on the Renderer type, which
    // may also work even if the Renderer is using a Texture?
    // I'm guessing that *all* renderers for a window have a Surface, but
    // textures as memory locations on the GPU are optional.
    // SDL_Surface is just a collection of pixels while SDL_Texture is an
    // efficient, driver-specific representation of pixel data
    // SDL_Texture on the other hand, is used in a hardware rendering, textures
    // are stored in VRAM and you don't have access to it directly as with SDL_Surface.
    // The rendering operations are accelerated by GPU, using, internally, either
    // OpenGL or DirectX (available only on Windows) API, which in turn are using
    // your video hardware, hence hardware rendering name.
    // http://stackoverflow.com/questions/21007329/what-is-a-sdl-renderer/21007477#21007477
    //
    // Actually the section "If your game just wants to get fully-rendered frames to the screen"
    // looks like what we want: https://wiki.libsdl.org/MigrationGuide
    //
    // should we use rendererbuilder target_texture? "Set the renderer to support rendering to a texture."
    // does that mean we can render to a texture then do whatever.... renderer.create_texture_target
    // renderer. render_target(&mut self) -> Option<RenderTarget>
    // When the render target has been set/created the draw_line/fill_rect etc calls are directed to that
    // render target.
    //
    // # drawing every pixel yourself in sfotware and then doing one big blit (software doom etc.)
    // - single sdlTexture to represent the screen
    // - texture access streaming (frequent content change of the texture)
    // - create surface or [u8] as RGBA buffer block of pixels (can convert if need to from other formats)
    // - sdl_updatetexture (texture.update) at the end which uploads the pixels to the gpu.
    // - [SDL_RenderSetLogicalSize()] so we get scaling as required.
    // - render clear
    // - rendercopy sdlTexture (put the texture on the gpu in to the backbuffer/framebuffer memory)
    // - render present
    // Examples:
    // - https://github.com/AngryLawyer/rust-sdl2/blob/master/examples/renderer-texture.rs shows streaming texture
    //   but does not draw any lines, just messes with a mutable [u8] buffer.
    //
    // # blitting multiple "sprites" to the screen (treating surfaces as sprites, not pixel buffers)
    //   textures tend to be static once uploaded
    // - create texture(s) one per sprite etc. +
    // - texture access static
    // (or createTextureFromSurface as these two steps)
    // (it's annoying that the rust api makes it hard to draw a line to a surface)

    let sdl_setup = sdl::init();

    // If the renderer is in fullscreen desktop mode we wouldn't know or care what the size
    // is but we could then SDL_RenderSetLogicalSize(sdlRenderer, 640, 480); for example.
    // The app works with a given logical size but sdl scales it on the GPU, even handling scaling
    // with different aspect ratios and letterboxing the difference.
    let window_width = 1280;
    let window_height = 720;
    let mut window_builder = sdl_setup.video_subsystem.window("Mazes", window_width, window_height);
    let window = window_builder.position_centered()
                               .resizable()
                               .build()
                               .unwrap();
    let mut renderer = window.renderer()
                             .present_vsync()
                             .accelerated()
                             .build()
                             .unwrap();

    let black = Color::RGB(0, 0, 0);
    let white = Color::RGB(0xff, 0xff, 0xff);
    let red = Color::RGB(0xff, 0, 0);
    let green = Color::RGB(0, 0xff, 0);
    let blue = Color::RGB(0, 0, 0xff);
    let yellow = Color::RGB(0xff, 0xff, 0);

    {
        // Well this won't work as there is no SurfaceRef.
        // There is a surface on the current window. Other surfaces can be
        // converted/optimized to the surface pixel format of the window.
        // what does renderer write to? Probably depends upon the builder.
        // http://stackoverflow.com/questions/21007329/what-is-a-sdl-renderer/21007477#21007477
        // Seems rennderer is drawing/creating textures behind the scenes!
        // In that case our problem is the constant re-creation
        //let surfaceRef: &SurfaceRef = renderer.surface()
        //                                      .expect("The renderer does not have a SurfaceRef.");
        // let surface = unsafe {
        //     // &SurfaceRef -> *mut ll::SDL_Surface -> Surface
        //     // just so we can give create_texture_from_surface a generic
        //     // AsRef type that can convert to &SurfaceRef again!
        //     // And I'm still worried about Surface Drop (SDL_FreeSurface)
        //     Surface::from_ll(surfaceRef.raw())
        // };

        //renderer.create_texture_from_surface(surface).expect("Failed on texture from surface");
    }
    // let texture = renderer.create_texture_from_surface(surfaceRef).unwrap();

    let mut done = false;
    let mut events = sdl_setup.sdl_context.event_pump().unwrap();
    'event: loop {
        for event in events.poll_iter() {
            match event {
                Event::Quit{..} => break 'event,
                Event::KeyDown{keycode: Some(sdl2::keyboard::Keycode::Q), ..} => break 'event,
                _ => continue,
            }
        }

        if !done {
          // clear the buffer to white
            renderer.set_draw_color(white);
            renderer.clear();

            let scale = 1.0;
            renderer.set_scale(scale, scale);
            renderer.set_draw_color(blue);

            // A limitation of drawing to something that we are showing on the screen is surely that the OS
            // may not want to show a window with a stupidly large pixel size, whereas an image file can
            // be at a much large scale. The window can still be shown and closed though.
            let cell_size_pixels = 10;
            let img_width = cell_size_pixels * grid.dimension();
            let img_height = cell_size_pixels * grid.dimension();

            for cell in grid.iter() {
                let column = cell.x as usize;
                let row = cell.y as usize;

                let x1 = (column * cell_size_pixels) as i32;
                let y1 = (row * cell_size_pixels) as i32;
                let x2 = ((column + 1) * cell_size_pixels) as i32;
                let y2 = ((row + 1) * cell_size_pixels) as i32;

                // special cases north and west to handle first row and column.
                if grid.neighbour_at_direction(&cell, GridDirection::North).is_none() {
                    renderer.draw_line(Point::new(x1, y1), Point::new(x2, y1));
                }
                if grid.neighbour_at_direction(&cell, GridDirection::West).is_none() {
                    renderer.draw_line(Point::new(x1, y1), Point::new(x1, y2));
                }

                if !grid.is_neighbour_linked(&cell, GridDirection::East) {
                    renderer.draw_line(Point::new(x2, y1), Point::new(x2, y2));
                }
                if !grid.is_neighbour_linked(&cell, GridDirection::South) {
                    renderer.draw_line(Point::new(x1, y2), Point::new(x2, y2));
                }
            }

            // why is the cpu maxed when vsync should be on? Number of lines I guess.
            // 1% CPU with 400 cells?, 2% 900 cells, 3.2% 1600 cells, 4.5% 2500 cells,
            // 7.5% 4900 cells, 9% 6400, 10.5% 8100, 13% 10,000. Max single core = 13% @ 4.2GHz.
            // draw_lines() can be used to avoid a context swap but all the lines in one batch must be connected
            // Need to check the FPS, assuming 60Hz until doing 10K line draws.
            // oh...and it's not a release build...10,000 = ~7.0% CPU usage. 16,900 celss ~11% and won't fit on screen.

            done = true;
        }
        renderer.present();

    }
}
<commit_msg>More docs<commit_after>use petgraph::graph::IndexType;
use sdl2;
use sdl2::event::Event;
use sdl2::pixels::Color;
use sdl2::rect::{Point, Rect};
use sdl2::surface::{Surface, SurfaceRef};

use sdl;
use squaregrid::{GridDirection, SquareGrid};

// impl AsRef for &SurfaceRef {
//     fn as_ref(&self) ->
// }

pub fn render_square_grid<GridIndexType>(grid: &mut SquareGrid<GridIndexType>)
    where GridIndexType: IndexType
{
    // For a non-text based view of a maze we need a GUI window if
    // we want to see anything live.
    // To just write out an image we only need an sdl surface
    // to draw on and write out as a PNG?
    // Renderer seems to require a window...though the window
    // could be hidden I guess?
    // - sdl_context -> video_subsystem -> window_builder
    // - window_builder options:
    //   - *hidden*, fullscreen, opengl enabled, borderless, resizable
    //
    // sdl2_image provides:
    // trait SaveSurface for Surface (save as PNG only)
    // trait LoadSurface for Surface (load any texture)
    // trait LoadTexture for Renderer
    //
    // sdl2 provides a Surface accessor on the Renderer type, which
    // may also work even if the Renderer is using a Texture?
    // I'm guessing that *all* renderers for a window have a Surface, but
    // textures as memory locations on the GPU are optional.
    // SDL_Surface is just a collection of pixels while SDL_Texture is an
    // efficient, driver-specific representation of pixel data
    // SDL_Texture on the other hand, is used in a hardware rendering, textures
    // are stored in VRAM and you don't have access to it directly as with SDL_Surface.
    // The rendering operations are accelerated by GPU, using, internally, either
    // OpenGL or DirectX (available only on Windows) API, which in turn are using
    // your video hardware, hence hardware rendering name.
    // http://stackoverflow.com/questions/21007329/what-is-a-sdl-renderer/21007477#21007477
    //
    // Actually the section "If your game just wants to get fully-rendered frames to the screen"
    // looks like what we want: https://wiki.libsdl.org/MigrationGuide
    //
    // should we use rendererbuilder target_texture? "Set the renderer to support rendering to a texture."
    // does that mean we can render to a texture then do whatever.... renderer.create_texture_target
    // renderer. render_target(&mut self) -> Option<RenderTarget>
    // When the render target has been set/created the draw_line/fill_rect etc calls are directed to that
    // render target.
    //
    // # drawing every pixel yourself in software and then doing one big blit (software doom etc.)
    // - single sdlTexture to represent the screen
    // - texture access streaming (frequent content change of the texture)
    // - create surface or [u8] as RGBA buffer block of pixels (can convert if need to from other formats)
    // - sdl_updatetexture (texture.update) at the end which uploads the pixels to the gpu.
    // - [SDL_RenderSetLogicalSize()] so we get scaling as required.
    // - render clear
    // - rendercopy sdlTexture (put the texture on the gpu in to the backbuffer/framebuffer memory)
    // - render present
    // Examples:
    // - https://github.com/AngryLawyer/rust-sdl2/blob/master/examples/renderer-texture.rs shows streaming texture
    //   but does not draw any lines, just messes with a mutable [u8] buffer.
    //
    // # blitting multiple "sprites" to the screen (treating surfaces as sprites, not pixel buffers)
    //   textures tend to be static once uploaded
    // - create texture(s) one per sprite etc. +
    // - texture access static
    // (or createTextureFromSurface as these two steps)
    // (it's annoying that the rust api makes it hard to draw a line to a surface)
    //
    // # Blit surfaces and modify individual pixels in the framebuffer.
    //   Round trips--reading data back from textures--can be painfully expensive; generally you want to be pushing data in one direction always.
    //   You are probably best off, in this case, keeping everything in software until the final push to the screen, so we'll combine the two previous techniques.
    //  - create 'screen' texture
    //  - create screen surface
    //  - compose the final framebuffer into the sdl_surface.
    //  - update texture from screen surface
    //  - render clear && render copy 'screen' texture && render present

    // N.B.
    // SDL_TEXTUREACCESS_STATIC changes rarely, not lockable
    // SDL_TEXTUREACCESS_STREAMING changes frequently, lockable
    // SDL_TEXTUREACCESS_TARGET can be used as a render target

    let sdl_setup = sdl::init();

    // If the renderer is in fullscreen desktop mode we wouldn't know or care what the size
    // is but we could then SDL_RenderSetLogicalSize(sdlRenderer, 640, 480); for example.
    // The app works with a given logical size but sdl scales it on the GPU, even handling scaling
    // with different aspect ratios and letterboxing the difference.
    let window_width = 1280;
    let window_height = 720;
    let mut window_builder = sdl_setup.video_subsystem.window("Mazes", window_width, window_height);
    let window = window_builder.position_centered()
                               .resizable()
                               .build()
                               .unwrap();
    let mut renderer = window.renderer()
                             .present_vsync()
                             .accelerated()
                             .build()
                             .unwrap();

    let black = Color::RGB(0, 0, 0);
    let white = Color::RGB(0xff, 0xff, 0xff);
    let red = Color::RGB(0xff, 0, 0);
    let green = Color::RGB(0, 0xff, 0);
    let blue = Color::RGB(0, 0, 0xff);
    let yellow = Color::RGB(0xff, 0xff, 0);

    {
        // Well this won't work as there is no SurfaceRef.
        // There is a surface on the current window. Other surfaces can be
        // converted/optimized to the surface pixel format of the window.
        // what does renderer write to? Probably depends upon the builder.
        // http://stackoverflow.com/questions/21007329/what-is-a-sdl-renderer/21007477#21007477
        // Seems rennderer is drawing/creating textures behind the scenes!
        // In that case our problem is the constant re-creation
        //let surfaceRef: &SurfaceRef = renderer.surface()
        //                                      .expect("The renderer does not have a SurfaceRef.");
        // let surface = unsafe {
        //     // &SurfaceRef -> *mut ll::SDL_Surface -> Surface
        //     // just so we can give create_texture_from_surface a generic
        //     // AsRef type that can convert to &SurfaceRef again!
        //     // And I'm still worried about Surface Drop (SDL_FreeSurface)
        //     Surface::from_ll(surfaceRef.raw())
        // };

        //renderer.create_texture_from_surface(surface).expect("Failed on texture from surface");
    }
    // let texture = renderer.create_texture_from_surface(surfaceRef).unwrap();

    let mut done = false;
    let mut events = sdl_setup.sdl_context.event_pump().unwrap();
    'event: loop {
        for event in events.poll_iter() {
            match event {
                Event::Quit{..} => break 'event,
                Event::KeyDown{keycode: Some(sdl2::keyboard::Keycode::Q), ..} => break 'event,
                _ => continue,
            }
        }

        if !done {
          // clear the buffer to white
            renderer.set_draw_color(white);
            renderer.clear();

            let scale = 1.0;
            renderer.set_scale(scale, scale);
            renderer.set_draw_color(blue);

            // A limitation of drawing to something that we are showing on the screen is surely that the OS
            // may not want to show a window with a stupidly large pixel size, whereas an image file can
            // be at a much large scale. The window can still be shown and closed though.
            let cell_size_pixels = 10;
            let img_width = cell_size_pixels * grid.dimension();
            let img_height = cell_size_pixels * grid.dimension();

            for cell in grid.iter() {
                let column = cell.x as usize;
                let row = cell.y as usize;

                let x1 = (column * cell_size_pixels) as i32;
                let y1 = (row * cell_size_pixels) as i32;
                let x2 = ((column + 1) * cell_size_pixels) as i32;
                let y2 = ((row + 1) * cell_size_pixels) as i32;

                // special cases north and west to handle first row and column.
                if grid.neighbour_at_direction(&cell, GridDirection::North).is_none() {
                    renderer.draw_line(Point::new(x1, y1), Point::new(x2, y1));
                }
                if grid.neighbour_at_direction(&cell, GridDirection::West).is_none() {
                    renderer.draw_line(Point::new(x1, y1), Point::new(x1, y2));
                }

                if !grid.is_neighbour_linked(&cell, GridDirection::East) {
                    renderer.draw_line(Point::new(x2, y1), Point::new(x2, y2));
                }
                if !grid.is_neighbour_linked(&cell, GridDirection::South) {
                    renderer.draw_line(Point::new(x1, y2), Point::new(x2, y2));
                }
            }

            // why is the cpu maxed when vsync should be on? Number of lines I guess.
            // 1% CPU with 400 cells?, 2% 900 cells, 3.2% 1600 cells, 4.5% 2500 cells,
            // 7.5% 4900 cells, 9% 6400, 10.5% 8100, 13% 10,000. Max single core = 13% @ 4.2GHz.
            // draw_lines() can be used to avoid a context swap but all the lines in one batch must be connected
            // Need to check the FPS, assuming 60Hz until doing 10K line draws.
            // oh...and it's not a release build...10,000 = ~7.0% CPU usage. 16,900 celss ~11% and won't fit on screen.

            done = true;
        }
        renderer.present();

    }
}
<|endoftext|>