<commit_before>extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate serde_json;
extern crate varlink;
extern crate varlink_parser;

use clap::{App, Arg, SubCommand};
use errors::*;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::str;
use varlink::{Connection, GetInterfaceDescriptionReply, MethodCall, OrgVarlinkServiceClient,
              OrgVarlinkServiceInterface};
use varlink_parser::Varlink;

mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
            Fmt(::std::fmt::Error);
            SerdeJson(::serde_json::Error);
            Clap(::clap::Error);
        }
        links {
            Varlink(::varlink::Error, ::varlink::ErrorKind);
            VarlinkParse(::varlink_parser::Error, ::varlink_parser::ErrorKind);
        }
        errors {
            NotImplemented(t: String) {
                display("Not yet implemented: '{}'", t)
            }
        }
    }
}

fn varlink_format(filename: &str) -> Result<()> {
    let mut buffer = String::new();
    File::open(Path::new(filename))?.read_to_string(&mut buffer)?;

    let v = Varlink::from_string(&buffer)?;
    println!("{}", v.interface);
    Ok(())
}

fn varlink_info(address: &str) -> Result<()> {
    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    let info = call.get_info()?;
    println!("Vendor: {}", info.vendor);
    println!("Product: {}", info.product);
    println!("Version: {}", info.version);
    println!("URL: {}", info.url);
    println!("Interfaces:");
    for i in info.interfaces {
        println!("  {}", i)
    }

    Ok(())
}

fn varlink_help(url: &str) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let interface: &str;

    match del.len() {
        2 => {
            address = del[0];
            interface = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    match call.get_interface_description(interface.to_string())? {
        GetInterfaceDescriptionReply {
            description: Some(desc),
        } => println!("{}", desc),
        _ => {
            return Err(ErrorKind::NotImplemented(format!("No description for {}", url)).into());
        }
    };

    Ok(())
}

fn varlink_call(url: &str, args: Option<&str>, more: bool) -> Result<()> {
    let del: Vec<&str> = url.split("/").collect();
    let address: &str;
    let method: &str;

    match del.len() {
        2 => {
            address = del[0];
            method = del[1];
        }
        _ => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            );
        }
    };

    let conn = Connection::new(address)?;
    let args = match args {
        Some(args) => serde_json::from_str(args)?,
        None => serde_json::Value::Null,
    };

    let mut call = MethodCall::<serde_json::Value, serde_json::Value, varlink::Error>::new(
        conn.clone(),
        String::from(method),
        args,
    );

    if !more {
        let reply = call.call()?;
        println!("{}", serde_json::to_string_pretty(&reply)?);
    } else {
        for reply in call.more()? {
            println!("{}", serde_json::to_string_pretty(&reply?)?);
        }
    }

    Ok(())
}

fn main() -> Result<()> {
    let mut app = App::new("varlink")
        .version("0.1")
        .arg(
            Arg::with_name("timeout")
                .short("t")
                .long("timeout")
                .value_name("SECONDS")
                .help("time in seconds to wait for a reply")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("resolver")
                .short("R")
                .long("resolver")
                .value_name("ADDRESS")
                .help("address of the resolver")
                .takes_value(true),
        )
        .subcommand(
            SubCommand::with_name("bridge")
                .about("Bridge varlink messages to services on this machine")
                .long_about(
                    "Bridge varlink messages on standard in and out to varlink services on this \
                     machine.",
                ),
        )
        .subcommand(
            SubCommand::with_name("call")
                .about("Call a method")
                .long_about("Call METHOD on INTERFACE at ADDRESS. ARGUMENTS must be valid JSON.")
                .arg(
                    Arg::with_name("more")
                        .short("m")
                        .long("more")
                        .help("wait for multiple method returns if supported"),
                )
                .arg(
                    Arg::with_name("METHOD")
                        .value_name("[ADDRESS/]INTERFACE.METHOD")
                        .required(true),
                )
                .arg(Arg::with_name("ARGUMENTS").required(false)),
        )
        .subcommand(
            SubCommand::with_name("format")
                .about("Format a varlink service file")
                .arg(
                    Arg::with_name("FILE")
                        .required(true)
                        .help("The varlink interface definition file to format"),
                ),
        )
        .subcommand(
            SubCommand::with_name("info")
                .about("Print information about a service")
                .long_about("Prints information about the service running at ADDRESS.")
                .arg(Arg::with_name("ADDRESS").required(true)),
        )
        .subcommand(
            SubCommand::with_name("help")
                .about("Print interface description or service information")
                .long_about("Prints information about INTERFACE.")
                .arg(
                    Arg::with_name("INTERFACE")
                        .value_name("[ADDRESS/]INTERFACE")
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("resolve")
                .about("Resolve an interface name to a varlink address")
                .long_about("Resolve INTERFACE to the varlink address that implements it.")
                .arg(Arg::with_name("INTERFACE").required(true)),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Generates completion scripts for your shell")
                .arg(
                    Arg::with_name("SHELL")
                        .required(true)
                        .possible_values(&["bash", "fish", "zsh"])
                        .help("The shell to generate the script for"),
                ),
        );
    let matches = app.clone().get_matches();

    match matches.subcommand() {
        ("completions", Some(sub_matches)) => {
            let shell = sub_matches.value_of("SHELL").unwrap();
            app.gen_completions_to("varlink", shell.parse().unwrap(), &mut io::stdout());
        }
        ("format", Some(sub_matches)) => {
            let filename = sub_matches.value_of("FILE").unwrap();
            varlink_format(filename)?
        }
        ("info", Some(sub_matches)) => {
            let address = sub_matches.value_of("ADDRESS").unwrap();
            varlink_info(address)?
        }
        ("help", Some(sub_matches)) => {
            let interface = sub_matches.value_of("INTERFACE").unwrap();
            varlink_help(interface)?
        }
        ("call", Some(sub_matches)) => {
            let method = sub_matches.value_of("METHOD").unwrap();
            let args = sub_matches.value_of("ARGUMENTS");
            let more = sub_matches.is_present("more");
            varlink_call(method, args, more)?
        }
        (_, _) => app.print_help()?,
    }
    Ok(())
}
<commit_msg>fixup string split<commit_after>extern crate clap;
#[macro_use]
extern crate error_chain;
extern crate serde_json;
extern crate varlink;
extern crate varlink_parser;

use clap::{App, Arg, SubCommand};
use errors::*;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::str;
use varlink::{Connection, GetInterfaceDescriptionReply, MethodCall, OrgVarlinkServiceClient,
              OrgVarlinkServiceInterface};
use varlink_parser::Varlink;

mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
            Fmt(::std::fmt::Error);
            SerdeJson(::serde_json::Error);
            Clap(::clap::Error);
        }
        links {
            Varlink(::varlink::Error, ::varlink::ErrorKind);
            VarlinkParse(::varlink_parser::Error, ::varlink_parser::ErrorKind);
        }
        errors {
            NotImplemented(t: String) {
                display("Not yet implemented: '{}'", t)
            }
        }
    }
}

fn varlink_format(filename: &str) -> Result<()> {
    let mut buffer = String::new();
    File::open(Path::new(filename))?.read_to_string(&mut buffer)?;

    let v = Varlink::from_string(&buffer)?;
    println!("{}", v.interface);
    Ok(())
}

fn varlink_info(address: &str) -> Result<()> {
    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    let info = call.get_info()?;
    println!("Vendor: {}", info.vendor);
    println!("Product: {}", info.product);
    println!("Version: {}", info.version);
    println!("URL: {}", info.url);
    println!("Interfaces:");
    for i in info.interfaces {
        println!("  {}", i)
    }

    Ok(())
}

fn varlink_help(url: &str) -> Result<()> {
    let del = match url.rfind("/") {
        None => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            )
        }
        Some(v) => v,
    };

    let address = &url[0..del];
    let interface = &url[(del + 1)..];

    let conn = Connection::new(address)?;
    let mut call = OrgVarlinkServiceClient::new(conn);
    match call.get_interface_description(interface.to_string())? {
        GetInterfaceDescriptionReply {
            description: Some(desc),
        } => println!("{}", desc),
        _ => {
            return Err(ErrorKind::NotImplemented(format!("No description for {}", url)).into());
        }
    };

    Ok(())
}

fn varlink_call(url: &str, args: Option<&str>, more: bool) -> Result<()> {
    let del = match url.rfind("/") {
        None => {
            return Err(
                ErrorKind::NotImplemented(format!("Resolver not yet implemented {}", url)).into(),
            )
        }
        Some(v) => v,
    };

    let address = &url[0..del];
    let method = &url[(del + 1)..];

    let conn = Connection::new(address)?;
    let args = match args {
        Some(args) => serde_json::from_str(args)?,
        None => serde_json::Value::Null,
    };

    let mut call = MethodCall::<serde_json::Value, serde_json::Value, varlink::Error>::new(
        conn.clone(),
        String::from(method),
        args,
    );

    if !more {
        let reply = call.call()?;
        println!("{}", serde_json::to_string_pretty(&reply)?);
    } else {
        for reply in call.more()? {
            println!("{}", serde_json::to_string_pretty(&reply?)?);
        }
    }

    Ok(())
}

fn main() -> Result<()> {
    let mut app = App::new("varlink")
        .version("0.1")
        .arg(
            Arg::with_name("timeout")
                .short("t")
                .long("timeout")
                .value_name("SECONDS")
                .help("time in seconds to wait for a reply")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("resolver")
                .short("R")
                .long("resolver")
                .value_name("ADDRESS")
                .help("address of the resolver")
                .takes_value(true),
        )
        .subcommand(
            SubCommand::with_name("bridge")
                .about("Bridge varlink messages to services on this machine")
                .long_about(
                    "Bridge varlink messages on standard in and out to varlink services on this \
                     machine.",
                ),
        )
        .subcommand(
            SubCommand::with_name("call")
                .about("Call a method")
                .long_about("Call METHOD on INTERFACE at ADDRESS. ARGUMENTS must be valid JSON.")
                .arg(
                    Arg::with_name("more")
                        .short("m")
                        .long("more")
                        .help("wait for multiple method returns if supported"),
                )
                .arg(
                    Arg::with_name("METHOD")
                        .value_name("[ADDRESS/]INTERFACE.METHOD")
                        .required(true),
                )
                .arg(Arg::with_name("ARGUMENTS").required(false)),
        )
        .subcommand(
            SubCommand::with_name("format")
                .about("Format a varlink service file")
                .arg(
                    Arg::with_name("FILE")
                        .required(true)
                        .help("The varlink interface definition file to format"),
                ),
        )
        .subcommand(
            SubCommand::with_name("info")
                .about("Print information about a service")
                .long_about("Prints information about the service running at ADDRESS.")
                .arg(Arg::with_name("ADDRESS").required(true)),
        )
        .subcommand(
            SubCommand::with_name("help")
                .about("Print interface description or service information")
                .long_about("Prints information about INTERFACE.")
                .arg(
                    Arg::with_name("INTERFACE")
                        .value_name("[ADDRESS/]INTERFACE")
                        .required(true),
                ),
        )
        .subcommand(
            SubCommand::with_name("resolve")
                .about("Resolve an interface name to a varlink address")
                .long_about("Resolve INTERFACE to the varlink address that implements it.")
                .arg(Arg::with_name("INTERFACE").required(true)),
        )
        .subcommand(
            SubCommand::with_name("completions")
                .about("Generates completion scripts for your shell")
                .arg(
                    Arg::with_name("SHELL")
                        .required(true)
                        .possible_values(&["bash", "fish", "zsh"])
                        .help("The shell to generate the script for"),
                ),
        );
    let matches = app.clone().get_matches();

    match matches.subcommand() {
        ("completions", Some(sub_matches)) => {
            let shell = sub_matches.value_of("SHELL").unwrap();
            app.gen_completions_to("varlink", shell.parse().unwrap(), &mut io::stdout());
        }
        ("format", Some(sub_matches)) => {
            let filename = sub_matches.value_of("FILE").unwrap();
            varlink_format(filename)?
        }
        ("info", Some(sub_matches)) => {
            let address = sub_matches.value_of("ADDRESS").unwrap();
            varlink_info(address)?
        }
        ("help", Some(sub_matches)) => {
            let interface = sub_matches.value_of("INTERFACE").unwrap();
            varlink_help(interface)?
        }
        ("call", Some(sub_matches)) => {
            let method = sub_matches.value_of("METHOD").unwrap();
            let args = sub_matches.value_of("ARGUMENTS");
            let more = sub_matches.is_present("more");
            varlink_call(method, args, more)?
        }
        (_, _) => app.print_help()?,
    }
    Ok(())
}
<|endoftext|>


<commit_before>use ptr::{null, addr_of};
use result::{Result, Ok, Err};
use libc::{c_char, c_uint};
use name_pool::{NamePool, add};
use str::raw::from_c_str;
use io::WriterUtil;
use jsapi::{JSBool, JSClass, JSContext, JSErrorReport, JSFunctionSpec,
               JSObject, JSRuntime, JSString, JSVERSION_LATEST, JSVal, jsid,
               JSPropertySpec, JSPropertyOp, JSStrictPropertyOp, JSProto_LIMIT};
use jsapi::bindgen::{JS_free, JS_AddObjectRoot, JS_DefineFunctions,
                        JS_DestroyContext, JS_EncodeString, JS_EvaluateScript,
                        JS_Finish, JS_GetContextPrivate, JS_GetPrivate,
                        JS_Init, JS_InitStandardClasses,
                        JS_NewGlobalObject, JS_NewContext,
                        JS_RemoveObjectRoot, JS_SetContextPrivate,
                        JS_SetErrorReporter, JS_SetOptions, JS_SetPrivate,
                        JS_SetVersion, JS_ValueToString, JS_DefineProperties,
                        JS_DefineProperty, JS_NewObject, JS_ComputeThis};
use libc::types::common::c99::{int8_t, int16_t, int32_t, int64_t, uint8_t,
                                  uint16_t, uint32_t, uint64_t};
use jsval::{JSVAL_TO_OBJECT, JSVAL_IS_PRIMITIVE};
use glue::bindgen::{RUST_JSVAL_TO_OBJECT, RUST_JSVAL_IS_PRIMITIVE};
use rust::jsobj;
pub use rust;

pub use NamePool;
pub use mod jsapi;
pub use mod glue;
pub use mod crust;

// These are just macros in jsapi.h
pub use JS_NewRuntime = jsapi::bindgen::JS_Init;
pub use JS_DestroyRuntime = jsapi::bindgen::JS_Finish;
/*
FIXME: Not sure where JS_Lock is
pub use JS_LockRuntime = jsapi::bindgen::JS_Lock;
pub use JS_UnlockRuntime = jsapi::bindgen::JS_Unlock;
*/

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
pub const JSOPTION_METHODJIT: uint32_t = (1 << 14) as u32;
pub const JSOPTION_TYPE_INFERENCE: uint32_t = (1 << 18) as u32;

pub const default_heapsize: u32 = 8_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSVAL_TAG_MAX_DOUBLE: u64 = 0x1FFF0;

pub const JSVAL_TYPE_DOUBLE: u64 = 0x00;
pub const JSVAL_TYPE_INT32: u64 = 0x01;
pub const JSVAL_TYPE_UNDEFINED: u64 = 0x02;
pub const JSVAL_TYPE_BOOLEAN: u64 = 0x03;
pub const JSVAL_TYPE_MAGIC: u64 = 0x04;
pub const JSVAL_TYPE_STRING: u64 = 0x05;
pub const JSVAL_TYPE_NULL: u64 = 0x06;
pub const JSVAL_TYPE_OBJECT: u64 = 0x07;
pub const JSVAL_TYPE_UNKNOWN: u64 = 0x20;

pub const JSVAL_TAG_SHIFT: int = 47;

//The following constants are totally broken on non-64bit platforms.
//See jsapi.h for the proper macro definitions.
pub const JSVAL_VOID: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNKNOWN) << JSVAL_TAG_SHIFT;
pub const JSVAL_NULL: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) << JSVAL_TAG_SHIFT;
pub const JSVAL_ZERO: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT;
pub const JSVAL_ONE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT) | 1;
pub const JSVAL_FALSE: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT;
pub const JSVAL_TRUE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT) | 1;

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1);

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub pure fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT
}

pub fn result(n: JSBool) -> Result<(),()> {
    if n != ERR {Ok(())} else {Err(())}
}
pub fn result_obj(o: jsobj) -> Result<jsobj, ()> {
    if o.ptr != null() {Ok(o)} else {Err(())}
}

pub type named_functions = @{
    names: ~[~str],
    funcs: ~[JSFunctionSpec]
};

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *JSContext, vp: *JSVal) -> *JSVal {
    ptr::offset(vp, 2u)
}

pub unsafe fn JS_SET_RVAL(_cx: *JSContext, vp: *JSVal, v: JSVal) {
    let vp: *mut JSVal = cast::reinterpret_cast(&vp);
    *vp = v;
}

#[inline(always)]
pub unsafe fn JS_THIS_OBJECT(cx: *JSContext, vp: *JSVal) -> *JSObject unsafe {
    let r = JSVAL_TO_OBJECT(
        if JSVAL_IS_PRIMITIVE(*ptr::offset(vp, 1)) {
            JS_ComputeThis(cx, vp)
        } else {
            *ptr::offset(vp, 1)
        });
    r
}

// This is a duplication of the shadow stuff from jsfriendapi.h.  Here
// there be dragons!
mod shadow {
    struct TypeObject {
        proto: *JSObject
    }

    struct BaseShape {
        clasp: *JSClass,
        parent: *JSObject
    }

    struct Shape {
        base: *BaseShape,
        _1: jsid,
        slotInfo: u32
    }
    const FIXED_SLOTS_SHIFT: u32 = 27;

    pub struct Object {
        shape: *Shape,
        objType: *TypeObject,
        slots: *JSVal,
        _1: *JSVal,
    }

    impl Object {
        #[inline(always)]
        pure fn numFixedSlots() -> libc::size_t unsafe {
            ((*self.shape).slotInfo >> FIXED_SLOTS_SHIFT) as libc::size_t
        }

        #[inline(always)]
        fn fixedSlots() -> *jsval {
            (ptr::offset(ptr::to_unsafe_ptr(&self), 1)) as *JSVal
        }

        // Like slotRef, but just returns the value, not a reference
        #[inline(always)]
        pure fn slotVal(slot: libc::size_t) -> JSVal unsafe {
            let nfixed : libc::size_t = self.numFixedSlots();
            if slot < nfixed {
                return *ptr::offset(self.fixedSlots(), slot as uint)
            }
            return *ptr::offset(self.slots, (slot - nfixed) as uint)
        }
    }
}

#[inline(always)]
pub unsafe fn GetReservedSlot(obj: *JSObject, slot: libc::size_t) -> JSVal {
    let s = obj as *shadow::Object;
    return (*s).slotVal(slot)
}
<commit_msg>Fix botched merge<commit_after>use ptr::{null, addr_of};
use result::{Result, Ok, Err};
use libc::{c_char, c_uint};
use name_pool::{NamePool, add};
use str::raw::from_c_str;
use io::WriterUtil;
use jsapi::{JSBool, JSClass, JSContext, JSErrorReport, JSFunctionSpec,
               JSObject, JSRuntime, JSString, JSVERSION_LATEST, JSVal, jsid,
               JSPropertySpec, JSPropertyOp, JSStrictPropertyOp, JSProto_LIMIT};
use jsapi::bindgen::{JS_free, JS_AddObjectRoot, JS_DefineFunctions,
                        JS_DestroyContext, JS_EncodeString, JS_EvaluateScript,
                        JS_Finish, JS_GetContextPrivate, JS_GetPrivate,
                        JS_Init, JS_InitStandardClasses,
                        JS_NewGlobalObject, JS_NewContext,
                        JS_RemoveObjectRoot, JS_SetContextPrivate,
                        JS_SetErrorReporter, JS_SetOptions, JS_SetPrivate,
                        JS_SetVersion, JS_ValueToString, JS_DefineProperties,
                        JS_DefineProperty, JS_NewObject, JS_ComputeThis};
use libc::types::common::c99::{int8_t, int16_t, int32_t, int64_t, uint8_t,
                                  uint16_t, uint32_t, uint64_t};
use jsval::{JSVAL_TO_OBJECT, JSVAL_IS_PRIMITIVE};
use glue::bindgen::{RUST_JSVAL_TO_OBJECT, RUST_JSVAL_IS_PRIMITIVE};
use rust::jsobj;
pub use rust;

pub use NamePool;
pub use mod jsapi;
pub use mod glue;
pub use mod crust;

// These are just macros in jsapi.h
pub use JS_NewRuntime = jsapi::bindgen::JS_Init;
pub use JS_DestroyRuntime = jsapi::bindgen::JS_Finish;
/*
FIXME: Not sure where JS_Lock is
pub use JS_LockRuntime = jsapi::bindgen::JS_Lock;
pub use JS_UnlockRuntime = jsapi::bindgen::JS_Unlock;
*/

// FIXME: Add the remaining options
pub const JSOPTION_STRICT: uint32_t =    0b00000000000001u32;
pub const JSOPTION_WERROR: uint32_t =    0b00000000000010u32;
pub const JSOPTION_VAROBJFIX: uint32_t = 0b00000000000100u32;
pub const JSOPTION_METHODJIT: uint32_t = (1 << 14) as u32;
pub const JSOPTION_TYPE_INFERENCE: uint32_t = (1 << 18) as u32;

pub const default_heapsize: u32 = 8_u32 * 1024_u32 * 1024_u32;
pub const default_stacksize: uint = 8192u;
pub const ERR: JSBool = 0_i32;

pub const JSVAL_TAG_MAX_DOUBLE: u64 = 0x1FFF0;

pub const JSVAL_TYPE_DOUBLE: u64 = 0x00;
pub const JSVAL_TYPE_INT32: u64 = 0x01;
pub const JSVAL_TYPE_UNDEFINED: u64 = 0x02;
pub const JSVAL_TYPE_BOOLEAN: u64 = 0x03;
pub const JSVAL_TYPE_MAGIC: u64 = 0x04;
pub const JSVAL_TYPE_STRING: u64 = 0x05;
pub const JSVAL_TYPE_NULL: u64 = 0x06;
pub const JSVAL_TYPE_OBJECT: u64 = 0x07;
pub const JSVAL_TYPE_UNKNOWN: u64 = 0x20;

pub const JSVAL_TAG_SHIFT: int = 47;

//The following constants are totally broken on non-64bit platforms.
//See jsapi.h for the proper macro definitions.
pub const JSVAL_VOID: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_UNKNOWN) << JSVAL_TAG_SHIFT;
pub const JSVAL_NULL: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_NULL) << JSVAL_TAG_SHIFT;
pub const JSVAL_ZERO: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT;
pub const JSVAL_ONE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_INT32) << JSVAL_TAG_SHIFT) | 1;
pub const JSVAL_FALSE: u64 = (JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT;
pub const JSVAL_TRUE: u64 = ((JSVAL_TAG_MAX_DOUBLE | JSVAL_TYPE_BOOLEAN) << JSVAL_TAG_SHIFT) | 1;

pub const JSPROP_ENUMERATE: c_uint = 0x01;
pub const JSPROP_READONLY: c_uint  = 0x02;
pub const JSPROP_SHARED: c_uint =    0x40;
pub const JSPROP_NATIVE_ACCESSORS: c_uint = 0x08;

pub const JSCLASS_RESERVED_SLOTS_SHIFT: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_WIDTH: c_uint = 8;
pub const JSCLASS_RESERVED_SLOTS_MASK: c_uint = ((1 << JSCLASS_RESERVED_SLOTS_WIDTH) - 1);

pub const JSCLASS_HIGH_FLAGS_SHIFT: c_uint =
    JSCLASS_RESERVED_SLOTS_SHIFT + JSCLASS_RESERVED_SLOTS_WIDTH;
pub const JSCLASS_IS_GLOBAL: c_uint = (1<<(JSCLASS_HIGH_FLAGS_SHIFT+1));

pub const JSCLASS_GLOBAL_SLOT_COUNT: c_uint = JSProto_LIMIT * 3 + 24;

pub pure fn JSCLASS_HAS_RESERVED_SLOTS(n: c_uint) -> c_uint {
    (n & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT
}

pub fn result(n: JSBool) -> Result<(),()> {
    if n != ERR {Ok(())} else {Err(())}
}
pub fn result_obj(o: jsobj) -> Result<jsobj, ()> {
    if o.ptr != null() {Ok(o)} else {Err(())}
}

pub type named_functions = @{
    names: ~[~str],
    funcs: ~[JSFunctionSpec]
};

#[inline(always)]
pub unsafe fn JS_ARGV(_cx: *JSContext, vp: *JSVal) -> *JSVal {
    ptr::offset(vp, 2u)
}

pub unsafe fn JS_SET_RVAL(_cx: *JSContext, vp: *JSVal, v: JSVal) {
    let vp: *mut JSVal = cast::reinterpret_cast(&vp);
    *vp = v;
}

#[inline(always)]
pub unsafe fn JS_THIS_OBJECT(cx: *JSContext, vp: *JSVal) -> *JSObject unsafe {
    let r = JSVAL_TO_OBJECT(
        if JSVAL_IS_PRIMITIVE(*ptr::offset(vp, 1)) {
            JS_ComputeThis(cx, vp)
        } else {
            *ptr::offset(vp, 1)
        });
    r
}

// This is a duplication of the shadow stuff from jsfriendapi.h.  Here
// there be dragons!
mod shadow {
    struct TypeObject {
        proto: *JSObject
    }

    struct BaseShape {
        clasp: *JSClass,
        parent: *JSObject
    }

    struct Shape {
        base: *BaseShape,
        _1: jsid,
        slotInfo: u32
    }
    const FIXED_SLOTS_SHIFT: u32 = 27;

    pub struct Object {
        shape: *Shape,
        objType: *TypeObject,
        slots: *JSVal,
        _1: *JSVal,
    }

    impl Object {
        #[inline(always)]
        pure fn numFixedSlots() -> libc::size_t unsafe {
            ((*self.shape).slotInfo >> FIXED_SLOTS_SHIFT) as libc::size_t
        }

        #[inline(always)]
        fn fixedSlots() -> *JSVal {
            (ptr::offset(ptr::to_unsafe_ptr(&self), 1)) as *JSVal
        }

        // Like slotRef, but just returns the value, not a reference
        #[inline(always)]
        pure fn slotVal(slot: libc::size_t) -> JSVal unsafe {
            let nfixed : libc::size_t = self.numFixedSlots();
            if slot < nfixed {
                return *ptr::offset(self.fixedSlots(), slot as uint)
            }
            return *ptr::offset(self.slots, (slot - nfixed) as uint)
        }
    }
}

#[inline(always)]
pub unsafe fn GetReservedSlot(obj: *JSObject, slot: libc::size_t) -> JSVal {
    let s = obj as *shadow::Object;
    return (*s).slotVal(slot)
}
<|endoftext|>


<commit_before>// use std::io::Error;
use std::fs::File;
use std::io::{BufWriter, SeekFrom, Cursor, Seek, Read};
use byteorder::{ByteOrder, BigEndian, WriteBytesExt, ReadBytesExt};
use std::path::Path;
use std::fmt;

use super::header::{ Header, read_header };
use super::write_op::{ WriteOp };

use super::archive_info::{ ArchiveInfo };
use whisper::point;

pub struct WhisperFile<'a> {
    pub path: &'a str,
    pub handle: File,
    pub header: Header
}

impl<'a> fmt::Debug for WhisperFile<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "path:  {:?}, header: {:?}", self.path, self.header)
    }
}

// TODO: Change error value to generic Error
pub fn open(path: &str) -> Result<WhisperFile, &'static str> {
    debug!("opening file");
    let file_handle = File::open(Path::new(path));

    match file_handle {
        Ok(f) => {
            let header = try!(read_header(&f));
            let whisper_file = WhisperFile { path: path, header: header, handle: f };
            Ok( whisper_file )
        },
        Err(_) => {
            Err("generic file error")
        }
    }
}

impl<'a> WhisperFile<'a> {
    // The header information is not thread safe,
    // other processes may open the file and modify the contents
    // which would be bad. It's your job, the caller, to make sure
    // the system can't do that.
    pub fn write(&self, current_time: u64, point: point::Point) -> Vec<WriteOp>{
        match self.split_archives(current_time, point.timestamp) {
            Some( (high_precision_archive, rest) ) => {
                let base_point = {
                    self.read_point(high_precision_archive.offset)
                };
                let base_timestamp = base_point.timestamp;
                self.calculate_write_ops( (high_precision_archive, rest) , point, base_timestamp)
            },
            None => {
                panic!("no archives satisfy current time")
            }
        }
    }

    fn split_archives(&self, current_time: u64, point_timestamp: u64) -> Option<(&ArchiveInfo, Vec<&ArchiveInfo>)>  {
        let mut archive_iter = self.header.archive_infos.iter();

        let hp_ai_option = archive_iter.find(|ai|
            (current_time - point_timestamp) < ai.retention
        );

        match hp_ai_option {
            Some(ai) => {
                let rest_of_archives = archive_iter;
                let low_res_archives : Vec<&ArchiveInfo> = rest_of_archives.collect();

                Some((ai, low_res_archives))
            },
            None => {
                None
            }
        }
    }

    pub fn calculate_write_ops(&self, (ai,_): (&ArchiveInfo, Vec<&ArchiveInfo>), point: point::Point, base_timestamp: u64) -> Vec<WriteOp> {
        let mut write_ops = vec![];

        {
            let write_op = build_write_op( ai, point, base_timestamp );
            write_ops.push( write_op );
        }

        write_ops
    }

    fn read_point(&self, offset: u64) -> point::Point {
        let seek = self.handle.seek(SeekFrom::Start(offset));

        match seek {
            Ok(_) => {
                let mut points_buf : [u8; 12] = [0; 12];
                let mut buf_ref : &mut [u8] = &mut points_buf;
                let read = self.handle.read(buf_ref);

                match read {
                    Ok(_) => {
                        let mut cursor = Cursor::new(buf_ref);
                        let timestamp = cursor.read_u32::<BigEndian>().unwrap() as u64;
                        let value = cursor.read_f64::<BigEndian>().unwrap();

                        point::Point{ timestamp: timestamp, value: value }
                    },
                    Err(err) => {
                        panic!("read point {:?}", err)
                    }
                }
            },
            Err(err) => {
                panic!("read_point {:?}", err)
            }
        }
    }

}

fn build_write_op(archive_info: &ArchiveInfo, point: point::Point, base_timestamp: u64) -> WriteOp {
    let mut output_data = [0; 12];
    let interval_ceiling = archive_info.interval_ceiling(&point);

    let point_value = point.value;
    {
        let buf : &mut [u8] = &mut output_data;
        let mut writer = BufWriter::new(buf);
        writer.write_u32::<BigEndian>(interval_ceiling as u32).unwrap();
        writer.write_f64::<BigEndian>(point_value).unwrap();
    }

    let seek_info = archive_info.calculate_seek(&point,  base_timestamp);

    return WriteOp {
        seek: seek_info,
        bytes: output_data
    }
}

#[test]
fn has_write_ops(){
    let whisper_file = WhisperFile{
        path: "/a/nonsense/path",
        header: Header {
            metadata: Metadata {
                aggregation_type: AggregationType::Average,
                max_retention: 86400,
                x_files_factor: 1056964608,
                archive_count: 1
            },
            archive_infos: vec![
                ArchiveInfo {
                    offset: 28,
                    seconds_per_point: 60,
                    points: 1440,
                    retention: 60 * 1440
                },
                ArchiveInfo {
                    offset: 56,
                    seconds_per_point: 60,
                    points: 1440,
                    retention: 60 * 1440
                }
            ]
        }
    };

    let fixture_time = 20;
    let write_ops = whisper_file.calculate_write_ops(
        fixture_time,
        point::Point{value: 0.0, timestamp: 10}
    );

    let expected = vec![
        WriteOp{offset: 28, value: 0.0},
        WriteOp{offset: 56, value: 0.0}
    ];
    assert_eq!(write_ops, expected);

    return;
}
<commit_msg>Use &mut self bindings<commit_after>// use std::io::Error;
use std::fs::File;
use std::io::{BufWriter, SeekFrom, Cursor, Seek, Read};
use byteorder::{ByteOrder, BigEndian, WriteBytesExt, ReadBytesExt};
use std::path::Path;
use std::fmt;

use super::header::{ Header, read_header };
use super::write_op::{ WriteOp };

use super::archive_info::{ ArchiveInfo };
use whisper::point;

pub struct WhisperFile<'a> {
    pub path: &'a str,
    pub handle: File,
    pub header: Header
}

impl<'a> fmt::Debug for WhisperFile<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "path:  {:?}, header: {:?}", self.path, self.header)
    }
}

// TODO: Change error value to generic Error
pub fn open(path: &str) -> Result<WhisperFile, &'static str> {
    debug!("opening file");
    let file_handle = File::open(Path::new(path));

    match file_handle {
        Ok(f) => {
            let header = try!(read_header(&f));
            let whisper_file = WhisperFile { path: path, header: header, handle: f };
            Ok( whisper_file )
        },
        Err(_) => {
            Err("generic file error")
        }
    }
}

impl<'a> WhisperFile<'a> {
    // The header information is not thread safe,
    // other processes may open the file and modify the contents
    // which would be bad. It's your job, the caller, to make sure
    // the system can't do that.
    pub fn write(&mut self, current_time: u64, point: point::Point) -> Vec<WriteOp>{
        let pair = {
            self.split_archives(current_time, point.timestamp)
        };

        match pair {
            Some( (high_precision_archive, rest) ) => {
                let base_point = {
                    self.read_point(high_precision_archive.offset)
                };
                let base_timestamp = base_point.timestamp;
                self.calculate_write_ops( (high_precision_archive, rest) , point, base_timestamp)
            },
            None => {
                panic!("no archives satisfy current time")
            }
        }
    }

    fn split_archives(&self, current_time: u64, point_timestamp: u64) -> Option<(&ArchiveInfo, Vec<&ArchiveInfo>)>  {
        let mut archive_iter = self.header.archive_infos.iter();

        let hp_ai_option = archive_iter.find(|ai|
            (current_time - point_timestamp) < ai.retention
        );

        match hp_ai_option {
            Some(ai) => {
                let rest_of_archives = archive_iter;
                let low_res_archives : Vec<&ArchiveInfo> = rest_of_archives.collect();

                Some((ai, low_res_archives))
            },
            None => {
                None
            }
        }
    }

    pub fn calculate_write_ops(&self, (ai,_): (&ArchiveInfo, Vec<&ArchiveInfo>), point: point::Point, base_timestamp: u64) -> Vec<WriteOp> {
        let mut write_ops = vec![];

        {
            let write_op = build_write_op( ai, point, base_timestamp );
            write_ops.push( write_op );
        }

        write_ops
    }

    fn read_point(&mut self, offset: u64) -> point::Point {
        let seek = self.handle.seek(SeekFrom::Start(offset));

        match seek {
            Ok(_) => {
                let mut points_buf : [u8; 12] = [0; 12];
                let mut buf_ref : &mut [u8] = &mut points_buf;
                let read = self.handle.read(buf_ref);

                match read {
                    Ok(_) => {
                        let mut cursor = Cursor::new(buf_ref);
                        let timestamp = cursor.read_u32::<BigEndian>().unwrap() as u64;
                        let value = cursor.read_f64::<BigEndian>().unwrap();

                        point::Point{ timestamp: timestamp, value: value }
                    },
                    Err(err) => {
                        panic!("read point {:?}", err)
                    }
                }
            },
            Err(err) => {
                panic!("read_point {:?}", err)
            }
        }
    }

}

fn build_write_op(archive_info: &ArchiveInfo, point: point::Point, base_timestamp: u64) -> WriteOp {
    let mut output_data = [0; 12];
    let interval_ceiling = archive_info.interval_ceiling(&point);

    let point_value = point.value;
    {
        let buf : &mut [u8] = &mut output_data;
        let mut writer = BufWriter::new(buf);
        writer.write_u32::<BigEndian>(interval_ceiling as u32).unwrap();
        writer.write_f64::<BigEndian>(point_value).unwrap();
    }

    let seek_info = archive_info.calculate_seek(&point,  base_timestamp);

    return WriteOp {
        seek: seek_info,
        bytes: output_data
    }
}

#[test]
fn has_write_ops(){
    let whisper_file = WhisperFile{
        path: "/a/nonsense/path",
        header: Header {
            metadata: Metadata {
                aggregation_type: AggregationType::Average,
                max_retention: 86400,
                x_files_factor: 1056964608,
                archive_count: 1
            },
            archive_infos: vec![
                ArchiveInfo {
                    offset: 28,
                    seconds_per_point: 60,
                    points: 1440,
                    retention: 60 * 1440
                },
                ArchiveInfo {
                    offset: 56,
                    seconds_per_point: 60,
                    points: 1440,
                    retention: 60 * 1440
                }
            ]
        }
    };

    let fixture_time = 20;
    let write_ops = whisper_file.calculate_write_ops(
        fixture_time,
        point::Point{value: 0.0, timestamp: 10}
    );

    let expected = vec![
        WriteOp{offset: 28, value: 0.0},
        WriteOp{offset: 56, value: 0.0}
    ];
    assert_eq!(write_ops, expected);

    return;
}
<|endoftext|>


<commit_before>use ecs::*;
use spatial_hash::*;
use util::LeakyReserver;
use math::Coord;

struct Env {
    sh: SpatialHashTable,
    ctx: EcsCtx,
    ids: LeakyReserver<EntityId>,
}

impl Env {
    fn new() -> Self {
        Env {
            sh: SpatialHashTable::new(),
            ctx: EcsCtx::new(),
            ids: LeakyReserver::new(),
        }
    }
}

#[test]
fn insert_remove() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(coord);
        entity.insert_solid();

        entity.id()
    };

    assert!(!env.sh.get(coord).solid());
    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action); // this resets the action so it can be reused
    assert!(env.sh.get(coord).solid());

    action.entity_mut(id).remove_solid();

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);
    assert!(!env.sh.get(coord).solid());
}

#[test]
fn insert_move() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);
    let end_coord = Coord::new(1, 3);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(!env.sh.get(start_coord).solid());
    assert!(env.sh.get(end_coord).solid());
}

#[test]
fn remove_position() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).remove_position();

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(!env.sh.get(start_coord).solid());
}

#[test]
fn insert_solid() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).insert_solid();

    assert!(!env.sh.get(start_coord).solid());

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());
}

#[test]
fn track_opacity() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);
    let end_coord = Coord::new(1, 3);

    // initialise with no opacity
    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.0 * 10.0);

    // add an opacity of 0.5
    action.entity_mut(id).insert_opacity(0.5);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.5 * 10.0);

    // decrease opacity to 0.2
    action.entity_mut(id).insert_opacity(0.2);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.2 * 10.0);

    // move the entity
    action.entity_mut(id).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);
    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.0 * 10.0);
    assert_eq!((env.sh.get(end_coord).opacity() * 10.0).round(), 0.2 * 10.0);
}
<commit_msg>Added additional spatial hash test<commit_after>use ecs::*;
use spatial_hash::*;
use util::LeakyReserver;
use math::Coord;

struct Env {
    sh: SpatialHashTable,
    ctx: EcsCtx,
    ids: LeakyReserver<EntityId>,
}

impl Env {
    fn new() -> Self {
        Env {
            sh: SpatialHashTable::new(),
            ctx: EcsCtx::new(),
            ids: LeakyReserver::new(),
        }
    }
}

#[test]
fn insert_remove() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(coord);
        entity.insert_solid();

        entity.id()
    };

    assert!(!env.sh.get(coord).solid());
    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action); // this resets the action so it can be reused
    assert!(env.sh.get(coord).solid());

    action.entity_mut(id).remove_solid();

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);
    assert!(!env.sh.get(coord).solid());
}

#[test]
fn insert_move() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);
    let end_coord = Coord::new(1, 3);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(!env.sh.get(start_coord).solid());
    assert!(env.sh.get(end_coord).solid());
}

#[test]
fn remove_position() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).remove_position();

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(!env.sh.get(start_coord).solid());
}

#[test]
fn insert_solid() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);

    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    action.entity_mut(id).insert_solid();

    assert!(!env.sh.get(start_coord).solid());

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());
}

#[test]
fn track_opacity() {
    let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);
    let end_coord = Coord::new(1, 3);

    // initialise with no opacity
    let id = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.0 * 10.0);

    // add an opacity of 0.5
    action.entity_mut(id).insert_opacity(0.5);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.5 * 10.0);

    // decrease opacity to 0.2
    action.entity_mut(id).insert_opacity(0.2);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.2 * 10.0);

    // move the entity
    action.entity_mut(id).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);
    assert_eq!((env.sh.get(start_coord).opacity() * 10.0).round(), 0.0 * 10.0);
    assert_eq!((env.sh.get(end_coord).opacity() * 10.0).round(), 0.2 * 10.0);
}

#[test]
fn insert_move_multiple() {
     let mut env = Env::new();

    let mut action = EcsAction::new();
    let start_coord = Coord::new(1, 2);
    let end_coord = Coord::new(1, 3);

    // add solid entity
    let id_a = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());

    // add second solid entity in same cell
    let id_b = {
        let mut entity = action.entity_mut(env.ids.reserve());
        entity.insert_position(start_coord);
        entity.insert_solid();

        entity.id()
    };

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());

    // move original entity
    action.entity_mut(id_a).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());
    assert!(env.sh.get(end_coord).solid());

    // move second entity
    action.entity_mut(id_b).insert_position(end_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(!env.sh.get(start_coord).solid());
    assert!(env.sh.get(end_coord).solid());

    // move both entities in single action
    action.entity_mut(id_a).insert_position(start_coord);
    action.entity_mut(id_b).insert_position(start_coord);

    env.sh.update(&env.ctx, &action);
    env.ctx.commit(&mut action);

    assert!(env.sh.get(start_coord).solid());
    assert!(!env.sh.get(end_coord).solid());
}
<|endoftext|>


<commit_before>/*! Audio generation and playing utilities */

use std::i16;
use std::f64::consts::{PI};
use std::sync::{Arc, Mutex};
use std::thread;
use cpal;
use ::{noise, lerp};

const SAMPLE_RATE: u32 = 44100;

/// Play a 44100 Hz mono sound using the sample data from the input
/// iterator. The input sound samples should be in range [-1.0, 1.0].
pub fn play<T: Iterator<Item=f64> + Send + 'static>(sample: Arc<Mutex<T>>) {
    let mut voice = cpal::Voice::new();

    thread::spawn(move || loop {
        {
            let mut wave_data = sample.lock().unwrap();
            let mut buffer = voice.append_data(1, cpal::SamplesRate(SAMPLE_RATE), 32768);
            for sample in buffer.iter_mut() {
                match wave_data.next() {
                    // XXX: https://github.com/tomaka/cpal/issues/39
                    // CPAL samples operate in the i16 space but must be cast to u16.
                    Some(w) => *sample = (w * i16::MAX as f64) as u16,
                    None => return,
                }
            }
        }
        voice.play();
    });
}

pub struct Sample<W> {
    wave: W,
    max_t: Option<usize>,
    t: usize,
}

impl<W: Wave> Sample<W> {
    /// Return an iterator that stops after the given duration.
    pub fn duration(mut self, duration_s: f64) -> Sample<W> {
        self.max_t = Some((duration_s * SAMPLE_RATE as f64) as usize);
        self
    }
}

impl<W: Wave> Iterator for Sample<W> {
    type Item = f64;

    fn next(&mut self) -> Option<f64> {
        if let Some(max_t) = self.max_t {
            if self.t >= max_t { return None; }
        }

        let sample = self.wave.sample(self.t as f64 / SAMPLE_RATE as f64);
        self.t += 1;
        Some(sample)
    }
}

/// Sampleable waveform.
pub trait Wave: Sized {
    /// Get the value of the wave in [-1.0, 1.0] at the given time in seconds.
    fn sample(&self, t: f64) -> f64;

    /// Set the volume of the sound.
    fn volume(self, vol: f64) -> Enveloped<Self, f64> {
        Enveloped { wave: self, envelope: vol }
    }

    /// Set the pitch of the sound.
    fn pitch(self, hz: f64) -> Pitched<Self> {
        Pitched { inner: self, hz: hz }
    }

    /// Give the sound an attack-decay-sustain-release envelope of a given
    /// duration.
    fn adsr(self, duration: f64, atk: f64, dec: f64, sus: f64, rel: f64) -> Enveloped<Self, Adsr> {
        Enveloped {
            wave: self,
            envelope: Adsr {
                attack_s: atk,
                decay_s: dec,
                sustain_volume: sus,
                release_s: rel,
                duration_s: duration,
            }
        }
    }

    /// Turn the sound into a sampling iterator.
    fn into_iter(self) -> Sample<Self> {
        Sample {
            wave: self,
            max_t: None,
            t: 0,
        }
    }
}

pub trait Envelope: Sized {
    fn volume(&self, t: f64) -> f64;
}

/// Different waves at 1 Hz.
pub enum Waveform {
    Sine,
    /// The parameter is the duty cycle of the square wave, in [0.0, 1.0]
    Square(f64),
    Saw,
    Triangle,
    Noise,
}

impl Wave for Waveform {
    fn sample(&self, t: f64) -> f64 {
        use self::Waveform::*;

        let frac = t % 1.0;

        match self {
            &Sine => (frac * PI).sin(),
            &Square(duty) => if frac < duty { 1.0 } else { -1.0 },
            &Saw => -1.0 + frac * 2.0,
            &Triangle => if frac < 0.5 { -1.0 + frac * 4.0 } else { 3.0 - frac * 4.0 },
            &Noise => noise((t * 1024.0) as i32) as f64,
        }
    }
}

pub struct Pitched<W> {
    inner: W,
    hz: f64,
}

impl<W: Wave> Wave for Pitched<W> {
    fn sample(&self, t: f64) -> f64 { self.inner.sample(t * self.hz) }
}

pub struct Enveloped<W, E> {
    wave: W,
    envelope: E,
}

impl<W: Wave, E: Envelope> Wave for Enveloped<W, E> {
    fn sample(&self, t: f64) -> f64 { self.wave.sample(t) * self.envelope.volume(t) }
}

pub struct Adsr {
    attack_s: f64,
    decay_s: f64,
    sustain_volume: f64,
    release_s: f64,
    duration_s: f64,
}

impl Envelope for Adsr {
    fn volume(&self, mut t: f64) -> f64 {
        if t < 0.0 || t > self.duration_s { return 0.0; }

        let sustain_s = self.duration_s - self.attack_s - self.decay_s - self.release_s;

        if t < self.attack_s { return lerp(0.0, 1.0, t / self.attack_s); }
        t -= self.attack_s;

        if t < self.decay_s { return lerp(1.0, self.sustain_volume, t / self.decay_s); }
        t -= self.decay_s;

        if t < sustain_s { return self.sustain_volume; }
        t -= sustain_s;

        lerp(self.sustain_volume, 0.0, t / self.release_s)
    }
}

// Volume adjustment
impl Envelope for f64 {
    fn volume(&self, _: f64) -> f64 { *self }
}

#[derive(Clone)]
pub struct Mixer {
    inner: Arc<Mutex<InnerMixer>>,
}

impl Mixer {
    /// Spawns a new mixer thread and returns a handle to control it.
    pub fn new() -> Mixer {
        let inner = Arc::new(Mutex::new(InnerMixer::new()));
        play(inner.clone());
        Mixer { inner: inner }
    }

    /// Acquire a lock to the inner mixer mutex and add a wave to it.
    pub fn add<T: Iterator<Item=f64>+Send+'static>(&mut self, wave: T) {
        self.inner.lock().unwrap().add(Box::new(wave));
    }

    /// Stop playing the mixer audio.
    pub fn stop(&mut self) {
        self.inner.lock().unwrap().running = false;
    }
}

struct InnerMixer {
    waves: Vec<Box<Iterator<Item=f64> + Send>>,
    running: bool,
}

impl InnerMixer {
    fn new() -> InnerMixer {
        InnerMixer {
            waves: Vec::new(),
            running: true,
        }
    }

    fn add(&mut self, wave: Box<Iterator<Item=f64> + Send>) {
        self.waves.push(wave);
    }
}

impl Iterator for InnerMixer {
    type Item = f64;

    fn next(&mut self) -> Option<f64> {
        if !self.running { return None; }

        let mut ret = 0.0;
        // Indexes of ended waves.
        let mut deletes = Vec::new();

        // Reverse the range because we want indices going to deletes vector
        // in reverse order.
        for i in (0..(self.waves.len())).rev() {
            match self.waves[i].next() {
                None => { deletes.push(i); }
                Some(a) => {
                    // Do some hacky equalization to keep summed waves from
                    // going outside the wave envelope.
                    ret = a + ret - a.signum() * (a * ret).max(0.0);
                }
            }
        }

        // Because deletes is in reverse order, we can swap-remove everything
        // we come across and not invalidate the values further in the list.
        for &del_idx in deletes.iter() {
            self.waves.swap_remove(del_idx);
        }

        Some(ret)
    }
}
<commit_msg>Disable the audio for now, will adapt to current CPAL when I need it<commit_after>/*! Audio generation and playing utilities */

//use std::i16;
use std::f64::consts::{PI};
use std::sync::{Arc, Mutex};
//use std::thread;
//use cpal;
use ::{noise, lerp};

const SAMPLE_RATE: u32 = 44100;

/// Play a 44100 Hz mono sound using the sample data from the input
/// iterator. The input sound samples should be in range [-1.0, 1.0].
pub fn play<T: Iterator<Item=f64> + Send + 'static>(_sample: Arc<Mutex<T>>) {
    // TODO: Get this working with the new CPAL API
    unimplemented!();
    /*
    let mut voice = cpal::Voice::new();

    thread::spawn(move || loop {
        {
            let mut wave_data = sample.lock().unwrap();
            let mut buffer = voice.append_data(1, cpal::SamplesRate(SAMPLE_RATE), 32768);
            for sample in buffer.iter_mut() {
                match wave_data.next() {
                    // XXX: https://github.com/tomaka/cpal/issues/39
                    // CPAL samples operate in the i16 space but must be cast to u16.
                    Some(w) => *sample = (w * i16::MAX as f64) as u16,
                    None => return,
                }
            }
        }
        voice.play();
    });
    */
}

pub struct Sample<W> {
    wave: W,
    max_t: Option<usize>,
    t: usize,
}

impl<W: Wave> Sample<W> {
    /// Return an iterator that stops after the given duration.
    pub fn duration(mut self, duration_s: f64) -> Sample<W> {
        self.max_t = Some((duration_s * SAMPLE_RATE as f64) as usize);
        self
    }
}

impl<W: Wave> Iterator for Sample<W> {
    type Item = f64;

    fn next(&mut self) -> Option<f64> {
        if let Some(max_t) = self.max_t {
            if self.t >= max_t { return None; }
        }

        let sample = self.wave.sample(self.t as f64 / SAMPLE_RATE as f64);
        self.t += 1;
        Some(sample)
    }
}

/// Sampleable waveform.
pub trait Wave: Sized {
    /// Get the value of the wave in [-1.0, 1.0] at the given time in seconds.
    fn sample(&self, t: f64) -> f64;

    /// Set the volume of the sound.
    fn volume(self, vol: f64) -> Enveloped<Self, f64> {
        Enveloped { wave: self, envelope: vol }
    }

    /// Set the pitch of the sound.
    fn pitch(self, hz: f64) -> Pitched<Self> {
        Pitched { inner: self, hz: hz }
    }

    /// Give the sound an attack-decay-sustain-release envelope of a given
    /// duration.
    fn adsr(self, duration: f64, atk: f64, dec: f64, sus: f64, rel: f64) -> Enveloped<Self, Adsr> {
        Enveloped {
            wave: self,
            envelope: Adsr {
                attack_s: atk,
                decay_s: dec,
                sustain_volume: sus,
                release_s: rel,
                duration_s: duration,
            }
        }
    }

    /// Turn the sound into a sampling iterator.
    fn into_iter(self) -> Sample<Self> {
        Sample {
            wave: self,
            max_t: None,
            t: 0,
        }
    }
}

pub trait Envelope: Sized {
    fn volume(&self, t: f64) -> f64;
}

/// Different waves at 1 Hz.
pub enum Waveform {
    Sine,
    /// The parameter is the duty cycle of the square wave, in [0.0, 1.0]
    Square(f64),
    Saw,
    Triangle,
    Noise,
}

impl Wave for Waveform {
    fn sample(&self, t: f64) -> f64 {
        use self::Waveform::*;

        let frac = t % 1.0;

        match self {
            &Sine => (frac * PI).sin(),
            &Square(duty) => if frac < duty { 1.0 } else { -1.0 },
            &Saw => -1.0 + frac * 2.0,
            &Triangle => if frac < 0.5 { -1.0 + frac * 4.0 } else { 3.0 - frac * 4.0 },
            &Noise => noise((t * 1024.0) as i32) as f64,
        }
    }
}

pub struct Pitched<W> {
    inner: W,
    hz: f64,
}

impl<W: Wave> Wave for Pitched<W> {
    fn sample(&self, t: f64) -> f64 { self.inner.sample(t * self.hz) }
}

pub struct Enveloped<W, E> {
    wave: W,
    envelope: E,
}

impl<W: Wave, E: Envelope> Wave for Enveloped<W, E> {
    fn sample(&self, t: f64) -> f64 { self.wave.sample(t) * self.envelope.volume(t) }
}

pub struct Adsr {
    attack_s: f64,
    decay_s: f64,
    sustain_volume: f64,
    release_s: f64,
    duration_s: f64,
}

impl Envelope for Adsr {
    fn volume(&self, mut t: f64) -> f64 {
        if t < 0.0 || t > self.duration_s { return 0.0; }

        let sustain_s = self.duration_s - self.attack_s - self.decay_s - self.release_s;

        if t < self.attack_s { return lerp(0.0, 1.0, t / self.attack_s); }
        t -= self.attack_s;

        if t < self.decay_s { return lerp(1.0, self.sustain_volume, t / self.decay_s); }
        t -= self.decay_s;

        if t < sustain_s { return self.sustain_volume; }
        t -= sustain_s;

        lerp(self.sustain_volume, 0.0, t / self.release_s)
    }
}

// Volume adjustment
impl Envelope for f64 {
    fn volume(&self, _: f64) -> f64 { *self }
}

#[derive(Clone)]
pub struct Mixer {
    inner: Arc<Mutex<InnerMixer>>,
}

impl Mixer {
    /// Spawns a new mixer thread and returns a handle to control it.
    pub fn new() -> Mixer {
        let inner = Arc::new(Mutex::new(InnerMixer::new()));
        play(inner.clone());
        Mixer { inner: inner }
    }

    /// Acquire a lock to the inner mixer mutex and add a wave to it.
    pub fn add<T: Iterator<Item=f64>+Send+'static>(&mut self, wave: T) {
        self.inner.lock().unwrap().add(Box::new(wave));
    }

    /// Stop playing the mixer audio.
    pub fn stop(&mut self) {
        self.inner.lock().unwrap().running = false;
    }
}

struct InnerMixer {
    waves: Vec<Box<Iterator<Item=f64> + Send>>,
    running: bool,
}

impl InnerMixer {
    fn new() -> InnerMixer {
        InnerMixer {
            waves: Vec::new(),
            running: true,
        }
    }

    fn add(&mut self, wave: Box<Iterator<Item=f64> + Send>) {
        self.waves.push(wave);
    }
}

impl Iterator for InnerMixer {
    type Item = f64;

    fn next(&mut self) -> Option<f64> {
        if !self.running { return None; }

        let mut ret = 0.0;
        // Indexes of ended waves.
        let mut deletes = Vec::new();

        // Reverse the range because we want indices going to deletes vector
        // in reverse order.
        for i in (0..(self.waves.len())).rev() {
            match self.waves[i].next() {
                None => { deletes.push(i); }
                Some(a) => {
                    // Do some hacky equalization to keep summed waves from
                    // going outside the wave envelope.
                    ret = a + ret - a.signum() * (a * ret).max(0.0);
                }
            }
        }

        // Because deletes is in reverse order, we can swap-remove everything
        // we come across and not invalidate the values further in the list.
        for &del_idx in deletes.iter() {
            self.waves.swap_remove(del_idx);
        }

        Some(ret)
    }
}
<|endoftext|>


<commit_before>use alto::{self, SourceTrait};
use std::fs::File;
use std::path::Path;
use std::sync::mpsc::{Receiver, SyncSender, sync_channel};
use std::thread::spawn;
use vorbis::Decoder;

/// The device is responsible of managing synchronization and audio playback by providing a playback
/// cursor to a closure used as demo.
///
/// You shouldn’t have more than one Device per program.
pub struct Device {
  /// Playback length.
  len: f32,
  /// Sender used to send requests to audio thread.
  req_sx: SyncSender<Request>,
  /// Receiver used to receive responses from the audio thread.
  resp_rx: Receiver<Response>
}

enum Request {
  Toggle,
  PlaybackLength,
  PlaybackCursor,
  SetCursor(f32)
}

enum Response {
  PlaybackLength(f32),
  PlaybackCursor(f32)
}

impl Device {
  pub fn new<P>(track_path: P) -> Self where P: AsRef<Path> {
    info!("loading soundtrack {:?}", track_path.as_ref());

    // FIXME: stream the file instead?
    // load PCM data from the file
    let vorbis_decoder = Decoder::new(File::open(track_path).unwrap()).unwrap();
    let mut pcm_buffer = Vec::new();

    for packet in vorbis_decoder.into_packets().map(Result::unwrap) {
      pcm_buffer.extend(packet.data);
    }

    // create a channel to send requests to / retrieve them from the audio thread
    let (req_sx, req_rx) = sync_channel(0);
    // create a channel to receive responses / to send responses from the audio thread
    let (mut resp_sx, resp_rx) = sync_channel(0);

    info!("starting audio thread");

    let _ = spawn(move || {
      deb!("initializing OpenAL");

      let alto = alto::Alto::load_default().unwrap();
      let al_device = alto.open(None).unwrap();
      let al_ctx = al_device.new_context(None).unwrap();

      // create the required objects to play the soundtrack
      let mut al_buffer = al_ctx.new_buffer().unwrap();
      let mut al_source = al_ctx.new_streaming_source().unwrap();

      // fill the OpenAL buffers with the PCM data
      let _ = al_buffer.set_data::<alto::Stereo<_>, _>(&pcm_buffer[..], 44100);

      // compute the length of soundtrack
      let l = (al_buffer.size().unwrap() * 8 / (al_buffer.channels().unwrap() * al_buffer.bits().unwrap())) as f32 / al_buffer.frequency().unwrap() as f32;

      let _ = al_source.queue_buffer(al_buffer);

      loop {
        match req_rx.recv() {
          Ok(req) => dispatch_request(&mut al_source, l, req, &mut resp_sx),
          Err(_) => break
        }
      }

      info!("leaving the audio thread");
    });

    // get the length
    let _ = req_sx.send(Request::PlaybackLength);

    let len = match resp_rx.recv() {
      Ok(Response::PlaybackLength(len)) => len,
      _ => unimplemented!()
    };

    Device {
      len: len,
      req_sx: req_sx,
      resp_rx: resp_rx
    }
  }

  pub fn toggle(&self) {
    let _ = self.req_sx.send(Request::Toggle);
  }

  pub fn playback_len(&self) -> f32 {
    self.len
  }

  pub fn playback_cursor(&self) -> f32 {
    let _ = self.req_sx.send(Request::PlaybackCursor);

    match self.resp_rx.recv() {
      Ok(Response::PlaybackCursor(c)) => c,
      _ => unimplemented!()
    }
  }

  pub fn set_playback_cursor(&self, t: f32) {
    let _ = self.req_sx.send(Request::SetCursor(t));
  }
}

fn dispatch_request(source: &mut alto::StreamingSource, len: f32, req: Request, resp_sx: &mut SyncSender<Response>) {
  match req {
    Request::Toggle => {
      toggle_source(source);
    },

    Request::PlaybackLength => {
      let _ = resp_sx.send(Response::PlaybackLength(len));
    },

    Request::PlaybackCursor => {
      let _ = resp_sx.send(Response::PlaybackCursor(playback_cursor_source(source, len)));
    },

    Request::SetCursor(t) => {
      set_playback_cursor_source(source, t, len);
    }
  }
}

fn toggle_source(source: &mut alto::StreamingSource) {
  if source.state().unwrap() == alto::SourceState::Playing {
    // pause the OpenAL source
    let _ = source.pause();
  } else {
    // unpause the OpenAL source
    let _ = source.play();
  }
}

fn playback_cursor_source(source: &mut alto::StreamingSource, len: f32) -> f32 {
  let cursor = source.sec_offset().unwrap();

  // loop the device if we hit the end of the demo
  if cursor > len {
    let _ = source.rewind();
    0.
  } else {
    cursor
  }
}

pub fn set_playback_cursor_source(source: &mut alto::StreamingSource, t: f32, len: f32) {
  assert!(t >= 0. && t <= 1.);
  let _ = source.set_sec_offset(t * len);
}
<commit_msg>loop+break -> while let<commit_after>use alto::{self, SourceTrait};
use std::fs::File;
use std::path::Path;
use std::sync::mpsc::{Receiver, SyncSender, sync_channel};
use std::thread::spawn;
use vorbis::Decoder;

/// The device is responsible of managing synchronization and audio playback by providing a playback
/// cursor to a closure used as demo.
///
/// You shouldn’t have more than one Device per program.
pub struct Device {
  /// Playback length.
  len: f32,
  /// Sender used to send requests to audio thread.
  req_sx: SyncSender<Request>,
  /// Receiver used to receive responses from the audio thread.
  resp_rx: Receiver<Response>
}

enum Request {
  Toggle,
  PlaybackLength,
  PlaybackCursor,
  SetCursor(f32)
}

enum Response {
  PlaybackLength(f32),
  PlaybackCursor(f32)
}

impl Device {
  pub fn new<P>(track_path: P) -> Self where P: AsRef<Path> {
    info!("loading soundtrack {:?}", track_path.as_ref());

    // FIXME: stream the file instead?
    // load PCM data from the file
    let vorbis_decoder = Decoder::new(File::open(track_path).unwrap()).unwrap();
    let mut pcm_buffer = Vec::new();

    for packet in vorbis_decoder.into_packets().map(Result::unwrap) {
      pcm_buffer.extend(packet.data);
    }

    // create a channel to send requests to / retrieve them from the audio thread
    let (req_sx, req_rx) = sync_channel(0);
    // create a channel to receive responses / to send responses from the audio thread
    let (mut resp_sx, resp_rx) = sync_channel(0);

    info!("starting audio thread");

    let _ = spawn(move || {
      deb!("initializing OpenAL");

      let alto = alto::Alto::load_default().unwrap();
      let al_device = alto.open(None).unwrap();
      let al_ctx = al_device.new_context(None).unwrap();

      // create the required objects to play the soundtrack
      let mut al_buffer = al_ctx.new_buffer().unwrap();
      let mut al_source = al_ctx.new_streaming_source().unwrap();

      // fill the OpenAL buffers with the PCM data
      let _ = al_buffer.set_data::<alto::Stereo<_>, _>(&pcm_buffer[..], 44100);

      // compute the length of soundtrack
      let l = (al_buffer.size().unwrap() * 8 / (al_buffer.channels().unwrap() * al_buffer.bits().unwrap())) as f32 / al_buffer.frequency().unwrap() as f32;

      let _ = al_source.queue_buffer(al_buffer);

      while let Ok(req) = req_rx.recv() {
        dispatch_request(&mut al_source, l, req, &mut resp_sx);
      }

      info!("leaving the audio thread");
    });

    // get the length
    let _ = req_sx.send(Request::PlaybackLength);

    let len = match resp_rx.recv() {
      Ok(Response::PlaybackLength(len)) => len,
      _ => unimplemented!()
    };

    Device {
      len: len,
      req_sx: req_sx,
      resp_rx: resp_rx
    }
  }

  pub fn toggle(&self) {
    let _ = self.req_sx.send(Request::Toggle);
  }

  pub fn playback_len(&self) -> f32 {
    self.len
  }

  pub fn playback_cursor(&self) -> f32 {
    let _ = self.req_sx.send(Request::PlaybackCursor);

    match self.resp_rx.recv() {
      Ok(Response::PlaybackCursor(c)) => c,
      _ => unimplemented!()
    }
  }

  pub fn set_playback_cursor(&self, t: f32) {
    let _ = self.req_sx.send(Request::SetCursor(t));
  }
}

fn dispatch_request(source: &mut alto::StreamingSource, len: f32, req: Request, resp_sx: &mut SyncSender<Response>) {
  match req {
    Request::Toggle => {
      toggle_source(source);
    },

    Request::PlaybackLength => {
      let _ = resp_sx.send(Response::PlaybackLength(len));
    },

    Request::PlaybackCursor => {
      let _ = resp_sx.send(Response::PlaybackCursor(playback_cursor_source(source, len)));
    },

    Request::SetCursor(t) => {
      set_playback_cursor_source(source, t, len);
    }
  }
}

fn toggle_source(source: &mut alto::StreamingSource) {
  if source.state().unwrap() == alto::SourceState::Playing {
    // pause the OpenAL source
    let _ = source.pause();
  } else {
    // unpause the OpenAL source
    let _ = source.play();
  }
}

fn playback_cursor_source(source: &mut alto::StreamingSource, len: f32) -> f32 {
  let cursor = source.sec_offset().unwrap();

  // loop the device if we hit the end of the demo
  if cursor > len {
    let _ = source.rewind();
    0.
  } else {
    cursor
  }
}

pub fn set_playback_cursor_source(source: &mut alto::StreamingSource, t: f32, len: f32) {
  assert!(t >= 0. && t <= 1.);
  let _ = source.set_sec_offset(t * len);
}
<|endoftext|>


<commit_before>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<[u8]>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        SoundData(Arc::from(data))
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Arc<[u8]>> for SoundData {
    #[inline]
    fn from(arc: Arc<[u8]>) -> Self {
        SoundData(arc)
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        SoundData(Arc::from(v))
    }
}

impl From<Box<[u8]>> for SoundData {
    fn from(b: Box<[u8]>) -> Self {
        SoundData(Arc::from(b))
    }
}

impl AsRef<[u8]> for SoundData {
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref().as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<commit_msg>Inline SoundData::as_ref<commit_after>//! Provides an interface to output sound to the user's speakers.
//!
//! It consists of two main types: `SoundData` is just raw sound data,
//! and a `Source` is a `SoundData` connected to a particular sound
//! channel.

use std::fmt;
use std::io;
use std::io::Read;
use std::path;

use std::sync::Arc;

use rodio;

use context::Context;
use GameError;
use GameResult;

/// A struct that contains all information for tracking sound info.
///
/// You generally don't have to create this yourself, it will be part
/// of your `Context` object.
pub struct AudioContext {
    endpoint: rodio::Endpoint,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));
        // This is deprecated in cpal but not in the most recent rodio (0.5.2) yet.
        #[allow(deprecated)]
        let e = rodio::get_default_endpoint().ok_or(error)?;
        Ok(AudioContext { endpoint: e })
    }
}

impl fmt::Debug for AudioContext {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<AudioContext: {:p}>", self)
    }
}

/// Static sound data stored in memory.
/// It is Arc'ed, so cheap to clone.
#[derive(Clone, Debug)]
pub struct SoundData(Arc<[u8]>);

impl SoundData {
    /// Copies the data in the given slice into a new SoundData object.
    pub fn from_bytes(data: &[u8]) -> Self {
        SoundData(Arc::from(data))
    }

    /// Creates a SoundData from any Read object; this involves
    /// copying it into a buffer.
    pub fn from_read<R>(reader: &mut R) -> GameResult<Self>
    where
        R: Read,
    {
        let mut buffer = Vec::new();
        reader.read_to_end(&mut buffer)?;

        Ok(SoundData::from(buffer))
    }
}

impl From<Arc<[u8]>> for SoundData {
    #[inline]
    fn from(arc: Arc<[u8]>) -> Self {
        SoundData(arc)
    }
}

impl From<Vec<u8>> for SoundData {
    fn from(v: Vec<u8>) -> Self {
        SoundData(Arc::from(v))
    }
}

impl From<Box<[u8]>> for SoundData {
    fn from(b: Box<[u8]>) -> Self {
        SoundData(Arc::from(b))
    }
}

impl AsRef<[u8]> for SoundData {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        self.0.as_ref()
    }
}

/// A source of audio data connected to a particular `Channel`.
/// Will stop playing when dropped.
// TODO: Check and see if this matches Love2d's semantics!
// Eventually it might read from a streaming decoder of some kind,
// but for now it is just an in-memory SoundData structure.
// The source of a rodio decoder must be Send, which something
// that contains a reference to a ZipFile is not, so we are going
// to just slurp all the data into memory for now.
// There's really a lot of work that needs to be done here, since
// rodio has gotten better (if still somewhat arcane) and our filesystem
// code has done the data-slurping-from-zip's for us
// but for now it works.
pub struct Source {
    data: io::Cursor<SoundData>,
    sink: rodio::Sink,
}

impl Source {
    /// Create a new Source from the given file.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let data = {
            let file = &mut context.filesystem.open(path)?;
            SoundData::from_read(file)?
        };
        Source::from_data(context, data)
    }

    /// Creates a new Source using the given SoundData object.
    pub fn from_data(context: &mut Context, data: SoundData) -> GameResult<Self> {
        let sink = rodio::Sink::new(&context.audio_context.endpoint);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            data: cursor,
            sink: sink,
        })
    }

    /// Plays the Source.
    pub fn play(&self) -> GameResult<()> {
        // Creating a new Decoder each time seems a little messy,
        // since it may do checking and data-type detection that is
        // redundant, but it's not super expensive.
        // See https://github.com/ggez/ggez/issues/98 for discussion
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        self.sink.append(decoder);
        Ok(())
    }

    /// Pauses playback
    pub fn pause(&self) {
        self.sink.pause()
    }

    /// Resumes playback
    pub fn resume(&self) {
        self.sink.play()
    }

    /// Stops playback
    pub fn stop(&self) {
        self.sink.stop()
    }

    /// Gets the current volume
    pub fn volume(&self) -> f32 {
        self.sink.volume()
    }

    /// Sets the current volume
    pub fn set_volume(&mut self, value: f32) {
        self.sink.set_volume(value)
    }

    /// Get whether or not the source is paused
    pub fn paused(&self) -> bool {
        self.sink.is_paused()
    }

    /// Get whether or not the source is playing
    pub fn playing(&self) -> bool {
        !self.paused() // && !self.stopped()
    }
}

impl fmt::Debug for Source {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Audio source: {:p}>", self)
    }
}

// TODO: global start, stop, volume?
<|endoftext|>


<commit_before>use std::num::Float;
use std::default::Default;
use std::fmt::{Show, Formatter, FormatError};
use std::rand::random;
use std::ascii::OwnedAsciiExt;
use serialize::hex::ToHex;
use serialize::{
    Decodable, Encodable,
    Decoder, Encoder,
    DecoderHelpers, EncoderHelpers
};
use utils::clampf32;

/// A basic color struct for general color use
/// made of red, green, blue and alpha elements.
pub struct Color(pub [f32, ..4]);

impl Color {

    /// Basic constructor for a Color struct.
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
        Color([r, g, b, a])
    }

    /// Returns the red component of the color.
    #[inline(always)]
    pub fn r(&self) -> f32 {
        let &Color(c) = self;
        c[0]
    }

    /// Returns the green component of the color.
    #[inline(always)]
    pub fn g(&self) -> f32 {
        let &Color(c) = self;
        c[1]
    }

    /// Returns the blue component of the color.
    #[inline(always)]
    pub fn b(&self) -> f32 {
        let &Color(c) = self;
        c[2]
    }

    /// Sets the red component of the color.
    #[inline(always)]
    pub fn set_r(&mut self, r: f32) {
        let &Color(ref mut c) = self;
        c[0] = r;
    }

    /// Sets the green component of the color.
    #[inline(always)]
    pub fn set_g(&mut self, g: f32) {
        let &Color(ref mut c) = self;
        c[1] = g;
    }

    /// Sets the blue component of the color.
    #[inline(always)]
    pub fn set_b(&mut self, b: f32) {
        let &Color(ref mut c) = self;
        c[2] = b;
    }

    /// Sets the alpha component of the color.
    #[inline(always)]
    pub fn set_a(&mut self, a: f32) {
        let &Color(ref mut c) = self;
        c[3] = a;
    }

    /// Returns the alpha component of the color.
    #[inline(always)]
    pub fn a(&self) -> f32 {
        let &Color(c) = self;
        c[3]
    }

    /// Basic constructor for a Black Color struct.
    pub fn black() -> Color {
        Color([0f32, 0f32, 0f32, 1f32])
    }

    /// Basic constructor for a White Color struct.
    pub fn white() -> Color {
        Color([1f32, 1f32, 1f32, 1f32])
    }

    /// Return color as a vector.
    pub fn as_vec(&self) -> Vec<f32> {
        vec![self.r(), self.g(), self.b(), self.a()]
    }

    /// Return color as a tuple.
    pub fn as_tuple(&self) -> (f32, f32, f32, f32) {
        let &Color(c) = self;
        (c[0], c[1], c[2], c[3])
    }

    /// Clamp the Color's values between 0f32 and 1f32.
    fn clamp(c: Color) -> Color {
        Color([
            clampf32(c.r()),
            clampf32(c.g()),
            clampf32(c.b()),
            clampf32(c.a())
        ])
    }

    /// Return a highlighted version of the current Color.
    pub fn highlighted(&self) -> Color {
        let luminance = self.luminance();
        let (r, g, b) = {
            if luminance > 0.8 {
                (self.r() - 0.2,
                 self.g() - 0.2,
                 self.b() - 0.2)
            }
            else if luminance < 0.2 {
                (self.r() + 0.2,
                 self.g() + 0.2,
                 self.b() + 0.2)
            }
            else {
                (clampf32((1f32 - self.r()) * 0.5f32 * self.r() + self.r()),
                 clampf32((1f32 - self.g()) * 0.1f32 * self.g() + self.g()),
                 clampf32((1f32 - self.b()) * 0.1f32 * self.b() + self.b()))
            }
        };
        let a = clampf32((1f32 - self.a()) * 0.5f32 + self.a());
        Color::new(r, g, b, a)
    }

    /// Return a clicked version of the current Color.
    pub fn clicked(&self) -> Color {
        let luminance = self.luminance();
        let (r, g, b) = {
            if luminance > 0.8 {
                (self.r(),
                 self.g() - 0.2,
                 self.b() - 0.2)
            }
            else if luminance < 0.2 {
                (self.r() + 0.4,
                 self.g() + 0.2,
                 self.b() + 0.2)
            }
            else {
                (clampf32((1f32 - self.r()) * 0.75f32 + self.r()),
                 clampf32((1f32 - self.g()) * 0.25f32 + self.g()),
                 clampf32((1f32 - self.b()) * 0.25f32 + self.b()))
            }
        };
        let a = clampf32((1f32 - self.a()) * 0.75f32 + self.a());
        Color::new(r, g, b, a)
    }

    /// Return a random color.
    pub fn random() -> Color {
        let r = random::<f32>();
        let g = random::<f32>();
        let b = random::<f32>();
        let a = 1f32;
        Color::new(r, g, b, a)
    }

    /// Return an inverted version of the color.
    pub fn invert(&self) -> Color {
        let r = (1f32 - self.r()).abs();
        let g = (1f32 - self.g()).abs();
        let b = (1f32 - self.b()).abs();
        Color::new(r, g, b, self.a())
    }

    /// Return either black or white, depending which contrasts
    /// the Color the most. This will be useful for determining
    /// a readable color for text on any given background Color.
    pub fn plain_contrast(&self) -> Color {
        if self.luminance() > 0.5f32 { Color::black() }
        else { Color::white() }
    }

    /// Return the luminance of the color.
    pub fn luminance(&self) -> f32 {
        (self.r() + self.g() + self.b()) / 3f32
    }

    /// Return an array of the channels in this color
    /// clamped to [0..255]
    pub fn to_32_bit(&self) -> [u8, ..4] {
        [
            to_8_bit(self.r()),
            to_8_bit(self.g()),
            to_8_bit(self.b()),
            to_8_bit(self.a()),
        ]
    }

    /// Return the hex representation of this color
    /// in the format #RRGGBBAA
    /// e.g. `Color(1.0, 0.0, 5.0, 1.0) == "#FF0080FF"`
    pub fn to_hex(&self) -> String {
        let vals = self.to_32_bit();
        // Hex colors are always uppercased
        let hex = vals.as_slice().to_hex().into_ascii_upper();
        format!("#{}", hex.as_slice())
    }
}

fn to_8_bit(chan: f32) -> u8 {
    let chan = clampf32(chan);
    (chan * 255.0) as u8
}

impl Clone for Color {
    fn clone(&self) -> Color {
        *self
    }
}

impl Default for Color {
    /// Default constructor for a Color struct.
    fn default() -> Color {
        Color([0.5, 0.8, 0.6, 1.0])
    }
}

impl Add<Color, Color> for Color {
    fn add(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() + rhs.r(),
                self.g() + rhs.g(),
                self.b() + rhs.b(),
                self.a()
            ])
        )
    }
}

impl Sub<Color, Color> for Color {
    fn sub(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() - rhs.r(),
                self.g() - rhs.g(),
                self.b() - rhs.b(),
                self.a()
            ])
        )
    }
}

impl Div<Color, Color> for Color {
    fn div(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() / rhs.r(),
                self.g() / rhs.g(),
                self.b() / rhs.b(),
                self.a() / rhs.a(),
            ])
        )
    }
}

impl Mul<Color, Color> for Color {
    fn mul(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() * rhs.r(),
                self.g() * rhs.g(),
                self.b() * rhs.b(),
                self.a() * rhs.a(),
            ])
        )
    }
}

impl Show for Color {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), FormatError> {
        let hex = self.to_hex();
        fmt.pad(hex.as_slice())
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for Color {
    fn decode(dec: &mut D) -> Result<Color, E> {
        let vec = try!(dec.read_to_vec(|le_dec| le_dec.read_f32()));

        if vec.len() != 4 {
            return Err(dec.error(format!(
                "Expected a 4 element vector when decoding Color.
                Found a vector of length {}.", vec.len()).as_slice()));
        }

        Ok(Color([vec[0], vec[1], vec[2], vec[3]]))
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Color {
    fn encode(&self, enc: &mut S) -> Result<(), E> {
        let Color(ref vec) = *self;

        enc.emit_from_vec(vec, |le_enc, elt| le_enc.emit_f32(*elt))
    }
}

/// A trait used for "colorable" widget context types.
pub trait Colorable {
    fn color(self, color: Color) -> Self;
    /// A method used for passing color as rgba.
    fn rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self;
}

<commit_msg>Upgraded to latest Rust<commit_after>use std::num::Float;
use std::default::Default;
use std::fmt::{Show, Formatter, Error};
use std::rand::random;
use std::ascii::OwnedAsciiExt;
use serialize::hex::ToHex;
use serialize::{
    Decodable, Encodable,
    Decoder, Encoder,
    DecoderHelpers, EncoderHelpers
};
use utils::clampf32;

/// A basic color struct for general color use
/// made of red, green, blue and alpha elements.
pub struct Color(pub [f32, ..4]);

impl Color {

    /// Basic constructor for a Color struct.
    pub fn new(r: f32, g: f32, b: f32, a: f32) -> Color {
        Color([r, g, b, a])
    }

    /// Returns the red component of the color.
    #[inline(always)]
    pub fn r(&self) -> f32 {
        let &Color(c) = self;
        c[0]
    }

    /// Returns the green component of the color.
    #[inline(always)]
    pub fn g(&self) -> f32 {
        let &Color(c) = self;
        c[1]
    }

    /// Returns the blue component of the color.
    #[inline(always)]
    pub fn b(&self) -> f32 {
        let &Color(c) = self;
        c[2]
    }

    /// Sets the red component of the color.
    #[inline(always)]
    pub fn set_r(&mut self, r: f32) {
        let &Color(ref mut c) = self;
        c[0] = r;
    }

    /// Sets the green component of the color.
    #[inline(always)]
    pub fn set_g(&mut self, g: f32) {
        let &Color(ref mut c) = self;
        c[1] = g;
    }

    /// Sets the blue component of the color.
    #[inline(always)]
    pub fn set_b(&mut self, b: f32) {
        let &Color(ref mut c) = self;
        c[2] = b;
    }

    /// Sets the alpha component of the color.
    #[inline(always)]
    pub fn set_a(&mut self, a: f32) {
        let &Color(ref mut c) = self;
        c[3] = a;
    }

    /// Returns the alpha component of the color.
    #[inline(always)]
    pub fn a(&self) -> f32 {
        let &Color(c) = self;
        c[3]
    }

    /// Basic constructor for a Black Color struct.
    pub fn black() -> Color {
        Color([0f32, 0f32, 0f32, 1f32])
    }

    /// Basic constructor for a White Color struct.
    pub fn white() -> Color {
        Color([1f32, 1f32, 1f32, 1f32])
    }

    /// Return color as a vector.
    pub fn as_vec(&self) -> Vec<f32> {
        vec![self.r(), self.g(), self.b(), self.a()]
    }

    /// Return color as a tuple.
    pub fn as_tuple(&self) -> (f32, f32, f32, f32) {
        let &Color(c) = self;
        (c[0], c[1], c[2], c[3])
    }

    /// Clamp the Color's values between 0f32 and 1f32.
    fn clamp(c: Color) -> Color {
        Color([
            clampf32(c.r()),
            clampf32(c.g()),
            clampf32(c.b()),
            clampf32(c.a())
        ])
    }

    /// Return a highlighted version of the current Color.
    pub fn highlighted(&self) -> Color {
        let luminance = self.luminance();
        let (r, g, b) = {
            if luminance > 0.8 {
                (self.r() - 0.2,
                 self.g() - 0.2,
                 self.b() - 0.2)
            }
            else if luminance < 0.2 {
                (self.r() + 0.2,
                 self.g() + 0.2,
                 self.b() + 0.2)
            }
            else {
                (clampf32((1f32 - self.r()) * 0.5f32 * self.r() + self.r()),
                 clampf32((1f32 - self.g()) * 0.1f32 * self.g() + self.g()),
                 clampf32((1f32 - self.b()) * 0.1f32 * self.b() + self.b()))
            }
        };
        let a = clampf32((1f32 - self.a()) * 0.5f32 + self.a());
        Color::new(r, g, b, a)
    }

    /// Return a clicked version of the current Color.
    pub fn clicked(&self) -> Color {
        let luminance = self.luminance();
        let (r, g, b) = {
            if luminance > 0.8 {
                (self.r(),
                 self.g() - 0.2,
                 self.b() - 0.2)
            }
            else if luminance < 0.2 {
                (self.r() + 0.4,
                 self.g() + 0.2,
                 self.b() + 0.2)
            }
            else {
                (clampf32((1f32 - self.r()) * 0.75f32 + self.r()),
                 clampf32((1f32 - self.g()) * 0.25f32 + self.g()),
                 clampf32((1f32 - self.b()) * 0.25f32 + self.b()))
            }
        };
        let a = clampf32((1f32 - self.a()) * 0.75f32 + self.a());
        Color::new(r, g, b, a)
    }

    /// Return a random color.
    pub fn random() -> Color {
        let r = random::<f32>();
        let g = random::<f32>();
        let b = random::<f32>();
        let a = 1f32;
        Color::new(r, g, b, a)
    }

    /// Return an inverted version of the color.
    pub fn invert(&self) -> Color {
        let r = (1f32 - self.r()).abs();
        let g = (1f32 - self.g()).abs();
        let b = (1f32 - self.b()).abs();
        Color::new(r, g, b, self.a())
    }

    /// Return either black or white, depending which contrasts
    /// the Color the most. This will be useful for determining
    /// a readable color for text on any given background Color.
    pub fn plain_contrast(&self) -> Color {
        if self.luminance() > 0.5f32 { Color::black() }
        else { Color::white() }
    }

    /// Return the luminance of the color.
    pub fn luminance(&self) -> f32 {
        (self.r() + self.g() + self.b()) / 3f32
    }

    /// Return an array of the channels in this color
    /// clamped to [0..255]
    pub fn to_32_bit(&self) -> [u8, ..4] {
        [
            to_8_bit(self.r()),
            to_8_bit(self.g()),
            to_8_bit(self.b()),
            to_8_bit(self.a()),
        ]
    }

    /// Return the hex representation of this color
    /// in the format #RRGGBBAA
    /// e.g. `Color(1.0, 0.0, 5.0, 1.0) == "#FF0080FF"`
    pub fn to_hex(&self) -> String {
        let vals = self.to_32_bit();
        // Hex colors are always uppercased
        let hex = vals.as_slice().to_hex().into_ascii_upper();
        format!("#{}", hex.as_slice())
    }
}

fn to_8_bit(chan: f32) -> u8 {
    let chan = clampf32(chan);
    (chan * 255.0) as u8
}

impl Clone for Color {
    fn clone(&self) -> Color {
        *self
    }
}

impl Default for Color {
    /// Default constructor for a Color struct.
    fn default() -> Color {
        Color([0.5, 0.8, 0.6, 1.0])
    }
}

impl Add<Color, Color> for Color {
    fn add(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() + rhs.r(),
                self.g() + rhs.g(),
                self.b() + rhs.b(),
                self.a()
            ])
        )
    }
}

impl Sub<Color, Color> for Color {
    fn sub(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() - rhs.r(),
                self.g() - rhs.g(),
                self.b() - rhs.b(),
                self.a()
            ])
        )
    }
}

impl Div<Color, Color> for Color {
    fn div(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() / rhs.r(),
                self.g() / rhs.g(),
                self.b() / rhs.b(),
                self.a() / rhs.a(),
            ])
        )
    }
}

impl Mul<Color, Color> for Color {
    fn mul(&self, rhs: &Color) -> Color {
        Color::clamp(
            Color([
                self.r() * rhs.r(),
                self.g() * rhs.g(),
                self.b() * rhs.b(),
                self.a() * rhs.a(),
            ])
        )
    }
}

impl Show for Color {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
        let hex = self.to_hex();
        fmt.pad(hex.as_slice())
    }
}

impl<E, D: Decoder<E>> Decodable<D, E> for Color {
    fn decode(dec: &mut D) -> Result<Color, E> {
        let vec = try!(dec.read_to_vec(|le_dec| le_dec.read_f32()));

        if vec.len() != 4 {
            return Err(dec.error(format!(
                "Expected a 4 element vector when decoding Color.
                Found a vector of length {}.", vec.len()).as_slice()));
        }

        Ok(Color([vec[0], vec[1], vec[2], vec[3]]))
    }
}

impl<E, S: Encoder<E>> Encodable<S, E> for Color {
    fn encode(&self, enc: &mut S) -> Result<(), E> {
        let Color(ref vec) = *self;

        enc.emit_from_vec(vec, |le_enc, elt| le_enc.emit_f32(*elt))
    }
}

/// A trait used for "colorable" widget context types.
pub trait Colorable {
    fn color(self, color: Color) -> Self;
    /// A method used for passing color as rgba.
    fn rgba(self, r: f32, g: f32, b: f32, a: f32) -> Self;
}

<|endoftext|>