<commit_before>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! Payloads are self-contained projects that encapsulate a specific
//! feature or system function. Think of them as reusable chunks of
//! code that can be run across multiple hosts. Any time you have a
//! task that you want to repeat, it should probably go into a
//! payload.
//!
//! For example, a payload could handle installing a specific
//! package, such as Nginx. Or, you could create a payload that
//! configures iptables.
//!
//! # Examples
//!
//! ```no_run
//! # use inapi::{Host, Payload};
#![cfg_attr(feature = "local-run", doc = "# let mut host = Host::local(Some(\"nodes/mynode.json\")).unwrap();")]
#![cfg_attr(feature = "remote-run", doc = "# let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
//! let payload = Payload::new("nginx::install").unwrap(); // format is "payload::executable"
//! payload.run(&mut host, None).unwrap();
//! ```

mod config;
pub mod ffi;

use czmq::{ZMsg, ZPoller, ZSock, ZSys};
use error::{Error, Result};
use host::{Host,HostSendRecv};
use self::config::Config;
use serde_json;
use std::env::{current_dir, set_current_dir};
use std::process::Command;
use std::path::PathBuf;
use std::thread;
use zdaemon::ConfigFile;

#[repr(C)]
#[derive(Clone, Debug, PartialEq, RustcDecodable, RustcEncodable)]
/// The payload's programming language.
pub enum Language {
    C,
    Php,
    Rust,
}

/// Container for running a Payload.
pub struct Payload {
    /// Path to the payload directory.
    path: PathBuf,
    /// Name of the executable/source file to run.
    artifact: String,
    /// Language the payload is written in.
    language: Language,
}

impl Payload {
    /// Create a new Payload using the payload/artifact notation.
    /// This notation is simply "payload" + separator ("::") +
    /// "executable/source file". For example: "nginx::install".
    ///
    /// By default, payloads live in
    /// <project root>/payloads/<payload_name>. Thus the payload name
    /// "nginx" will resolve to <project root>/payloads/nginx/. You
    /// can also specify an absolute path to your payload, which will
    /// override the resolved path.
    ///
    /// ```no_run
    /// # use inapi::Payload;
    /// // Using standard payload/artifact notation...
    /// let payload = Payload::new("iptables::update").unwrap();
    ///
    /// // Using an absolute path...
    /// let payload = Payload::new("/mnt/intecture/payloads/iptables::update").unwrap();
    /// ```
    pub fn new(payload_artifact: &str) -> Result<Payload> {
        let mut parts: Vec<&str> = payload_artifact.split("::").collect();
        let payload = if parts.len() > 0 {
            parts.remove(0)
        } else {
            return Err(Error::Generic("Invalid payload string".into()));
        };
        let artifact = if parts.len() > 1 {
            parts.remove(1)
        } else {
            "default"
        };

        let mut buf = PathBuf::from("payloads");
        buf.push(payload);

        buf.push("payload.json");
        let config = try!(Config::load(&buf));
        buf.pop();

        // Check dependencies
        if let Some(deps) = config.dependencies {
            try!(Self::check_deps(&deps));
        }

        Ok(Payload {
            path: buf,
            artifact: artifact.into(),
            language: config.language,
        })
    }

    /// Compile a payload's source code. This function is also called
    /// by Payload::run(), but is useful for precompiling payloads
    /// ahead of time to catch build errors early.
    ///
    /// Note that this is only useful for compiled languages. If this
    /// function is run on a payload that uses an interpreted
    /// language, it will safely be ignored.
    pub fn build(&self) -> Result<()> {
        let mut make_path = self.path.clone();
        make_path.push("Makefile");

        match self.language {
            Language::C | Language::Rust if make_path.exists() && make_path.is_file() => {
                let current_dir = try!(current_dir());
                try!(set_current_dir(&self.path));

                let output = try!(Command::new("make").output());

                try!(set_current_dir(&current_dir));

                if !output.status.success() {
                    return Err(Error::BuildFailed(try!(String::from_utf8(output.stderr))).into());
                }
            },
            Language::Rust => {
                let manifest_path = format!("{}/Cargo.toml", self.path.to_str().unwrap());
                let output = try!(Command::new("cargo").arg("build").arg("--manifest-path").arg(&manifest_path).output());
                if !output.status.success() {
                    return Err(Error::BuildFailed(try!(String::from_utf8(output.stderr))).into());
                }
            },
            _ => ()
        }

        Ok(())
    }

    /// Execute the payload's artifact.
    ///
    /// For compiled languages, the artifact will be executed
    /// directly. For interpreted languages, the artifact will be
    /// passed as an argument to the interpreter.
    ///
    /// ```no_run
    /// # use inapi::{Host, Payload};
    #[cfg_attr(feature = "local-run", doc = "# let mut host = Host::local(Some(\"nodes/mynode.json\")).unwrap();")]
    #[cfg_attr(feature = "remote-run", doc = "# let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
    /// let payload = Payload::new("iptables::configure").unwrap();
    /// payload.run(&mut host, Some(vec![
    ///     "add_rule",
    ///     "..."
    /// ])).unwrap();
    /// ```
    pub fn run(&self, host: &mut Host, user_args: Option<Vec<&str>>) -> Result<()> {
        // Build payload to make sure it's up to date
        try!(self.build());

        let api_endpoint = format!("inproc://payload_{}_{}_api", self.path.to_str().unwrap(), self.artifact);
        let mut api_pipe = try!(ZSock::new_pair(&api_endpoint));
        let file_endpoint = format!("inproc://payload_{}_{}_file", self.path.to_str().unwrap(), self.artifact);
        let mut file_pipe = try!(ZSock::new_pair(&file_endpoint));

        let (mut parent, child) = try!(ZSys::create_pipe());
        let language = self.language.clone();
        let mut payload_path = PathBuf::from(&self.path);
        let artifact = self.artifact.clone();
        let user_args_c: Option<Vec<String>> = match user_args {
            Some(a) => Some(a.into_iter().map(|arg| String::from(arg)).collect()),
            None => None,
        };

        let handle = thread::spawn(move || {
            match language {
                Language::C => {
                    payload_path.push(&artifact);

                    let mut args = vec![api_endpoint, file_endpoint];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new(payload_path.to_str().unwrap()).args(&args).output());
                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                },
                Language::Php => {
                    payload_path.push(&artifact);
                    if payload_path.extension().is_none() {
                        payload_path.set_extension("php");
                    }

                    let mut args = vec![payload_path.to_str().unwrap().into(), api_endpoint, file_endpoint];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new("php").args(&args).output());
                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                },
                Language::Rust => {
                    payload_path.push("Cargo.toml");

                    let mut args = vec![
                        "run".into(),
                        "--release".into(),
                        "--bin".into(),
                        artifact,
                        "--manifest-path".into(),
                        payload_path.to_str().unwrap().into(),
                        "--".into(),
                        api_endpoint,
                        file_endpoint
                    ];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new("cargo").args(&args).output());

                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                }
            }

            try!(child.signal(0));
            Ok(())
        });

        // Send data to payload
        let json = try!(serde_json::to_string(host.data()));
        try!(api_pipe.send_str(&json));

        let mut poller = try!(ZPoller::new());
        try!(poller.add(&mut parent));
        try!(poller.add(&mut api_pipe));
        try!(poller.add(&mut file_pipe));

        loop {
            let sock: Option<ZSock> = poller.wait(None);
            if let Some(mut s) = sock {
                if s == api_pipe || s == file_pipe {
                    let msg = try!(ZMsg::recv(&mut s));
                    try!(host.send(msg));
                }
                else if s == parent {
                    break;
                } else {
                    unreachable!();
                }
            }

            if poller.terminated() {
                break;
            }
        }

        let cmd_result: Result<()> = try!(handle.join());
        try!(cmd_result);

        Ok(())
    }

    fn check_deps(payloads: &[String]) -> Result<()> {
        for payload in payloads {
            try!(Payload::new(payload));
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use host::Host;
    use super::config::Config;
    use std::fs;
    use std::io::Write;
    use std::path::PathBuf;
    use std::process::Command;
    use super::*;
    use tempdir::TempDir;
    use zdaemon::ConfigFile;

    #[test]
    fn test_new_nodeps() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_new_nodeps").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: Some(vec!["missing_payload".into()]),
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        assert!(Payload::new(buf.to_str().unwrap()).is_err());
    }

    #[test]
    fn test_build_rust() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_build_rust").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.build().unwrap();
    }

    #[test]
    fn test_build_c() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_build_c").unwrap();
        let mut buf = tempdir.path().to_owned();

        buf.push("Makefile");
        let mut fh = fs::File::create(&buf).unwrap();
        fh.write_all(b"all:
\ttouch test").unwrap();
        buf.pop();

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::C,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.build().unwrap();

        buf.push("test");
        assert!(buf.exists());
    }

    #[test]
    fn test_run() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_run").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let mut host = Host::test_new(None, None, None, None);
        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.run(&mut host, Some(vec!["abc"])).unwrap();
    }

    fn create_cargo_proj(buf: &mut PathBuf) {
        let output = Command::new("cargo")
                             .args(&["init", buf.to_str().unwrap(), "--bin", "--name", "default"])
                             .output()
                             .expect("Failed to execute process");
        assert!(output.status.success());
    }
}
<commit_msg>Minor doc change<commit_after>// Copyright 2015-2016 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

//! Payloads are self-contained projects that encapsulate a specific
//! feature or system function. Think of them as reusable chunks of
//! code that can be run across multiple hosts. Any time you have a
//! task that you want to repeat, it should probably go into a
//! payload.
//!
//! For example, a payload could handle installing a specific
//! package, such as Nginx. Or, you could create a payload that
//! configures iptables.
//!
//! # Examples
//!
//! ```no_run
//! # use inapi::{Host, Payload};
#![cfg_attr(feature = "local-run", doc = "# let mut host = Host::local(Some(\"nodes/mynode.json\")).unwrap();")]
#![cfg_attr(feature = "remote-run", doc = "# let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
//! let payload = Payload::new("nginx::install").unwrap(); // format is "payload::executable"
//! payload.run(&mut host, None).unwrap();
//! ```

mod config;
pub mod ffi;

use czmq::{ZMsg, ZPoller, ZSock, ZSys};
use error::{Error, Result};
use host::{Host,HostSendRecv};
use self::config::Config;
use serde_json;
use std::env::{current_dir, set_current_dir};
use std::process::Command;
use std::path::PathBuf;
use std::thread;
use zdaemon::ConfigFile;

#[repr(C)]
#[derive(Clone, Debug, PartialEq, RustcDecodable, RustcEncodable)]
/// The payload's programming language.
pub enum Language {
    C,
    Php,
    Rust,
}

/// Container for running a Payload.
pub struct Payload {
    /// Path to the payload directory.
    path: PathBuf,
    /// Name of the executable/source file to run.
    artifact: String,
    /// Language the payload is written in.
    language: Language,
}

impl Payload {
    /// Create a new Payload using the payload::artifact notation.
    /// This notation is simply "payload" + separator ("::") +
    /// "executable/source file". For example: "nginx::install".
    ///
    /// By default, payloads live in
    /// <project root>/payloads/<payload_name>. Thus the payload name
    /// "nginx" will resolve to <project root>/payloads/nginx/. You
    /// can also specify an absolute path to your payload, which will
    /// override the resolved path.
    ///
    /// ```no_run
    /// # use inapi::Payload;
    /// // Using standard payload/artifact notation...
    /// let payload = Payload::new("iptables::update").unwrap();
    ///
    /// // Using an absolute path...
    /// let payload = Payload::new("/mnt/intecture/payloads/iptables::update").unwrap();
    /// ```
    pub fn new(payload_artifact: &str) -> Result<Payload> {
        let mut parts: Vec<&str> = payload_artifact.split("::").collect();
        let payload = if parts.len() > 0 {
            parts.remove(0)
        } else {
            return Err(Error::Generic("Invalid payload string".into()));
        };
        let artifact = if parts.len() > 1 {
            parts.remove(1)
        } else {
            "default"
        };

        let mut buf = PathBuf::from("payloads");
        buf.push(payload);

        buf.push("payload.json");
        let config = try!(Config::load(&buf));
        buf.pop();

        // Check dependencies
        if let Some(deps) = config.dependencies {
            try!(Self::check_deps(&deps));
        }

        Ok(Payload {
            path: buf,
            artifact: artifact.into(),
            language: config.language,
        })
    }

    /// Compile a payload's source code. This function is also called
    /// by Payload::run(), but is useful for precompiling payloads
    /// ahead of time to catch build errors early.
    ///
    /// Note that this is only useful for compiled languages. If this
    /// function is run on a payload that uses an interpreted
    /// language, it will safely be ignored.
    pub fn build(&self) -> Result<()> {
        let mut make_path = self.path.clone();
        make_path.push("Makefile");

        match self.language {
            Language::C | Language::Rust if make_path.exists() && make_path.is_file() => {
                let current_dir = try!(current_dir());
                try!(set_current_dir(&self.path));

                let output = try!(Command::new("make").output());

                try!(set_current_dir(&current_dir));

                if !output.status.success() {
                    return Err(Error::BuildFailed(try!(String::from_utf8(output.stderr))).into());
                }
            },
            Language::Rust => {
                let manifest_path = format!("{}/Cargo.toml", self.path.to_str().unwrap());
                let output = try!(Command::new("cargo").arg("build").arg("--manifest-path").arg(&manifest_path).output());
                if !output.status.success() {
                    return Err(Error::BuildFailed(try!(String::from_utf8(output.stderr))).into());
                }
            },
            _ => ()
        }

        Ok(())
    }

    /// Execute the payload's artifact.
    ///
    /// For compiled languages, the artifact will be executed
    /// directly. For interpreted languages, the artifact will be
    /// passed as an argument to the interpreter.
    ///
    /// ```no_run
    /// # use inapi::{Host, Payload};
    #[cfg_attr(feature = "local-run", doc = "# let mut host = Host::local(Some(\"nodes/mynode.json\")).unwrap();")]
    #[cfg_attr(feature = "remote-run", doc = "# let mut host = Host::connect(\"nodes/mynode.json\").unwrap();")]
    /// let payload = Payload::new("iptables::configure").unwrap();
    /// payload.run(&mut host, Some(vec![
    ///     "add_rule",
    ///     "..."
    /// ])).unwrap();
    /// ```
    pub fn run(&self, host: &mut Host, user_args: Option<Vec<&str>>) -> Result<()> {
        // Build payload to make sure it's up to date
        try!(self.build());

        let api_endpoint = format!("inproc://payload_{}_{}_api", self.path.to_str().unwrap(), self.artifact);
        let mut api_pipe = try!(ZSock::new_pair(&api_endpoint));
        let file_endpoint = format!("inproc://payload_{}_{}_file", self.path.to_str().unwrap(), self.artifact);
        let mut file_pipe = try!(ZSock::new_pair(&file_endpoint));

        let (mut parent, child) = try!(ZSys::create_pipe());
        let language = self.language.clone();
        let mut payload_path = PathBuf::from(&self.path);
        let artifact = self.artifact.clone();
        let user_args_c: Option<Vec<String>> = match user_args {
            Some(a) => Some(a.into_iter().map(|arg| String::from(arg)).collect()),
            None => None,
        };

        let handle = thread::spawn(move || {
            match language {
                Language::C => {
                    payload_path.push(&artifact);

                    let mut args = vec![api_endpoint, file_endpoint];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new(payload_path.to_str().unwrap()).args(&args).output());
                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                },
                Language::Php => {
                    payload_path.push(&artifact);
                    if payload_path.extension().is_none() {
                        payload_path.set_extension("php");
                    }

                    let mut args = vec![payload_path.to_str().unwrap().into(), api_endpoint, file_endpoint];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new("php").args(&args).output());
                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                },
                Language::Rust => {
                    payload_path.push("Cargo.toml");

                    let mut args = vec![
                        "run".into(),
                        "--release".into(),
                        "--bin".into(),
                        artifact,
                        "--manifest-path".into(),
                        payload_path.to_str().unwrap().into(),
                        "--".into(),
                        api_endpoint,
                        file_endpoint
                    ];
                    if let Some(mut a) = user_args_c {
                        args.append(&mut a);
                    }

                    let output = try!(Command::new("cargo").args(&args).output());

                    if !output.status.success() {
                        try!(child.signal(0));
                        return Err(Error::RunFailed(try!(String::from_utf8(output.stderr))).into());
                    }
                }
            }

            try!(child.signal(0));
            Ok(())
        });

        // Send data to payload
        let json = try!(serde_json::to_string(host.data()));
        try!(api_pipe.send_str(&json));

        let mut poller = try!(ZPoller::new());
        try!(poller.add(&mut parent));
        try!(poller.add(&mut api_pipe));
        try!(poller.add(&mut file_pipe));

        loop {
            let sock: Option<ZSock> = poller.wait(None);
            if let Some(mut s) = sock {
                if s == api_pipe || s == file_pipe {
                    let msg = try!(ZMsg::recv(&mut s));
                    try!(host.send(msg));
                }
                else if s == parent {
                    break;
                } else {
                    unreachable!();
                }
            }

            if poller.terminated() {
                break;
            }
        }

        let cmd_result: Result<()> = try!(handle.join());
        try!(cmd_result);

        Ok(())
    }

    fn check_deps(payloads: &[String]) -> Result<()> {
        for payload in payloads {
            try!(Payload::new(payload));
        }

        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use host::Host;
    use super::config::Config;
    use std::fs;
    use std::io::Write;
    use std::path::PathBuf;
    use std::process::Command;
    use super::*;
    use tempdir::TempDir;
    use zdaemon::ConfigFile;

    #[test]
    fn test_new_nodeps() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_new_nodeps").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: Some(vec!["missing_payload".into()]),
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        assert!(Payload::new(buf.to_str().unwrap()).is_err());
    }

    #[test]
    fn test_build_rust() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_build_rust").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.build().unwrap();
    }

    #[test]
    fn test_build_c() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_build_c").unwrap();
        let mut buf = tempdir.path().to_owned();

        buf.push("Makefile");
        let mut fh = fs::File::create(&buf).unwrap();
        fh.write_all(b"all:
\ttouch test").unwrap();
        buf.pop();

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::C,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.build().unwrap();

        buf.push("test");
        assert!(buf.exists());
    }

    #[test]
    fn test_run() {
        let _ = ::_MOCK_ENV.init();

        let tempdir = TempDir::new("test_payload_run").unwrap();
        let mut buf = tempdir.path().to_owned();

        create_cargo_proj(&mut buf);

        let conf = Config {
            author: "Dr. Hibbert".into(),
            repository: "https://github.com/dhibbz/hehehe.git".into(),
            language: Language::Rust,
            dependencies: None,
        };

        buf.push("payload.json");
        conf.save(&buf).unwrap();
        buf.pop();

        let mut host = Host::test_new(None, None, None, None);
        let payload = Payload::new(buf.to_str().unwrap()).unwrap();
        payload.run(&mut host, Some(vec!["abc"])).unwrap();
    }

    fn create_cargo_proj(buf: &mut PathBuf) {
        let output = Command::new("cargo")
                             .args(&["init", buf.to_str().unwrap(), "--bin", "--name", "default"])
                             .output()
                             .expect("Failed to execute process");
        assert!(output.status.success());
    }
}
<|endoftext|>


<commit_before>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use r = device::rast;

#[deriving(Clone, PartialEq, Show)]
pub struct DrawState {
    pub primitive: r::Primitive,
    pub stencil: Option<r::Stencil>,
    pub depth: Option<r::Depth>,
    pub blend: Option<r::Blend>,
}

impl DrawState {
	pub fn new() -> DrawState {
		DrawState {
			primitive: r::Primitive {
				front_face: r::Ccw,
				method: r::Fill(r::DrawFront, r::DrawBack),
				offset: r::NoOffset,
			},
			stencil: None,
			depth: None,
			blend: None,
		}
	}
}
<commit_msg>Added cull & depth helpers for DrawState<commit_after>// Copyright 2014 The Gfx-rs Developers.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use r = device::rast;

#[deriving(Clone, PartialEq, Show)]
pub struct DrawState {
    pub primitive: r::Primitive,
    pub stencil: Option<r::Stencil>,
    pub depth: Option<r::Depth>,
    pub blend: Option<r::Blend>,
}

impl DrawState {
	pub fn new() -> DrawState {
		DrawState {
			primitive: r::Primitive {
				front_face: r::Ccw,
				method: r::Fill(r::DrawFront, r::DrawBack),
				offset: r::NoOffset,
			},
			stencil: None,
			depth: None,
			blend: None,
		}
	}

	/// set the cull mode to back faces
	pub fn cull(mut self) -> DrawState {
		self.primitive.method = r::Fill(r::DrawFront, r::CullBack);
		self
	}

	/// set the depth test with the mask
	pub fn depth(mut self, fun: &str, write: bool) -> DrawState {
		let cmp = match fun {
			"!"  => r::Comparison(r::NoLess, r::NoEqual, r::NoGreater),
			"<"  => r::Comparison(r::Less,   r::NoEqual, r::NoGreater),
			"==" => r::Comparison(r::NoLess, r::Equal,   r::NoGreater),
			"<=" => r::Comparison(r::Less,   r::Equal,   r::NoGreater),
			">"  => r::Comparison(r::NoLess, r::NoEqual, r::Greater),
			"!=" => r::Comparison(r::Less,   r::NoEqual, r::Greater),
			">=" => r::Comparison(r::NoLess, r::Equal,   r::Greater),
			"*"  => r::Comparison(r::Less,   r::Equal,   r::Greater),
			_	 => fail!("Unknown depth func: {}", fun)
		};
		self.depth = Some(r::Depth {
			fun: cmp,
			write: write,
		});
		self
	}
}
<|endoftext|>


<commit_before>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use core::option::{Option, None};

const ISRCOUNT: uint = 139;

#[link_section=".isr_vector_nvic"]
#[no_mangle]
pub static NVIC_VECTOR: [Option<unsafe extern fn()>, ..ISRCOUNT] = [
    None,                      // GPIO Port A
    None,                      // GPIO Port B
    None,                      // GPIO Port C
    None,                      // GPIO Port D
    None,                      // GPIO Port E
    None,                      // UART0 Rx and Tx
    None,                      // UART1 Rx and Tx
    None,                      // SSI0 Rx and Tx
    None,                      // I2C0 Master and Slave
    None,                      // PWM Fault
    None,                      // PWM Generator 0
    None,                      // PWM Generator 1
    None,                      // PWM Generator 2
    None,                      // Quadrature Encoder 0
    None,                      // ADC Sequence 0
    None,                      // ADC Sequence 1
    None,                      // ADC Sequence 2
    None,                      // ADC Sequence 3
    None,                      // Watchdog timer
    None,                      // Timer 0 subtimer A
    None,                      // Timer 0 subtimer B
    None,                      // Timer 1 subtimer A
    None,                      // Timer 1 subtimer B
    None,                      // Timer 2 subtimer A
    None,                      // Timer 2 subtimer B
    None,                      // Analog Comparator 0
    None,                      // Analog Comparator 1
    None,                      // Analog Comparator 2
    None,                      // System Control (PLL, OSC, BO)
    None,                      // FLASH Control
    None,                      // GPIO Port F
    None,                      // GPIO Port G
    None,                      // GPIO Port H
    None,                      // UART2 Rx and Tx
    None,                      // SSI1 Rx and Tx
    None,                      // Timer 3 subtimer A
    None,                      // Timer 3 subtimer B
    None,                      // I2C1 Master and Slave
    None,                      // Quadrature Encoder 1
    None,                      // CAN0
    None,                      // CAN1
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Hibernate
    None,                      // USB0
    None,                      // PWM Generator 3
    None,                      // uDMA Software Transfer
    None,                      // uDMA Error
    None,                      // ADC1 Sequence 0
    None,                      // ADC1 Sequence 1
    None,                      // ADC1 Sequence 2
    None,                      // ADC1 Sequence 3
    None,                      // Reserved
    None,                      // Reserved
    None,                      // GPIO Port J
    None,                      // GPIO Port K
    None,                      // GPIO Port L
    None,                      // SSI2 Rx and Tx
    None,                      // SSI3 Rx and Tx
    None,                      // UART3 Rx and Tx
    None,                      // UART4 Rx and Tx
    None,                      // UART5 Rx and Tx
    None,                      // UART6 Rx and Tx
    None,                      // UART7 Rx and Tx
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // I2C2 Master and Slave
    None,                      // I2C3 Master and Slave
    None,                      // Timer 4 subtimer A
    None,                      // Timer 4 subtimer B
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Timer 5 subtimer A
    None,                      // Timer 5 subtimer B
    None,                      // Wide Timer 0 subtimer A
    None,                      // Wide Timer 0 subtimer B
    None,                      // Wide Timer 1 subtimer A
    None,                      // Wide Timer 1 subtimer B
    None,                      // Wide Timer 2 subtimer A
    None,                      // Wide Timer 2 subtimer B
    None,                      // Wide Timer 3 subtimer A
    None,                      // Wide Timer 3 subtimer B
    None,                      // Wide Timer 4 subtimer A
    None,                      // Wide Timer 4 subtimer B
    None,                      // Wide Timer 5 subtimer A
    None,                      // Wide Timer 5 subtimer B
    None,                      // FPU
    None,                      // Reserved
    None,                      // Reserved
    None,                      // I2C4 Master and Slave
    None,                      // I2C5 Master and Slave
    None,                      // GPIO Port M
    None,                      // GPIO Port N
    None,                      // Quadrature Encoder 2
    None,                      // Reserved
    None,                      // Reserved
    None,                      // GPIO Port P (Summary or P0)
    None,                      // GPIO Port P1
    None,                      // GPIO Port P2
    None,                      // GPIO Port P3
    None,                      // GPIO Port P4
    None,                      // GPIO Port P5
    None,                      // GPIO Port P6
    None,                      // GPIO Port P7
    None,                      // GPIO Port Q (Summary or Q0)
    None,                      // GPIO Port Q1
    None,                      // GPIO Port Q2
    None,                      // GPIO Port Q3
    None,                      // GPIO Port Q4
    None,                      // GPIO Port Q5
    None,                      // GPIO Port Q6
    None,                      // GPIO Port Q7
    None,                      // GPIO Port R
    None,                      // GPIO Port S
    None,                      // PWM 1 Generator 0
    None,                      // PWM 1 Generator 1
    None,                      // PWM 1 Generator 2
    None,                      // PWM 1 Generator 3
    None,                      // PWM 1 Fault*/
];
<commit_msg>tiva: Fix Option namespacing<commit_after>// Zinc, the bare metal stack for rust.
// Copyright 2014 Lionel Flandrin <lionel@svkt.org>
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use core::option::Option::{mod, None};

const ISRCOUNT: uint = 139;

#[link_section=".isr_vector_nvic"]
#[no_mangle]
pub static NVIC_VECTOR: [Option<unsafe extern fn()>, ..ISRCOUNT] = [
    None,                      // GPIO Port A
    None,                      // GPIO Port B
    None,                      // GPIO Port C
    None,                      // GPIO Port D
    None,                      // GPIO Port E
    None,                      // UART0 Rx and Tx
    None,                      // UART1 Rx and Tx
    None,                      // SSI0 Rx and Tx
    None,                      // I2C0 Master and Slave
    None,                      // PWM Fault
    None,                      // PWM Generator 0
    None,                      // PWM Generator 1
    None,                      // PWM Generator 2
    None,                      // Quadrature Encoder 0
    None,                      // ADC Sequence 0
    None,                      // ADC Sequence 1
    None,                      // ADC Sequence 2
    None,                      // ADC Sequence 3
    None,                      // Watchdog timer
    None,                      // Timer 0 subtimer A
    None,                      // Timer 0 subtimer B
    None,                      // Timer 1 subtimer A
    None,                      // Timer 1 subtimer B
    None,                      // Timer 2 subtimer A
    None,                      // Timer 2 subtimer B
    None,                      // Analog Comparator 0
    None,                      // Analog Comparator 1
    None,                      // Analog Comparator 2
    None,                      // System Control (PLL, OSC, BO)
    None,                      // FLASH Control
    None,                      // GPIO Port F
    None,                      // GPIO Port G
    None,                      // GPIO Port H
    None,                      // UART2 Rx and Tx
    None,                      // SSI1 Rx and Tx
    None,                      // Timer 3 subtimer A
    None,                      // Timer 3 subtimer B
    None,                      // I2C1 Master and Slave
    None,                      // Quadrature Encoder 1
    None,                      // CAN0
    None,                      // CAN1
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Hibernate
    None,                      // USB0
    None,                      // PWM Generator 3
    None,                      // uDMA Software Transfer
    None,                      // uDMA Error
    None,                      // ADC1 Sequence 0
    None,                      // ADC1 Sequence 1
    None,                      // ADC1 Sequence 2
    None,                      // ADC1 Sequence 3
    None,                      // Reserved
    None,                      // Reserved
    None,                      // GPIO Port J
    None,                      // GPIO Port K
    None,                      // GPIO Port L
    None,                      // SSI2 Rx and Tx
    None,                      // SSI3 Rx and Tx
    None,                      // UART3 Rx and Tx
    None,                      // UART4 Rx and Tx
    None,                      // UART5 Rx and Tx
    None,                      // UART6 Rx and Tx
    None,                      // UART7 Rx and Tx
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // I2C2 Master and Slave
    None,                      // I2C3 Master and Slave
    None,                      // Timer 4 subtimer A
    None,                      // Timer 4 subtimer B
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Reserved
    None,                      // Timer 5 subtimer A
    None,                      // Timer 5 subtimer B
    None,                      // Wide Timer 0 subtimer A
    None,                      // Wide Timer 0 subtimer B
    None,                      // Wide Timer 1 subtimer A
    None,                      // Wide Timer 1 subtimer B
    None,                      // Wide Timer 2 subtimer A
    None,                      // Wide Timer 2 subtimer B
    None,                      // Wide Timer 3 subtimer A
    None,                      // Wide Timer 3 subtimer B
    None,                      // Wide Timer 4 subtimer A
    None,                      // Wide Timer 4 subtimer B
    None,                      // Wide Timer 5 subtimer A
    None,                      // Wide Timer 5 subtimer B
    None,                      // FPU
    None,                      // Reserved
    None,                      // Reserved
    None,                      // I2C4 Master and Slave
    None,                      // I2C5 Master and Slave
    None,                      // GPIO Port M
    None,                      // GPIO Port N
    None,                      // Quadrature Encoder 2
    None,                      // Reserved
    None,                      // Reserved
    None,                      // GPIO Port P (Summary or P0)
    None,                      // GPIO Port P1
    None,                      // GPIO Port P2
    None,                      // GPIO Port P3
    None,                      // GPIO Port P4
    None,                      // GPIO Port P5
    None,                      // GPIO Port P6
    None,                      // GPIO Port P7
    None,                      // GPIO Port Q (Summary or Q0)
    None,                      // GPIO Port Q1
    None,                      // GPIO Port Q2
    None,                      // GPIO Port Q3
    None,                      // GPIO Port Q4
    None,                      // GPIO Port Q5
    None,                      // GPIO Port Q6
    None,                      // GPIO Port Q7
    None,                      // GPIO Port R
    None,                      // GPIO Port S
    None,                      // PWM 1 Generator 0
    None,                      // PWM 1 Generator 1
    None,                      // PWM 1 Generator 2
    None,                      // PWM 1 Generator 3
    None,                      // PWM 1 Fault*/
];
<|endoftext|>


<commit_before>use crate::config::Config;
use crate::errors::RunError;
use crate::traces::{CoverageStat, TraceMap};
use std::fs::File;
use std::io::Write;

pub fn export(coverage_data: &TraceMap, config: &Config) -> Result<(), RunError> {
    let file_path = config.output_dir().join("lcov.info");
    let mut file = match File::create(file_path) {
        Ok(k) => k,
        Err(e) => {
            return Err(RunError::Lcov(format!(
                "File is not writeable: {}",
                e.to_string()
            )))
        }
    };

    for (path, traces) in coverage_data.iter() {
        if traces.is_empty() {
            continue;
        }
        writeln!(file, "TN:")?;
        writeln!(file, "SF:{}", path.to_str().unwrap())?;

        let mut fns: Vec<String> = vec![];
        let mut fnda: Vec<String> = vec![];
        let mut da: Vec<(u64, u64)> = vec![];

        for trace in traces {
            if trace.fn_name.is_some() {
                let fn_name = trace.fn_name.clone().unwrap();
                let fn_hits = match trace.stats {
                    CoverageStat::Line(hits) => hits,
                    _ => {
                        return Err(RunError::Lcov(
                            "Function doesn't have hits number".to_string(),
                        ))
                    }
                };

                fns.push(format!("FN:{},{}", trace.line, fn_name));
                fnda.push(format!("FNDA:{},{}", fn_hits, fn_name));
            }

            match trace.stats {
                CoverageStat::Line(hits) => da.push((trace.line, hits)),
                _ => (),
            };
        }

        for fn_line in fns.iter() {
            writeln!(file, "{}", fn_line)?;
        }

        writeln!(file, "FNF:{}", fns.len())?;

        for fnda_line in fnda {
            writeln!(file, "{}", fnda_line)?;
        }

        for (line, hits) in da.iter() {
            writeln!(file, "DA:{},{}", line, hits)?;
        }

        writeln!(file, "LF:{}", da.len())?;
        writeln!(
            file,
            "LH:{}",
            da.iter().filter(|(_, hits)| *hits != 0).count()
        )?;

        // TODO: add support for branching
        // BRDA (BRDA:<line number>,<block number>,<branch number>,<hits>)
        // BRF (branches found)
        // BRH (branches hit)
        // More at http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php

        writeln!(file, "end_of_record")?;
    }

    Ok(())
}
<commit_msg>Use if/let instead of empty match<commit_after>use crate::config::Config;
use crate::errors::RunError;
use crate::traces::{CoverageStat, TraceMap};
use std::fs::File;
use std::io::Write;

pub fn export(coverage_data: &TraceMap, config: &Config) -> Result<(), RunError> {
    let file_path = config.output_dir().join("lcov.info");
    let mut file = match File::create(file_path) {
        Ok(k) => k,
        Err(e) => {
            return Err(RunError::Lcov(format!(
                "File is not writeable: {}",
                e.to_string()
            )))
        }
    };

    for (path, traces) in coverage_data.iter() {
        if traces.is_empty() {
            continue;
        }
        writeln!(file, "TN:")?;
        writeln!(file, "SF:{}", path.to_str().unwrap())?;

        let mut fns: Vec<String> = vec![];
        let mut fnda: Vec<String> = vec![];
        let mut da: Vec<(u64, u64)> = vec![];

        for trace in traces {
            if trace.fn_name.is_some() {
                let fn_name = trace.fn_name.clone().unwrap();
                let fn_hits = match trace.stats {
                    CoverageStat::Line(hits) => hits,
                    _ => {
                        return Err(RunError::Lcov(
                            "Function doesn't have hits number".to_string(),
                        ))
                    }
                };

                fns.push(format!("FN:{},{}", trace.line, fn_name));
                fnda.push(format!("FNDA:{},{}", fn_hits, fn_name));
            }

            if let CoverageStat::Line(hits) = trace.stats { da.push((trace.line, hits)) }
        }

        for fn_line in fns.iter() {
            writeln!(file, "{}", fn_line)?;
        }

        writeln!(file, "FNF:{}", fns.len())?;

        for fnda_line in fnda {
            writeln!(file, "{}", fnda_line)?;
        }

        for (line, hits) in da.iter() {
            writeln!(file, "DA:{},{}", line, hits)?;
        }

        writeln!(file, "LF:{}", da.len())?;
        writeln!(
            file,
            "LH:{}",
            da.iter().filter(|(_, hits)| *hits != 0).count()
        )?;

        // TODO: add support for branching
        // BRDA (BRDA:<line number>,<block number>,<branch number>,<hits>)
        // BRF (branches found)
        // BRH (branches hit)
        // More at http://ltp.sourceforge.net/coverage/lcov/geninfo.1.php

        writeln!(file, "end_of_record")?;
    }

    Ok(())
}
<|endoftext|>


<commit_before>//! Provides the Sampler trait which is implemented by the various samplers
//! to provide stratified, low-discrepancy, adaptive sampling methods and so
//! on through a simple trait interface

use rand::StdRng;
use film::ImageSample;

pub use self::uniform::Uniform;
pub use self::ld::LowDiscrepancy;
pub use self::adaptive::Adaptive;
pub use self::block_queue::BlockQueue;

pub mod morton;
pub mod uniform;
pub mod ld;
pub mod adaptive;
pub mod block_queue;

/// Provides the interface for all samplers to implement. Defines functions for
/// getting samples from the sampler and checking the sampler has finished sampling
/// the region
pub trait Sampler {
    /// Fill the vector with 2D pixel coordinate samples for a single pixel
    /// in the region being sampled. If the sampler doesn't have any more samples
    /// for the region the vector will be empty
    /// Samplers that use randomness to compute samples will use the thread rng
    fn get_samples(&mut self, samples: &mut Vec<(f32, f32)>, rng: &mut StdRng);
    /// Fill the slice with 2D samples from the sampler
    fn get_samples_2d(&mut self, samples: &mut [(f32, f32)], rng: &mut StdRng);
    /// Fill the slice with 1D samples from the sampler
    fn get_samples_1d(&mut self, samples: &mut [f32], rng: &mut StdRng);
    /// Get the max number of samples this sampler will take per pixel
    fn max_spp(&self) -> usize;
    /// Check if the sampler has more samples for the region being sampled
    fn has_samples(&self) -> bool;
    /// Get the dimensions of the region being sampled in pixels
    fn dimensions(&self) -> (u32, u32);
    /// Move to a new block of the image to sample with this sampler by specifying
    /// the starting `(x, y)` block index for the new block. The block starting
    /// position will be calculated as `dimensions * start`
    fn select_block(&mut self, start: (u32, u32));
    /// Get the region being samples
    fn get_region(&self) -> &Region;
    /// Let the sampler inspect the results of sampling the pixel so it can
    /// determine if more samples should be taken. Returns true if these samples
    /// are ok to use, false if more need to be taken. The default implementation
    /// just returns true.
    fn report_results(&mut self, samples: &[ImageSample]) -> bool { true }
}

/// Provides a simple way to pass around a 3 component sample consisting of one 2D and
/// one 1D sample
#[derive(Debug)]
pub struct Sample {
    /// The 2D sample
    pub two_d: (f32, f32),
    /// The 1D sample
    pub one_d: f32,
}

impl Sample {
    /// Create a new sample taking the 2D sample values from the slice
    pub fn new(two_d: &(f32, f32), one_d: f32) -> Sample {
        Sample { two_d: *two_d, one_d: one_d }
    }
}

/// Defines a region of the image being sampled in pixel coordinates
#[derive(Clone, Copy, Debug)]
pub struct Region {
    /// Current coordinates of the pixel to sample (x, y)
    pub current: (u32, u32),
    /// Coordinates of the start of region being sampled (x, y)
    pub start: (u32, u32),
    /// Coordinates of the end of the region being sampled (x, y)
    pub end: (u32, u32),
    /// Dimensions of the region being sampled
    pub dim: (u32, u32),
}

impl Region {
    /// Create a new region starting at `start` with dimension `dim`
    pub fn new(start: (u32, u32), dim: (u32, u32)) -> Region {
        Region { current: start, start: start,
                 end: (start.0 + dim.0, start.1 + dim.1), dim: dim }
    }
    /// Select a new region starting at region indices `start` with the same dimensions
    /// eg. with blocks of width 8 the 2nd region along x is at 16 so to get
    /// this block you'd set start.0 = 2
    pub fn select_region(&mut self, start: (u32, u32)) {
        self.start.0 = start.0 * self.dim.0;
        self.start.1 = start.1 * self.dim.1;
        self.end.0 = self.start.0 + self.dim.0;
        self.end.1 = self.start.1 + self.dim.1;
        self.current.0 = self.start.0;
        self.current.1 = self.start.1;
    }
}

<commit_msg>Clean up warning on unused parameter that is left in for docs<commit_after>//! Provides the Sampler trait which is implemented by the various samplers
//! to provide stratified, low-discrepancy, adaptive sampling methods and so
//! on through a simple trait interface

use rand::StdRng;
use film::ImageSample;

pub use self::uniform::Uniform;
pub use self::ld::LowDiscrepancy;
pub use self::adaptive::Adaptive;
pub use self::block_queue::BlockQueue;

pub mod morton;
pub mod uniform;
pub mod ld;
pub mod adaptive;
pub mod block_queue;

/// Provides the interface for all samplers to implement. Defines functions for
/// getting samples from the sampler and checking the sampler has finished sampling
/// the region
pub trait Sampler {
    /// Fill the vector with 2D pixel coordinate samples for a single pixel
    /// in the region being sampled. If the sampler doesn't have any more samples
    /// for the region the vector will be empty
    /// Samplers that use randomness to compute samples will use the thread rng
    fn get_samples(&mut self, samples: &mut Vec<(f32, f32)>, rng: &mut StdRng);
    /// Fill the slice with 2D samples from the sampler
    fn get_samples_2d(&mut self, samples: &mut [(f32, f32)], rng: &mut StdRng);
    /// Fill the slice with 1D samples from the sampler
    fn get_samples_1d(&mut self, samples: &mut [f32], rng: &mut StdRng);
    /// Get the max number of samples this sampler will take per pixel
    fn max_spp(&self) -> usize;
    /// Check if the sampler has more samples for the region being sampled
    fn has_samples(&self) -> bool;
    /// Get the dimensions of the region being sampled in pixels
    fn dimensions(&self) -> (u32, u32);
    /// Move to a new block of the image to sample with this sampler by specifying
    /// the starting `(x, y)` block index for the new block. The block starting
    /// position will be calculated as `dimensions * start`
    fn select_block(&mut self, start: (u32, u32));
    /// Get the region being samples
    fn get_region(&self) -> &Region;
    /// Let the sampler inspect the results of sampling the pixel so it can
    /// determine if more samples should be taken. Returns true if these samples
    /// are ok to use, false if more need to be taken. The default implementation
    /// just returns true.
    fn report_results(&mut self, _samples: &[ImageSample]) -> bool { true }
}

/// Provides a simple way to pass around a 3 component sample consisting of one 2D and
/// one 1D sample
#[derive(Debug)]
pub struct Sample {
    /// The 2D sample
    pub two_d: (f32, f32),
    /// The 1D sample
    pub one_d: f32,
}

impl Sample {
    /// Create a new sample taking the 2D sample values from the slice
    pub fn new(two_d: &(f32, f32), one_d: f32) -> Sample {
        Sample { two_d: *two_d, one_d: one_d }
    }
}

/// Defines a region of the image being sampled in pixel coordinates
#[derive(Clone, Copy, Debug)]
pub struct Region {
    /// Current coordinates of the pixel to sample (x, y)
    pub current: (u32, u32),
    /// Coordinates of the start of region being sampled (x, y)
    pub start: (u32, u32),
    /// Coordinates of the end of the region being sampled (x, y)
    pub end: (u32, u32),
    /// Dimensions of the region being sampled
    pub dim: (u32, u32),
}

impl Region {
    /// Create a new region starting at `start` with dimension `dim`
    pub fn new(start: (u32, u32), dim: (u32, u32)) -> Region {
        Region { current: start, start: start,
                 end: (start.0 + dim.0, start.1 + dim.1), dim: dim }
    }
    /// Select a new region starting at region indices `start` with the same dimensions
    /// eg. with blocks of width 8 the 2nd region along x is at 16 so to get
    /// this block you'd set start.0 = 2
    pub fn select_region(&mut self, start: (u32, u32)) {
        self.start.0 = start.0 * self.dim.0;
        self.start.1 = start.1 * self.dim.1;
        self.end.0 = self.start.0 + self.dim.0;
        self.end.1 = self.start.1 + self.dim.1;
        self.current.0 = self.start.0;
        self.current.1 = self.start.1;
    }
}

<|endoftext|>


<commit_before>use crate::effect::Ability;
use crate::item::Slot;
use crate::mutate::Mutate;
use crate::query::Query;
use crate::sector::WorldSkeleton;
use crate::world::World;
use calx::Dir6;
use calx::Incremental;
use calx_ecs::Entity;
use serde_derive::{Deserialize, Serialize};

/// Return type for actions that might fail.
///
/// Used for early exit with ?-operator in the action functions.
///
/// Return true if the action advances time, false if the player can keep giving commands.
pub type ActionOutcome = Option<bool>;

/// Player command events that the world is updated with.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum Command {
    /// Called to update the state on frames where the player can't act.
    Wait,
    /// Do nothing, skip your turn.
    Pass,
    /// Take a step in direction.
    Step(Dir6),
    /// Melee attack in direction.
    Melee(Dir6),
    /// Pick up the topmost item from the floor where you're standing on.
    ///
    /// TODO: Item selection support.
    Take,
    /// Drop an item from inventory slot.
    Drop(Slot),
    /// Equip or unequip an item in slot.
    ///
    /// Items in equipment slots are unequipped to inventory. Items in inventory slots are equipped
    /// to the appropriate equipment slot.
    Equip(Slot),
    /// Place an item in inventory slot.
    InventoryPlace(Entity, Slot),
    /// Swap two slotted items in inventory.
    InventorySwap(Slot, Slot),
    /// Use an undirected action that may be invoked via an item.
    UntargetedAbility {
        ability: Ability,
        item: Option<Entity>,
    },
    /// Use a directed action that may be invoked via an item.
    TargetedAbility {
        ability: Ability,
        dir: Dir6,
        item: Option<Entity>,
    },
}

impl Incremental for World {
    type Seed = u32;
    type Event = Command;

    fn from_seed(s: &Self::Seed) -> Self { World::new(*s, WorldSkeleton::overworld_sprawl()) }

    fn update(&mut self, e: &Command) {
        self.clear_events();
        if self.player_can_act() {
            debug_assert!(*e != Command::Wait, "Calling wait during player's turn");
            self.process_cmd(e);
        } else {
            debug_assert!(*e == Command::Wait, "Giving inputs outside player's turn");
        }

        self.next_tick();
    }
}

impl World {
    /// Return whether a command will work in the current world state.
    ///
    /// Mostly for cases where the feedback is important for the UI (eg. inventory logic).
    pub fn can_command(&self, cmd: &Command) -> bool {
        use Command::*;

        if self.player().is_none() {
            return *cmd == Command::Wait;
        }
        let player = self.player().unwrap();

        match cmd {
            Wait => !self.player_can_act(),

            InventoryPlace(item, slot) => {
                if !self.entity_contains(player, *item) {
                    return false;
                }
                if self.entity_equipped(player, *slot).is_some() {
                    return false;
                }
                if !slot.accepts(self.equip_type(*item)) {
                    return false;
                }
                true
            }

            InventorySwap(slot1, slot2) => {
                if slot1 == slot2 {
                    return false;
                }
                if let Some(e) = self.entity_equipped(player, *slot1) {
                    if !slot2.accepts(self.equip_type(e)) {
                        return false;
                    }
                }

                if let Some(e) = self.entity_equipped(player, *slot2) {
                    if !slot1.accepts(self.equip_type(e)) {
                        return false;
                    }
                }
                true
            }

            // TODO: Add failure checks for the rest as needed.
            _ => true,
        }
    }

    fn process_cmd(&mut self, cmd: &Command) -> ActionOutcome {
        use Command::*;
        match cmd {
            Wait => Some(true),
            Pass => {
                let player = self.player()?;
                self.idle(player)
            }
            Step(dir) => {
                let player = self.player()?;
                self.entity_step(player, *dir)
            }
            Melee(dir) => {
                let player = self.player()?;
                self.entity_melee(player, *dir)
            }
            Take => {
                let player = self.player()?;
                let item = self.item_at(self.location(player)?)?;
                self.entity_take(player, item)
            }
            Drop(slot) => {
                let player = self.player()?;
                self.place_entity(self.entity_equipped(player, *slot)?, self.location(player)?);
                Some(true)
            }
            Equip(slot) => {
                let player = self.player()?;
                let item = self.entity_equipped(player, *slot)?;
                let swap_slot = if slot.is_equipment_slot() {
                    // Remove equipped.
                    // TODO: Items that can't be removed because of curses etc. trip here.
                    self.free_bag_slot(player)?
                } else {
                    // Equip from bag.
                    // TODO: Inability to equip item because stats limits etc. trips here.
                    self.free_equip_slot(player, item)?
                };

                self.equip_item(item, player, swap_slot);
                Some(false)
            }
            InventoryPlace(item, slot) => {
                // TODO: Check curses, limits etc, as in Equip
                let player = self.player()?;

                // Checks implemented in can_command, piggyback on those.
                if !self.can_command(cmd) {
                    return None;
                }

                self.equip_item(*item, player, *slot);
                Some(false)
            }
            InventorySwap(slot1, slot2) => {
                // TODO: Check curses, limits etc, as in Equip
                let player = self.player()?;

                // Checks implemented in can_command, piggyback on those.
                if !self.can_command(cmd) {
                    return None;
                }

                let e1 = self.entity_equipped(player, *slot1);
                let e2 = self.entity_equipped(player, *slot2);

                if let Some(e1) = e1 {
                    // XXX: Ad hoc "move it out of the way" slot.
                    self.equip_item(e1, player, Slot::Bag(999999));
                }
                if let Some(e2) = e2 {
                    self.equip_item(e2, player, *slot1);
                }
                if let Some(e1) = e1 {
                    self.equip_item(e1, player, *slot2);
                }

                Some(false)
            }

            UntargetedAbility { ability, item } => {
                // XXX: Should these be asserts or just returns?
                debug_assert!(!ability.is_targeted());

                let player = self.player()?;
                if let Some(item) = item {
                    self.use_item_ability(player, *item, *ability)
                } else {
                    self.use_ability(player, *ability)
                }
            }

            TargetedAbility { ability, dir, item } => {
                debug_assert!(ability.is_targeted());
                let player = self.player()?;
                if let Some(item) = item {
                    self.use_targeted_item_ability(player, *item, *ability, *dir)
                } else {
                    self.use_targeted_ability(player, *ability, *dir)
                }
            }
        }
    }
}
<commit_msg>Dropping items doesn't cost a turn<commit_after>use crate::effect::Ability;
use crate::item::Slot;
use crate::mutate::Mutate;
use crate::query::Query;
use crate::sector::WorldSkeleton;
use crate::world::World;
use calx::Dir6;
use calx::Incremental;
use calx_ecs::Entity;
use serde_derive::{Deserialize, Serialize};

/// Return type for actions that might fail.
///
/// Used for early exit with ?-operator in the action functions.
///
/// Return true if the action advances time, false if the player can keep giving commands.
pub type ActionOutcome = Option<bool>;

/// Player command events that the world is updated with.
#[derive(Copy, Clone, Eq, PartialEq, Debug, Serialize, Deserialize)]
pub enum Command {
    /// Called to update the state on frames where the player can't act.
    Wait,
    /// Do nothing, skip your turn.
    Pass,
    /// Take a step in direction.
    Step(Dir6),
    /// Melee attack in direction.
    Melee(Dir6),
    /// Pick up the topmost item from the floor where you're standing on.
    ///
    /// TODO: Item selection support.
    Take,
    /// Drop an item from inventory slot.
    Drop(Slot),
    /// Equip or unequip an item in slot.
    ///
    /// Items in equipment slots are unequipped to inventory. Items in inventory slots are equipped
    /// to the appropriate equipment slot.
    Equip(Slot),
    /// Place an item in inventory slot.
    InventoryPlace(Entity, Slot),
    /// Swap two slotted items in inventory.
    InventorySwap(Slot, Slot),
    /// Use an undirected action that may be invoked via an item.
    UntargetedAbility {
        ability: Ability,
        item: Option<Entity>,
    },
    /// Use a directed action that may be invoked via an item.
    TargetedAbility {
        ability: Ability,
        dir: Dir6,
        item: Option<Entity>,
    },
}

impl Incremental for World {
    type Seed = u32;
    type Event = Command;

    fn from_seed(s: &Self::Seed) -> Self { World::new(*s, WorldSkeleton::overworld_sprawl()) }

    fn update(&mut self, e: &Command) {
        self.clear_events();
        if self.player_can_act() {
            debug_assert!(*e != Command::Wait, "Calling wait during player's turn");
            self.process_cmd(e);
        } else {
            debug_assert!(*e == Command::Wait, "Giving inputs outside player's turn");
        }

        self.next_tick();
    }
}

impl World {
    /// Return whether a command will work in the current world state.
    ///
    /// Mostly for cases where the feedback is important for the UI (eg. inventory logic).
    pub fn can_command(&self, cmd: &Command) -> bool {
        use Command::*;

        if self.player().is_none() {
            return *cmd == Command::Wait;
        }
        let player = self.player().unwrap();

        match cmd {
            Wait => !self.player_can_act(),

            InventoryPlace(item, slot) => {
                if !self.entity_contains(player, *item) {
                    return false;
                }
                if self.entity_equipped(player, *slot).is_some() {
                    return false;
                }
                if !slot.accepts(self.equip_type(*item)) {
                    return false;
                }
                true
            }

            InventorySwap(slot1, slot2) => {
                if slot1 == slot2 {
                    return false;
                }
                if let Some(e) = self.entity_equipped(player, *slot1) {
                    if !slot2.accepts(self.equip_type(e)) {
                        return false;
                    }
                }

                if let Some(e) = self.entity_equipped(player, *slot2) {
                    if !slot1.accepts(self.equip_type(e)) {
                        return false;
                    }
                }
                true
            }

            // TODO: Add failure checks for the rest as needed.
            _ => true,
        }
    }

    fn process_cmd(&mut self, cmd: &Command) -> ActionOutcome {
        use Command::*;
        match cmd {
            Wait => Some(true),
            Pass => {
                let player = self.player()?;
                self.idle(player)
            }
            Step(dir) => {
                let player = self.player()?;
                self.entity_step(player, *dir)
            }
            Melee(dir) => {
                let player = self.player()?;
                self.entity_melee(player, *dir)
            }
            Take => {
                let player = self.player()?;
                let item = self.item_at(self.location(player)?)?;
                self.entity_take(player, item)
            }
            Drop(slot) => {
                let player = self.player()?;
                self.place_entity(self.entity_equipped(player, *slot)?, self.location(player)?);
                // Dropping items does not cost a turn since you'll be doing it from the inventory
                // screen.
                Some(false)
            }
            Equip(slot) => {
                let player = self.player()?;
                let item = self.entity_equipped(player, *slot)?;
                let swap_slot = if slot.is_equipment_slot() {
                    // Remove equipped.
                    // TODO: Items that can't be removed because of curses etc. trip here.
                    self.free_bag_slot(player)?
                } else {
                    // Equip from bag.
                    // TODO: Inability to equip item because stats limits etc. trips here.
                    self.free_equip_slot(player, item)?
                };

                self.equip_item(item, player, swap_slot);
                Some(false)
            }
            InventoryPlace(item, slot) => {
                // TODO: Check curses, limits etc, as in Equip
                let player = self.player()?;

                // Checks implemented in can_command, piggyback on those.
                if !self.can_command(cmd) {
                    return None;
                }

                self.equip_item(*item, player, *slot);
                Some(false)
            }
            InventorySwap(slot1, slot2) => {
                // TODO: Check curses, limits etc, as in Equip
                let player = self.player()?;

                // Checks implemented in can_command, piggyback on those.
                if !self.can_command(cmd) {
                    return None;
                }

                let e1 = self.entity_equipped(player, *slot1);
                let e2 = self.entity_equipped(player, *slot2);

                if let Some(e1) = e1 {
                    // XXX: Ad hoc "move it out of the way" slot.
                    self.equip_item(e1, player, Slot::Bag(999999));
                }
                if let Some(e2) = e2 {
                    self.equip_item(e2, player, *slot1);
                }
                if let Some(e1) = e1 {
                    self.equip_item(e1, player, *slot2);
                }

                Some(false)
            }

            UntargetedAbility { ability, item } => {
                // XXX: Should these be asserts or just returns?
                debug_assert!(!ability.is_targeted());

                let player = self.player()?;
                if let Some(item) = item {
                    self.use_item_ability(player, *item, *ability)
                } else {
                    self.use_ability(player, *ability)
                }
            }

            TargetedAbility { ability, dir, item } => {
                debug_assert!(ability.is_targeted());
                let player = self.player()?;
                if let Some(item) = item {
                    self.use_targeted_item_ability(player, *item, *ability, *dir)
                } else {
                    self.use_targeted_ability(player, *ability, *dir)
                }
            }
        }
    }
}
<|endoftext|>