<commit_before>#![crate_name="nock"]

#![feature(box_syntax, box_patterns)]

use std::fmt;
use std::str;

// Nock(a)          *a
// [a b c]          [a [b c]]
//
// ?[a b]           0
// ?a               1
// +[a b]           +[a b]
// +a               1 + a
// =[a a]           0
// =[a b]           1
// =a               =a
//
// /[1 a]           a
// /[2 a b]         a
// /[3 a b]         b
// /[(a + a) b]     /[2 /[a b]]
// /[(a + a + 1) b] /[3 /[a b]]
// /a               /a
//
// *[a [b c] d]     [*[a b c] *[a d]]
//
// *[a 0 b]         /[b a]
// *[a 1 b]         b
// *[a 2 b c]       *[*[a b] *[a c]]
// *[a 3 b]         ?*[a b]
// *[a 4 b]         +*[a b]
// *[a 5 b]         =*[a b]
//
// *[a 6 b c d]     *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
// *[a 7 b c]       *[a 2 b 1 c]
// *[a 8 b c]       *[a 7 [[7 [0 1] b] 0 1] c]
// *[a 9 b c]       *[a 7 c 2 [0 1] 0 b]
// *[a 10 [b c] d]  *[a 8 c 7 [0 3] d]
// *[a 10 b c]      *[a c]
//
// *a               *a

#[derive(Clone, PartialEq, Eq)]
pub enum Noun {
    Atom(u64), // TODO: Need bigints?
    Cell(Box<Noun>, Box<Noun>),
}

impl Noun {
    pub fn new<T: Into<Noun>>(val: T) -> Noun {
        val.into()
    }
}

impl Into<Noun> for u64 {
    fn into(self) -> Noun { Noun::Atom(self) }
}

impl fmt::Display for Noun {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Atom(ref n) => write!(f, "{}", n),
            &Cell(ref a, ref b) => {
                try!(write!(f, "[{} ", a));
                // List pretty-printer.
                let mut cur = b;
                loop {
                    match cur {
                        &box Cell(ref a, ref b) => {
                            try!(write!(f, "{} ", a));
                            cur = &b;
                        }
                        &box Atom(ref n) => {
                            return write!(f, "{}]", n);
                        }
                    }
                }
            }
        }
    }
}

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

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ParseNockError;

impl str::FromStr for Noun {
    type Err = ParseNockError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        unimplemented!();
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Op {
    Wut, // ?
    Lus, // +
    Tis, // =
    Fas, // /
    Tar, // *
}

use Op::*;
use Noun::*;

pub fn eval(op: Op, noun: Noun) -> Result<Noun, Noun> {
    match (op, noun) {
        (Wut, Cell(_, _)) => Ok(Atom(0)),
        (Wut, Atom(_)) => Ok(Atom(1)),
        (Lus, Atom(a)) => Ok(Atom(1 + a)),
        (Lus, a) => Err(a),
        (Tis, Cell(a, b)) => Ok(Atom(if a == b { 0 } else { 1 })),
        (Tis, a) => Err(a),

        (Fas, Cell(box Atom(1), box a)) => Ok(a),
        (Fas, Cell(box Atom(2), box Cell(box a, _))) => Ok(a),
        (Fas, Cell(box Atom(3), box Cell(_, box b))) => Ok(b),
        (Fas, Cell(box Atom(a), b)) => {
            let x = try!(eval(Fas, Cell(box Atom(a / 2), b)));
            eval(Fas, Cell(box Atom(if a % 2 == 0 { 2 } else { 3 }), box x))
        }
        (Fas, a) => Err(a),

        (Tar, Cell(a, box Cell(box Cell(b, c), d))) => {
            let x = try!(eval(Tar, Cell(a.clone(), box Cell(b, c))));
            let y = try!(eval(Tar, Cell(a, d)));
            Ok(Cell(box x, box y))
        }

        // TODO: Numbered tar ops.

        (Tar, a) => Err(a)
    }
}

pub fn nock(noun: Noun) -> Result<Noun, Noun> { eval(Wut, noun) }

/*
/// Helper macro for defining nouns.
macro_rules! noun {
    [[$x:expr, $($xs:expr),+], [$y:expr, $($ys:expr),+]] => { ::nock::Noun::Cell(box noun![$x, $($xs),+], box noun![$y, $($ys),+]) };
    [[$x:expr, $($xs:expr),+], $y:expr] => { ::nock::Noun::Cell(box noun![$x, $($xs),+], box ::nock::Noun::Atom($y)) };
    [$x:expr, [$y:expr, $($ys:expr),+]] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box noun![$y, $($ys),+]) };
    [$x:expr, $y:expr] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box ::nock::Noun::Atom($y)) };
    [$x:expr, $y:expr, $($ys:expr),+] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box noun![$y, $($ys),+]) };
    [$x:expr] => { ::nock::Noun::Atom($x) };
}
*/

pub fn a(val: u64) -> Noun { Noun::Atom(val) }

macro_rules! n {
    [$x:expr, $y:expr] => { ::nock::Noun::Cell(box ::nock::Noun::new($x), box ::nock::Noun::new($y)) };
    [$x:expr, $y:expr, $($ys:expr),+] => { ::nock::Noun::Cell(box ::nock::Noun::new($x), box n![$y, $($ys),+]) };
}

// Re-export hack for testing macros.
mod nock {
    pub use Noun;
}

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

        assert_eq!(n![1, 2], Cell(box Atom(1), box Atom(2)));
        assert_eq!(n![1, n![2, 3]], Cell(box Atom(1), box Cell(box Atom(2), box Atom(3))));
        assert_eq!(n![1, 2, 3], Cell(box Atom(1), box Cell(box Atom(2), box Atom(3))));
        assert_eq!(n![n![1, 2], 3], Cell(box Cell(box Atom(1), box Atom(2)), box Atom(3)));
        assert_eq!(n![n![1, 2], n![3, 4]], Cell(box Cell(box Atom(1), box Atom(2)), box Cell(box Atom(3), box Atom(4))));
        assert_eq!(n![n![1, 2], n![3, 4], n![5, 6]],
                   Cell(box Cell(box Atom(1), box Atom(2)),
                   box Cell(box Cell(box Atom(3), box Atom(4)),
                   box Cell(box Atom(5), box Atom(6)))));
    }
#[test]
    fn test_eval() {
        use super::Op::*;
        use super::{a, eval};

        // Examples from https://github.com/cgyarvin/urbit/blob/master/doc/book/1-nock.markdown

        assert_eq!(eval(Tis, n![0, 0]), Ok(a(0)));
        assert_eq!(eval(Tis, n![2, 2]), Ok(a(0)));
        assert_eq!(eval(Tis, n![0, 1]), Ok(a(1)));

        assert_eq!(eval(Fas, n![1, n![n![4, 5], n![6, 14, 15]]]), Ok(n![n![4, 5], n![6, 14, 15]]));
        assert_eq!(eval(Fas, n![2, n![n![4, 5], n![6, 14, 15]]]), Ok(n![4, 5]));
        assert_eq!(eval(Fas, n![3, n![n![4, 5], n![6, 14, 15]]]), Ok(n![6, 14, 15]));
        assert_eq!(eval(Fas, n![7, n![n![4, 5], n![6, 14, 15]]]), Ok(n![14, 15]));
    }

#[test]
    fn test_parse() {
        use super::Noun;

        assert!("".parse::<Noun>().is_err());
        assert!("[]".parse::<Noun>().is_err());
        assert!("[1]".parse::<Noun>().is_err());
        assert!("[x y]".parse::<Noun>().is_err());
        assert_eq!("[1 2]".parse::<Noun>(), Ok(n![1, 2]));
        assert_eq!("[1 2 3]".parse::<Noun>(), Ok(n![1, 2, 3]));
        assert_eq!("[[1 2] 3]".parse::<Noun>(), Ok(n![n![1, 2], 3]));
        assert_eq!("[1 [2 3]]".parse::<Noun>(), Ok(n![1, n![2, 3]]));
    }
}
<commit_msg>Type for explicit formula representation<commit_after>#![crate_name="nock"]

#![feature(box_syntax, box_patterns)]

use std::fmt;
use std::str;

// Nock(a)          *a
// [a b c]          [a [b c]]
//
// ?[a b]           0
// ?a               1
// +[a b]           +[a b]
// +a               1 + a
// =[a a]           0
// =[a b]           1
// =a               =a
//
// /[1 a]           a
// /[2 a b]         a
// /[3 a b]         b
// /[(a + a) b]     /[2 /[a b]]
// /[(a + a + 1) b] /[3 /[a b]]
// /a               /a
//
// *[a [b c] d]     [*[a b c] *[a d]]
//
// *[a 0 b]         /[b a]
// *[a 1 b]         b
// *[a 2 b c]       *[*[a b] *[a c]]
// *[a 3 b]         ?*[a b]
// *[a 4 b]         +*[a b]
// *[a 5 b]         =*[a b]
//
// *[a 6 b c d]     *[a 2 [0 1] 2 [1 c d] [1 0] 2 [1 2 3] [1 0] 4 4 b]
// *[a 7 b c]       *[a 2 b 1 c]
// *[a 8 b c]       *[a 7 [[7 [0 1] b] 0 1] c]
// *[a 9 b c]       *[a 7 c 2 [0 1] 0 b]
// *[a 10 [b c] d]  *[a 8 c 7 [0 3] d]
// *[a 10 b c]      *[a c]
//
// *a               *a

/// A Nock noun, the basic unit of representation.
///
/// The noun is either an atom a that's a positive integer or a pair of two
/// nouns, [a b].
#[derive(Clone, PartialEq, Eq)]
pub enum Noun {
    Atom(u64), // TODO: Need bigints?
    Cell(Box<Noun>, Box<Noun>),
}

impl Noun {
    pub fn new<T: Into<Noun>>(val: T) -> Noun {
        val.into()
    }
}

impl Into<Noun> for u64 {
    fn into(self) -> Noun { Noun::Atom(self) }
}

impl fmt::Display for Noun {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Atom(ref n) => write!(f, "{}", n),
            &Cell(ref a, ref b) => {
                try!(write!(f, "[{} ", a));
                // List pretty-printer.
                let mut cur = b;
                loop {
                    match cur {
                        &box Cell(ref a, ref b) => {
                            try!(write!(f, "{} ", a));
                            cur = &b;
                        }
                        &box Atom(ref n) => {
                            return write!(f, "{}]", n);
                        }
                    }
                }
            }
        }
    }
}

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

#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct ParseNockError;

impl str::FromStr for Noun {
    type Err = ParseNockError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        unimplemented!();
    }
}

/// An operator for a Nock formula.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum Op {
    Wut, // ?
    Lus, // +
    Tis, // =
    Fas, // /
    Tar, // *
}

#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Formula(pub Op, pub Noun);

impl str::FromStr for Formula {
    type Err = ParseNockError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        unimplemented!();
    }
}

use Op::*;
use Noun::*;

pub fn eval(op: Op, noun: Noun) -> Result<Noun, Noun> {
    match (op, noun) {
        (Wut, Cell(_, _)) => Ok(Atom(0)),
        (Wut, Atom(_)) => Ok(Atom(1)),
        (Lus, Atom(a)) => Ok(Atom(1 + a)),
        (Lus, a) => Err(a),
        (Tis, Cell(a, b)) => Ok(Atom(if a == b { 0 } else { 1 })),
        (Tis, a) => Err(a),

        (Fas, Cell(box Atom(1), box a)) => Ok(a),
        (Fas, Cell(box Atom(2), box Cell(box a, _))) => Ok(a),
        (Fas, Cell(box Atom(3), box Cell(_, box b))) => Ok(b),
        (Fas, Cell(box Atom(a), b)) => {
            let x = try!(eval(Fas, Cell(box Atom(a / 2), b)));
            eval(Fas, Cell(box Atom(if a % 2 == 0 { 2 } else { 3 }), box x))
        }
        (Fas, a) => Err(a),

        (Tar, Cell(a, box Cell(box Cell(b, c), d))) => {
            let x = try!(eval(Tar, Cell(a.clone(), box Cell(b, c))));
            let y = try!(eval(Tar, Cell(a, d)));
            Ok(Cell(box x, box y))
        }

        // TODO: Numbered tar ops.

        (Tar, a) => Err(a)
    }
}

pub fn nock(noun: Noun) -> Result<Noun, Noun> { eval(Wut, noun) }

/*
/// Helper macro for defining nouns.
macro_rules! noun {
    [[$x:expr, $($xs:expr),+], [$y:expr, $($ys:expr),+]] => { ::nock::Noun::Cell(box noun![$x, $($xs),+], box noun![$y, $($ys),+]) };
    [[$x:expr, $($xs:expr),+], $y:expr] => { ::nock::Noun::Cell(box noun![$x, $($xs),+], box ::nock::Noun::Atom($y)) };
    [$x:expr, [$y:expr, $($ys:expr),+]] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box noun![$y, $($ys),+]) };
    [$x:expr, $y:expr] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box ::nock::Noun::Atom($y)) };
    [$x:expr, $y:expr, $($ys:expr),+] => { ::nock::Noun::Cell(box ::nock::Noun::Atom($x), box noun![$y, $($ys),+]) };
    [$x:expr] => { ::nock::Noun::Atom($x) };
}
*/

pub fn a(val: u64) -> Noun { Noun::Atom(val) }

macro_rules! n {
    [$x:expr, $y:expr] => { ::nock::Noun::Cell(box ::nock::Noun::new($x), box ::nock::Noun::new($y)) };
    [$x:expr, $y:expr, $($ys:expr),+] => { ::nock::Noun::Cell(box ::nock::Noun::new($x), box n![$y, $($ys),+]) };
}

// Re-export hack for testing macros.
mod nock {
    pub use Noun;
}

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

        assert_eq!(n![1, 2], Cell(box Atom(1), box Atom(2)));
        assert_eq!(n![1, n![2, 3]], Cell(box Atom(1), box Cell(box Atom(2), box Atom(3))));
        assert_eq!(n![1, 2, 3], Cell(box Atom(1), box Cell(box Atom(2), box Atom(3))));
        assert_eq!(n![n![1, 2], 3], Cell(box Cell(box Atom(1), box Atom(2)), box Atom(3)));
        assert_eq!(n![n![1, 2], n![3, 4]], Cell(box Cell(box Atom(1), box Atom(2)), box Cell(box Atom(3), box Atom(4))));
        assert_eq!(n![n![1, 2], n![3, 4], n![5, 6]],
                   Cell(box Cell(box Atom(1), box Atom(2)),
                   box Cell(box Cell(box Atom(3), box Atom(4)),
                   box Cell(box Atom(5), box Atom(6)))));
    }
#[test]
    fn test_eval() {
        use super::Op::*;
        use super::{a, eval};

        // Examples from https://github.com/cgyarvin/urbit/blob/master/doc/book/1-nock.markdown

        assert_eq!(eval(Tis, n![0, 0]), Ok(a(0)));
        assert_eq!(eval(Tis, n![2, 2]), Ok(a(0)));
        assert_eq!(eval(Tis, n![0, 1]), Ok(a(1)));

        assert_eq!(eval(Fas, n![1, n![n![4, 5], n![6, 14, 15]]]), Ok(n![n![4, 5], n![6, 14, 15]]));
        assert_eq!(eval(Fas, n![2, n![n![4, 5], n![6, 14, 15]]]), Ok(n![4, 5]));
        assert_eq!(eval(Fas, n![3, n![n![4, 5], n![6, 14, 15]]]), Ok(n![6, 14, 15]));
        assert_eq!(eval(Fas, n![7, n![n![4, 5], n![6, 14, 15]]]), Ok(n![14, 15]));
    }

#[test]
    fn test_parse_noun() {
        use super::Noun;

        assert!("".parse::<Noun>().is_err());
        assert!("[]".parse::<Noun>().is_err());
        assert!("[1]".parse::<Noun>().is_err());
        assert!("[x y]".parse::<Noun>().is_err());
        assert_eq!("[1 2]".parse::<Noun>(), Ok(n![1, 2]));
        assert_eq!("[1 2 3]".parse::<Noun>(), Ok(n![1, 2, 3]));
        assert_eq!("[[1 2] 3]".parse::<Noun>(), Ok(n![n![1, 2], 3]));
        assert_eq!("[1 [2 3]]".parse::<Noun>(), Ok(n![1, n![2, 3]]));

    }

#[test]
    fn test_parse_formula() {
        use super::Op::*;
        use super::Formula;

        assert_eq!("+[1 2]".parse::<Formula>(), Ok(Formula(Lus, n![1, 2])));
    }
}
<|endoftext|>


<commit_before>/// EventFD binding
///
/// This crate implements a simple binding for Linux eventfd(). See
/// eventfd(2) for specific details of behaviour.

extern crate libc;

use libc::{c_int, c_uint, c_void};
use std::io::{IoResult, IoError};

pub struct EventFD {
    fd: uint,
    flags: uint,
}

/// Construct a semaphore-style EventFD.
pub const EFD_SEMAPHORE: uint = 0x00001; // 00000001

/// In non-blocking mode, reads and writes will return IoError
/// containing EAGAIN rather than blocking.
pub const EFD_NONBLOCK: uint  = 0x00800; // 00004000

/// Set the close-on-exec flag on the eventfd.
pub const EFD_CLOEXEC: uint   = 0x80000; // 02000000

impl EventFD {
    /// Create a new EventFD. Flags is the bitwise OR of EFD_* constants, or 0 for no flags.
    /// The underlying file descriptor is closed when the EventFD instance's lifetime ends.
    ///
    /// TODO: work out how to integrate this FD into the wider world
    /// of fds. There's currently no way to poll/select on the fd.
    pub fn new(initval: uint, flags: uint) -> IoResult<EventFD> {
        let r = unsafe { eventfd(initval as u32, flags as u32) };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(EventFD { fd: r as uint, flags: flags })
        }
    }

    /// Read the current value of the eventfd. This will block until
    /// the value is non-zero. In semaphore mode this will only ever
    /// decrement the count by 1 and return 1; otherwise it atomically
    /// returns the current value and sets it to zero.
    pub fn read(&self) -> IoResult<u64> {
        let mut ret : u64 = 0;
        let r = unsafe {
            let ptr : *mut u64 = &mut ret;
            ::libc::read(self.fd as c_int, ptr as *mut c_void, std::mem::size_of_val(&ret) as u64)
        };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(ret)
        }
    }

    /// Add to the current value. Blocks if the value would wrap u64.
    pub fn write(&self, val: u64) -> IoResult<()> {
        let r = unsafe {
            let ptr : *const u64 = &val;
            ::libc::write(self.fd as c_int, ptr as *const c_void, std::mem::size_of_val(&val) as u64)
        };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(())
        }
    }

    /// Return a stream of events. The channel has a synchronous
    /// sender because there's no point in building up a queue of
    /// events; if this task blocks on send, the event state will
    /// still update.
    ///
    /// The task will exit if the receiver end is shut down.
    ///
    /// This will be a CPU-spin loop if the EventFD is created
    /// non-blocking.
    ///
    /// XXX FIXME This has no way of terminating except if the other
    /// end closes the connection, and only then if we're not blocked
    /// in the read()...
    pub fn events(&self) -> std::comm::Receiver<u64> {
        let (tx, rx) = std::comm::sync_channel(1);
        let c = self.clone();

        spawn(proc() {
            loop {
                match c.read() {
                    Ok(v) => match tx.send_opt(v) {
                        Ok(_) => (),
                        Err(_) => break,
                    },
                    Err(e) => panic!("read failed: {}", e),
                }
            }
        });

        rx
    }
}

impl Sync for EventFD {}
impl Send for EventFD {}

impl Drop for EventFD {
    fn drop(&mut self) {
        let r = unsafe { ::libc::close(self.fd as c_int) };
        if r < 0 {
            panic!("eventfd close {} failed", self.fd)
        }
    }
}

/// Construct a linked clone of an existing EventFD. Once created, the
/// new instance interacts with the original in a way that's
/// indistinguishable from the original.
impl Clone for EventFD {
    fn clone(&self) -> EventFD {
        let r = unsafe { ::libc::dup(self.fd as c_int) };
        if r < 0 {
            panic!("EventFD clone dup failed: {}", IoError::last_error())
        }

        EventFD { fd: r as uint, flags: self.flags }
    }
}

#[link(name = "c")]
extern "C" {
    fn eventfd(initval: c_uint, flags: c_uint) -> c_int;
}

#[test]
fn test_basic() {
    let (tx,rx) = std::comm::channel();
    let efd = match EventFD::new(10, 0) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };
    let cefd = efd.clone();

    assert!(efd.read() == Ok(10));

    spawn(proc() {
        assert!(cefd.read() == Ok(7));
        assert!(cefd.write(1) == Ok(()));
        assert!(cefd.write(2) == Ok(()));
        tx.send(());
    });

    assert!(efd.write(7) == Ok(()));
    rx.recv();
    assert!(efd.read() == Ok(3));
}

#[test]
fn test_sema() {
    let efd = match EventFD::new(0, EFD_SEMAPHORE | EFD_NONBLOCK) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };

    match efd.read() {
        Err(ref e) if e.kind == std::io::ResourceUnavailable => (), // ok
        Err(e) => panic!("unexpected error {}", e),
        Ok(v) => panic!("unexpected success {}", v),
    }

    assert!(efd.write(5) == Ok(()));

    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    match efd.read() {
        Err(ref e) if e.kind == std::io::ResourceUnavailable => (), // ok
        Err(e) => panic!("unexpected error {}", e),
        Ok(v) => panic!("unexpected success {}", v),
    }
}

#[test]
fn test_stream() {
    let efd = match EventFD::new(11, EFD_SEMAPHORE) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };
    let mut count = 0;

    // only take 10 of 11 so the stream task doesn't block in read and hang the test
    for v in efd.events().iter().take(10) {
        assert!(v == 1);
        count += v;
    }

    assert!(count == 10)
}

#[test]
fn test_chan() {
    let (tx,rx) = std::comm::channel();
    let efd = match EventFD::new(10, 0) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };

    assert!(efd.write(1) == Ok(()));
    tx.send(efd);

    let t = std::task::try(proc() {
        let efd = rx.recv();
        assert!(efd.read() == Ok(11))
    });

    match t {
        Ok(_) => println!("ok"),
        Err(_) => panic!("failed"),
    }
}
<commit_msg>Add unsafe method to get raw fd.<commit_after>/// EventFD binding
///
/// This crate implements a simple binding for Linux eventfd(). See
/// eventfd(2) for specific details of behaviour.

extern crate libc;

use libc::{c_int, c_uint, c_void};
use std::io::{IoResult, IoError};

pub struct EventFD {
    fd: uint,
    flags: uint,
}

/// Construct a semaphore-style EventFD.
pub const EFD_SEMAPHORE: uint = 0x00001; // 00000001

/// In non-blocking mode, reads and writes will return IoError
/// containing EAGAIN rather than blocking.
pub const EFD_NONBLOCK: uint  = 0x00800; // 00004000

/// Set the close-on-exec flag on the eventfd.
pub const EFD_CLOEXEC: uint   = 0x80000; // 02000000

impl EventFD {
    /// Create a new EventFD. Flags is the bitwise OR of EFD_* constants, or 0 for no flags.
    /// The underlying file descriptor is closed when the EventFD instance's lifetime ends.
    ///
    /// TODO: work out how to integrate this FD into the wider world
    /// of fds. There's currently no way to poll/select on the fd.
    pub fn new(initval: uint, flags: uint) -> IoResult<EventFD> {
        let r = unsafe { eventfd(initval as u32, flags as u32) };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(EventFD { fd: r as uint, flags: flags })
        }
    }

    /// Return the raw underlying fd. The caller must make sure self's
    /// lifetime is longer than any users of the fd.
    pub unsafe fn get_fd(&self) -> uint {
        self.fd
    }

    /// Read the current value of the eventfd. This will block until
    /// the value is non-zero. In semaphore mode this will only ever
    /// decrement the count by 1 and return 1; otherwise it atomically
    /// returns the current value and sets it to zero.
    pub fn read(&self) -> IoResult<u64> {
        let mut ret : u64 = 0;
        let r = unsafe {
            let ptr : *mut u64 = &mut ret;
            ::libc::read(self.fd as c_int, ptr as *mut c_void, std::mem::size_of_val(&ret) as u64)
        };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(ret)
        }
    }

    /// Add to the current value. Blocks if the value would wrap u64.
    pub fn write(&self, val: u64) -> IoResult<()> {
        let r = unsafe {
            let ptr : *const u64 = &val;
            ::libc::write(self.fd as c_int, ptr as *const c_void, std::mem::size_of_val(&val) as u64)
        };

        if r < 0 {
            Err(IoError::last_error())
        } else {
            Ok(())
        }
    }

    /// Return a stream of events. The channel has a synchronous
    /// sender because there's no point in building up a queue of
    /// events; if this task blocks on send, the event state will
    /// still update.
    ///
    /// The task will exit if the receiver end is shut down.
    ///
    /// This will be a CPU-spin loop if the EventFD is created
    /// non-blocking.
    ///
    /// XXX FIXME This has no way of terminating except if the other
    /// end closes the connection, and only then if we're not blocked
    /// in the read()...
    pub fn events(&self) -> std::comm::Receiver<u64> {
        let (tx, rx) = std::comm::sync_channel(1);
        let c = self.clone();

        spawn(proc() {
            loop {
                match c.read() {
                    Ok(v) => match tx.send_opt(v) {
                        Ok(_) => (),
                        Err(_) => break,
                    },
                    Err(e) => panic!("read failed: {}", e),
                }
            }
        });

        rx
    }
}

impl Sync for EventFD {}
impl Send for EventFD {}

impl Drop for EventFD {
    fn drop(&mut self) {
        let r = unsafe { ::libc::close(self.fd as c_int) };
        if r < 0 {
            panic!("eventfd close {} failed", self.fd)
        }
    }
}

/// Construct a linked clone of an existing EventFD. Once created, the
/// new instance interacts with the original in a way that's
/// indistinguishable from the original.
impl Clone for EventFD {
    fn clone(&self) -> EventFD {
        let r = unsafe { ::libc::dup(self.fd as c_int) };
        if r < 0 {
            panic!("EventFD clone dup failed: {}", IoError::last_error())
        }

        EventFD { fd: r as uint, flags: self.flags }
    }
}

#[link(name = "c")]
extern "C" {
    fn eventfd(initval: c_uint, flags: c_uint) -> c_int;
}

#[test]
fn test_basic() {
    let (tx,rx) = std::comm::channel();
    let efd = match EventFD::new(10, 0) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };
    let cefd = efd.clone();

    assert!(efd.read() == Ok(10));

    spawn(proc() {
        assert!(cefd.read() == Ok(7));
        assert!(cefd.write(1) == Ok(()));
        assert!(cefd.write(2) == Ok(()));
        tx.send(());
    });

    assert!(efd.write(7) == Ok(()));
    rx.recv();
    assert!(efd.read() == Ok(3));
}

#[test]
fn test_sema() {
    let efd = match EventFD::new(0, EFD_SEMAPHORE | EFD_NONBLOCK) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };

    match efd.read() {
        Err(ref e) if e.kind == std::io::ResourceUnavailable => (), // ok
        Err(e) => panic!("unexpected error {}", e),
        Ok(v) => panic!("unexpected success {}", v),
    }

    assert!(efd.write(5) == Ok(()));

    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    assert!(efd.read() == Ok(1));
    match efd.read() {
        Err(ref e) if e.kind == std::io::ResourceUnavailable => (), // ok
        Err(e) => panic!("unexpected error {}", e),
        Ok(v) => panic!("unexpected success {}", v),
    }
}

#[test]
fn test_stream() {
    let efd = match EventFD::new(11, EFD_SEMAPHORE) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };
    let mut count = 0;

    // only take 10 of 11 so the stream task doesn't block in read and hang the test
    for v in efd.events().iter().take(10) {
        assert!(v == 1);
        count += v;
    }

    assert!(count == 10)
}

#[test]
fn test_chan() {
    let (tx,rx) = std::comm::channel();
    let efd = match EventFD::new(10, 0) {
        Err(e) => panic!("new failed {}", e),
        Ok(fd) => fd,
    };

    assert!(efd.write(1) == Ok(()));
    tx.send(efd);

    let t = std::task::try(proc() {
        let efd = rx.recv();
        assert!(efd.read() == Ok(11))
    });

    match t {
        Ok(_) => println!("ok"),
        Err(_) => panic!("failed"),
    }
}
<|endoftext|>


<commit_before>//! Library for manipulating Game Maker Studio's "data.win" (GEN8) data files.

#![feature(read_exact, associated_consts, associated_type_defaults)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts)]

#[macro_use]
extern crate quick_error;
extern crate byteorder;
#[macro_use]
extern crate log;

mod gamedata_io;

pub use gamedata_io::{ReadError, StringReadError};

use std::io::{self, Read, Write};
use std::path;

/// The data of a Game Maker Studio game.
///
/// This is the collective information acquired from "data.win".
pub struct GameData {
    metadata: MetaData,
    options: Options,
    extn: Extn,
    sounds: Sounds,
    audio_groups: Option<AudioGroups>,
    sprites: Sprites,
    backgrounds: Backgrounds,
    paths: Paths,
    scripts: Scripts,
    shaders: Shaders,
    /// The fonts of the game.
    pub fonts: Fonts,
    timelines: Timelines,
    objects: Objects,
    rooms: Rooms,
    dafl: Dafl,
    tpag: Tpag,
    code: Code,
    variables: Variables,
    functions: Functions,
    /// The strings of the game.
    pub strings: Strings,
    textures: Textures,
    audio: Audio,
}

/// A reader that satisfies the requirements for reading a GameData.
pub trait GameDataRead: Read + io::Seek {}
impl<T: Read + io::Seek> GameDataRead for T {}
/// A writer that satisfies the requirements for writing a GameData.
pub trait GameDataWrite: Write + io::Seek {}
impl<T: Write + io::Seek> GameDataWrite for T {}

impl GameData {
    /// Reads a GameData from a reader.
    pub fn from_reader<R: GameDataRead>(reader: &mut R) -> Result<GameData, ReadError> {
        gamedata_io::read(reader)
    }
    /// Reads a GameData from a file.
    pub fn from_file<P: AsRef<path::Path>>(path: &P) -> Result<GameData, ReadError> {
        use std::fs::File;
        use std::io::BufReader;
        let file = try!(File::open(path));
        GameData::from_reader(&mut BufReader::new(file))
    }
    /// Writes self to a writer.
    pub fn write_to_writer<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        gamedata_io::write(self, writer)
    }
    /// Writes self to a file.
    pub fn save_to_file<P: AsRef<path::Path>>(&self, path: &P) -> io::Result<()> {
        use std::fs::File;
        use std::io::BufWriter;
        let file = try!(File::create(path));
        self.write_to_writer(&mut BufWriter::new(file))
    }
    /// Returns the window width of the game.
    pub fn window_width(&self) -> u32 {
        self.metadata.window_width
    }
    /// Returns the window height of the game.
    pub fn window_height(&self) -> u32 {
        self.metadata.window_height
    }
    /// Sets the window dimensions of the game.
    pub fn set_window_dimensions(&mut self, width: u32, height: u32) {
        self.metadata.window_width = width;
        self.metadata.window_height = height;
    }
    /// Returns the window title of the game.
    pub fn window_title(&self) -> &str {
        &self.strings.strings[self.metadata.window_title_index]
    }
    /// Sets the window title of the game.
    pub fn set_window_title<S: Into<String>>(&mut self, new: S) {
        self.strings.strings[self.metadata.window_title_index] = new.into();
    }
}

/// Contains various metadata, for example, the window width/height/title.
struct MetaData {
    unk1: u32, // Purpose unknown
    game_id_1_index: usize, // Some kind of game id
    default_index: usize, // Points to "Default"
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    game_id_2_index: usize, // Some kind of game id, identical to game_id_1
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    window_width: u32,
    window_height: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    unk16: u32,
    unk17: u32,
    unk18: u32,
    unk19: u32,
    unk20: u32,
    window_title_index: usize,
    unknown: Vec<u32>,
}

/// Game Maker project Options
struct Options {
    unk1: u32, // Unknown
    unk2: u32, // Unknown
    icon_offset: u32, // Points to texture data (icon?)
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    constant1_name_index: usize, // Offset of the name of this constant in the string table
    constant2_name_index: usize, // Offset of the name of this constant in the string table
    constant3_name_index: usize, // Offset of the name of this constant in the string table
    constant4_name_index: usize, // Offset of the name of this constant in the string table
    constant5_name_index: usize, // Offset of the name of this constant in the string table
    constant6_name_index: usize, // Offset of the name of this constant in the string table
    constant7_name_index: usize, // Offset of the name of this constant in the string table
    constant8_name_index: usize, // Offset of the name of this constant in the string table
    constant9_name_index: usize, // Offset of the name of this constant in the string table
    constant10_name_index: usize, // Offset of the name of this constant in the string table
    constant11_name_index: usize, // Offset of the name of this constant in the string table
    constant12_name_index: usize, // Offset of the name of this constant in the string table
    constant13_name_index: usize, // Offset of the name of this constant in the string table
    constant14_name_index: usize, // Offset of the name of this constant in the string table
}

/// Purpose unknown.
struct Extn {
    raw: Vec<u8>, // Data not analyzed yet
}

struct Sound {
    name_index: usize,
    unk1: u32,
    ext_index: usize,
    filename_index: usize,
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
}

/// A collection of sounds.
struct Sounds {
    sounds: Vec<Sound>,
}

/// Collection of audio groups. Not present in all games.
struct AudioGroups {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of sprites.
struct Sprites {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of backgrounds.
struct Backgrounds {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of paths.
struct Paths {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker script.
struct Script {
    /// Index of the name of the script in the string table
    pub name_index: usize,
    unknown: u32, // Unknown
}

/// A collection of scripts.
struct Scripts {
    pub scripts: Vec<Script>,
}

/// A collection of shaders.
struct Shaders {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A font.
#[derive(Clone)]
pub struct Font {
    /// Index of the font's resource name in the string table.
    pub name_index: usize,
    /// Index of the font's font name in the string table.
    pub font_name_index: usize,
    /// Point size of the font.
    pub point_size: u32,
    data: Vec<u8>,
}

/// A collection of fonts.
pub struct Fonts {
    /// The fonts.
    pub fonts: Vec<Font>,
    unknown: Vec<u8>, // Unknown trailing data
}

/// A collection of timelines.
struct Timelines {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of objects.
struct Objects {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of rooms.
struct Rooms {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Dafl {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Tpag {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Something to do with code. Maybe code of scripts?
struct Code {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker variable.
struct Variable {
    /// Index of the name of the variable in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section
}

/// A collection of variables.
struct Variables {
    pub variables: Vec<Variable>,
}

/// A game maker function.
struct Function {
    /// Index of the name of the function in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section.
}

/// A collection of functions.
struct Functions {
    pub functions: Vec<Function>,
}

/// A collection of strings.
///
/// All strings are assumed to be valid UTF-8.
pub struct Strings {
    /// The vector holding the strings.
    pub strings: Vec<String>,
}

struct Texture {
    unknown: u32, // Purpose unknown. Always seems to be 1.
    offset: u32, // Offset of data in the texture data
}

/// A collection of textures.
struct Textures {
    pub textures: Vec<Texture>,
    texture_data: Vec<u8>,
}

struct AudioData {
    data: Vec<u8>,
}

/// A collection of audio data.
struct Audio {
    audio: Vec<AudioData>,
    offsets: Vec<u32>, // Audio data is not contiguous, so we need to store relative offsets
    size: u32, // Fuck it
}
<commit_msg>Make sound data publicly accessible<commit_after>//! Library for manipulating Game Maker Studio's "data.win" (GEN8) data files.

#![feature(read_exact, associated_consts, associated_type_defaults)]
#![warn(missing_docs, trivial_casts, trivial_numeric_casts)]

#[macro_use]
extern crate quick_error;
extern crate byteorder;
#[macro_use]
extern crate log;

mod gamedata_io;

pub use gamedata_io::{ReadError, StringReadError};

use std::io::{self, Read, Write};
use std::path;

/// The data of a Game Maker Studio game.
///
/// This is the collective information acquired from "data.win".
pub struct GameData {
    metadata: MetaData,
    options: Options,
    extn: Extn,
    /// The sounds of the game.
    pub sounds: Sounds,
    audio_groups: Option<AudioGroups>,
    sprites: Sprites,
    backgrounds: Backgrounds,
    paths: Paths,
    scripts: Scripts,
    shaders: Shaders,
    /// The fonts of the game.
    pub fonts: Fonts,
    timelines: Timelines,
    objects: Objects,
    rooms: Rooms,
    dafl: Dafl,
    tpag: Tpag,
    code: Code,
    variables: Variables,
    functions: Functions,
    /// The strings of the game.
    pub strings: Strings,
    textures: Textures,
    audio: Audio,
}

/// A reader that satisfies the requirements for reading a GameData.
pub trait GameDataRead: Read + io::Seek {}
impl<T: Read + io::Seek> GameDataRead for T {}
/// A writer that satisfies the requirements for writing a GameData.
pub trait GameDataWrite: Write + io::Seek {}
impl<T: Write + io::Seek> GameDataWrite for T {}

impl GameData {
    /// Reads a GameData from a reader.
    pub fn from_reader<R: GameDataRead>(reader: &mut R) -> Result<GameData, ReadError> {
        gamedata_io::read(reader)
    }
    /// Reads a GameData from a file.
    pub fn from_file<P: AsRef<path::Path>>(path: &P) -> Result<GameData, ReadError> {
        use std::fs::File;
        use std::io::BufReader;
        let file = try!(File::open(path));
        GameData::from_reader(&mut BufReader::new(file))
    }
    /// Writes self to a writer.
    pub fn write_to_writer<W: GameDataWrite>(&self, writer: &mut W) -> io::Result<()> {
        gamedata_io::write(self, writer)
    }
    /// Writes self to a file.
    pub fn save_to_file<P: AsRef<path::Path>>(&self, path: &P) -> io::Result<()> {
        use std::fs::File;
        use std::io::BufWriter;
        let file = try!(File::create(path));
        self.write_to_writer(&mut BufWriter::new(file))
    }
    /// Returns the window width of the game.
    pub fn window_width(&self) -> u32 {
        self.metadata.window_width
    }
    /// Returns the window height of the game.
    pub fn window_height(&self) -> u32 {
        self.metadata.window_height
    }
    /// Sets the window dimensions of the game.
    pub fn set_window_dimensions(&mut self, width: u32, height: u32) {
        self.metadata.window_width = width;
        self.metadata.window_height = height;
    }
    /// Returns the window title of the game.
    pub fn window_title(&self) -> &str {
        &self.strings.strings[self.metadata.window_title_index]
    }
    /// Sets the window title of the game.
    pub fn set_window_title<S: Into<String>>(&mut self, new: S) {
        self.strings.strings[self.metadata.window_title_index] = new.into();
    }
}

/// Contains various metadata, for example, the window width/height/title.
struct MetaData {
    unk1: u32, // Purpose unknown
    game_id_1_index: usize, // Some kind of game id
    default_index: usize, // Points to "Default"
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    game_id_2_index: usize, // Some kind of game id, identical to game_id_1
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    window_width: u32,
    window_height: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    unk16: u32,
    unk17: u32,
    unk18: u32,
    unk19: u32,
    unk20: u32,
    window_title_index: usize,
    unknown: Vec<u32>,
}

/// Game Maker project Options
struct Options {
    unk1: u32, // Unknown
    unk2: u32, // Unknown
    icon_offset: u32, // Points to texture data (icon?)
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
    unk7: u32,
    unk8: u32,
    unk9: u32,
    unk10: u32,
    unk11: u32,
    unk12: u32,
    unk13: u32,
    unk14: u32,
    unk15: u32,
    constant1_name_index: usize, // Offset of the name of this constant in the string table
    constant2_name_index: usize, // Offset of the name of this constant in the string table
    constant3_name_index: usize, // Offset of the name of this constant in the string table
    constant4_name_index: usize, // Offset of the name of this constant in the string table
    constant5_name_index: usize, // Offset of the name of this constant in the string table
    constant6_name_index: usize, // Offset of the name of this constant in the string table
    constant7_name_index: usize, // Offset of the name of this constant in the string table
    constant8_name_index: usize, // Offset of the name of this constant in the string table
    constant9_name_index: usize, // Offset of the name of this constant in the string table
    constant10_name_index: usize, // Offset of the name of this constant in the string table
    constant11_name_index: usize, // Offset of the name of this constant in the string table
    constant12_name_index: usize, // Offset of the name of this constant in the string table
    constant13_name_index: usize, // Offset of the name of this constant in the string table
    constant14_name_index: usize, // Offset of the name of this constant in the string table
}

/// Purpose unknown.
struct Extn {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker sound.
pub struct Sound {
    /// Index of the name in the string table.
    pub name_index: usize,
    unk1: u32,
    /// Index of the extension in the string table.
    pub ext_index: usize,
    /// Index of the filename in the string table.
    pub filename_index: usize,
    unk2: u32,
    unk3: u32,
    unk4: u32,
    unk5: u32,
    unk6: u32,
}

/// A collection of sounds.
pub struct Sounds {
    /// The sounds.
    pub sounds: Vec<Sound>,
}

/// Collection of audio groups. Not present in all games.
struct AudioGroups {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of sprites.
struct Sprites {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of backgrounds.
struct Backgrounds {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of paths.
struct Paths {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker script.
struct Script {
    /// Index of the name of the script in the string table
    pub name_index: usize,
    unknown: u32, // Unknown
}

/// A collection of scripts.
struct Scripts {
    pub scripts: Vec<Script>,
}

/// A collection of shaders.
struct Shaders {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A font.
#[derive(Clone)]
pub struct Font {
    /// Index of the font's resource name in the string table.
    pub name_index: usize,
    /// Index of the font's font name in the string table.
    pub font_name_index: usize,
    /// Point size of the font.
    pub point_size: u32,
    data: Vec<u8>,
}

/// A collection of fonts.
pub struct Fonts {
    /// The fonts.
    pub fonts: Vec<Font>,
    unknown: Vec<u8>, // Unknown trailing data
}

/// A collection of timelines.
struct Timelines {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of objects.
struct Objects {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A collection of rooms.
struct Rooms {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Dafl {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Purpose unknown.
struct Tpag {
    raw: Vec<u8>, // Data not analyzed yet
}

/// Something to do with code. Maybe code of scripts?
struct Code {
    raw: Vec<u8>, // Data not analyzed yet
}

/// A game maker variable.
struct Variable {
    /// Index of the name of the variable in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section
}

/// A collection of variables.
struct Variables {
    pub variables: Vec<Variable>,
}

/// A game maker function.
struct Function {
    /// Index of the name of the function in the strings section.
    pub name_index: usize,
    unknown: u32, // Purpose unknown. Ranges from 1 to a few thousand.
    code_offset: u32, // Points into the code section.
}

/// A collection of functions.
struct Functions {
    pub functions: Vec<Function>,
}

/// A collection of strings.
///
/// All strings are assumed to be valid UTF-8.
pub struct Strings {
    /// The vector holding the strings.
    pub strings: Vec<String>,
}

struct Texture {
    unknown: u32, // Purpose unknown. Always seems to be 1.
    offset: u32, // Offset of data in the texture data
}

/// A collection of textures.
struct Textures {
    pub textures: Vec<Texture>,
    texture_data: Vec<u8>,
}

struct AudioData {
    data: Vec<u8>,
}

/// A collection of audio data.
struct Audio {
    audio: Vec<AudioData>,
    offsets: Vec<u32>, // Audio data is not contiguous, so we need to store relative offsets
    size: u32, // Fuck it
}
<|endoftext|>


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

#![crate_type = "lib"]
#![crate_name = "rs_es"]

//! A client for ElasticSearch's REST API

#[macro_use]
extern crate log;
extern crate hyper;
extern crate rustc_serialize;

#[macro_use]
pub mod util;

pub mod error;
pub mod operations;
pub mod query;

use hyper::status::StatusCode;

use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};

use error::EsError;
use operations::delete::{DeleteOperation, DeleteByQueryOperation};
use operations::get::GetOperation;
use operations::index::IndexOperation;
use operations::search::{SearchURIOperation, SearchQueryOperation};
use operations::RefreshOperation;

// The client

/// Process the result of an HTTP request
fn do_req(resp: &mut hyper::client::response::Response)
          -> Result<(StatusCode, Option<Json>), EsError> {
    info!("Response: {:?}", resp);
    match resp.status {
        StatusCode::Ok |
        StatusCode::Created |
        StatusCode::NotFound => match Json::from_reader(resp) {
            Ok(json) => Ok((resp.status, Some(json))),
            Err(e)   => Err(EsError::from(e))
        },
        _                    => Err(EsError::from(resp))
    }
}

/// The core of the ElasticSearch client, owns a HTTP connection
pub struct Client {
    base_url:    String,
    http_client: hyper::Client
}

/// Create a HTTP function for the given method (GET/PUT/POST/DELETE)
macro_rules! es_op {
    ($n:ident,$cn:ident) => {
        fn $n(&mut self, url: &str)
              -> Result<(StatusCode, Option<Json>), EsError> {
            info!("Doing {} on {}", stringify!($n), url);
            let mut result = try!(self.http_client.$cn(&format!("{}/{}", self.base_url, url)).send());
            do_req(&mut result)
        }
    }
}

/// Create a HTTP function with a request body for the given method
/// (GET/PUT/POST/DELETE)
macro_rules! es_body_op {
    ($n:ident,$cn:ident) => {
        fn $n<E>(&mut self, url: &str, body: &E)
                 -> Result<(StatusCode, Option<Json>), EsError>
            where E: Encodable {
                info!("Doing {} on {}", stringify!($n), url);
                let json_string = json::encode(body).unwrap();
                info!("Body: {}", json_string);
                let mut result = try!(self.http_client
                                      .$cn(&format!("{}/{}", self.base_url, url))
                                      .body(&json_string)
                                      .send());

                do_req(&mut result)
            }
    }
}

impl Client {
    /// Create a new client
    pub fn new(host: &str, port: u32) -> Client {
        Client {
            base_url:    format!("http://{}:{}", host, port),
            http_client: hyper::Client::new()
        }
    }

    es_op!(get_op, get);

    es_op!(post_op, post);
    es_body_op!(post_body_op, post);
    es_op!(put_op, put);
    es_body_op!(put_body_op, put);
    es_op!(delete_op, delete);
    es_body_op!(delete_body_op, delete);

    /// Calls the base ES path, returning the version number
    pub fn version(&mut self) -> Result<String, EsError> {
        let (_, result) = try!(self.get_op("/"));
        let json = result.unwrap();
        match json.find_path(&["version", "number"]) {
            Some(version) => match version.as_string() {
                Some(string) => Ok(string.to_string()),
                None         => Err(EsError::EsError(format!("Cannot find version number in: {:?}",
                                                             json)))
            },
            None          => Err(EsError::EsError(format!("Cannot find version number in {:?}",
                                                          json)))
        }
    }

    /// An index operation to index a document in the specified index
    pub fn index<'a, 'b, E: Encodable>(&'a mut self, index: &'b str, doc_type: &'b str)
                                       -> IndexOperation<'a, 'b, E> {
        IndexOperation::new(self, index, doc_type)
    }

    /// Implementation of the ES GET API
    pub fn get<'a>(&'a mut self,
                   index: &'a str,
                   id:    &'a str) -> GetOperation {
        GetOperation::new(self, index, id)
    }

    /// Delete by ID
    pub fn delete<'a>(&'a mut self,
                      index:    &'a str,
                      doc_type: &'a str,
                      id:       &'a str) -> DeleteOperation {
        DeleteOperation::new(self, index, doc_type, id)
    }

    /// Delete by query
    pub fn delete_by_query<'a>(&'a mut self) -> DeleteByQueryOperation {
        DeleteByQueryOperation::new(self)
    }

    /// Refresh
    pub fn refresh<'a>(&'a mut self) -> RefreshOperation {
        RefreshOperation::new(self)
    }

    /// Search via the query parameter
    pub fn search_uri<'a>(&'a mut self) -> SearchURIOperation {
        SearchURIOperation::new(self)
    }

    /// Search via the query DSL
    pub fn search_query<'a>(&'a mut self) -> SearchQueryOperation {
        SearchQueryOperation::new(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    extern crate regex;

    use super::Client;
    use super::operations::index::OpType;

    use super::query::{Filter, Query};

    use std::env;

    use self::regex::Regex;

    // test setup

    fn make_client() -> Client {
        let hostname = match env::var("ES_HOST") {
            Ok(val) => val,
            Err(_)  => "localhost".to_string()
        };
        Client::new(&hostname, 9200)
    }

    #[derive(Debug, RustcDecodable, RustcEncodable)]
    struct TestDocument {
        str_field: String,
        int_field: i64
    }

    impl TestDocument {
        fn new() -> TestDocument {
            TestDocument {
                str_field: "I am a test".to_string(),
                int_field: 1
            }
        }

        fn with_str_field(mut self, s: &str) -> TestDocument {
            self.str_field = s.to_string();
            self
        }

        fn with_int_field(mut self, i: i64) -> TestDocument {
            self.int_field = i;
            self
        }
    }

    fn clean_db(client: &mut Client,
                test_idx: &str) {
        client.delete_by_query()
            .with_indexes(&[test_idx])
            .with_query(&Query::build_match_all().build())
            .send()
            .unwrap();
    }

    // tests

    #[test]
    fn it_works() {
        env_logger::init().unwrap();

        let mut client = make_client();
        let result = client.version().unwrap();

        let expected_regex = Regex::new(r"^\d\.\d\.\d$").unwrap();
        assert_eq!(expected_regex.is_match(&result), true);
    }

    #[test]
    fn test_indexing() {
        let index_name = "test_indexing";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        {
            let result_wrapped = client
                .index(index_name, "test_type")
                .with_doc(&TestDocument::new().with_int_field(1))
                .with_ttl(&927500)
                .send();
            info!("TEST RESULT: {:?}", result_wrapped);
            let result = result_wrapped.unwrap();
            assert_eq!(result.created, true);
            assert_eq!(result.index, index_name);
            assert_eq!(result.doc_type, "test_type");
            assert!(result.id.len() > 0);
            assert_eq!(result.version, 1);
        }
        {
            let delete_result = client.delete(index_name, "test_type", "TEST_INDEXING_2").send();
            info!("DELETE RESULT: {:?}", delete_result);

            let result_wrapped = client
                .index(index_name, "test_type")
                .with_doc(&TestDocument::new().with_int_field(2))
                .with_id("TEST_INDEXING_2")
                .with_op_type(&OpType::Create)
                .send();
            let result = result_wrapped.unwrap();

            assert_eq!(result.created, true);
            assert_eq!(result.index, index_name);
            assert_eq!(result.doc_type, "test_type");
            assert_eq!(result.id, "TEST_INDEXING_2");
            assert!(result.version >= 1);
        }
    }

    #[test]
    fn test_get() {
        let index_name = "test_get";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        {
            let doc = TestDocument::new().with_int_field(3);
            client
                .index(index_name, "test_type")
                .with_id("TEST_GETTING")
                .with_doc(&doc)
                .send().unwrap();
        }
        {
            let mut getter = client.get(index_name, "TEST_GETTING");
            let result_wrapped = getter
                .with_doc_type("test_type")
                .send();
            info!("RESULT: {:?}", result_wrapped);
            let result = result_wrapped.unwrap();
            assert_eq!(result.id, "TEST_GETTING");

            let source:TestDocument = result.source().unwrap();
            assert_eq!(source.str_field, "I am a test");
            assert_eq!(source.int_field, 3);
        }
    }

    #[test]
    fn test_delete_by_query() {
        let index_name = "test_delete_by_query";
        let mut client = make_client();
        clean_db(&mut client, index_name);

        let td1 = TestDocument::new().with_str_field("TEST DOC 1").with_int_field(100);
        let td2 = TestDocument::new().with_str_field("TEST DOC 2").with_int_field(200);

        client
            .index(index_name, "test_type")
            .with_id("ABC123")
            .with_doc(&td1)
            .send().unwrap();
        client
            .index(index_name, "test_type")
            .with_id("ABC124")
            .with_doc(&td2)
            .send().unwrap();

        let delete_result = client
            .delete_by_query()
            .with_indexes(&[index_name])
            .with_doc_types(&["test_type"])
            .with_query(&Query::build_match("int_field", 200)
                        .with_lenient(false)
                        .build())
            .send().unwrap();

        assert!(delete_result.unwrap().successful());

        let doc1 = client.get(index_name, "ABC123").with_doc_type("test_type").send().unwrap();
        let doc2 = client.get(index_name, "ABC124").with_doc_type("test_type").send().unwrap();

        assert!(doc1.found);
        assert!(!doc2.found);
    }

    fn setup_search_test_data(client: &mut Client, index_name: &str) {
        let documents = vec![
            TestDocument::new().with_str_field("Document A123").with_int_field(1),
            TestDocument::new().with_str_field("Document B456").with_int_field(2),
            TestDocument::new().with_str_field("Document 1ABC").with_int_field(3)
                ];
        for ref doc in documents {
            client.index(index_name, "test_type")
                .with_doc(doc)
                .send()
                .unwrap();
        }
        client.refresh().with_indexes(&[index_name]).send().unwrap();
    }

    #[test]
    fn test_search_uri() {
        let index_name = "test_search_uri";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        setup_search_test_data(&mut client, index_name);

        let all_results = client.search_uri().with_indexes(&[index_name]).send().unwrap();
        assert_eq!(3, all_results.hits.total);

        let doc_a = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("A123".to_string())
            .send()
            .unwrap();
        assert_eq!(1, doc_a.hits.total);

        let doc_1 = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("str_field:1ABC".to_string())
            .send()
            .unwrap();
        assert_eq!(1, doc_1.hits.total);

        let limited_fields = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("str_field:B456".to_string())
            .with_fields(&["int_field"])
            .send()
            .unwrap();
        assert_eq!(1, limited_fields.hits.total);
    }

    #[test]
    fn test_search_body() {
        let index_name = "test_search_body";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        setup_search_test_data(&mut client, index_name);

        let all_results = client
            .search_query()
            .with_indexes(&[index_name])
            .with_query(&Query::build_match_all().build())
            .send().unwrap();
        assert_eq!(3, all_results.hits.total);

        let within_range = client
            .search_query()
            .with_indexes(&[index_name])
            .with_query(&Query::build_filtered(Filter::build_range("int_field")
                                               .with_gte(2)
                                               .with_lte(3)
                                               .build())
                        .build())
            .send().unwrap();
        assert_eq!(2, within_range.hits.total);
    }
}
<commit_msg>More detailed documentation.<commit_after>/*
 * Copyright 2015 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#![crate_type = "lib"]
#![crate_name = "rs_es"]

//! A client for ElasticSearch's REST API
//!
//! The `Client` itself is used as the central access point, from which numerous
//! operations are defined implementing each of the specific ElasticSearch APIs.
//!
//! Warning: at the time of writing the majority of such APIs are currently
//! unimplemented.

#[macro_use]
extern crate log;
extern crate hyper;
extern crate rustc_serialize;

#[macro_use]
pub mod util;

pub mod error;
pub mod operations;
pub mod query;

use hyper::status::StatusCode;

use rustc_serialize::Encodable;
use rustc_serialize::json::{self, Json};

use error::EsError;
use operations::delete::{DeleteOperation, DeleteByQueryOperation};
use operations::get::GetOperation;
use operations::index::IndexOperation;
use operations::search::{SearchURIOperation, SearchQueryOperation};
use operations::RefreshOperation;

// The client

/// Process the result of an HTTP request, returning the status code and the
/// `Json` result (if the result had a body) or an `EsError` if there were any
/// errors
fn do_req(resp: &mut hyper::client::response::Response)
          -> Result<(StatusCode, Option<Json>), EsError> {
    info!("Response: {:?}", resp);
    match resp.status {
        StatusCode::Ok |
        StatusCode::Created |
        StatusCode::NotFound => match Json::from_reader(resp) {
            Ok(json) => Ok((resp.status, Some(json))),
            Err(e)   => Err(EsError::from(e))
        },
        _                    => Err(EsError::from(resp))
    }
}

/// The core of the ElasticSearch client, owns a HTTP connection.
///
/// Each instance of `Client` is reusable, but only one thread can use each one
/// at once.  This will be enforced by the borrow-checker as most methods are
/// defined on `&mut self`.
///
/// To create a `Client`, the hostname and port need to be specified:
///
/// # Examples
///
/// ```
/// let mut client = Client::new("localhost", 9200);
/// ```
///
/// Each ElasticSearch API operation is defined as a method on `Client`.  Any
/// compulsory parameters must be given as arguments to this method.  It returns
/// an operation builder that can be used to add any optional parameters.
/// Finally `send` is called to submit the operation:
///
/// ```
/// let result = client.search_uri()
///                    .with_indexes(&["index_name])
///                    .with_query("field:value")
///                    .send()
/// ```
///
/// See the specific operations and their builder objects for details.
pub struct Client {
    base_url:    String,
    http_client: hyper::Client
}

/// Create a HTTP function for the given method (GET/PUT/POST/DELETE)
macro_rules! es_op {
    ($n:ident,$cn:ident) => {
        fn $n(&mut self, url: &str)
              -> Result<(StatusCode, Option<Json>), EsError> {
            info!("Doing {} on {}", stringify!($n), url);
            let mut result = try!(self.http_client.$cn(&format!("{}/{}", self.base_url, url)).send());
            do_req(&mut result)
        }
    }
}

/// Create a HTTP function with a request body for the given method
/// (GET/PUT/POST/DELETE)
macro_rules! es_body_op {
    ($n:ident,$cn:ident) => {
        fn $n<E>(&mut self, url: &str, body: &E)
                 -> Result<(StatusCode, Option<Json>), EsError>
            where E: Encodable {
                info!("Doing {} on {}", stringify!($n), url);
                let json_string = json::encode(body).unwrap();
                info!("Body: {}", json_string);
                let mut result = try!(self.http_client
                                      .$cn(&format!("{}/{}", self.base_url, url))
                                      .body(&json_string)
                                      .send());

                do_req(&mut result)
            }
    }
}

impl Client {
    /// Create a new client
    pub fn new(host: &str, port: u32) -> Client {
        Client {
            base_url:    format!("http://{}:{}", host, port),
            http_client: hyper::Client::new()
        }
    }

    es_op!(get_op, get);

    es_op!(post_op, post);
    es_body_op!(post_body_op, post);
    es_op!(put_op, put);
    es_body_op!(put_body_op, put);
    es_op!(delete_op, delete);
    es_body_op!(delete_body_op, delete);

    /// Calls the base ES path, returning the version number
    pub fn version(&mut self) -> Result<String, EsError> {
        let (_, result) = try!(self.get_op("/"));
        let json = result.unwrap();
        match json.find_path(&["version", "number"]) {
            Some(version) => match version.as_string() {
                Some(string) => Ok(string.to_string()),
                None         => Err(EsError::EsError(format!("Cannot find version number in: {:?}",
                                                             json)))
            },
            None          => Err(EsError::EsError(format!("Cannot find version number in {:?}",
                                                          json)))
        }
    }

    // Indices APIs

    /// Refresh
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/indices-refresh.html
    pub fn refresh<'a>(&'a mut self) -> RefreshOperation {
        RefreshOperation::new(self)
    }

    // Document APIs

    /// An index operation to index a document in the specified index.
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/docs-index_.html
    pub fn index<'a, 'b, E: Encodable>(&'a mut self, index: &'b str, doc_type: &'b str)
                                       -> IndexOperation<'a, 'b, E> {
        IndexOperation::new(self, index, doc_type)
    }

    /// Implementation of the ES GET API
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/docs-get.html
    pub fn get<'a>(&'a mut self,
                   index: &'a str,
                   id:    &'a str) -> GetOperation {
        GetOperation::new(self, index, id)
    }

    /// Delete by ID
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/docs-delete.html
    pub fn delete<'a>(&'a mut self,
                      index:    &'a str,
                      doc_type: &'a str,
                      id:       &'a str) -> DeleteOperation {
        DeleteOperation::new(self, index, doc_type, id)
    }

    // Multi-document APIs

    /// Delete by query
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/docs-delete-by-query.html
    ///
    /// Warning: will be removed in ElasticSearch 2.0
    pub fn delete_by_query<'a>(&'a mut self) -> DeleteByQueryOperation {
        DeleteByQueryOperation::new(self)
    }

    // Search APIs

    /// Search via the query parameter
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/search-uri-request.html
    pub fn search_uri<'a>(&'a mut self) -> SearchURIOperation {
        SearchURIOperation::new(self)
    }

    /// Search via the query DSL
    ///
    /// See: https://www.elastic.co/guide/en/elasticsearch/reference/1.x/search-request-body.html
    pub fn search_query<'a>(&'a mut self) -> SearchQueryOperation {
        SearchQueryOperation::new(self)
    }
}

#[cfg(test)]
mod tests {
    extern crate env_logger;
    extern crate regex;

    use super::Client;
    use super::operations::index::OpType;

    use super::query::{Filter, Query};

    use std::env;

    use self::regex::Regex;

    // test setup

    fn make_client() -> Client {
        let hostname = match env::var("ES_HOST") {
            Ok(val) => val,
            Err(_)  => "localhost".to_string()
        };
        Client::new(&hostname, 9200)
    }

    #[derive(Debug, RustcDecodable, RustcEncodable)]
    struct TestDocument {
        str_field: String,
        int_field: i64
    }

    impl TestDocument {
        fn new() -> TestDocument {
            TestDocument {
                str_field: "I am a test".to_string(),
                int_field: 1
            }
        }

        fn with_str_field(mut self, s: &str) -> TestDocument {
            self.str_field = s.to_string();
            self
        }

        fn with_int_field(mut self, i: i64) -> TestDocument {
            self.int_field = i;
            self
        }
    }

    fn clean_db(client: &mut Client,
                test_idx: &str) {
        client.delete_by_query()
            .with_indexes(&[test_idx])
            .with_query(&Query::build_match_all().build())
            .send()
            .unwrap();
    }

    // tests

    #[test]
    fn it_works() {
        env_logger::init().unwrap();

        let mut client = make_client();
        let result = client.version().unwrap();

        let expected_regex = Regex::new(r"^\d\.\d\.\d$").unwrap();
        assert_eq!(expected_regex.is_match(&result), true);
    }

    #[test]
    fn test_indexing() {
        let index_name = "test_indexing";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        {
            let result_wrapped = client
                .index(index_name, "test_type")
                .with_doc(&TestDocument::new().with_int_field(1))
                .with_ttl(&927500)
                .send();
            info!("TEST RESULT: {:?}", result_wrapped);
            let result = result_wrapped.unwrap();
            assert_eq!(result.created, true);
            assert_eq!(result.index, index_name);
            assert_eq!(result.doc_type, "test_type");
            assert!(result.id.len() > 0);
            assert_eq!(result.version, 1);
        }
        {
            let delete_result = client.delete(index_name, "test_type", "TEST_INDEXING_2").send();
            info!("DELETE RESULT: {:?}", delete_result);

            let result_wrapped = client
                .index(index_name, "test_type")
                .with_doc(&TestDocument::new().with_int_field(2))
                .with_id("TEST_INDEXING_2")
                .with_op_type(&OpType::Create)
                .send();
            let result = result_wrapped.unwrap();

            assert_eq!(result.created, true);
            assert_eq!(result.index, index_name);
            assert_eq!(result.doc_type, "test_type");
            assert_eq!(result.id, "TEST_INDEXING_2");
            assert!(result.version >= 1);
        }
    }

    #[test]
    fn test_get() {
        let index_name = "test_get";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        {
            let doc = TestDocument::new().with_int_field(3);
            client
                .index(index_name, "test_type")
                .with_id("TEST_GETTING")
                .with_doc(&doc)
                .send().unwrap();
        }
        {
            let mut getter = client.get(index_name, "TEST_GETTING");
            let result_wrapped = getter
                .with_doc_type("test_type")
                .send();
            info!("RESULT: {:?}", result_wrapped);
            let result = result_wrapped.unwrap();
            assert_eq!(result.id, "TEST_GETTING");

            let source:TestDocument = result.source().unwrap();
            assert_eq!(source.str_field, "I am a test");
            assert_eq!(source.int_field, 3);
        }
    }

    #[test]
    fn test_delete_by_query() {
        let index_name = "test_delete_by_query";
        let mut client = make_client();
        clean_db(&mut client, index_name);

        let td1 = TestDocument::new().with_str_field("TEST DOC 1").with_int_field(100);
        let td2 = TestDocument::new().with_str_field("TEST DOC 2").with_int_field(200);

        client
            .index(index_name, "test_type")
            .with_id("ABC123")
            .with_doc(&td1)
            .send().unwrap();
        client
            .index(index_name, "test_type")
            .with_id("ABC124")
            .with_doc(&td2)
            .send().unwrap();

        let delete_result = client
            .delete_by_query()
            .with_indexes(&[index_name])
            .with_doc_types(&["test_type"])
            .with_query(&Query::build_match("int_field", 200)
                        .with_lenient(false)
                        .build())
            .send().unwrap();

        assert!(delete_result.unwrap().successful());

        let doc1 = client.get(index_name, "ABC123").with_doc_type("test_type").send().unwrap();
        let doc2 = client.get(index_name, "ABC124").with_doc_type("test_type").send().unwrap();

        assert!(doc1.found);
        assert!(!doc2.found);
    }

    fn setup_search_test_data(client: &mut Client, index_name: &str) {
        let documents = vec![
            TestDocument::new().with_str_field("Document A123").with_int_field(1),
            TestDocument::new().with_str_field("Document B456").with_int_field(2),
            TestDocument::new().with_str_field("Document 1ABC").with_int_field(3)
                ];
        for ref doc in documents {
            client.index(index_name, "test_type")
                .with_doc(doc)
                .send()
                .unwrap();
        }
        client.refresh().with_indexes(&[index_name]).send().unwrap();
    }

    #[test]
    fn test_search_uri() {
        let index_name = "test_search_uri";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        setup_search_test_data(&mut client, index_name);

        let all_results = client.search_uri().with_indexes(&[index_name]).send().unwrap();
        assert_eq!(3, all_results.hits.total);

        let doc_a = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("A123".to_string())
            .send()
            .unwrap();
        assert_eq!(1, doc_a.hits.total);

        let doc_1 = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("str_field:1ABC".to_string())
            .send()
            .unwrap();
        assert_eq!(1, doc_1.hits.total);

        let limited_fields = client
            .search_uri()
            .with_indexes(&[index_name])
            .with_query("str_field:B456".to_string())
            .with_fields(&["int_field"])
            .send()
            .unwrap();
        assert_eq!(1, limited_fields.hits.total);
    }

    #[test]
    fn test_search_body() {
        let index_name = "test_search_body";
        let mut client = make_client();
        clean_db(&mut client, index_name);
        setup_search_test_data(&mut client, index_name);

        let all_results = client
            .search_query()
            .with_indexes(&[index_name])
            .with_query(&Query::build_match_all().build())
            .send().unwrap();
        assert_eq!(3, all_results.hits.total);

        let within_range = client
            .search_query()
            .with_indexes(&[index_name])
            .with_query(&Query::build_filtered(Filter::build_range("int_field")
                                               .with_gte(2)
                                               .with_lte(3)
                                               .build())
                        .build())
            .send().unwrap();
        assert_eq!(2, within_range.hits.total);
    }
}
<|endoftext|>


<commit_before>#![allow(unknown_lints)]
#![warn(missing_docs)]
// Necessary for some larger quick-error based errors.
#![recursion_limit = "128"]
// Clippy false positives
#![allow(block_in_if_condition_stmt, redundant_closure_call)]

//! livesplit-core is a library that provides a lot of functionality for creating a speedrun timer.
//!
//! # Examples
//!
//! ```
//! use livesplit_core::{Run, Segment, Timer, TimerPhase};
//!
//! // Create a run object that we can use with at least one segment.
//! let mut run = Run::new();
//! run.set_game_name("Super Mario Odyssey");
//! run.set_category_name("Any%");
//! run.push_segment(Segment::new("Cap Kingdom"));
//!
//! // Create the timer from the run.
//! let mut timer = Timer::new(run).expect("Run with at least one segment provided");
//!
//! // Start a new attempt.
//! timer.start();
//! assert_eq!(timer.current_phase(), TimerPhase::Running);
//!
//! // Create a split.
//! timer.split();
//!
//! // The run should be finished now.
//! assert_eq!(timer.current_phase(), TimerPhase::Ended);
//!
//! // Reset the attempt and confirm that we want to store the attempt.
//! timer.reset(true);
//!
//! // The attempt is now over.
//! assert_eq!(timer.current_phase(), TimerPhase::NotRunning);
//! ```

extern crate base64;
extern crate byteorder;
extern crate chrono;
#[macro_use]
extern crate derive_more;
extern crate image as imagelib;
extern crate odds;
extern crate ordered_float;
#[macro_use]
extern crate quick_error;
extern crate quick_xml;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate unicase;

pub extern crate indexmap;
pub extern crate livesplit_hotkey as hotkey;
pub extern crate palette;
pub extern crate parking_lot;

mod platform;

#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
pub use platform::*;

macro_rules! catch {
    ($($code:tt)*) => {
        (|| { Some({ $($code)* }) })()
    }
}

pub mod analysis;
pub mod comparison;
pub mod component;
mod hotkey_config;
mod hotkey_system;
mod image;
pub mod layout;
pub mod run;
pub mod settings;
#[cfg(test)]
mod tests_helper;
pub mod timing;

pub use self::hotkey_config::HotkeyConfig;
pub use self::hotkey_system::HotkeySystem;
pub use self::image::Image;
pub use self::layout::{
    Component, Editor as LayoutEditor, GeneralSettings as GeneralLayoutSettings, Layout,
};
pub use self::run::{Attempt, Editor as RunEditor, Run, RunMetadata, Segment, SegmentHistory};
pub use self::timing::{
    AtomicDateTime, GameTime, RealTime, SharedTimer, Time, TimeSpan, TimeStamp, Timer, TimerPhase,
    TimingMethod,
};
pub use chrono::{DateTime, Utc};
<commit_msg>Integrate clippy into RLS<commit_after>#![allow(unknown_lints)]
#![warn(missing_docs, clippy)]
// Necessary for some larger quick-error based errors.
#![recursion_limit = "128"]
// Clippy false positives
#![allow(block_in_if_condition_stmt, redundant_closure_call)]

//! livesplit-core is a library that provides a lot of functionality for creating a speedrun timer.
//!
//! # Examples
//!
//! ```
//! use livesplit_core::{Run, Segment, Timer, TimerPhase};
//!
//! // Create a run object that we can use with at least one segment.
//! let mut run = Run::new();
//! run.set_game_name("Super Mario Odyssey");
//! run.set_category_name("Any%");
//! run.push_segment(Segment::new("Cap Kingdom"));
//!
//! // Create the timer from the run.
//! let mut timer = Timer::new(run).expect("Run with at least one segment provided");
//!
//! // Start a new attempt.
//! timer.start();
//! assert_eq!(timer.current_phase(), TimerPhase::Running);
//!
//! // Create a split.
//! timer.split();
//!
//! // The run should be finished now.
//! assert_eq!(timer.current_phase(), TimerPhase::Ended);
//!
//! // Reset the attempt and confirm that we want to store the attempt.
//! timer.reset(true);
//!
//! // The attempt is now over.
//! assert_eq!(timer.current_phase(), TimerPhase::NotRunning);
//! ```

extern crate base64;
extern crate byteorder;
extern crate chrono;
#[macro_use]
extern crate derive_more;
extern crate image as imagelib;
extern crate odds;
extern crate ordered_float;
#[macro_use]
extern crate quick_error;
extern crate quick_xml;
extern crate serde;
#[macro_use]
extern crate serde_derive;
extern crate serde_json;
extern crate unicase;

pub extern crate indexmap;
pub extern crate livesplit_hotkey as hotkey;
pub extern crate palette;
pub extern crate parking_lot;

mod platform;

#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
pub use platform::*;

macro_rules! catch {
    ($($code:tt)*) => {
        (|| { Some({ $($code)* }) })()
    }
}

pub mod analysis;
pub mod comparison;
pub mod component;
mod hotkey_config;
mod hotkey_system;
mod image;
pub mod layout;
pub mod run;
pub mod settings;
#[cfg(test)]
mod tests_helper;
pub mod timing;

pub use self::hotkey_config::HotkeyConfig;
pub use self::hotkey_system::HotkeySystem;
pub use self::image::Image;
pub use self::layout::{
    Component, Editor as LayoutEditor, GeneralSettings as GeneralLayoutSettings, Layout,
};
pub use self::run::{Attempt, Editor as RunEditor, Run, RunMetadata, Segment, SegmentHistory};
pub use self::timing::{
    AtomicDateTime, GameTime, RealTime, SharedTimer, Time, TimeSpan, TimeStamp, Timer, TimerPhase,
    TimingMethod,
};
pub use chrono::{DateTime, Utc};
<|endoftext|>


<commit_before>//! A library for creating General Game Playing (GGP) players. This library is based off of
//! [GGP Base](https://github.com/ggp-org/ggp-base). While GGP Base allows the creation of
//! players backed by a propositional network or a logic prover, this library currently only
//! supports logic prover based players.
//!
//! # Example
//!
//! To create your own player, simply implement the `Player` trait for your struct and pass the
//! host/port you want the player to run at and the player itself to the `run` function.
//!
//! The following example implements a RandomPlayer, which plays random legal moves each turn.
//!
//! ```ignore
//! extern crate rand;
//! extern crate ggp_rs;
//!
//! use ggp_rs::{Player, Game, Move};
//! use std::net::Ipv4Addr;
//!
//! struct RandomPlayer;
//!
//! impl Player for RandomPlayer {
//!     fn get_name(&self) -> String {
//!         "RandomPlayer".to_string()
//!     }
//!
//!     fn select_move(&self, game: &Game) -> Move {
//!         let state = game.get_current_state();
//!         let role = game.get_role();
//!         let mut moves = game.get_legal_moves(state, role);
//!         let r = rand::random::<usize>() % moves.len();
//!         moves.swap_remove(r)
//!     }
//! }
//!
//! fn main() {
//!     ggp_rs::run((Ipv4Addr::new(0,0,0,0), 9147), RandomPlayer);
//! }
//! ```

#![feature(std_misc, plugin, collections)]
#![cfg_attr(test, feature(test))]
#![plugin(regex_macros)]

extern crate gdl_parser;
extern crate hyper;
extern crate regex;
extern crate unicase;

#[macro_use]
extern crate log;

mod game_manager;
mod gdl;
mod prover;

use unicase::UniCase;

use hyper::Server;
use hyper::server::{Request, Response, Handler};
use hyper::header::{ContentLength, ContentType, AccessControlAllowOrigin,
                    AccessControlAllowHeaders};
use hyper::net::Fresh;
use hyper::version::HttpVersion;

use std::net::ToSocketAddrs;
use std::io::Read;
use std::io::Write;
use std::ascii::OwnedAsciiExt;
use std::sync::Mutex;

use game_manager::GameManager;

pub use game_manager::{Game, State};
pub use gdl::{Move, Role, Score};

/// A GGP player
pub trait Player {
    fn get_name(&self) -> String;

    fn meta_game(&self, _: &Game) {}

    fn select_move(&self, game: &Game) -> Move;

    fn stop(&self, _: &Game) {}
}

/// Starts a GGP player listening at `host`
pub fn run<T: ToSocketAddrs + 'static, P: Player + Sync + Send + 'static>(host: T, player: P) {
    let handler = GameHandler::new(player);
    Server::http(handler).listen(host).unwrap();
}

struct GameHandler<P: Player + Send> {
    pub gm: Mutex<GameManager<P>>
}

impl<P: Player + Send> GameHandler<P> {
    pub fn new(player: P) -> GameHandler<P> {
        GameHandler { gm: Mutex::new(GameManager::new(player)) }
    }
}

impl<P: Player + Sync + Send> Handler for GameHandler<P> {
    fn handle(&self, mut req: Request, mut res: Response<Fresh>) {
        let mut s = String::new();
        req.read_to_string(&mut s).unwrap();
        let s = s.into_ascii_lowercase();
        info!("Got request: {}", s);

        let mut gm = self.gm.lock().unwrap();
        let s = gm.handle(s);
        let s = s.into_bytes();
        res.headers_mut().set(ContentLength(s.len() as u64));
        res.headers_mut().set(ContentType("text/acl".parse().unwrap()));
        res.headers_mut().set(AccessControlAllowOrigin::Any);
        res.headers_mut().set(AccessControlAllowHeaders(
            vec![UniCase("Content-Type".to_string())]));
        res.version = HttpVersion::Http10;
        let mut res = res.start().unwrap();
        res.write_all(&s).unwrap();
        res.end().unwrap();
    }
}
<commit_msg>Output server error messages<commit_after>//! A library for creating General Game Playing (GGP) players. This library is based off of
//! [GGP Base](https://github.com/ggp-org/ggp-base). While GGP Base allows the creation of
//! players backed by a propositional network or a logic prover, this library currently only
//! supports logic prover based players.
//!
//! # Example
//!
//! To create your own player, simply implement the `Player` trait for your struct and pass the
//! host/port you want the player to run at and the player itself to the `run` function.
//!
//! The following example implements a RandomPlayer, which plays random legal moves each turn.
//!
//! ```ignore
//! extern crate rand;
//! extern crate ggp_rs;
//!
//! use ggp_rs::{Player, Game, Move};
//! use std::net::Ipv4Addr;
//!
//! struct RandomPlayer;
//!
//! impl Player for RandomPlayer {
//!     fn get_name(&self) -> String {
//!         "RandomPlayer".to_string()
//!     }
//!
//!     fn select_move(&self, game: &Game) -> Move {
//!         let state = game.get_current_state();
//!         let role = game.get_role();
//!         let mut moves = game.get_legal_moves(state, role);
//!         let r = rand::random::<usize>() % moves.len();
//!         moves.swap_remove(r)
//!     }
//! }
//!
//! fn main() {
//!     ggp_rs::run((Ipv4Addr::new(0,0,0,0), 9147), RandomPlayer);
//! }
//! ```

#![feature(std_misc, plugin, collections)]
#![cfg_attr(test, feature(test))]
#![plugin(regex_macros)]

extern crate gdl_parser;
extern crate hyper;
extern crate regex;
extern crate unicase;

#[macro_use]
extern crate log;

mod game_manager;
mod gdl;
mod prover;

use unicase::UniCase;

use hyper::Server;
use hyper::error::HttpError::HttpIoError;
use hyper::server::{Request, Response, Handler};
use hyper::header::{ContentLength, ContentType, AccessControlAllowOrigin,
                    AccessControlAllowHeaders};
use hyper::net::Fresh;
use hyper::version::HttpVersion;

use std::net::ToSocketAddrs;
use std::io::{Read, Write};
use std::ascii::OwnedAsciiExt;
use std::sync::Mutex;

use game_manager::GameManager;

pub use game_manager::{Game, State};
pub use gdl::{Move, Role, Score};

/// A GGP player
pub trait Player {
    fn get_name(&self) -> String;

    fn meta_game(&self, _: &Game) {}

    fn select_move(&self, game: &Game) -> Move;

    fn stop(&self, _: &Game) {}
}

/// Starts a GGP player listening at `host`
pub fn run<T: ToSocketAddrs + 'static, P: Player + Sync + Send + 'static>(host: T, player: P) {
    let handler = GameHandler::new(player);
    match Server::http(handler).listen(host) {
        Err(HttpIoError(e)) => panic!("{}", e),
        Err(e) => panic!("{}", e),
        _ => ()
    }
}

struct GameHandler<P: Player + Send> {
    pub gm: Mutex<GameManager<P>>
}

impl<P: Player + Send> GameHandler<P> {
    pub fn new(player: P) -> GameHandler<P> {
        GameHandler { gm: Mutex::new(GameManager::new(player)) }
    }
}

impl<P: Player + Sync + Send> Handler for GameHandler<P> {
    fn handle(&self, mut req: Request, mut res: Response<Fresh>) {
        let mut s = String::new();
        req.read_to_string(&mut s).unwrap();
        let s = s.into_ascii_lowercase();
        info!("Got request: {}", s);

        let mut gm = self.gm.lock().unwrap();
        let s = gm.handle(s);
        let s = s.into_bytes();
        res.headers_mut().set(ContentLength(s.len() as u64));
        res.headers_mut().set(ContentType("text/acl".parse().unwrap()));
        res.headers_mut().set(AccessControlAllowOrigin::Any);
        res.headers_mut().set(AccessControlAllowHeaders(
            vec![UniCase("Content-Type".to_string())]));
        res.version = HttpVersion::Http10;
        let mut res = res.start().unwrap();
        res.write_all(&s).unwrap();
        res.end().unwrap();
    }
}
<|endoftext|>