<commit_before>use std::cast;
use std::mem::size_of;
use std::raw::Slice;
use std::num::Zero;
use std::rc::Rc;
use sync::Mutex;

mod buffer;
mod schema;

static LEAF_MARKER: u8 = 0b111111111;
static BRANCH_MARKER: u8 = 0b0;

/* simple type alias to simplify signatures */
type ConcurrentManager = Rc<Mutex<buffer::BufferManager>>;

struct BTree<'a, K> {
	segment: u64,
	manager: ConcurrentManager,
	tree: LazyNode,
	next_free_page: u64,
}

impl<'a, K: TotalOrd + Zero> BTree<'a, K> {
	fn new<'b>(segment_id: u64, manager: ConcurrentManager) -> BTree<'b, K> {
		let tree_base = buffer::join_segment(segment_id, 1);
		// TODO read tree and next free page from page 0
		BTree {
			segment: segment_id,
			manager: manager,
			tree: LazyNode::new(tree_base),
			next_free_page: 2,
		}
	}

	fn insert(&mut self, key: K, value: schema::TID) {
		let node = self.tree.load(self.manager.clone());
		// try insertion and see if the root was split
		let candidate = match node {
			Branch(mut n) => n.insert_value(self, key, value),
			Leaf(mut n) => n.insert_value(key, value),
		};
		// set new tree root if it was split
		match candidate {
			Some(new_node) => self.tree = new_node,
			None => (),
		}
	}

	fn next_page(&mut self) -> u64 {
		let n = self.next_free_page;
		self.next_free_page += 1;
		n
	}

	fn create_branch_page(&mut self) -> LazyNode {
		let next = self.next_page();
		let page_path = buffer::join_segment(self.segment, next);
		let mut manager = self.manager.lock();
		let pagelock = manager.fix_page(page_path).unwrap();
		let mut page = pagelock.write();
		let data = page.get_mut_data();
		data[0] = BRANCH_MARKER;
		LazyNode::new(page_path)
	}

	fn create_leaf_page(&mut self) -> LazyNode {
		let next = self.next_page();
		let page_path = buffer::join_segment(self.segment, next);
		let mut manager = self.manager.lock();
		let pagelock = manager.fix_page(page_path).unwrap();
		let mut page = pagelock.write();
		let data = page.get_mut_data();
		// marker to be a leaf page
		data[0] = LEAF_MARKER;
		LazyNode::new(page_path)

	}

	fn erase(&mut self, key: K) {
	}

	fn lookup(self, key: &K) -> Option<schema::TID> {
		let node = self.tree.load(self.manager.clone());
		match node {
			Branch(n) => n.lookup(self.manager.clone(), key),
			Leaf(n) => n.lookup(key),
		}
	}
}

/* a placeholder for the actual page */
struct LazyNode {
	page_id: u64,
}

impl LazyNode {
	fn new(page_id: u64) -> LazyNode {
		LazyNode {page_id: page_id}
	}

	/*
	 * As this is just a placeholder, return the actual node that his is
	 * representing
	 */
	fn load<'a, K: TotalOrd + Zero>(&self, manager: ConcurrentManager) -> Node<'a, K> {
		let pagelock = {
			let mut managerlock = manager.lock();
			managerlock.fix_page(self.page_id).unwrap()
		};

		let mut is_leaf = false;
		{
			let page = pagelock.read();
			let page_data = page.get_data();
			if page_data[0] == LEAF_MARKER {
				is_leaf = true;
			} else if page_data[0] == BRANCH_MARKER {
				is_leaf = false;
			} else {
				fail!("Invalid page type");
			}
		}
		if is_leaf {
			Leaf(LeafNode::new(manager, pagelock))
		} else {
			Branch(BranchNode::new(manager, pagelock))
		}
	}

}

/* a node might either be an inner node (branch node) or a leaf node (LeafNode) */
enum Node<'a, K> {
	Branch(BranchNode<'a, K>),
	Leaf(LeafNode<'a, K>),
}

/* leaf pages consist mainly of leaf entries which are (K, TID) pairs */
struct LeafEntry<K> {
	key: K,
	tid: schema::TID,
}

/* branch pages consist mainly of branch entries which are (K, page_id) pairs */
struct BranchEntry<K> {
	key: K,
	page_id: u64,
}

struct LeafNode<'a, K> {
	capacity: uint,
	entries: &'a mut [LeafEntry<K>],
	manager: ConcurrentManager,
	frame: buffer::ConcurrentFrame,
}

impl<'a, K: TotalOrd + Zero> LeafNode<'a, K> {
	fn new(manager: ConcurrentManager, frame: buffer::ConcurrentFrame) -> LeafNode<'a, K> {
		let mut r = {
			let framelock = frame.read();
			let page = framelock.get_data();
			// first byte is a Leaf/Branch marker
			let entry_size = size_of::<LeafEntry<K>>();
			let entry_num = (page.len() - size_of::<u8>()) / entry_size;
			let start_from = entry_size;

			let mut entries: &mut[LeafEntry<K>] = unsafe {
				cast::transmute(
					Slice::<LeafEntry<K>> {
						data: page.slice_from(start_from).as_ptr() as *() as *LeafEntry<K>,
						len: entry_num,
					}
				)
			};
			entries
		};

		/* find slots that are used */
		let mut capacity = r.len();
		for i in range(0, r.len()) {
			if !r[i].key.is_zero() && !r[i].tid.is_invalid() {
				capacity -= 1;
			}
		}

		println!("Creating leaf node");
		LeafNode {
			entries: r,
			capacity: capacity,
			manager: manager,
			frame: frame,
		}
	}

	fn insert_value(&mut self, key: K, tid: schema::TID) -> Option<LazyNode> {
		if self.capacity == 0 {
			// TODO split
			fail!("Not implemented");
			return None
		}

		// find place to insert
		let location = self.find_slot(&key);
		println!("Location found: {}", location);
		// free that spot
		self.shift_from(location);
		// and put it in
		self.entries[location].key = key;
		self.entries[location].tid = tid;

		// insertion went fine, done
		None
	}

	/* finds the location at which a key should be inserted */
	fn find_slot(&self, key: &K) -> uint {
		let mut found = 0;
		for i in range(0, self.entries.len()) {
			println!("Entry {:?}", self.entries[i]);
			if &self.entries[i].key > key {

				found = i - 1;
				break;
			}
		}
		found
	}

	/* moves all items from `index` one number back */
	fn shift_from(&mut self, index: uint) {
		// actually, this rotates right by 1 starting from index
		let last_elem = self.entries.len() - 1;
		for i in range(index, last_elem) {
			self.entries.swap(i, last_elem);
		}
	}

	fn lookup(self, key: &K) -> Option<schema::TID> {
		for i in range(0, self.entries.len()) {
			if &self.entries[i].key == key {
				return Some(self.entries[i].tid)
			}
		}
		None
	}
}

#[unsafe_destructor]
impl<'a, K> Drop for LeafNode<'a, K> {
	fn drop(&mut self) {
		let mut manager = self.manager.lock();
		println!("Ohai, unfixing page");
		// TODO: figure out if page was modified since loading
		manager.unfix_page(self.frame.clone(), true);
	}
}

struct BranchNode<'a, K> {
	capacity: uint,
	entries: &'a mut [BranchEntry<K>],
	manager: ConcurrentManager,
	frame: buffer::ConcurrentFrame,
}

impl<'a, K: TotalOrd + Zero> BranchNode<'a, K> {
	fn new(manager: ConcurrentManager, frame: buffer::ConcurrentFrame) -> BranchNode<'a, K> {
		let mut r = {
			let framelock = frame.read();
			let page = framelock.get_data();

			let entry_size = size_of::<BranchEntry<K>>();
			let entry_num = (page.len() - size_of::<u8>()) / entry_size;
			let start_from = entry_size;
			let mut r: &mut [BranchEntry<K>] = unsafe {
				cast::transmute(
					Slice::<BranchEntry<K>> {
						data: page.slice_from(start_from).as_ptr() as *() as *BranchEntry<K>,
						len: entry_num,
					}
				)
			};
			r
		};

		/* find slots that are used */
		let mut capacity = r.len();
		for i in range(0, r.len()) {
			if r[i].page_id != 0 {
				capacity -= 1;
			}
		}

		println!("BranchNode computed capacity: {}", capacity);

		BranchNode {
			entries: r,
			capacity: capacity,
			manager: manager,
			frame: frame,
		}
	}

	/* might return a new branch node if this one was split */
	fn insert_value(&mut self, tree: &mut BTree<K>, key: K, value: schema::TID) -> Option<LazyNode> {
		let mut place = 0;
		// locate the place where to insert
		for i in range(0, self.entries.len()) {
			if self.entries[i].page_id == 0 {
				continue
			}
			println!("Entry i {:?}, key {:?}", self.entries[i], self.entries[i].key);
			// TODO find place
		}
		let go_to_page = self.entries[place].page_id;
		if go_to_page == 0 {
			let lazy_node = tree.create_leaf_page();
			let new_node = lazy_node.load(tree.manager.clone());
			let split_candidate = match new_node {
				Leaf(mut n) => n.insert_value(key, value),
				Branch(_) => fail!("Did not create a leaf page"),
			};
			self.entries[place].page_id = lazy_node.page_id;
			return split_candidate
		}

		// currently not caring about splitting branch pages
		None
	}

	fn lookup(self, manager: ConcurrentManager, key: &K) -> Option<schema::TID> {
		// find the page to descend to
		let mut next_page = None;
		for i in range(0, self.entries.len()) {
			// skip all empty fields
			if self.entries[i].key.is_zero() && self.entries[i].page_id == 0 {
				continue
			}
			println!("Entry i {:?}, key {:?}", self.entries[i], self.entries[i].key);
			if &self.entries[i].key <= key {
				// found the branch into which to descend
				next_page = Some(self.entries[i].page_id);
				break;
			}
		}

		match next_page {
			// if there is no page to descend to, it can't be found
			None => None,
			Some(page_id) => {
				let ln = LazyNode::new(page_id);
				let node = ln.load(manager);
				match node {
					Branch(n) => n.lookup(self.manager.clone(), key),
					Leaf(n) => n.lookup(key),
				}
			}
		}
	}
}

#[unsafe_destructor]
impl<'a, K> Drop for BranchNode<'a, K> {
	fn drop(&mut self) {
		let mut manager = self.manager.lock();
		println!("Ohai, unfixing page");
		// TODO: figure out if page was modified since loading
		manager.unfix_page(self.frame.clone(), true);
	}
}

#[test]
fn simple_insert() {
	let p = Path::new(".");
	let manager = buffer::BufferManager::new(1024, p.clone());
	let mut bt = BTree::new(23, Rc::new(Mutex::new(manager)));
	let some_tid = schema::TID::new(0, 0);
	bt.insert(42, some_tid);
	let res = bt.lookup(&42).unwrap();
	assert_eq!(some_tid, res);
}

#[test]
fn lookup_nonexisting() {
	let p = Path::new(".");
	let manager = buffer::BufferManager::new(1024, p.clone());
	let bt = BTree::new(23, Rc::new(Mutex::new(manager)));
	let result = bt.lookup(&42);
	assert_eq!(result, None);
}

#[test]
fn create_leaf_page() {
	let p = Path::new(".");
	let mut manager = buffer::BufferManager::new(1024, p.clone());
	let pagelock = manager.fix_page(10).unwrap();
	let page = pagelock.read();
	//let lp: LeafNode<u64> = LeafNode::new(page.get_data(), Rc::new(Mutex::new(manager)), pagelock);
	//println!("lp len: {}", lp.entries.len());
	assert!(false);
}
<commit_msg>Added test and fixed lookup signature<commit_after>use std::cast;
use std::mem::size_of;
use std::raw::Slice;
use std::num::Zero;
use std::rc::Rc;
use sync::Mutex;

mod buffer;
mod schema;

static LEAF_MARKER: u8 = 0b111111111;
static BRANCH_MARKER: u8 = 0b0;

/* simple type alias to simplify signatures */
type ConcurrentManager = Rc<Mutex<buffer::BufferManager>>;

struct BTree<'a, K> {
	segment: u64,
	manager: ConcurrentManager,
	tree: LazyNode,
	next_free_page: u64,
}

impl<'a, K: TotalOrd + Zero> BTree<'a, K> {
	fn new<'b>(segment_id: u64, manager: ConcurrentManager) -> BTree<'b, K> {
		let tree_base = buffer::join_segment(segment_id, 1);
		// TODO read tree and next free page from page 0
		BTree {
			segment: segment_id,
			manager: manager,
			tree: LazyNode::new(tree_base),
			next_free_page: 2,
		}
	}

	fn insert(&mut self, key: K, value: schema::TID) {
		let node = self.tree.load(self.manager.clone());
		// try insertion and see if the root was split
		let candidate = match node {
			Branch(mut n) => n.insert_value(self, key, value),
			Leaf(mut n) => n.insert_value(key, value),
		};
		// set new tree root if it was split
		match candidate {
			Some(new_node) => self.tree = new_node,
			None => (),
		}
	}

	fn next_page(&mut self) -> u64 {
		let n = self.next_free_page;
		self.next_free_page += 1;
		n
	}

	fn create_branch_page(&mut self) -> LazyNode {
		let next = self.next_page();
		let page_path = buffer::join_segment(self.segment, next);
		let mut manager = self.manager.lock();
		let pagelock = manager.fix_page(page_path).unwrap();
		let mut page = pagelock.write();
		let data = page.get_mut_data();
		data[0] = BRANCH_MARKER;
		LazyNode::new(page_path)
	}

	fn create_leaf_page(&mut self) -> LazyNode {
		let next = self.next_page();
		let page_path = buffer::join_segment(self.segment, next);
		let mut manager = self.manager.lock();
		let pagelock = manager.fix_page(page_path).unwrap();
		let mut page = pagelock.write();
		let data = page.get_mut_data();
		// marker to be a leaf page
		data[0] = LEAF_MARKER;
		LazyNode::new(page_path)

	}

	fn erase(&mut self, key: K) {
	}

	fn lookup(&self, key: &K) -> Option<schema::TID> {
		let node = self.tree.load(self.manager.clone());
		match node {
			Branch(n) => n.lookup(self.manager.clone(), key),
			Leaf(n) => n.lookup(key),
		}
	}
}

/* a placeholder for the actual page */
struct LazyNode {
	page_id: u64,
}

impl LazyNode {
	fn new(page_id: u64) -> LazyNode {
		LazyNode {page_id: page_id}
	}

	/*
	 * As this is just a placeholder, return the actual node that his is
	 * representing
	 */
	fn load<'a, K: TotalOrd + Zero>(&self, manager: ConcurrentManager) -> Node<'a, K> {
		let pagelock = {
			let mut managerlock = manager.lock();
			managerlock.fix_page(self.page_id).unwrap()
		};

		let mut is_leaf = false;
		{
			let page = pagelock.read();
			let page_data = page.get_data();
			if page_data[0] == LEAF_MARKER {
				is_leaf = true;
			} else if page_data[0] == BRANCH_MARKER {
				is_leaf = false;
			} else {
				fail!("Invalid page type");
			}
		}
		if is_leaf {
			Leaf(LeafNode::new(manager, pagelock))
		} else {
			Branch(BranchNode::new(manager, pagelock))
		}
	}

}

/* a node might either be an inner node (branch node) or a leaf node (LeafNode) */
enum Node<'a, K> {
	Branch(BranchNode<'a, K>),
	Leaf(LeafNode<'a, K>),
}

/* leaf pages consist mainly of leaf entries which are (K, TID) pairs */
struct LeafEntry<K> {
	key: K,
	tid: schema::TID,
}

/* branch pages consist mainly of branch entries which are (K, page_id) pairs */
struct BranchEntry<K> {
	key: K,
	page_id: u64,
}

struct LeafNode<'a, K> {
	capacity: uint,
	entries: &'a mut [LeafEntry<K>],
	manager: ConcurrentManager,
	frame: buffer::ConcurrentFrame,
}

impl<'a, K: TotalOrd + Zero> LeafNode<'a, K> {
	fn new(manager: ConcurrentManager, frame: buffer::ConcurrentFrame) -> LeafNode<'a, K> {
		let mut r = {
			let framelock = frame.read();
			let page = framelock.get_data();
			// first byte is a Leaf/Branch marker
			let entry_size = size_of::<LeafEntry<K>>();
			let entry_num = (page.len() - size_of::<u8>()) / entry_size;
			let start_from = entry_size;

			let mut entries: &mut[LeafEntry<K>] = unsafe {
				cast::transmute(
					Slice::<LeafEntry<K>> {
						data: page.slice_from(start_from).as_ptr() as *() as *LeafEntry<K>,
						len: entry_num,
					}
				)
			};
			entries
		};

		/* find slots that are used */
		let mut capacity = r.len();
		for i in range(0, r.len()) {
			if !r[i].key.is_zero() && !r[i].tid.is_invalid() {
				capacity -= 1;
			}
		}

		println!("Creating leaf node");
		LeafNode {
			entries: r,
			capacity: capacity,
			manager: manager,
			frame: frame,
		}
	}

	fn insert_value(&mut self, key: K, tid: schema::TID) -> Option<LazyNode> {
		if self.capacity == 0 {
			// TODO split
			fail!("Not implemented");
			return None
		}

		// find place to insert
		let location = self.find_slot(&key);
		println!("Location found: {}", location);
		// free that spot
		self.shift_from(location);
		// and put it in
		self.entries[location].key = key;
		self.entries[location].tid = tid;

		// insertion went fine, done
		None
	}

	/* finds the location at which a key should be inserted */
	fn find_slot(&self, key: &K) -> uint {
		let mut found = 0;
		for i in range(0, self.entries.len()) {
			println!("Entry {:?}", self.entries[i]);
			if &self.entries[i].key > key {

				found = i - 1;
				break;
			}
		}
		found
	}

	/* moves all items from `index` one number back */
	fn shift_from(&mut self, index: uint) {
		// actually, this rotates right by 1 starting from index
		let last_elem = self.entries.len() - 1;
		for i in range(index, last_elem) {
			self.entries.swap(i, last_elem);
		}
	}

	fn lookup(self, key: &K) -> Option<schema::TID> {
		for i in range(0, self.entries.len()) {
			if &self.entries[i].key == key {
				return Some(self.entries[i].tid)
			}
		}
		None
	}
}

#[unsafe_destructor]
impl<'a, K> Drop for LeafNode<'a, K> {
	fn drop(&mut self) {
		let mut manager = self.manager.lock();
		println!("Ohai, unfixing page");
		// TODO: figure out if page was modified since loading
		manager.unfix_page(self.frame.clone(), true);
	}
}

struct BranchNode<'a, K> {
	capacity: uint,
	entries: &'a mut [BranchEntry<K>],
	manager: ConcurrentManager,
	frame: buffer::ConcurrentFrame,
}

impl<'a, K: TotalOrd + Zero> BranchNode<'a, K> {
	fn new(manager: ConcurrentManager, frame: buffer::ConcurrentFrame) -> BranchNode<'a, K> {
		let mut r = {
			let framelock = frame.read();
			let page = framelock.get_data();

			let entry_size = size_of::<BranchEntry<K>>();
			let entry_num = (page.len() - size_of::<u8>()) / entry_size;
			let start_from = entry_size;
			let mut r: &mut [BranchEntry<K>] = unsafe {
				cast::transmute(
					Slice::<BranchEntry<K>> {
						data: page.slice_from(start_from).as_ptr() as *() as *BranchEntry<K>,
						len: entry_num,
					}
				)
			};
			r
		};

		/* find slots that are used */
		let mut capacity = r.len();
		for i in range(0, r.len()) {
			if r[i].page_id != 0 {
				capacity -= 1;
			}
		}

		println!("BranchNode computed capacity: {}", capacity);

		BranchNode {
			entries: r,
			capacity: capacity,
			manager: manager,
			frame: frame,
		}
	}

	/* might return a new branch node if this one was split */
	fn insert_value(&mut self, tree: &mut BTree<K>, key: K, value: schema::TID) -> Option<LazyNode> {
		let mut place = 0;
		// locate the place where to insert
		for i in range(0, self.entries.len()) {
			if self.entries[i].page_id == 0 {
				continue
			}
			println!("Entry i {:?}, key {:?}", self.entries[i], self.entries[i].key);
			// TODO find place
		}
		let go_to_page = self.entries[place].page_id;
		if go_to_page == 0 {
			let lazy_node = tree.create_leaf_page();
			let new_node = lazy_node.load(tree.manager.clone());
			let split_candidate = match new_node {
				Leaf(mut n) => n.insert_value(key, value),
				Branch(_) => fail!("Did not create a leaf page"),
			};
			self.entries[place].page_id = lazy_node.page_id;
			return split_candidate
		}

		// currently not caring about splitting branch pages
		None
	}

	fn lookup(self, manager: ConcurrentManager, key: &K) -> Option<schema::TID> {
		// find the page to descend to
		let mut next_page = None;
		for i in range(0, self.entries.len()) {
			// skip all empty fields
			if self.entries[i].key.is_zero() && self.entries[i].page_id == 0 {
				continue
			}
			println!("Entry i {:?}, key {:?}", self.entries[i], self.entries[i].key);
			if &self.entries[i].key <= key {
				// found the branch into which to descend
				next_page = Some(self.entries[i].page_id);
				break;
			}
		}

		match next_page {
			// if there is no page to descend to, it can't be found
			None => None,
			Some(page_id) => {
				let ln = LazyNode::new(page_id);
				let node = ln.load(manager);
				match node {
					Branch(n) => n.lookup(self.manager.clone(), key),
					Leaf(n) => n.lookup(key),
				}
			}
		}
	}
}

#[unsafe_destructor]
impl<'a, K> Drop for BranchNode<'a, K> {
	fn drop(&mut self) {
		let mut manager = self.manager.lock();
		println!("Ohai, unfixing page");
		// TODO: figure out if page was modified since loading
		manager.unfix_page(self.frame.clone(), true);
	}
}

#[test]
fn simple_insert() {
	let p = Path::new(".");
	let manager = buffer::BufferManager::new(1024, p.clone());
	let mut bt = BTree::new(23, Rc::new(Mutex::new(manager)));
	let some_tid = schema::TID::new(0, 0);
	bt.insert(42, some_tid);
	let res = bt.lookup(&42).unwrap();
	assert_eq!(some_tid, res);
}

#[test]
fn split_insert() {
	let p = Path::new(".");
	let manager = buffer::BufferManager::new(1024, p.clone());
	let mut bt = BTree::new(23, Rc::new(Mutex::new(manager)));
	let some_tid = schema::TID::new(0, 0);
	for i in range(0, 300) {
		bt.insert(i, some_tid);
		let res = bt.lookup(&i).unwrap();
		assert_eq!(some_tid, res);
	}
	assert!(false);
}

#[test]
fn lookup_nonexisting() {
	let p = Path::new(".");
	let manager = buffer::BufferManager::new(1024, p.clone());
	let bt = BTree::new(23, Rc::new(Mutex::new(manager)));
	let result = bt.lookup(&42);
	assert_eq!(result, None);
}

#[test]
fn create_leaf_page() {
	let p = Path::new(".");
	let mut manager = buffer::BufferManager::new(1024, p.clone());
	let pagelock = manager.fix_page(10).unwrap();
	let page = pagelock.read();
	//let lp: LeafNode<u64> = LeafNode::new(page.get_data(), Rc::new(Mutex::new(manager)), pagelock);
	//println!("lp len: {}", lp.entries.len());
	assert!(false);
}
<|endoftext|>


<commit_before>
use utils::clampf32;
use std::default::Default;
use std::fmt::{Show, Formatter, FormatError};
use std::num::abs;
use std::rand::random;
use std::ascii::OwnedAsciiExt;
use serialize::hex::ToHex;

/// 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 = abs(1f32 - self.r());
        let g = abs(1f32 - self.g());
        let b = abs(1f32 - self.b());
        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())
    }
}
<commit_msg>Implement Decodable for Color. Closes #59.<commit_after>
use utils::clampf32;
use std::default::Default;
use std::fmt::{Show, Formatter, FormatError};
use std::num::abs;
use std::rand::random;
use std::ascii::OwnedAsciiExt;
use serialize::hex::ToHex;
use serialize::{Decodable, Decoder, DecoderHelpers};

/// 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 = abs(1f32 - self.r());
        let g = abs(1f32 - self.g());
        let b = abs(1f32 - self.b());
        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<E, D> 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 dec.err(format!(
                "Expected a 4 element vector when decoding Color.
                Found a vector of length {}.", vec.len()));
        }

        Ok(Color([vec[0], vec[1], vec[2], vec[3]]))
    }
}
<|endoftext|>


<commit_before>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::path::Path;
use store;
use AplibObject;
use AplibType;
use audit::{
    audit_get_str_value, audit_get_int_value, audit_get_bool_value,
    Report, SkipReason
};

#[derive(Debug, PartialEq, Clone)]
pub enum Subclass {
    Invalid,
    Implicit,
    Smart,
    User
}

impl Subclass {

    fn from(v: i64) -> Self {
        match v {
            0 => Subclass::Invalid,
            1 => Subclass::Implicit,
            2 => Subclass::Smart,
            3 => Subclass::User,
            _ => {
                println!("Unknown subclass value {}", v);
                Subclass::Invalid
            }
        }
    }

    fn from_option(o: Option<i64>) -> Option<Self> {
        if let Some(v) = o {
            Some(Self::from(v))
        } else {
            None
        }
    }

    pub fn to_int(v: &Self) -> i64 {
        match *v {
            Subclass::Invalid => 0,
            Subclass::Implicit => 1,
            Subclass::Smart => 2,
            Subclass::User => 3,
        }
    }
}

/// Album object.
#[derive(Debug)]
pub struct Album {
    /// uuid
    uuid: Option<String>,
    /// Folder uuid it represents.
    folder_uuid: Option<String>,
    model_id: Option<i64>,

    /// Subclass. See ``Subclass`` enum. (Normal, Smart)
    pub subclass: Option<Subclass>,
    /// ```Type```. Seems to always be 1
    pub album_type: Option<i64>,
    /// UUID of folder it is querying content of (smart only)
    pub query_folder_uuid: Option<String>,
    /// Version of db
    pub db_version: Option<i64>,
    /// Sort ascending
    pub sort_asc: Option<bool>,
    /// Date key for sort
    pub sort_key: Option<String>,
    /// Name of the album.
    pub name: Option<String>,
    /// Content list
    pub content: Option<Vec<String>>,
}

impl AplibObject for Album {
    fn from_path(plist_path: &Path,
                 mut auditor: Option<&mut Report>) -> Option<Album> {

        use plutils::*;

        let plist = parse_plist(plist_path);
        match plist {
            Plist::Dictionary(ref dict) => {
                if let Some(info_dict) = get_dict_value(dict, "InfoDictionary") {
                    let subclass =
                        Subclass::from_option(
                            audit_get_int_value(&info_dict,
                                                "albumSubclass", &mut auditor));
                    Some(Album {
                        uuid: audit_get_str_value(
                            &info_dict, "uuid", &mut auditor),
                        folder_uuid: audit_get_str_value(
                            &info_dict, "folderUuid", &mut auditor),
                        subclass: subclass.clone(),
                        album_type: audit_get_int_value(
                            &info_dict, "albumType", &mut auditor),
                        db_version: audit_get_int_value(
                            &info_dict, "version", &mut auditor),
                        model_id: audit_get_int_value(
                            &info_dict, "modelId", &mut auditor),
                        sort_asc: audit_get_bool_value(
                            &info_dict, "sortAscending", &mut auditor),
                        sort_key: audit_get_str_value(
                            &info_dict, "sortKeyPath", &mut auditor),
                        name: audit_get_str_value(
                            &info_dict, "name", &mut auditor),
                        query_folder_uuid: audit_get_str_value(
                            &info_dict, "queryFolderUuid", &mut auditor),
                        content: {
                            if let Some(array) = get_array_value(&dict, "versionUuids") {
                                if subclass == Some(Subclass::User) {
                                    let content: Vec<String>;
                                    content = array.iter().filter_map(
                                        |elem|
                                        match *elem {
                                            Plist::String(ref s) =>
                                                Some(s.to_owned()),
                                            _ =>
                                                None
                                        }
                                    ).collect();
                                    if let Some(ref mut report) = auditor {
                                        report.parsed("versionUuids");
                                    }
                                    Some(content)
                                } else {
                                    if let Some(ref mut report) = auditor {
                                        report.skip("versionUuids", SkipReason::InvalidData);
                                    }
                                    None
                                }
                            } else {
                                None
                            }
                        },
                    })
                } else {
                    None
                }
            },
            _ =>
                None
        }
    }
    fn obj_type(&self) -> AplibType {
        AplibType::Album
    }
    fn uuid(&self) -> &Option<String> {
        &self.uuid
    }
    fn parent(&self) -> &Option<String> {
        &self.folder_uuid
    }
    fn model_id(&self) -> i64 {
        self.model_id.unwrap_or(0)
    }
    fn is_valid(&self) -> bool {
        self.uuid.is_some()
    }
    fn wrap(obj: Album) -> store::Wrapper {
        store::Wrapper::Album(Box::new(obj))
    }
}

impl Album {
}

#[cfg(test)]
#[test]
fn test_album_parse() {
    use testutils;

    let album = Album::from_path(
        testutils::get_test_file_path("gOnttfpzQoOxcwLpFS9DQg.apalbum")
            .as_path(), None);
    assert!(album.is_some());
    let album = album.unwrap();

    assert_eq!(album.uuid.as_ref().unwrap(), "gOnttfpzQoOxcwLpFS9DQg");
    assert_eq!(album.folder_uuid.as_ref().unwrap(), "TopLevelAlbums");
    assert_eq!(album.model_id.unwrap(), 601);
    assert_eq!(*album.subclass.as_ref().unwrap(), Subclass::Implicit);
    assert_eq!(album.album_type.unwrap(), 1);
    assert!(album.query_folder_uuid.is_none());
    assert_eq!(album.db_version.unwrap(), 110);
    assert_eq!(album.sort_asc.unwrap(), true);
    assert_eq!(album.sort_key.as_ref().unwrap(), "exifProperties.ImageDate");
    assert!(album.name.is_none());

//    let report = album.audit();
    // XXX fix when have actual audit.
//    println!("report {:?}", report);
}

#[cfg(test)]
#[test]
fn test_album_content_parse() {
    use testutils;

    let album = Album::from_path(
        testutils::get_test_file_path("x6yNun58SB2sImfCarTJHA.apalbum")
            .as_path(), None);
    assert!(album.is_some());
    let album = album.unwrap();

    assert_eq!(album.uuid.as_ref().unwrap(), "x6yNun58SB2sImfCarTJHA");
    assert_eq!(album.folder_uuid.as_ref().unwrap(), "TopLevelAlbums");
    assert_eq!(album.model_id.unwrap(), 181);
    assert_eq!(*album.subclass.as_ref().unwrap(), Subclass::User);
    assert_eq!(album.album_type.unwrap(), 1);
    assert!(album.query_folder_uuid.is_none());
    assert_eq!(album.db_version.unwrap(), 110);
    assert_eq!(album.sort_asc.unwrap(), true);
    assert_eq!(album.sort_key.as_ref().unwrap(), "exifProperties.ImageDate");
    assert_eq!(album.name.as_ref().unwrap(), "Flickr");
    assert!(album.content.is_some());
    let content = &album.content.as_ref().unwrap();
    assert_eq!(content.len(), 1);
    assert_eq!(content[0], "BF6nuoBnTumzoXyexdmXlw");

//    let report = album.audit();
    // XXX fix when have actual audit.
//    println!("report {:?}", report);
}
<commit_msg>albums: refactor parsing. Properly report.<commit_after>/*
  This Source Code Form is subject to the terms of the Mozilla Public
  License, v. 2.0. If a copy of the MPL was not distributed with this
  file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

use std::collections::BTreeMap;
use std::path::Path;
use store;
use AplibObject;
use AplibType;
use audit::{
    audit_get_str_value, audit_get_int_value, audit_get_bool_value,
    Report, SkipReason
};
use plutils::{get_array_value, Plist};

#[derive(Debug, PartialEq, Clone)]
pub enum Subclass {
    Invalid,
    Implicit,
    Smart,
    User
}

impl Subclass {

    fn from(v: i64) -> Self {
        match v {
            0 => Subclass::Invalid,
            1 => Subclass::Implicit,
            2 => Subclass::Smart,
            3 => Subclass::User,
            _ => {
                println!("Unknown subclass value {}", v);
                Subclass::Invalid
            }
        }
    }

    fn from_option(o: Option<i64>) -> Option<Self> {
        if let Some(v) = o {
            Some(Self::from(v))
        } else {
            None
        }
    }

    pub fn to_int(v: &Self) -> i64 {
        match *v {
            Subclass::Invalid => 0,
            Subclass::Implicit => 1,
            Subclass::Smart => 2,
            Subclass::User => 3,
        }
    }
}

/// Album object.
#[derive(Debug)]
pub struct Album {
    /// uuid
    uuid: Option<String>,
    /// Folder uuid it represents.
    folder_uuid: Option<String>,
    model_id: Option<i64>,

    /// Subclass. See ``Subclass`` enum. (Normal, Smart)
    pub subclass: Option<Subclass>,
    /// ```Type```. Seems to always be 1
    pub album_type: Option<i64>,
    /// UUID of folder it is querying content of (smart only)
    pub query_folder_uuid: Option<String>,
    /// Version of db
    pub db_version: Option<i64>,
    /// Sort ascending
    pub sort_asc: Option<bool>,
    /// Date key for sort
    pub sort_key: Option<String>,
    /// Name of the album.
    pub name: Option<String>,
    /// Content list
    pub content: Option<Vec<String>>,
}

impl AplibObject for Album {
    fn from_path(plist_path: &Path,
                 mut auditor: Option<&mut Report>) -> Option<Album> {

        use plutils::*;

        let plist = parse_plist(plist_path);
        match plist {
            Plist::Dictionary(ref dict) => {
                if let Some(info_dict) = get_dict_value(dict, "InfoDictionary") {
                    let subclass =
                        Subclass::from_option(
                            audit_get_int_value(&info_dict,
                                                "albumSubclass", &mut auditor));
                    let result = Some(Album {
                        uuid: audit_get_str_value(
                            &info_dict, "uuid", &mut auditor),
                        folder_uuid: audit_get_str_value(
                            &info_dict, "folderUuid", &mut auditor),
                        subclass: subclass.clone(),
                        album_type: audit_get_int_value(
                            &info_dict, "albumType", &mut auditor),
                        db_version: audit_get_int_value(
                            &info_dict, "version", &mut auditor),
                        model_id: audit_get_int_value(
                            &info_dict, "modelId", &mut auditor),
                        sort_asc: audit_get_bool_value(
                            &info_dict, "sortAscending", &mut auditor),
                        sort_key: audit_get_str_value(
                            &info_dict, "sortKeyPath", &mut auditor),
                        name: audit_get_str_value(
                            &info_dict, "name", &mut auditor),
                        query_folder_uuid: audit_get_str_value(
                            &info_dict, "queryFolderUuid", &mut auditor),
                        content: Album::content_from(
                            &dict, &subclass, &mut auditor),
                    });
                    if auditor.is_some() {
                        let ref mut auditor = auditor.unwrap();
                        auditor.audit_ignored(&info_dict);
                    }
                    result
                } else {
                    None
                }
            },
            _ =>
                None
        }
    }
    fn obj_type(&self) -> AplibType {
        AplibType::Album
    }
    fn uuid(&self) -> &Option<String> {
        &self.uuid
    }
    fn parent(&self) -> &Option<String> {
        &self.folder_uuid
    }
    fn model_id(&self) -> i64 {
        self.model_id.unwrap_or(0)
    }
    fn is_valid(&self) -> bool {
        self.uuid.is_some()
    }
    fn wrap(obj: Album) -> store::Wrapper {
        store::Wrapper::Album(Box::new(obj))
    }
}

impl Album {
    fn content_from(dict: &BTreeMap<String, Plist>,
                    subclass: &Option<Subclass>,
                    auditor: &mut Option<&mut Report>) -> Option<Vec<String>> {
        if let Some(array) = get_array_value(&dict, "versionUuids") {
            if *subclass == Some(Subclass::User) {
                let content: Vec<String>;
                content = array.iter().filter_map(
                    |elem|
                    match *elem {
                        Plist::String(ref s) =>
                            Some(s.to_owned()),
                        _ =>
                            None
                    }
                ).collect();
                if let Some(ref mut report) = *auditor {
                    report.parsed("versionUuids");
                }
                Some(content)
            } else {
                if let Some(ref mut report) = *auditor {
                    report.skip("versionUuids", SkipReason::InvalidData);
                }
                None
            }
        } else {
            None
        }
    }
}

#[cfg(test)]
#[test]
fn test_album_parse() {
    use testutils;

    let album = Album::from_path(
        testutils::get_test_file_path("gOnttfpzQoOxcwLpFS9DQg.apalbum")
            .as_path(), None);
    assert!(album.is_some());
    let album = album.unwrap();

    assert_eq!(album.uuid.as_ref().unwrap(), "gOnttfpzQoOxcwLpFS9DQg");
    assert_eq!(album.folder_uuid.as_ref().unwrap(), "TopLevelAlbums");
    assert_eq!(album.model_id.unwrap(), 601);
    assert_eq!(*album.subclass.as_ref().unwrap(), Subclass::Implicit);
    assert_eq!(album.album_type.unwrap(), 1);
    assert!(album.query_folder_uuid.is_none());
    assert_eq!(album.db_version.unwrap(), 110);
    assert_eq!(album.sort_asc.unwrap(), true);
    assert_eq!(album.sort_key.as_ref().unwrap(), "exifProperties.ImageDate");
    assert!(album.name.is_none());

//    let report = album.audit();
    // XXX fix when have actual audit.
//    println!("report {:?}", report);
}

#[cfg(test)]
#[test]
fn test_album_content_parse() {
    use testutils;

    let album = Album::from_path(
        testutils::get_test_file_path("x6yNun58SB2sImfCarTJHA.apalbum")
            .as_path(), None);
    assert!(album.is_some());
    let album = album.unwrap();

    assert_eq!(album.uuid.as_ref().unwrap(), "x6yNun58SB2sImfCarTJHA");
    assert_eq!(album.folder_uuid.as_ref().unwrap(), "TopLevelAlbums");
    assert_eq!(album.model_id.unwrap(), 181);
    assert_eq!(*album.subclass.as_ref().unwrap(), Subclass::User);
    assert_eq!(album.album_type.unwrap(), 1);
    assert!(album.query_folder_uuid.is_none());
    assert_eq!(album.db_version.unwrap(), 110);
    assert_eq!(album.sort_asc.unwrap(), true);
    assert_eq!(album.sort_key.as_ref().unwrap(), "exifProperties.ImageDate");
    assert_eq!(album.name.as_ref().unwrap(), "Flickr");
    assert!(album.content.is_some());
    let content = &album.content.as_ref().unwrap();
    assert_eq!(content.len(), 1);
    assert_eq!(content[0], "BF6nuoBnTumzoXyexdmXlw");

//    let report = album.audit();
    // XXX fix when have actual audit.
//    println!("report {:?}", report);
}
<|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 {
    device: rodio::Device,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));

        let e = rodio::default_output_device().ok_or(error)?;
        Ok(AudioContext { device: 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 {
    /// Create a new SoundData from the file at the given path.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let file = &mut context.filesystem.open(path)?;
        SoundData::from_read(file)
    }

    /// 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,
    repeat: bool,
}

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 = SoundData::new(context, path)?;
        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.device);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            sink,
            data: cursor,
            repeat: false,
        })
    }

    /// 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
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        if self.repeat {
            let repeating = decoder.repeat_infinite();
            self.sink.append(repeating);
        } else {
            self.sink.append(decoder);
        }
        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

    /// 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()
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    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)
    }
}
<commit_msg>Use a better name for local variable<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 {
    device: rodio::Device,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let error = GameError::AudioError(String::from(
            "Could not initialize sound system (for \
             some reason)",
        ));

        let device = rodio::default_output_device().ok_or(error)?;
        Ok(AudioContext { device: device })
    }
}

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 {
    /// Create a new SoundData from the file at the given path.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let file = &mut context.filesystem.open(path)?;
        SoundData::from_read(file)
    }

    /// 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,
    repeat: bool,
}

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 = SoundData::new(context, path)?;
        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.device);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            sink,
            data: cursor,
            repeat: false,
        })
    }

    /// 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
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        if self.repeat {
            let repeating = decoder.repeat_infinite();
            self.sink.append(repeating);
        } else {
            self.sink.append(decoder);
        }
        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

    /// 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()
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    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)
    }
}
<|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::mem;
use std::path;

use std::sync::Arc;

use mint;
use rodio;

use context::Context;
use filesystem;
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 {
    device: rodio::Device,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let device = rodio::default_output_device().ok_or_else(|| {
            GameError::AudioError(String::from(
                "Could not initialize sound system (for some reason)",
            ))
        })?;
        Ok(AudioContext { device: device })
    }
}

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 {
    /// Create a new SoundData from the file at the given path.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let file = &mut filesystem::open(context, path)?;
        SoundData::from_read(file)
    }

    /// 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();
        let _ = 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,
    repeat: bool,
}

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 = SoundData::new(context, path)?;
        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.device);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            sink,
            data: cursor,
            repeat: false,
        })
    }

    /// Plays the Source; defaults to `play_restart`
    #[inline(always)]
    pub fn play(&mut self) -> GameResult {
        self.play_restart()
    }

    /// Plays the Source; restarts the sound if currently playing
    pub fn play_restart(&mut self) -> GameResult {
        self.stop();
        self.play_later()
    }

    /// Plays the Source; waits until done if the sound is currently playing
    pub fn play_later(&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
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        if self.repeat {
            let repeating = decoder.repeat_infinite();
            self.sink.append(repeating);
        } else {
            self.sink.append(decoder);
        }
        Ok(())
    }

    /// Play source "in the background"; cannot be stopped
    pub fn play_detached(&mut self) -> GameResult {
        self.stop();
        self.play_later()?;

        let device = rodio::default_output_device().unwrap();
        let new_sink = rodio::Sink::new(&device);
        let old_sink = mem::replace(&mut self.sink, new_sink);
        old_sink.detach();

        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

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

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

    /// Stops playback
    pub fn stop(&mut self) {
        // Sinks cannot be reused after calling `.stop()`. See
        // https://github.com/tomaka/rodio/issues/171 for information.
        // To stop the current sound we have to drop the old sink and
        // create a new one in its place.
        // This is most ugly because in order to create a new sink
        // we need a `device`. However, we can only get the default
        // device without having access to a context. Currently that's
        // fine because the `AudioContext` uses the default device too,
        // but it may cause problems in the future if devices become
        // customizable.
        let device = rodio::default_output_device().unwrap();
        self.sink = rodio::Sink::new(&device);
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    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)
    }
}

/// A source of audio data located in space relative to a listener's ears.
/// Will stop playing when dropped.
pub struct SpatialSource {
    data: io::Cursor<SoundData>,
    sink: rodio::SpatialSink,
    repeat: bool,

    left_ear: mint::Point3<f32>,
    right_ear: mint::Point3<f32>,
    emitter_position: mint::Point3<f32>,
}

impl SpatialSource {
    /// 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 = SoundData::new(context, path)?;
        SpatialSource::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::SpatialSink::new(
            &context.audio_context.device,
            [0.0, 0.0, 0.0],
            [-1.0, 0.0, 0.0],
            [1.0, 0.0, 0.0],
        );

        let cursor = io::Cursor::new(data);

        Ok(SpatialSource {
            sink,
            data: cursor,
            repeat: false,
            left_ear: [-1.0, 0.0, 0.0].into(),
            right_ear: [1.0, 0.0, 0.0].into(),
            emitter_position: [0.0, 0.0, 0.0].into(),
        })
    }

    /// Plays the Source; defaults to `play_restart`
    #[inline(always)]
    pub fn play(&mut self) -> GameResult {
        self.play_restart()
    }

    /// Plays the Source; restarts the sound if currently playing
    pub fn play_restart(&mut self) -> GameResult {
        self.stop();
        self.play_later()
    }

    /// Plays the Source; waits until done if the sound is currently playing
    pub fn play_later(&self) -> GameResult {
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;
        if self.repeat {
            let repeating = decoder.repeat_infinite();
            self.sink.append(repeating);
        } else {
            self.sink.append(decoder);
        }
        Ok(())
    }

    /// Play source "in the background"; cannot be stopped
    pub fn play_detached(&mut self) -> GameResult {
        self.stop();
        self.play_later()?;

        let device = rodio::default_output_device().unwrap();
        let new_sink = rodio::SpatialSink::new(
            &device,
            self.emitter_position.into(),
            self.left_ear.into(),
            self.right_ear.into(),
        );
        let old_sink = mem::replace(&mut self.sink, new_sink);
        old_sink.detach();

        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

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

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

    /// Stops playback
    pub fn stop(&mut self) {
        // `rodio::SpatialSink` does not have a `.stop()` method at
        // the moment. To stop the current sound we drop the old
        // sink and create a new one in its place.
        // This is most ugly because in order to create a new sink
        // we need a `device`. However, we can only get the default
        // device without having access to a context. Currently that's
        // fine because the `AudioContext` uses the default device too,
        // but it may cause problems in the future if devices become
        // customizable.
        let device = rodio::default_output_device().unwrap();
        self.sink = rodio::SpatialSink::new(
            &device,
            self.emitter_position.into(),
            self.left_ear.into(),
            self.right_ear.into(),
        );
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    pub fn playing(&self) -> bool {
        !self.paused() && !self.stopped()
    }

    /// Set location of the sound
    pub fn set_position<P>(&mut self, pos: P)
    where
        P: Into<mint::Point3<f32>>,
    {
        self.emitter_position = pos.into();
        self.sink.set_emitter_position(self.emitter_position.into());
    }

    /// Set locations of the listener's ears
    pub fn set_ears<P>(&mut self, left: P, right: P)
    where
        P: Into<mint::Point3<f32>>,
    {
        self.left_ear = left.into();
        self.right_ear = right.into();
        self.sink.set_left_ear_position(self.left_ear.into());
        self.sink.set_right_ear_position(self.right_ear.into());
    }
}

impl fmt::Debug for SpatialSource {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Spatial audio source: {:p}>", self)
    }
}
<commit_msg>Add fade-in<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::mem;
use std::path;
use std::time;

use std::sync::Arc;

use mint;
use rodio;

use context::Context;
use filesystem;
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 {
    device: rodio::Device,
}

impl AudioContext {
    /// Create new AudioContext.
    pub fn new() -> GameResult<AudioContext> {
        let device = rodio::default_output_device().ok_or_else(|| {
            GameError::AudioError(String::from(
                "Could not initialize sound system (for some reason)",
            ))
        })?;
        Ok(AudioContext { device: device })
    }
}

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 {
    /// Create a new SoundData from the file at the given path.
    pub fn new<P: AsRef<path::Path>>(context: &mut Context, path: P) -> GameResult<Self> {
        let path = path.as_ref();
        let file = &mut filesystem::open(context, path)?;
        SoundData::from_read(file)
    }

    /// 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();
        let _ = 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,
    repeat: bool,
    fade_in: time::Duration,
}

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 = SoundData::new(context, path)?;
        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.device);
        let cursor = io::Cursor::new(data);
        Ok(Source {
            sink,
            data: cursor,
            repeat: false,
            fade_in: time::Duration::from_millis(0),
        })
    }

    /// Plays the Source; defaults to `play_restart`
    #[inline(always)]
    pub fn play(&mut self) -> GameResult {
        self.play_restart()
    }

    /// Plays the Source; restarts the sound if currently playing
    pub fn play_restart(&mut self) -> GameResult {
        self.stop();
        self.play_later()
    }

    /// Plays the Source; waits until done if the sound is currently playing
    pub fn play_later(&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
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;

        match (self.repeat, self.fade_in) {
            (true, dur) if dur == time::Duration::from_secs(0) => {
                self.sink.append(decoder.repeat_infinite())
            }
            (false, dur) if dur == time::Duration::from_secs(0) => self.sink.append(decoder),
            (true, dur) => self.sink.append(decoder.repeat_infinite().fade_in(dur)),
            (false, dur) => self.sink.append(decoder.fade_in(dur)),
        }

        Ok(())
    }

    /// Play source "in the background"; cannot be stopped
    pub fn play_detached(&mut self) -> GameResult {
        self.stop();
        self.play_later()?;

        let device = rodio::default_output_device().unwrap();
        let new_sink = rodio::Sink::new(&device);
        let old_sink = mem::replace(&mut self.sink, new_sink);
        old_sink.detach();

        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Sets the fade-in time of the source
    pub fn set_fade_in(&mut self, dur: time::Duration) {
        self.fade_in = dur;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

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

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

    /// Stops playback
    pub fn stop(&mut self) {
        // Sinks cannot be reused after calling `.stop()`. See
        // https://github.com/tomaka/rodio/issues/171 for information.
        // To stop the current sound we have to drop the old sink and
        // create a new one in its place.
        // This is most ugly because in order to create a new sink
        // we need a `device`. However, we can only get the default
        // device without having access to a context. Currently that's
        // fine because the `AudioContext` uses the default device too,
        // but it may cause problems in the future if devices become
        // customizable.
        let device = rodio::default_output_device().unwrap();
        self.sink = rodio::Sink::new(&device);
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    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)
    }
}

/// A source of audio data located in space relative to a listener's ears.
/// Will stop playing when dropped.
pub struct SpatialSource {
    data: io::Cursor<SoundData>,
    sink: rodio::SpatialSink,
    repeat: bool,
    fade_in: time::Duration,

    left_ear: mint::Point3<f32>,
    right_ear: mint::Point3<f32>,
    emitter_position: mint::Point3<f32>,
}

impl SpatialSource {
    /// 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 = SoundData::new(context, path)?;
        SpatialSource::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::SpatialSink::new(
            &context.audio_context.device,
            [0.0, 0.0, 0.0],
            [-1.0, 0.0, 0.0],
            [1.0, 0.0, 0.0],
        );

        let cursor = io::Cursor::new(data);

        Ok(SpatialSource {
            sink,
            data: cursor,
            repeat: false,
            fade_in: time::Duration::from_millis(0),
            left_ear: [-1.0, 0.0, 0.0].into(),
            right_ear: [1.0, 0.0, 0.0].into(),
            emitter_position: [0.0, 0.0, 0.0].into(),
        })
    }

    /// Plays the Source; defaults to `play_restart`
    #[inline(always)]
    pub fn play(&mut self) -> GameResult {
        self.play_restart()
    }

    /// Plays the Source; restarts the sound if currently playing
    pub fn play_restart(&mut self) -> GameResult {
        self.stop();
        self.play_later()
    }

    /// Plays the Source; waits until done if the sound is currently playing
    pub fn play_later(&self) -> GameResult {
        use rodio::Source;
        let cursor = self.data.clone();
        let decoder = rodio::Decoder::new(cursor)?;

        match (self.repeat, self.fade_in) {
            (true, dur) if dur == time::Duration::from_secs(0) => {
                self.sink.append(decoder.repeat_infinite())
            }
            (false, dur) if dur == time::Duration::from_secs(0) => self.sink.append(decoder),
            (true, dur) => self.sink.append(decoder.repeat_infinite().fade_in(dur)),
            (false, dur) => self.sink.append(decoder.fade_in(dur)),
        }

        Ok(())
    }

    /// Play source "in the background"; cannot be stopped
    pub fn play_detached(&mut self) -> GameResult {
        self.stop();
        self.play_later()?;

        let device = rodio::default_output_device().unwrap();
        let new_sink = rodio::SpatialSink::new(
            &device,
            self.emitter_position.into(),
            self.left_ear.into(),
            self.right_ear.into(),
        );
        let old_sink = mem::replace(&mut self.sink, new_sink);
        old_sink.detach();

        Ok(())
    }

    /// Sets the source to repeat playback infinitely on next `play()`
    pub fn set_repeat(&mut self, repeat: bool) {
        self.repeat = repeat;
    }

    /// Sets the fade-in time of the source
    pub fn set_fade_in(&mut self, dur: time::Duration) {
        self.fade_in = dur;
    }

    /// Gets whether or not the source is set to repeat.
    pub fn repeat(&self) -> bool {
        self.repeat
    }

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

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

    /// Stops playback
    pub fn stop(&mut self) {
        // `rodio::SpatialSink` does not have a `.stop()` method at
        // the moment. To stop the current sound we drop the old
        // sink and create a new one in its place.
        // This is most ugly because in order to create a new sink
        // we need a `device`. However, we can only get the default
        // device without having access to a context. Currently that's
        // fine because the `AudioContext` uses the default device too,
        // but it may cause problems in the future if devices become
        // customizable.
        let device = rodio::default_output_device().unwrap();
        self.sink = rodio::SpatialSink::new(
            &device,
            self.emitter_position.into(),
            self.left_ear.into(),
            self.right_ear.into(),
        );
    }

    /// Returns whether or not the source is stopped
    /// -- that is, has no more data to play.
    pub fn stopped(&self) -> bool {
        self.sink.empty()
    }

    /// 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 (ie, not paused
    /// and not stopped)
    pub fn playing(&self) -> bool {
        !self.paused() && !self.stopped()
    }

    /// Set location of the sound
    pub fn set_position<P>(&mut self, pos: P)
    where
        P: Into<mint::Point3<f32>>,
    {
        self.emitter_position = pos.into();
        self.sink.set_emitter_position(self.emitter_position.into());
    }

    /// Set locations of the listener's ears
    pub fn set_ears<P>(&mut self, left: P, right: P)
    where
        P: Into<mint::Point3<f32>>,
    {
        self.left_ear = left.into();
        self.right_ear = right.into();
        self.sink.set_left_ear_position(self.left_ear.into());
        self.sink.set_right_ear_position(self.right_ear.into());
    }
}

impl fmt::Debug for SpatialSource {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "<Spatial audio source: {:p}>", self)
    }
}
<|endoftext|>


<commit_before>use ::{Position, Color, Grid, Axes, Directions, Alignment};
use ::{get_alignments, get_free_threes, get_captures};

#[derive(Debug, Clone)]
struct StonesStats {
    rest_stones: usize,
    taken_stones: usize
}

#[derive(Debug, Clone)]
pub struct Board {
    grid: Grid,
    to_take_stones: usize, // TODO remove this ?
    black_stones: StonesStats,
    white_stones: StonesStats,
}

#[derive(Debug)]
pub enum PlaceError {
    TileNotEmpty(Position),
    DoubleFreeThrees(Axes<bool>),
}

#[derive(Debug)]
pub enum VictoryCondition {
    CapturedStones(usize, Directions<bool>), // TODO clear this ?
    FiveStonesAligned(Axes<Alignment>),
}

#[derive(Debug)]
pub enum PlaceInfo {
    Nothing,
    Captures(Directions<bool>),
    FiveStonesAligned, // TODO add capturable stones
    Victory(VictoryCondition)
}

/// The type returned by the board when placing a stone
pub type PlaceResult = Result<PlaceInfo, PlaceError>;

impl Board {
    pub fn new() -> Board {
        Board {
            grid: [[None; ::GRID_LEN]; ::GRID_LEN],
            to_take_stones: 10,
            black_stones: StonesStats { rest_stones: 20, taken_stones: 0 },
            white_stones: StonesStats { rest_stones: 20, taken_stones: 0 }
        }
    }

    /// create a board with a limited number of stones for players
    pub fn with_stone_limit(limit: usize) -> Board { // TODO remove this ?
        Board {
            grid: [[None; ::GRID_LEN]; ::GRID_LEN],
            to_take_stones: 10,
            black_stones: StonesStats { rest_stones: limit, taken_stones: 0 },
            white_stones: StonesStats { rest_stones: limit, taken_stones: 0 }
        }
    }

    /// Simply puts a stone on the board
    pub fn raw_place_stone(&mut self, (x, y): Position, color: Color) {
        self.grid[x][y] = Some(color)
    }

    /// Simply removes a stone from the board
    pub fn raw_remove_stone(&mut self, (x, y): Position) {
        self.grid[x][y] = None;
    }

    fn increase_captures(&mut self, color: Color, nb_captures: usize) -> usize {
        match color {
            Color::Black => {
                self.black_stones.taken_stones += nb_captures;
                self.white_stones.taken_stones
            },
            Color::White => {
                self.white_stones.taken_stones += nb_captures;
                self.white_stones.taken_stones
            },
        }
    }

    fn remove_captured_stones(&mut self, _captures: &Directions<bool>) {
        //
    }

    /// Try placing a stone on board, respecting rules,
    /// Return an error if a stone is already present...
    pub fn try_place_stone(&mut self, color: Color, pos: Position) -> PlaceResult {
        let (x, y) = pos;
        if self.grid[x][y].is_some() {
            return Err(PlaceError::TileNotEmpty(pos))
        }

        let alignements = get_alignments(&self.grid, pos, color);
        let free_threes = get_free_threes(&self.grid, pos, color, &alignements);
        let captures = get_captures(&self.grid, pos, color);

        if free_threes.count(|x| *x == true) == 2 {
            Err(PlaceError::DoubleFreeThrees(free_threes))
        }
        else {
            if alignements.any(|x| x.len() >= 5) {

                // Check if an alignement of five stone is not blocked
                // by captures, allow victory in this case

                // if can_be_took(alignement_of_five_or_more) {
                //     Ok(PlaceInfo::FiveStonesAligned)
                // }
                // else {

                self.raw_place_stone(pos, color);
                Ok(PlaceInfo::Victory(VictoryCondition::FiveStonesAligned(alignements)))
            }
            else {
                let nb_captures = captures.count(|x| *x == true);
                let taken_stones = self.increase_captures(color, nb_captures);
                if taken_stones >= self.to_take_stones {
                    self.raw_place_stone(pos, color);
                    Ok(PlaceInfo::Victory(VictoryCondition::CapturedStones(taken_stones, captures)))
                }
                else {
                    Ok(PlaceInfo::Nothing)
                }
            }
        }
    }
}
<commit_msg>board_rules: Add update_captures function<commit_after>use ::{Position, Color, Grid, Axes, Directions, Alignment};
use ::{get_alignments, get_free_threes, get_captures};
use ::directions::*;

#[derive(Debug, Clone)]
struct StonesStats {
    rest_stones: usize,
    stones_taken: usize
}

#[derive(Debug, Clone)]
pub struct Board {
    grid: Grid,
    to_take_stones: usize, // TODO remove this ?
    black_stones: StonesStats,
    white_stones: StonesStats,
}

#[derive(Debug)]
pub enum PlaceError {
    TileNotEmpty(Position),
    DoubleFreeThrees(Axes<bool>),
}

#[derive(Debug)]
pub enum VictoryCondition {
    CapturedStones(usize, Directions<bool>), // TODO clear this ?
    FiveStonesAligned(Axes<Alignment>),
}

#[derive(Debug)]
pub enum PlaceInfo {
    Nothing,
    Captures(Directions<bool>),
    FiveStonesAligned, // TODO add capturable stones
    Victory(VictoryCondition)
}

/// The type returned by the board when placing a stone
pub type PlaceResult = Result<PlaceInfo, PlaceError>;

impl Board {
    pub fn new() -> Board {
        Board {
            grid: [[None; ::GRID_LEN]; ::GRID_LEN],
            to_take_stones: 10,
            black_stones: StonesStats { rest_stones: 20, stones_taken: 0 },
            white_stones: StonesStats { rest_stones: 20, stones_taken: 0 }
        }
    }

    /// create a board with a limited number of stones for players
    pub fn with_stone_limit(limit: usize) -> Board { // TODO remove this ?
        Board {
            grid: [[None; ::GRID_LEN]; ::GRID_LEN],
            to_take_stones: 10,
            black_stones: StonesStats { rest_stones: limit, stones_taken: 0 },
            white_stones: StonesStats { rest_stones: limit, stones_taken: 0 }
        }
    }

    /// Simply puts a stone on the board
    pub fn raw_place_stone(&mut self, (x, y): Position, color: Color) {
        self.grid[x][y] = Some(color)
    }

    /// Simply removes a stone from the board
    pub fn raw_remove_stone(&mut self, (x, y): Position) {
        self.grid[x][y] = None;
    }

    fn remove_captured_stones(&mut self, (x, y): Position, captures: &Directions<bool>) {
        if captures[TOP] {
            self.grid[x - 1][y] = None;
            self.grid[x - 2][y] = None;
        }
        if captures[TOP_RIGHT] {
            self.grid[x - 1][y + 1] = None;
            self.grid[x - 2][y + 2] = None;
        }
        if captures[RIGHT] {
            self.grid[x][y + 1] = None;
            self.grid[x][y + 2] = None;
        }
        if captures[BOT_RIGHT] {
            self.grid[x + 1][y + 1] = None;
            self.grid[x + 2][y + 2] = None;
        }
        if captures[BOT] {
            self.grid[x + 1][y] = None;
            self.grid[x + 2][y] = None;
        }
        if captures[BOT_LEFT] {
            self.grid[x + 1][y - 1] = None;
            self.grid[x + 2][y - 2] = None;
        }
        if captures[LEFT] {
            self.grid[x][y - 1] = None;
            self.grid[x][y - 2] = None;
        }
        if captures[TOP_LEFT] {
            self.grid[x - 1][y - 1] = None;
            self.grid[x - 2][y - 2] = None;
        }
    }

    fn update_captures(&mut self, pos: Position, color: Color, captures: &Directions<bool>) -> usize {
        self.remove_captured_stones(pos, &captures);
        let nb_captures = captures.count(|x| *x == true);
        match color {
            Color::Black => {
                self.black_stones.stones_taken += nb_captures;
                self.white_stones.stones_taken
            },
            Color::White => {
                self.white_stones.stones_taken += nb_captures;
                self.white_stones.stones_taken
            },
        }
    }

    /// Try placing a stone on board, respecting rules
    pub fn try_place_stone(&mut self, color: Color, pos: Position) -> PlaceResult {
        let (x, y) = pos;
        if self.grid[x][y].is_some() {
            return Err(PlaceError::TileNotEmpty(pos))
        }
        let alignements = get_alignments(&self.grid, pos, color);
        let free_threes = get_free_threes(&self.grid, pos, color, &alignements);
        let captures = get_captures(&self.grid, pos, color);

        if free_threes.count(|x| *x == true) == 2 {
            Err(PlaceError::DoubleFreeThrees(free_threes))
        }
        else {
            let stones_taken = self.update_captures(pos, color, &captures);
            if alignements.any(|x| x.len() >= 5) {

                // Check if an alignement of five stone is not blocked
                // by captures, allow victory in this case

                // if can_be_took(alignement_of_five_or_more) {
                //     Ok(PlaceInfo::FiveStonesAligned)
                // }
                // else {

                self.raw_place_stone(pos, color);
                Ok(PlaceInfo::Victory(VictoryCondition::FiveStonesAligned(alignements)))
            }
            else if stones_taken >= self.to_take_stones {
                self.raw_place_stone(pos, color);
                Ok(PlaceInfo::Victory(VictoryCondition::CapturedStones(stones_taken, captures)))
            }
            else {
                Ok(PlaceInfo::Nothing)
            }
        }
    }
}
<|endoftext|>