<commit_before>#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
#![feature(array_methods)]
#![feature(array_windows)]
#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(cell_update)]
#![feature(cfg_panic)]
#![feature(cfg_target_has_atomic)]
#![feature(const_assume)]
#![feature(const_cell_into_inner)]
#![feature(const_maybe_uninit_assume_init)]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_ptr_offset)]
#![feature(const_trait_impl)]
#![feature(const_num_from_num)]
#![feature(core_intrinsics)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
#![feature(dec2flt)]
#![feature(div_duration)]
#![feature(duration_consts_2)]
#![feature(duration_constants)]
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(array_from_fn)]
#![feature(hashmap_internals)]
#![feature(try_find)]
#![feature(is_sorted)]
#![feature(pattern)]
#![feature(sort_internals)]
#![feature(slice_partition_at_index)]
#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
#![feature(numfmt)]
#![feature(step_trait)]
#![feature(str_internals)]
#![feature(test)]
#![feature(trusted_len)]
#![feature(try_trait_v2)]
#![feature(slice_internals)]
#![feature(slice_partition_dedup)]
#![feature(int_log)]
#![feature(iter_advance_by)]
#![feature(iter_partition_in_place)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
#![feature(iter_order_by)]
#![feature(const_mut_refs)]
#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_raw_ptr_deref)]
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(option_result_unwrap_unchecked)]
#![feature(result_into_ok_or_err)]
#![feature(ptr_metadata)]
#![feature(once_cell)]
#![feature(unsized_tuple_coercion)]
#![feature(const_option)]
#![feature(integer_atomics)]
#![feature(int_roundings)]
#![feature(slice_group_by)]
#![feature(trusted_random_access)]
#![feature(unsize)]
#![feature(unzip_option)]
#![deny(unsafe_op_in_unsafe_fn)]

extern crate test;

mod alloc;
mod any;
mod array;
mod ascii;
mod atomic;
mod bool;
mod cell;
mod char;
mod clone;
mod cmp;
mod const_ptr;
mod fmt;
mod hash;
mod intrinsics;
mod iter;
mod lazy;
mod macros;
mod manually_drop;
mod mem;
mod nonzero;
mod num;
mod ops;
mod option;
mod pattern;
mod pin;
mod ptr;
mod result;
mod slice;
mod str;
mod str_lossy;
mod task;
mod time;
mod tuple;
mod unicode;
<commit_msg>Add `#![cfg_attr(bootstrap, feature(const_panic))]` to `library/core/tests/lib.rs`<commit_after>#![feature(alloc_layout_extra)]
#![feature(array_chunks)]
#![feature(array_methods)]
#![feature(array_windows)]
#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(cell_update)]
#![feature(cfg_panic)]
#![feature(cfg_target_has_atomic)]
#![feature(const_assume)]
#![feature(const_cell_into_inner)]
#![feature(const_maybe_uninit_assume_init)]
#![cfg_attr(bootstrap, feature(const_panic))]
#![feature(const_ptr_read)]
#![feature(const_ptr_write)]
#![feature(const_ptr_offset)]
#![feature(const_trait_impl)]
#![feature(const_num_from_num)]
#![feature(core_intrinsics)]
#![feature(core_private_bignum)]
#![feature(core_private_diy_float)]
#![feature(dec2flt)]
#![feature(div_duration)]
#![feature(duration_consts_2)]
#![feature(duration_constants)]
#![feature(exact_size_is_empty)]
#![feature(extern_types)]
#![feature(flt2dec)]
#![feature(fmt_internals)]
#![feature(array_from_fn)]
#![feature(hashmap_internals)]
#![feature(try_find)]
#![feature(is_sorted)]
#![feature(pattern)]
#![feature(sort_internals)]
#![feature(slice_partition_at_index)]
#![feature(maybe_uninit_uninit_array)]
#![feature(maybe_uninit_array_assume_init)]
#![feature(maybe_uninit_extra)]
#![feature(maybe_uninit_write_slice)]
#![feature(min_specialization)]
#![feature(numfmt)]
#![feature(step_trait)]
#![feature(str_internals)]
#![feature(test)]
#![feature(trusted_len)]
#![feature(try_trait_v2)]
#![feature(slice_internals)]
#![feature(slice_partition_dedup)]
#![feature(int_log)]
#![feature(iter_advance_by)]
#![feature(iter_partition_in_place)]
#![feature(iter_intersperse)]
#![feature(iter_is_partitioned)]
#![feature(iter_order_by)]
#![feature(const_mut_refs)]
#![feature(const_pin)]
#![feature(const_slice_from_raw_parts)]
#![feature(const_raw_ptr_deref)]
#![feature(never_type)]
#![feature(unwrap_infallible)]
#![feature(option_result_unwrap_unchecked)]
#![feature(result_into_ok_or_err)]
#![feature(ptr_metadata)]
#![feature(once_cell)]
#![feature(unsized_tuple_coercion)]
#![feature(const_option)]
#![feature(integer_atomics)]
#![feature(int_roundings)]
#![feature(slice_group_by)]
#![feature(trusted_random_access)]
#![feature(unsize)]
#![feature(unzip_option)]
#![deny(unsafe_op_in_unsafe_fn)]

extern crate test;

mod alloc;
mod any;
mod array;
mod ascii;
mod atomic;
mod bool;
mod cell;
mod char;
mod clone;
mod cmp;
mod const_ptr;
mod fmt;
mod hash;
mod intrinsics;
mod iter;
mod lazy;
mod macros;
mod manually_drop;
mod mem;
mod nonzero;
mod num;
mod ops;
mod option;
mod pattern;
mod pin;
mod ptr;
mod result;
mod slice;
mod str;
mod str_lossy;
mod task;
mod time;
mod tuple;
mod unicode;
<|endoftext|>


<commit_before>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
use collections::HashMap;
use std::io::{File, Open, Read, Write, TempDir};
use std::num::Zero;
use std::comm::{Data, Empty, Disconnected};
use sync::{Arc, RWLock};
use sync::Future;
use rand::task_rng;
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

struct BufferManager {
	size: uint,
	entries: HashMap<u64, BufferEntry>,
	path: Path,
}

struct BufferEntry {
	frame: Arc<RWLock<BufferFrame>>,
}

#[deriving(Eq)]
enum Status {
	Free,
	Fixed(uint),
}

struct BufferFrame {
	page_id: u64,
	data: Vec<u8>,
	fixed: Status,
}

/* Destructor trait implementation */
impl Drop for BufferManager {
	fn drop(&mut self) {
		// TODO
		info!("Dropping");
	}
}

impl BufferManager {
	pub fn new(size: uint, path: Path) -> BufferManager {
		let h = HashMap::with_capacity(size);
		BufferManager {size: size, entries: h, path: path}
	}

	fn open_or_create(&self, page_id: u64) -> File {
		let file_path = self.path.join(page_id.to_str());
		match File::open_mode(&file_path, Open, Read) {
			Ok(f) => f,
			Err(_) => {
				match File::open_mode(&file_path, Open, Write) {
					Ok(mut f) => {
						match f.write([0_u8, ..4 * 1024]) {
							Ok(_) => (),
							Err(e) => fail!("writing page failed: {}", e),
						}
						match File::open_mode(&file_path, Open, Read) {
							Ok(f) => f,
							Err(e) => fail!("failed reading file: {}", e),
						}
					},
					Err(e) => fail!("Writing file failed: {}", e),
				}
			},
		}
	}

	/*
	 * returns true if page could be loaded
	 */
	fn load_page(&mut self, page_id: u64) -> bool {
		if self.entries.len() == self.size {
			if !self.evict_page() {
				return false;
			}
		}

		let mut file_handle = self.open_or_create(page_id);
		//let content = match file_handle.read_exact(4*1024) {
		let content = match file_handle.read_exact(10) {
			Ok(c) => Vec::from_slice(c),
			Err(e) => fail!("Couldn't read from page: {}", e),
		};

		let frame = BufferFrame {data: content, page_id: page_id, fixed: Free};
		let entry = BufferEntry {frame: Arc::new(RWLock::new(frame))};
		self.entries.insert(page_id, entry);
		true
	}

	/*
	 * return false if no page could be evicted
	 */
	fn evict_page(&mut self) -> bool {
		let random_key = {
			let mut free_keys = self.entries.iter().filter_map(|e| {
				// iterate over entries, check if they are free
				// and return the keys of the free entries
				let (k,v) = e;
				let frame = v.frame.read();
				if frame.fixed == Free {
					Some(k)
				} else {
					None
				}
			});
			// pick a random key
			sample(&mut free_keys).map(|v| v.clone())
		};

		match random_key {
			None => false,
			Some(key) => self.entries.remove(&key),
		}
	}

	pub fn fix_page(&mut self, page_id: u64) -> Option<Arc<RWLock<BufferFrame>>> {
		if !self.entries.contains_key(&page_id) {
			if !self.load_page(page_id) {
				return None;
			}
		}
		let entry = self.entries.get(&page_id);
		{
			let mut f = entry.frame.write();
			f.fixed = match f.fixed {
				Free => Fixed(1),
				Fixed(n) => Fixed(n+1),
			};
		}
		// Arcs can be cloned and they will all point to the same RWLock
		Some(entry.frame.clone())
	}

	pub fn unfix_page(&mut self, frame: Arc<RWLock<BufferFrame>>, is_dirty: bool) {
		{
			let mut frame = frame.write();
			frame.fixed = match frame.fixed {
				Fixed(1) => Free,
				Fixed(n) => Fixed(n-1),
				Free => fail!("Unfixing unfixed page"),
			};
		}

		if !is_dirty {
			return;
		}
		let frame = frame.read();
		if frame.fixed == Free {
			info!("writing back page \\#{}", frame.page_id);
			self.write_page(frame.page_id, frame.get_data());
		}
	}

	fn write_page(&mut self, page_id: u64, data: &[u8]) {
		let file_path = self.path.join(page_id.to_str());
		let mut handle = match File::open_mode(&file_path, Open, Write) {
			Ok(handle) => handle,
			Err(e) => fail!("Opening file for writing failed: {}", e),
		};

		match handle.write(data) {
			Ok(_) => (),
			Err(e) => fail!("Writing to file failed: {}", e),
		};
	}
}

/*
 * A buffer frame, the unit that gets the data
 */
impl BufferFrame {
	/*
	 * get_mut_data returns a borrowed reference to a mutable slice to the vector
	 * contents.
	 */
	pub fn get_mut_data<'a>(&'a mut self) -> &'a mut [u8] {
		self.data.as_mut_slice()
	}
	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

fn sample<'a, T, I:Iterator<T>>(from: &'a mut I) -> Option<T> {
	let from: ~[T] = from.collect();
	let l = from.len();
	if l == 0 {
		return None;
	}
	let index = randrange(l);
	Some(from[index])
}

fn randrange<X: SampleRange + Ord + Zero>(high: X) -> X {
	let between: Range<X> = Range::new(Zero::zero(), high);
	let mut rng = rand::task_rng();
	between.ind_sample(&mut rng)
}

#[test]
fn test_create() {
	let dir = match TempDir::new("buffermanager") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};

	let mut bm = BufferManager::new(16, dir.path().clone());
	let pageref = match bm.fix_page(42) {
		Some(p) => p,
		None => fail!("Getting page failed"),
	};
	{
		let mut page = pageref.write();
		let data = page.get_mut_data();
		data[0] = 42;
	}
	bm.unfix_page(pageref, true);
}

#[test]
fn test_threads() {
	use rand::random;
	use std::task::spawn;
	use std::os;

	let pages_in_ram: uint = match os::getenv("PAGES_IN_RAM") {
		Some(v) => from_str(v).expect("PAGES_IN_RAM expects integer"),
		None => 1,
	};
	let pages_on_disk: u64 = match os::getenv("PAGES_ON_DISK") {
		Some(v) => from_str(v).expect("PAGES_ON_DISK expects integer"),
		None => 20,
	};
	let thread_count: uint = match os::getenv("THREADS") {
		Some(v) => from_str(v).expect("THREADS expects integer"),
		None => 3,
	};
	let dir = match TempDir::new("buffermanager") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};
	let p = dir.path();
	let p = Path::new(".");

	let mut buffermanager = BufferManager::new(pages_in_ram, p.clone());

	for i in range(0, pages_on_disk) {
		let bf = match buffermanager.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page {}", i),
		};
		{
			let mut lock = bf.write();
			lock.get_mut_data()[0] = 0;
		}
		buffermanager.unfix_page(bf, true);
	}
	let bm = Arc::new(RWLock::new(buffermanager));

	// start scan thread
	let (tx, rx) = channel();
	let bm_scan = bm.clone();
	let mut scan = Future::spawn(proc() {
		let mut counters = Vec::from_elem(pages_on_disk as uint, 0_u8);
		loop {
			info!("Running scan thread...");
			match rx.try_recv() {
				Empty => {
					let page_number = randrange(pages_on_disk);
					let mut bm = bm_scan.write();
					let bf = match bm.fix_page(page_number) {
						Some(frame) => frame,
						None => fail!("Couldn't scan/fix page"),
					};
					let current_val = {
						let lock = bf.read();
						lock.get_data()[0]
					};
					bm.unfix_page(bf, false);
					bm.downgrade();
					// check if the value is going up
					assert!(&current_val >= counters.get(page_number as uint));
					// set to the new value
					let v = counters.get_mut(page_number as uint);
					*v = current_val;

				},
				/*
				 * terminate on both disconnect and when any kind of
				 * data arrives
				 */
				Disconnected => break,
				Data(_) => break,
			};
		}
	});

	// start read/write threads
	let mut rw_threads: Vec<Future<int>> = Vec::new();
	for _ in range(0, thread_count) {
		let bm = bm.clone();
		rw_threads.push(Future::spawn(proc() {
			let is_write = random::<bool>();
			info!("Creating new {} task",
				if is_write {"write"} else {"read"});
			let page_number = randrange(pages_on_disk);
			let mut bm = bm.write();
			if is_write {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Couldn't fix page"),
				};
				{
					let mut lock = bf.write();
					let data = lock.get_mut_data();
					data[0] = data[0] + 1;
					info!("Wrote to page {}", page_number);
					debug!("data: {}", Vec::from_slice(data));
				}
				bm.unfix_page(bf, is_write);
			} else {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Couldn't fix page"),
				};
				bm.unfix_page(bf, is_write);
			}
			// return whether we wrote (1) or read (0) as future
			if is_write {1} else {0}
		}));
	}

	// Rust does not have join, but we can wait on Futures which does the same
	let total_count = rw_threads.mut_iter().fold(0, |acc, val| acc + val.get());
	info!("Total count in RAM: {}", total_count);

	// terminate the scan thread and wait until it has completed
	tx.send("terminate");
	scan.get();

	// re-open the pages and check whether all numbers got saved
	let mut bm = BufferManager::new(pages_in_ram, p.clone());
	let mut total_count_on_disk = 0;
	for i in range(0, pages_on_disk) {
		let bf = match bm.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page")
		};
		let value = {
			let lock = bf.read();
			let data = lock.get_data();
			data[0]
		};
		bm.unfix_page(bf, false);
		// cast up from u8 to int
		total_count_on_disk += value as int;
	}
	info!("Total count on disk: {}", total_count_on_disk);
	assert_eq!(total_count, total_count_on_disk);
}
<commit_msg>Made page size compile-time configurable<commit_after>#![feature(phase)]
#[phase(syntax, link)] extern crate log;
extern crate collections;
extern crate sync;
extern crate rand;
use collections::HashMap;
use std::io::{File, Open, Read, Write, TempDir};
use std::num::Zero;
use std::comm::{Data, Empty, Disconnected};
use sync::{Arc, RWLock};
use sync::Future;
use rand::task_rng;
use rand::distributions::{IndependentSample, Range};
use rand::distributions::range::SampleRange;

static PAGE_SIZE: uint = 4 * 1024;

struct BufferManager {
	size: uint,
	entries: HashMap<u64, BufferEntry>,
	path: Path,
}

struct BufferEntry {
	frame: Arc<RWLock<BufferFrame>>,
}

#[deriving(Eq)]
enum Status {
	Free,
	Fixed(uint),
}

struct BufferFrame {
	page_id: u64,
	data: Vec<u8>,
	fixed: Status,
}

/* Destructor trait implementation */
impl Drop for BufferManager {
	fn drop(&mut self) {
		// TODO
		info!("Dropping");
	}
}

impl BufferManager {
	pub fn new(size: uint, path: Path) -> BufferManager {
		let h = HashMap::with_capacity(size);
		BufferManager {size: size, entries: h, path: path}
	}

	fn open_or_create(&self, page_id: u64) -> File {
		let file_path = self.path.join(page_id.to_str());
		match File::open_mode(&file_path, Open, Read) {
			Ok(f) => f,
			Err(_) => {
				match File::open_mode(&file_path, Open, Write) {
					Ok(mut f) => {
						match f.write([0_u8, ..PAGE_SIZE]) {
							Ok(_) => (),
							Err(e) => fail!("writing page failed: {}", e),
						}
						match File::open_mode(&file_path, Open, Read) {
							Ok(f) => f,
							Err(e) => fail!("failed reading file: {}", e),
						}
					},
					Err(e) => fail!("Writing file failed: {}", e),
				}
			},
		}
	}

	/*
	 * returns true if page could be loaded
	 */
	fn load_page(&mut self, page_id: u64) -> bool {
		if self.entries.len() == self.size {
			if !self.evict_page() {
				return false;
			}
		}

		let mut file_handle = self.open_or_create(page_id);
		let content = match file_handle.read_exact(PAGE_SIZE) {
			Ok(c) => Vec::from_slice(c),
			Err(e) => fail!("Couldn't read from page: {}", e),
		};

		let frame = BufferFrame {data: content, page_id: page_id, fixed: Free};
		let entry = BufferEntry {frame: Arc::new(RWLock::new(frame))};
		self.entries.insert(page_id, entry);
		true
	}

	/*
	 * return false if no page could be evicted
	 */
	fn evict_page(&mut self) -> bool {
		let random_key = {
			let mut free_keys = self.entries.iter().filter_map(|e| {
				// iterate over entries, check if they are free
				// and return the keys of the free entries
				let (k,v) = e;
				let frame = v.frame.read();
				if frame.fixed == Free {
					Some(k)
				} else {
					None
				}
			});
			// pick a random key
			sample(&mut free_keys).map(|v| v.clone())
		};

		match random_key {
			None => false,
			Some(key) => self.entries.remove(&key),
		}
	}

	pub fn fix_page(&mut self, page_id: u64) -> Option<Arc<RWLock<BufferFrame>>> {
		if !self.entries.contains_key(&page_id) {
			if !self.load_page(page_id) {
				return None;
			}
		}
		let entry = self.entries.get(&page_id);
		{
			let mut f = entry.frame.write();
			f.fixed = match f.fixed {
				Free => Fixed(1),
				Fixed(n) => Fixed(n+1),
			};
		}
		// Arcs can be cloned and they will all point to the same RWLock
		Some(entry.frame.clone())
	}

	pub fn unfix_page(&mut self, frame: Arc<RWLock<BufferFrame>>, is_dirty: bool) {
		{
			let mut frame = frame.write();
			frame.fixed = match frame.fixed {
				Fixed(1) => Free,
				Fixed(n) => Fixed(n-1),
				Free => fail!("Unfixing unfixed page"),
			};
		}

		if !is_dirty {
			return;
		}
		let frame = frame.read();
		if frame.fixed == Free {
			info!("writing back page \\#{}", frame.page_id);
			self.write_page(frame.page_id, frame.get_data());
		}
	}

	fn write_page(&mut self, page_id: u64, data: &[u8]) {
		let file_path = self.path.join(page_id.to_str());
		let mut handle = match File::open_mode(&file_path, Open, Write) {
			Ok(handle) => handle,
			Err(e) => fail!("Opening file for writing failed: {}", e),
		};

		match handle.write(data) {
			Ok(_) => (),
			Err(e) => fail!("Writing to file failed: {}", e),
		};
	}
}

/*
 * A buffer frame, the unit that gets the data
 */
impl BufferFrame {
	/*
	 * get_mut_data returns a borrowed reference to a mutable slice to the vector
	 * contents.
	 */
	pub fn get_mut_data<'a>(&'a mut self) -> &'a mut [u8] {
		self.data.as_mut_slice()
	}
	pub fn get_data<'a>(&'a self) -> &'a [u8] {
		self.data.as_slice()
	}
}

fn sample<'a, T, I:Iterator<T>>(from: &'a mut I) -> Option<T> {
	let from: ~[T] = from.collect();
	let l = from.len();
	if l == 0 {
		return None;
	}
	let index = randrange(l);
	Some(from[index])
}

fn randrange<X: SampleRange + Ord + Zero>(high: X) -> X {
	let between: Range<X> = Range::new(Zero::zero(), high);
	let mut rng = rand::task_rng();
	between.ind_sample(&mut rng)
}

#[test]
fn test_create() {
	let dir = match TempDir::new("buffermanager") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};

	let mut bm = BufferManager::new(16, dir.path().clone());
	let pageref = match bm.fix_page(42) {
		Some(p) => p,
		None => fail!("Getting page failed"),
	};
	{
		let mut page = pageref.write();
		let data = page.get_mut_data();
		data[0] = 42;
	}
	bm.unfix_page(pageref, true);
}

#[test]
fn test_threads() {
	use rand::random;
	use std::task::spawn;
	use std::os;

	let pages_in_ram: uint = match os::getenv("PAGES_IN_RAM") {
		Some(v) => from_str(v).expect("PAGES_IN_RAM expects integer"),
		None => 1,
	};
	let pages_on_disk: u64 = match os::getenv("PAGES_ON_DISK") {
		Some(v) => from_str(v).expect("PAGES_ON_DISK expects integer"),
		None => 20,
	};
	let thread_count: uint = match os::getenv("THREADS") {
		Some(v) => from_str(v).expect("THREADS expects integer"),
		None => 3,
	};
	let dir = match TempDir::new("buffermanager") {
		Some(temp_dir) => temp_dir,
		None => fail!("creation of temporary directory"),
	};
	let p = dir.path();
	let p = Path::new(".");

	let mut buffermanager = BufferManager::new(pages_in_ram, p.clone());

	for i in range(0, pages_on_disk) {
		let bf = match buffermanager.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page {}", i),
		};
		{
			let mut lock = bf.write();
			lock.get_mut_data()[0] = 0;
		}
		buffermanager.unfix_page(bf, true);
	}
	let bm = Arc::new(RWLock::new(buffermanager));

	// start scan thread
	let (tx, rx) = channel();
	let bm_scan = bm.clone();
	let mut scan = Future::spawn(proc() {
		let mut counters = Vec::from_elem(pages_on_disk as uint, 0_u8);
		loop {
			info!("Running scan thread...");
			match rx.try_recv() {
				Empty => {
					let page_number = randrange(pages_on_disk);
					let mut bm = bm_scan.write();
					let bf = match bm.fix_page(page_number) {
						Some(frame) => frame,
						None => fail!("Couldn't scan/fix page"),
					};
					let current_val = {
						let lock = bf.read();
						lock.get_data()[0]
					};
					bm.unfix_page(bf, false);
					bm.downgrade();
					// check if the value is going up
					assert!(&current_val >= counters.get(page_number as uint));
					// set to the new value
					let v = counters.get_mut(page_number as uint);
					*v = current_val;

				},
				/*
				 * terminate on both disconnect and when any kind of
				 * data arrives
				 */
				Disconnected => break,
				Data(_) => break,
			};
		}
	});

	// start read/write threads
	let mut rw_threads: Vec<Future<int>> = Vec::new();
	for _ in range(0, thread_count) {
		let bm = bm.clone();
		rw_threads.push(Future::spawn(proc() {
			let is_write = random::<bool>();
			info!("Creating new {} task",
				if is_write {"write"} else {"read"});
			let page_number = randrange(pages_on_disk);
			let mut bm = bm.write();
			if is_write {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Couldn't fix page"),
				};
				{
					let mut lock = bf.write();
					let data = lock.get_mut_data();
					data[0] = data[0] + 1;
					info!("Wrote to page {}", page_number);
					debug!("data: {}", Vec::from_slice(data));
				}
				bm.unfix_page(bf, is_write);
			} else {
				let bf = match bm.fix_page(page_number) {
					Some(frame) => frame,
					None => fail!("Couldn't fix page"),
				};
				bm.unfix_page(bf, is_write);
			}
			// return whether we wrote (1) or read (0) as future
			if is_write {1} else {0}
		}));
	}

	// Rust does not have join, but we can wait on Futures which does the same
	let total_count = rw_threads.mut_iter().fold(0, |acc, val| acc + val.get());
	info!("Total count in RAM: {}", total_count);

	// terminate the scan thread and wait until it has completed
	tx.send("terminate");
	scan.get();

	// re-open the pages and check whether all numbers got saved
	let mut bm = BufferManager::new(pages_in_ram, p.clone());
	let mut total_count_on_disk = 0;
	for i in range(0, pages_on_disk) {
		let bf = match bm.fix_page(i) {
			Some(frame) => frame,
			None => fail!("Couldn't fix page")
		};
		let value = {
			let lock = bf.read();
			let data = lock.get_data();
			data[0]
		};
		bm.unfix_page(bf, false);
		// cast up from u8 to int
		total_count_on_disk += value as int;
	}
	info!("Total count on disk: {}", total_count_on_disk);
	assert_eq!(total_count, total_count_on_disk);
}
<|endoftext|>


<commit_before>///! Reference counter for channels.
use std::isize;
use std::ops;
use std::process;
use std::sync::atomic::{AtomicUsize, Ordering};

/// Reference counter internals.
struct Counter<C> {
    /// The number of senders associated with the channel.
    senders: AtomicUsize,

    /// The number of receivers associated with the channel.
    receivers: AtomicUsize,

    /// The internal channel.
    chan: C,
}

/// Wraps a channel into the reference counter.
pub fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
    let counter = Box::into_raw(Box::new(Counter {
        senders: AtomicUsize::new(1),
        receivers: AtomicUsize::new(1),
        chan,
    }));
    let s = Sender { counter };
    let r = Receiver { counter };
    (s, r)
}

/// The sending side.
pub struct Sender<C> {
    counter: *mut Counter<C>,
}

impl<C> Sender<C> {
    /// Returns the internal `Counter`.
    fn counter(&self) -> &Counter<C> {
        unsafe { &*self.counter }
    }

    /// Acquires another sender reference.
    pub fn acquire(&self) -> Sender<C> {
        let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);

        // Cloning senders and calling `mem::forget` on the clones could potentially overflow the
        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
        // just abort when the count becomes very large.
        if count > isize::MAX as usize {
            process::abort();
        }

        Sender {
            counter: self.counter,
        }
    }

    /// Releases the sender reference.
    ///
    /// Function `disconnect` will be called if this is the last sender reference.
    pub unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
        if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
            if !disconnect(&self.counter().chan) {
                drop(Box::from_raw(self.counter));
            }
        }
    }
}

impl<C> ops::Deref for Sender<C> {
    type Target = C;

    fn deref(&self) -> &C {
        &self.counter().chan
    }
}

impl<C> PartialEq for Sender<C> {
    fn eq(&self, other: &Sender<C>) -> bool {
        self.counter == other.counter
    }
}

/// The receiving side.
pub struct Receiver<C> {
    counter: *mut Counter<C>,
}

impl<C> Receiver<C> {
    /// Returns the internal `Counter`.
    fn counter(&self) -> &Counter<C> {
        unsafe { &*self.counter }
    }

    /// Acquires another receiver reference.
    pub fn acquire(&self) -> Receiver<C> {
        let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);

        // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
        // just abort when the count becomes very large.
        if count > isize::MAX as usize {
            process::abort();
        }

        Receiver {
            counter: self.counter,
        }
    }

    /// Releases the receiver reference.
    ///
    /// Function `disconnect` will be called if this is the last receiver reference.
    pub unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
        if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
            if !disconnect(&self.counter().chan) {
                drop(Box::from_raw(self.counter));
            }
        }
    }
}

impl<C> ops::Deref for Receiver<C> {
    type Target = C;

    fn deref(&self) -> &C {
        &self.counter().chan
    }
}

impl<C> PartialEq for Receiver<C> {
    fn eq(&self, other: &Receiver<C>) -> bool {
        self.counter == other.counter
    }
}
<commit_msg>Fix a race in channel disconnection<commit_after>///! Reference counter for channels.

use std::isize;
use std::ops;
use std::process;
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};

/// Reference counter internals.
struct Counter<C> {
    /// The number of senders associated with the channel.
    senders: AtomicUsize,

    /// The number of receivers associated with the channel.
    receivers: AtomicUsize,

    /// Set to `true` if the last sender or the last receiver reference deallocates the channel.
    destroy: AtomicBool,

    /// The internal channel.
    chan: C,
}

/// Wraps a channel into the reference counter.
pub fn new<C>(chan: C) -> (Sender<C>, Receiver<C>) {
    let counter = Box::into_raw(Box::new(Counter {
        senders: AtomicUsize::new(1),
        receivers: AtomicUsize::new(1),
        destroy: AtomicBool::new(false),
        chan,
    }));
    let s = Sender { counter };
    let r = Receiver { counter };
    (s, r)
}

/// The sending side.
pub struct Sender<C> {
    counter: *mut Counter<C>,
}

impl<C> Sender<C> {
    /// Returns the internal `Counter`.
    fn counter(&self) -> &Counter<C> {
        unsafe { &*self.counter }
    }

    /// Acquires another sender reference.
    pub fn acquire(&self) -> Sender<C> {
        let count = self.counter().senders.fetch_add(1, Ordering::Relaxed);

        // Cloning senders and calling `mem::forget` on the clones could potentially overflow the
        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
        // just abort when the count becomes very large.
        if count > isize::MAX as usize {
            process::abort();
        }

        Sender {
            counter: self.counter,
        }
    }

    /// Releases the sender reference.
    ///
    /// Function `disconnect` will be called if this is the last sender reference.
    pub unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
        if self.counter().senders.fetch_sub(1, Ordering::AcqRel) == 1 {
            disconnect(&self.counter().chan);

            if self.counter().destroy.swap(true, Ordering::AcqRel) {
                drop(Box::from_raw(self.counter));
            }
        }
    }
}

impl<C> ops::Deref for Sender<C> {
    type Target = C;

    fn deref(&self) -> &C {
        &self.counter().chan
    }
}

impl<C> PartialEq for Sender<C> {
    fn eq(&self, other: &Sender<C>) -> bool {
        self.counter == other.counter
    }
}

/// The receiving side.
pub struct Receiver<C> {
    counter: *mut Counter<C>,
}

impl<C> Receiver<C> {
    /// Returns the internal `Counter`.
    fn counter(&self) -> &Counter<C> {
        unsafe { &*self.counter }
    }

    /// Acquires another receiver reference.
    pub fn acquire(&self) -> Receiver<C> {
        let count = self.counter().receivers.fetch_add(1, Ordering::Relaxed);

        // Cloning receivers and calling `mem::forget` on the clones could potentially overflow the
        // counter. It's very difficult to recover sensibly from such degenerate scenarios so we
        // just abort when the count becomes very large.
        if count > isize::MAX as usize {
            process::abort();
        }

        Receiver {
            counter: self.counter,
        }
    }

    /// Releases the receiver reference.
    ///
    /// Function `disconnect` will be called if this is the last receiver reference.
    pub unsafe fn release<F: FnOnce(&C) -> bool>(&self, disconnect: F) {
        if self.counter().receivers.fetch_sub(1, Ordering::AcqRel) == 1 {
            disconnect(&self.counter().chan);

            if self.counter().destroy.swap(true, Ordering::AcqRel) {
                drop(Box::from_raw(self.counter));
            }
        }
    }
}

impl<C> ops::Deref for Receiver<C> {
    type Target = C;

    fn deref(&self) -> &C {
        &self.counter().chan
    }
}

impl<C> PartialEq for Receiver<C> {
    fn eq(&self, other: &Receiver<C>) -> bool {
        self.counter == other.counter
    }
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/")]
fn hello() -> &'static str {
    "Hello, world!"
}

fn main() {
    rocket::ignite().mount("/", routes![hello]).launch()
}

#[cfg(test)]
mod test {
    use super::rocket;
    use rocket::testing::MockRequest;
    use rocket::http::Method::*;

    #[test]
    fn test_hello() {
        let rocket = rocket::ignite().mount("/", routes![super::hello]);
        let mut req = MockRequest::new(Get, "/");
        let mut response = req.dispatch_with(&rocket);

        let body_string = response.body().and_then(|b| b.into_string());
        assert_eq!(body_string, Some("Hello, world!".to_string()));
    }
}
<commit_msg>Sync testing exmaple with guide.<commit_after>#![feature(plugin)]
#![plugin(rocket_codegen)]

extern crate rocket;

#[get("/")]
fn hello() -> &'static str {
    "Hello, world!"
}

fn main() {
    rocket::ignite().mount("/", routes![hello]).launch()
}

#[cfg(test)]
mod test {
    use super::rocket;
    use rocket::testing::MockRequest;
    use rocket::http::Status;
    use rocket::http::Method::*;

    #[test]
    fn test_hello() {
        let rocket = rocket::ignite().mount("/", routes![super::hello]);
        let mut req = MockRequest::new(Get, "/");
        let mut response = req.dispatch_with(&rocket);
        assert_eq!(response.status(), Status::Ok);

        let body_string = response.body().and_then(|b| b.into_string());
        assert_eq!(body_string, Some("Hello, world!".to_string()));
    }
}
<|endoftext|>


<commit_before>// Copyright 2018 Google LLC
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.

#[cfg(feature = "serde")]
#[doc(hidden)]
#[macro_export]
macro_rules! add_serde_if_enabled {
    ($(#[$attr:meta])* -- $i:item) => {
        $(#[$attr])*
        #[derive($crate::serde::Serialize, $crate::serde::Deserialize)]
        $i
    }
}

#[cfg(not(feature = "serde"))]
#[doc(hidden)]
#[macro_export]
macro_rules! add_serde_if_enabled {
    ($(#[$attr:meta])* -- $i:item) => {
        $(#[$attr])*
        $i
    }
}

/// The main macro that creates RPC services.
///
/// Rpc methods are specified, mirroring trait syntax:
///
/// ```
/// # #![feature(async_await, proc_macro_hygiene)]
/// # fn main() {}
/// # tarpc::service! {
/// /// Say hello
/// rpc hello(name: String) -> String;
/// # }
/// ```
///
/// Attributes can be attached to each rpc. These attributes
/// will then be attached to the generated service traits'
/// corresponding `fn`s, as well as to the client stubs' RPCs.
///
/// The following items are expanded in the enclosing module:
///
/// * `trait Service` -- defines the RPC service.
///   * `fn serve` -- turns a service impl into a request handler.
/// * `Client` -- a client stub with a fn for each RPC.
///   * `fn new_stub` -- creates a new Client stub.
///
#[macro_export]
macro_rules! service {
    () => {
        compile_error!("Must define at least one RPC method.");
    };
// Entry point
    (
        $(
            $(#[$attr:meta])*
            rpc $fn_name:ident( $( $(#[$argattr:meta])* $arg:ident : $in_:ty ),* ) $(-> $out:ty)*;
        )*
    ) => {
        $crate::service! {{
            $(
                $(#[$attr])*
                rpc $fn_name( $( $(#[$argattr])* $arg : $in_ ),* ) $(-> $out)*;
            )*
        }}
    };
// Pattern for when the next rpc has an implicit unit return type.
    (
        {
            $(#[$attr:meta])*
            rpc $fn_name:ident( $( $(#[$argattr:meta])* $arg:ident : $in_:ty ),* );

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        $crate::service! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            rpc $fn_name( $( $(#[$argattr])* $arg : $in_ ),* ) -> ();
        }
    };
// Pattern for when the next rpc has an explicit return type.
    (
        {
            $(#[$attr:meta])*
            rpc $fn_name:ident( $( $(#[$argattr:meta])* $arg:ident : $in_:ty ),* ) -> $out:ty;

            $( $unexpanded:tt )*
        }
        $( $expanded:tt )*
    ) => {
        $crate::service! {
            { $( $unexpanded )* }

            $( $expanded )*

            $(#[$attr])*
            rpc $fn_name( $( $(#[$argattr])* $arg : $in_ ),* ) -> $out;
        }
    };
// Pattern for when all return types have been expanded
    (
        { } // none left to expand
        $(
            $(#[$attr:meta])*
            rpc $fn_name:ident ( $( $(#[$argattr:meta])* $arg:ident : $in_:ty ),* ) -> $out:ty;
        )*
    ) => {
        $crate::add_serde_if_enabled! {
            /// The request sent over the wire from the client to the server.
            #[derive(Debug)]
            #[allow(non_camel_case_types, unused)]
            --
            pub enum Request {
                $(
                    $(#[$attr])*
                    $fn_name{ $( $(#[$argattr])* $arg: $in_,)* }
                ),*
            }
        }

        $crate::add_serde_if_enabled! {
            /// The response sent over the wire from the server to the client.
            #[derive(Debug)]
            #[allow(non_camel_case_types, unused)]
            --
            pub enum Response {
                $(
                    $(#[$attr])*
                    $fn_name($out)
                ),*
            }
        }

        // TODO: proc_macro can't currently parse $crate, so this needs to be imported for the
        // usage of snake_to_camel! to work.
        use $crate::futures::Future as Future__;

        /// Defines the RPC service. The additional trait bounds are required so that services can
        /// multiplex requests across multiple tasks, potentially on multiple threads.
        pub trait Service: Clone + Send + 'static {
            $(
                $crate::snake_to_camel! {
                    /// The type of future returned by `{}`.
                    type $fn_name: Future__<Output = $out> + Send;
                }

                $(#[$attr])*
                fn $fn_name(self, ctx: $crate::context::Context, $($arg:$in_),*) -> $crate::ty_snake_to_camel!(Self::$fn_name);
            )*
        }

        // TODO: use an existential type instead of this when existential types work.
        /// A future resolving to a server [`Response`].
        #[allow(non_camel_case_types)]
        pub enum ResponseFut<S: Service> {
            $(
                $(#[$attr])*
                $fn_name($crate::ty_snake_to_camel!(<S as Service>::$fn_name)),
            )*
        }

        impl<S: Service> ::std::fmt::Debug for ResponseFut<S> {
            fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
                fmt.debug_struct("Response").finish()
            }
        }

        impl<S: Service> ::std::future::Future for ResponseFut<S> {
            type Output = ::std::io::Result<Response>;

            fn poll(self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>)
                -> ::std::task::Poll<::std::io::Result<Response>>
            {
                unsafe {
                    match ::std::pin::Pin::get_unchecked_mut(self) {
                        $(
                            ResponseFut::$fn_name(resp) =>
                                ::std::pin::Pin::new_unchecked(resp)
                                    .poll(cx)
                                    .map(Response::$fn_name)
                                    .map(Ok),
                        )*
                    }
                }
            }
        }

        /// Returns a serving function to use with rpc::server::Server.
        pub fn serve<S: Service>(service: S)
            -> impl FnOnce($crate::context::Context, Request) -> ResponseFut<S> + Send + 'static + Clone {
                move |ctx, req| {
                    match req {
                        $(
                            Request::$fn_name{ $($arg,)* } => {
                                let resp = Service::$fn_name(service.clone(), ctx, $($arg),*);
                                ResponseFut::$fn_name(resp)
                            }
                        )*
                    }
                }
            }

        #[allow(unused)]
        #[derive(Clone, Debug)]
        /// The client stub that makes RPC calls to the server. Exposes a Future interface.
        pub struct Client<C = $crate::client::Channel<Request, Response>>(C);

        /// Returns a new client stub that sends requests over the given transport.
        pub async fn new_stub<T>(config: $crate::client::Config, transport: T)
            -> ::std::io::Result<Client>
        where
            T: $crate::Transport<$crate::ClientMessage<Request>, $crate::Response<Response>> + Send + 'static,
        {
            Ok(Client($crate::client::new(config, transport).await?))
        }

        impl<C> From<C> for Client<C>
            where for <'a> C: $crate::Client<'a, Request, Response = Response>
        {
            fn from(client: C) -> Self {
                Client(client)
            }
        }

        impl<C> Client<C>
            where for<'a> C: $crate::Client<'a, Request, Response = Response>
        {
            $(
                #[allow(unused)]
                $(#[$attr])*
                pub fn $fn_name(&mut self, ctx: $crate::context::Context, $($arg: $in_),*)
                    -> impl ::std::future::Future<Output = ::std::io::Result<$out>> + '_ {
                    let request__ = Request::$fn_name { $($arg,)* };
                    let resp = $crate::Client::call(&mut self.0, ctx, request__);
                    async move {
                        match resp.await? {
                            Response::$fn_name(msg__) => ::std::result::Result::Ok(msg__),
                            _ => unreachable!(),
                        }
                    }
                }
            )*
        }
    }
}

// allow dead code; we're just testing that the macro expansion compiles
#[allow(dead_code)]
#[cfg(test)]
mod syntax_test {
    service! {
        #[deny(warnings)]
        #[allow(non_snake_case)]
        rpc TestCamelCaseDoesntConflict();
        rpc hello() -> String;
        #[doc="attr"]
        rpc attr(s: String) -> String;
        rpc no_args_no_return();
        rpc no_args() -> ();
        rpc one_arg(foo: String) -> i32;
        rpc two_args_no_return(bar: String, baz: u64);
        rpc two_args(bar: String, baz: u64) -> String;
        rpc no_args_ret_error() -> i32;
        rpc one_arg_ret_error(foo: String) -> String;
        rpc no_arg_implicit_return_error();
        #[doc="attr"]
        rpc one_arg_implicit_return_error(foo: String);
    }
}

#[cfg(test)]
mod functional_test {
    use futures::{
        future::{ready, Ready},
        prelude::*,
    };
    use rpc::{client, context, server::Handler, transport::channel};

    service! {
        rpc add(x: i32, y: i32) -> i32;
        rpc hey(name: String) -> String;
    }

    #[derive(Clone)]
    struct Server;

    impl Service for Server {
        type AddFut = Ready<i32>;

        fn add(self, _: context::Context, x: i32, y: i32) -> Self::AddFut {
            ready(x + y)
        }

        type HeyFut = Ready<String>;

        fn hey(self, _: context::Context, name: String) -> Self::HeyFut {
            ready(format!("Hey, {}.", name))
        }
    }

    #[runtime::test(runtime_tokio::TokioCurrentThread)]
    async fn sequential() {
        let _ = env_logger::try_init();

        let (tx, rx) = channel::unbounded();
        let _ = runtime::spawn(
            crate::Server::default()
                .incoming(stream::once(ready(rx)))
                .respond_with(serve(Server)),
        );

        let mut client = new_stub(client::Config::default(), tx).await.unwrap();
        assert_eq!(3, client.add(context::current(), 1, 2).await.unwrap());
        assert_eq!(
            "Hey, Tim.",
            client
                .hey(context::current(), "Tim".to_string())
                .await
                .unwrap()
        );
    }

    #[runtime::test(runtime_tokio::TokioCurrentThread)]
    async fn concurrent() {
        let _ = env_logger::try_init();

        let (tx, rx) = channel::unbounded();
        let _ = runtime::spawn(
            rpc::Server::default()
                .incoming(stream::once(ready(rx)))
                .respond_with(serve(Server)),
        );

        let client = new_stub(client::Config::default(), tx).await.unwrap();
        let mut c = client.clone();
        let req1 = c.add(context::current(), 1, 2);
        let mut c = client.clone();
        let req2 = c.add(context::current(), 3, 4);
        let mut c = client.clone();
        let req3 = c.hey(context::current(), "Tim".to_string());

        assert_eq!(3, req1.await.unwrap());
        assert_eq!(7, req2.await.unwrap());
        assert_eq!("Hey, Tim.", req3.await.unwrap());
    }
}
<commit_msg>Remove macros.rs<commit_after><|endoftext|>


<commit_before>extern crate byteorder;
extern crate futures;
extern crate linear_map;
extern crate protobuf;

extern crate librespot_core;
extern crate librespot_protocol as protocol;

pub mod cover;

use futures::future;
use futures::Future;
use linear_map::LinearMap;

use librespot_core::mercury::MercuryError;
use librespot_core::session::Session;
use librespot_core::spotify_id::{FileId, SpotifyAudioType, SpotifyId};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
where
    I: IntoIterator<Item = &'s protocol::metadata::Restriction>,
{
    let mut forbidden = "".to_string();
    let mut has_forbidden = false;

    let mut allowed = "".to_string();
    let mut has_allowed = false;

    let rs = restrictions
        .into_iter()
        .filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned()));

    for r in rs {
        if r.has_countries_forbidden() {
            forbidden.push_str(r.get_countries_forbidden());
            has_forbidden = true;
        }

        if r.has_countries_allowed() {
            allowed.push_str(r.get_countries_allowed());
            has_allowed = true;
        }
    }

    (has_forbidden || has_allowed)
        && (!has_forbidden || !countrylist_contains(forbidden.as_str(), country))
        && (!has_allowed || countrylist_contains(allowed.as_str(), country))
}

// A wrapper with fields the player needs
#[derive(Debug, Clone)]
pub struct AudioItem {
    pub id: SpotifyId,
    pub uri: String,
    pub files: LinearMap<FileFormat, FileId>,
    pub name: String,
    pub available: bool,
    pub alternatives: Option<Vec<SpotifyId>>,
}

impl AudioItem {
    pub fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        match id.audio_type {
            SpotifyAudioType::Track => Track::get_audio_item(session, id),
            SpotifyAudioType::Podcast => Episode::get_audio_item(session, id),
            SpotifyAudioType::NonPlayable => {
                Box::new(future::err::<AudioItem, MercuryError>(MercuryError))
            }
        }
    }
}

trait AudioFiles {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>>;
}

impl AudioFiles for Track {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        Box::new(Self::get(session, id).and_then(move |item| {
            Ok(AudioItem {
                id: id,
                uri: format!("spotify:track:{}", id.to_base62()),
                files: item.files,
                name: item.name,
                available: item.available,
                alternatives: Some(item.alternatives),
            })
        }))
    }
}

impl AudioFiles for Episode {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        Box::new(Self::get(session, id).and_then(move |item| {
            Ok(AudioItem {
                id: id,
                uri: format!("spotify:episode:{}", id.to_base62()),
                files: item.files,
                name: item.name,
                available: item.available,
                alternatives: None,
            })
        }))
    }
}
pub trait Metadata: Send + Sized + 'static {
    type Message: protobuf::Message;

    fn request_url(id: SpotifyId) -> String;
    fn parse(msg: &Self::Message, session: &Session) -> Self;

<<<<<<< HEAD
    fn get(session: &Session, id: SpotifyId) -> Box<dyn Future<Item = Self, Error = MercuryError>> {
        let uri = format!("{}/{}", Self::base_url(), id.to_base16());
=======
    fn get(session: &Session, id: SpotifyId) -> Box<Future<Item = Self, Error = MercuryError>> {
        let uri = Self::request_url(id);
>>>>>>> Make standard metadata get handle also playlists
        let request = session.mercury().get(uri);

        let session = session.clone();
        Box::new(request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(Self::parse(&msg, &session))
        }))
    }
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub duration: i32,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Episode {
    pub id: SpotifyId,
    pub name: String,
    pub external_url: String,
    pub duration: i32,
    pub language: String,
    pub show: SpotifyId,
    pub files: LinearMap<FileFormat, FileId>,
    pub covers: Vec<FileId>,
    pub available: bool,
    pub explicit: bool,
}

#[derive(Debug, Clone)]
pub struct Show {
    pub id: SpotifyId,
    pub name: String,
    pub publisher: String,
    pub episodes: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Playlist {
    pub user: String,
    pub length: i32,
    pub name: String,
    pub tracks: Vec<SpotifyId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl Metadata for Track {
    type Message = protocol::metadata::Track;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/track/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg
            .get_artist()
            .iter()
            .filter(|artist| artist.has_gid())
            .map(|artist| SpotifyId::from_raw(artist.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let files = msg
            .get_file()
            .iter()
            .filter(|file| file.has_file_id())
            .map(|file| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(file.get_file_id());
                (file.get_format(), FileId(dst))
            })
            .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            duration: msg.get_duration(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()).unwrap(),
            artists: artists,
            files: files,
            alternatives: msg
                .get_alternative()
                .iter()
                .map(|alt| SpotifyId::from_raw(alt.get_gid()).unwrap())
                .collect(),
            available: parse_restrictions(msg.get_restriction(), &country, "premium"),
        }
    }
}

impl Metadata for Album {
    type Message = protocol::metadata::Album;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/album/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg
            .get_artist()
            .iter()
            .filter(|artist| artist.has_gid())
            .map(|artist| SpotifyId::from_raw(artist.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let tracks = msg
            .get_disc()
            .iter()
            .flat_map(|disc| disc.get_track())
            .filter(|track| track.has_gid())
            .map(|track| SpotifyId::from_raw(track.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let covers = msg
            .get_cover_group()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl Metadata for Playlist {
    type Message = protocol::playlist4changes::SelectedListContent;

    fn base_url() -> &'static str {
        "hm://playlist/v2/playlist"
    }

    fn get(session: &Session, id: SpotifyId) -> Box<Future<Item = Self, Error = MercuryError>> {
        let uri = format!("{}/{}", Self::base_url(), id.to_base62());
        let request = session.mercury().get(uri);

        let session = session.clone();
        Box::new(request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(Self::parse(&msg, &session))
        }))
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {

        let tracks = msg
            .get_contents()
            .get_items()
            .iter()
            .map(|item| {
                let uri_split = item.get_uri().split(":");
                let uri_parts: Vec<&str> = uri_split.collect();
                SpotifyId::from_base62(uri_parts[2]).unwrap()
            })
            .collect::<Vec<_>>();

        println!("parse Message: {:?}", msg);
        Playlist {
            //id: SpotifyId::from_raw(msg.get_attributes().get_id()).unwrap(),
            name: msg.get_attributes().get_name().to_owned(),
            length: msg.get_length(),
            tracks: tracks,
            user: msg.get_owner_username().to_string(),
        }
    }
}

impl Metadata for Artist {
    type Message = protocol::metadata::Artist;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/artist/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks: Vec<SpotifyId> = match msg
            .get_top_track()
            .iter()
            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
        {
            Some(tracks) => tracks
                .get_track()
                .iter()
                .filter(|track| track.has_gid())
                .map(|track| SpotifyId::from_raw(track.get_gid()).unwrap())
                .collect::<Vec<_>>(),
            None => Vec::new(),
        };

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks,
        }
    }
}

// Podcast
impl Metadata for Episode {
    type Message = protocol::metadata::Episode;

    fn base_url() -> &'static str {
        "hm://metadata/3/episode"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let files = msg
            .get_file()
            .iter()
            .filter(|file| file.has_file_id())
            .map(|file| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(file.get_file_id());
                (file.get_format(), FileId(dst))
            })
            .collect();

        let covers = msg
            .get_covers()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Episode {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            external_url: msg.get_external_url().to_owned(),
            duration: msg.get_duration().to_owned(),
            language: msg.get_language().to_owned(),
            show: SpotifyId::from_raw(msg.get_show().get_gid()).unwrap(),
            covers: covers,
            files: files,
            available: parse_restrictions(msg.get_restriction(), &country, "premium"),
            explicit: msg.get_explicit().to_owned(),
        }
    }
}

impl Metadata for Show {
    type Message = protocol::metadata::Show;

    fn base_url() -> &'static str {
        "hm://metadata/3/show"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let episodes = msg
            .get_episode()
            .iter()
            .filter(|episode| episode.has_gid())
            .map(|episode| SpotifyId::from_raw(episode.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let covers = msg
            .get_covers()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Show {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            publisher: msg.get_publisher().to_owned(),
            episodes: episodes,
            covers: covers,
        }
    }
}

struct StrChunks<'s>(&'s str, usize);

trait StrChunksExt {
    fn chunks(&self, size: usize) -> StrChunks;
}

impl StrChunksExt for str {
    fn chunks(&self, size: usize) -> StrChunks {
        StrChunks(self, size)
    }
}

impl<'s> Iterator for StrChunks<'s> {
    type Item = &'s str;
    fn next(&mut self) -> Option<&'s str> {
        let &mut StrChunks(data, size) = self;
        if data.is_empty() {
            None
        } else {
            let ret = Some(&data[..size]);
            self.0 = &data[size..];
            ret
        }
    }
}
<commit_msg>Make standard metadata get handle also playlists<commit_after>extern crate byteorder;
extern crate futures;
extern crate linear_map;
extern crate protobuf;

extern crate librespot_core;
extern crate librespot_protocol as protocol;

pub mod cover;

use futures::future;
use futures::Future;
use linear_map::LinearMap;

use librespot_core::mercury::MercuryError;
use librespot_core::session::Session;
use librespot_core::spotify_id::{FileId, SpotifyAudioType, SpotifyId};

pub use protocol::metadata::AudioFile_Format as FileFormat;

fn countrylist_contains(list: &str, country: &str) -> bool {
    list.chunks(2).any(|cc| cc == country)
}

fn parse_restrictions<'s, I>(restrictions: I, country: &str, catalogue: &str) -> bool
where
    I: IntoIterator<Item = &'s protocol::metadata::Restriction>,
{
    let mut forbidden = "".to_string();
    let mut has_forbidden = false;

    let mut allowed = "".to_string();
    let mut has_allowed = false;

    let rs = restrictions
        .into_iter()
        .filter(|r| r.get_catalogue_str().contains(&catalogue.to_owned()));

    for r in rs {
        if r.has_countries_forbidden() {
            forbidden.push_str(r.get_countries_forbidden());
            has_forbidden = true;
        }

        if r.has_countries_allowed() {
            allowed.push_str(r.get_countries_allowed());
            has_allowed = true;
        }
    }

    (has_forbidden || has_allowed)
        && (!has_forbidden || !countrylist_contains(forbidden.as_str(), country))
        && (!has_allowed || countrylist_contains(allowed.as_str(), country))
}

// A wrapper with fields the player needs
#[derive(Debug, Clone)]
pub struct AudioItem {
    pub id: SpotifyId,
    pub uri: String,
    pub files: LinearMap<FileFormat, FileId>,
    pub name: String,
    pub available: bool,
    pub alternatives: Option<Vec<SpotifyId>>,
}

impl AudioItem {
    pub fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        match id.audio_type {
            SpotifyAudioType::Track => Track::get_audio_item(session, id),
            SpotifyAudioType::Podcast => Episode::get_audio_item(session, id),
            SpotifyAudioType::NonPlayable => {
                Box::new(future::err::<AudioItem, MercuryError>(MercuryError))
            }
        }
    }
}

trait AudioFiles {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>>;
}

impl AudioFiles for Track {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        Box::new(Self::get(session, id).and_then(move |item| {
            Ok(AudioItem {
                id: id,
                uri: format!("spotify:track:{}", id.to_base62()),
                files: item.files,
                name: item.name,
                available: item.available,
                alternatives: Some(item.alternatives),
            })
        }))
    }
}

impl AudioFiles for Episode {
    fn get_audio_item(
        session: &Session,
        id: SpotifyId,
    ) -> Box<dyn Future<Item = AudioItem, Error = MercuryError>> {
        Box::new(Self::get(session, id).and_then(move |item| {
            Ok(AudioItem {
                id: id,
                uri: format!("spotify:episode:{}", id.to_base62()),
                files: item.files,
                name: item.name,
                available: item.available,
                alternatives: None,
            })
        }))
    }
}
pub trait Metadata: Send + Sized + 'static {
    type Message: protobuf::Message;

    fn request_url(id: SpotifyId) -> String;
    fn parse(msg: &Self::Message, session: &Session) -> Self;

<<<<<<< HEAD
    fn get(session: &Session, id: SpotifyId) -> Box<dyn Future<Item = Self, Error = MercuryError>> {
        let uri = format!("{}/{}", Self::base_url(), id.to_base16());
=======
    fn get(session: &Session, id: SpotifyId) -> Box<Future<Item = Self, Error = MercuryError>> {
        let uri = Self::request_url(id);
>>>>>>> Make standard metadata get handle also playlists
        let request = session.mercury().get(uri);

        let session = session.clone();
        Box::new(request.and_then(move |response| {
            let data = response.payload.first().expect("Empty payload");
            let msg: Self::Message = protobuf::parse_from_bytes(data).unwrap();

            Ok(Self::parse(&msg, &session))
        }))
    }
}

#[derive(Debug, Clone)]
pub struct Track {
    pub id: SpotifyId,
    pub name: String,
    pub duration: i32,
    pub album: SpotifyId,
    pub artists: Vec<SpotifyId>,
    pub files: LinearMap<FileFormat, FileId>,
    pub alternatives: Vec<SpotifyId>,
    pub available: bool,
}

#[derive(Debug, Clone)]
pub struct Album {
    pub id: SpotifyId,
    pub name: String,
    pub artists: Vec<SpotifyId>,
    pub tracks: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Episode {
    pub id: SpotifyId,
    pub name: String,
    pub external_url: String,
    pub duration: i32,
    pub language: String,
    pub show: SpotifyId,
    pub files: LinearMap<FileFormat, FileId>,
    pub covers: Vec<FileId>,
    pub available: bool,
    pub explicit: bool,
}

#[derive(Debug, Clone)]
pub struct Show {
    pub id: SpotifyId,
    pub name: String,
    pub publisher: String,
    pub episodes: Vec<SpotifyId>,
    pub covers: Vec<FileId>,
}

#[derive(Debug, Clone)]
pub struct Playlist {
    pub user: String,
    pub length: i32,
    pub name: String,
    pub tracks: Vec<SpotifyId>,
}

#[derive(Debug, Clone)]
pub struct Artist {
    pub id: SpotifyId,
    pub name: String,
    pub top_tracks: Vec<SpotifyId>,
}

impl Metadata for Track {
    type Message = protocol::metadata::Track;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/track/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let artists = msg
            .get_artist()
            .iter()
            .filter(|artist| artist.has_gid())
            .map(|artist| SpotifyId::from_raw(artist.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let files = msg
            .get_file()
            .iter()
            .filter(|file| file.has_file_id())
            .map(|file| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(file.get_file_id());
                (file.get_format(), FileId(dst))
            })
            .collect();

        Track {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            duration: msg.get_duration(),
            album: SpotifyId::from_raw(msg.get_album().get_gid()).unwrap(),
            artists: artists,
            files: files,
            alternatives: msg
                .get_alternative()
                .iter()
                .map(|alt| SpotifyId::from_raw(alt.get_gid()).unwrap())
                .collect(),
            available: parse_restrictions(msg.get_restriction(), &country, "premium"),
        }
    }
}

impl Metadata for Album {
    type Message = protocol::metadata::Album;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/album/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let artists = msg
            .get_artist()
            .iter()
            .filter(|artist| artist.has_gid())
            .map(|artist| SpotifyId::from_raw(artist.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let tracks = msg
            .get_disc()
            .iter()
            .flat_map(|disc| disc.get_track())
            .filter(|track| track.has_gid())
            .map(|track| SpotifyId::from_raw(track.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let covers = msg
            .get_cover_group()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Album {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            artists: artists,
            tracks: tracks,
            covers: covers,
        }
    }
}

impl Metadata for Playlist {
    type Message = protocol::playlist4changes::SelectedListContent;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://playlist/v2/playlist/{}", id.to_base62())
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {

        let tracks = msg
            .get_contents()
            .get_items()
            .iter()
            .map(|item| {
                let uri_split = item.get_uri().split(":");
                let uri_parts: Vec<&str> = uri_split.collect();
                SpotifyId::from_base62(uri_parts[2]).unwrap()
            })
            .collect::<Vec<_>>();

        println!("parse Message: {:?}", msg);
        Playlist {
            //id: SpotifyId::from_raw(msg.get_attributes().get_id()).unwrap(),
            name: msg.get_attributes().get_name().to_owned(),
            length: msg.get_length(),
            tracks: tracks,
            user: msg.get_owner_username().to_string(),
        }
    }
}

impl Metadata for Artist {
    type Message = protocol::metadata::Artist;

    fn request_url(id: SpotifyId) -> String {
        format!("hm://metadata/3/artist/{}", id.to_base16())
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let top_tracks: Vec<SpotifyId> = match msg
            .get_top_track()
            .iter()
            .find(|tt| !tt.has_country() || countrylist_contains(tt.get_country(), &country))
        {
            Some(tracks) => tracks
                .get_track()
                .iter()
                .filter(|track| track.has_gid())
                .map(|track| SpotifyId::from_raw(track.get_gid()).unwrap())
                .collect::<Vec<_>>(),
            None => Vec::new(),
        };

        Artist {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            top_tracks: top_tracks,
        }
    }
}

// Podcast
impl Metadata for Episode {
    type Message = protocol::metadata::Episode;

    fn base_url() -> &'static str {
        "hm://metadata/3/episode"
    }

    fn parse(msg: &Self::Message, session: &Session) -> Self {
        let country = session.country();

        let files = msg
            .get_file()
            .iter()
            .filter(|file| file.has_file_id())
            .map(|file| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(file.get_file_id());
                (file.get_format(), FileId(dst))
            })
            .collect();

        let covers = msg
            .get_covers()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Episode {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            external_url: msg.get_external_url().to_owned(),
            duration: msg.get_duration().to_owned(),
            language: msg.get_language().to_owned(),
            show: SpotifyId::from_raw(msg.get_show().get_gid()).unwrap(),
            covers: covers,
            files: files,
            available: parse_restrictions(msg.get_restriction(), &country, "premium"),
            explicit: msg.get_explicit().to_owned(),
        }
    }
}

impl Metadata for Show {
    type Message = protocol::metadata::Show;

    fn base_url() -> &'static str {
        "hm://metadata/3/show"
    }

    fn parse(msg: &Self::Message, _: &Session) -> Self {
        let episodes = msg
            .get_episode()
            .iter()
            .filter(|episode| episode.has_gid())
            .map(|episode| SpotifyId::from_raw(episode.get_gid()).unwrap())
            .collect::<Vec<_>>();

        let covers = msg
            .get_covers()
            .get_image()
            .iter()
            .filter(|image| image.has_file_id())
            .map(|image| {
                let mut dst = [0u8; 20];
                dst.clone_from_slice(image.get_file_id());
                FileId(dst)
            })
            .collect::<Vec<_>>();

        Show {
            id: SpotifyId::from_raw(msg.get_gid()).unwrap(),
            name: msg.get_name().to_owned(),
            publisher: msg.get_publisher().to_owned(),
            episodes: episodes,
            covers: covers,
        }
    }
}

struct StrChunks<'s>(&'s str, usize);

trait StrChunksExt {
    fn chunks(&self, size: usize) -> StrChunks;
}

impl StrChunksExt for str {
    fn chunks(&self, size: usize) -> StrChunks {
        StrChunks(self, size)
    }
}

impl<'s> Iterator for StrChunks<'s> {
    type Item = &'s str;
    fn next(&mut self) -> Option<&'s str> {
        let &mut StrChunks(data, size) = self;
        if data.is_empty() {
            None
        } else {
            let ret = Some(&data[..size]);
            self.0 = &data[size..];
            ret
        }
    }
}
<|endoftext|>


<commit_before>use nom;
use nom::{IResult, alpha, alphanumeric, digit, multispace};

use std::str;
use std::str::{from_utf8, FromStr};

// Identifier
// IDENT := /[a-zA-Z_]\w*/g
named!(ident<&str, &str>, re_match!(r"[a-zA-Z_](?:\w|[?!])*"));

// Whitespace
named!(whitespace, chain!(many0!(multispace), || { &b""[..] }));

// Float literal e.g. `.123` `0.123`
named!(
    float<f32>,
    map_res!(
        map_res!(
            recognize!(
                alt!(
                    delimited!(digit, tag!("."), opt!(complete!(digit))) |
                    delimited!(opt!(digit), tag!("."), digit)
                )
            ),
            str::from_utf8
        ),
        FromStr::from_str
    )
);

#[test]
fn float_test() {
    assert_eq!(float(&b"13.37"[..]), IResult::Done(&b""[..], 13.37));
    assert_eq!(float(&b".1337"[..]), IResult::Done(&b""[..], 0.1337));
}

#[test]
fn ident_test() {
    assert_eq!(ident(&"test?"[..]), IResult::Done("", "test?"));
    assert_eq!(ident(&"test!"[..]), IResult::Done("", "test!"));
    assert_eq!(ident(&"test!?"[..]), IResult::Done("", "test!?"));
    assert_eq!(ident(&"_test!"[..]), IResult::Done("", "_test!"));
}<commit_msg>Add unstable floating point expressions<commit_after>use nom;
use nom::{IResult, alpha, alphanumeric, digit, multispace};

use std::str;
use std::str::{from_utf8, FromStr};

// Identifier
// IDENT := /[a-zA-Z_]\w*/g
named!(ident<&str, &str>, re_match!(r"[a-zA-Z_](?:\w|[?!])*"));

// Whitespace
named!(whitespace, chain!(many0!(multispace), || { &b""[..] }));

// Float literal e.g. `.123` `0.123`
named!(float<f32>,
    map_res!(
        map_res!(
            recognize!(
                alt!(
                    delimited!(digit, tag!("."), opt!(complete!(digit))) |
                    delimited!(opt!(digit), tag!("."), digit)
                )
            ),
            str::from_utf8
        ),
        FromStr::from_str
    )
);

// Parens
named!(parens <f32>,
    ws!(
        delimited!(
            tag!("("), expr, tag!(")")
        )
    )
);

// Factor := digit | float | (expr)
named!(factor<f32>,
    alt!(
        float
        |
        map_res!(
            map_res!(
                ws!(digit),
                str::from_utf8
            ),
            FromStr::from_str
        )
        |
        parens
    )
);

// Term := factor * | / factor
named!(term <f32>, do_parse!(
    init: factor >>
    res:  fold_many0!(
        pair!(alt!(tag!("*") | tag!("/")), factor),
        init,
        |acc, (op, val): (&[u8], f32)| {
            if op[0] as char == '*' {
                acc * val
            } else {
                acc / val
            }
        }
    ) >> (res)
  )
);

// Expression := term + | - term
named!(expr <f32>,
    do_parse!(
        init: term >>
        res:  fold_many0!(
            pair!(alt!(tag!("+") | tag!("-")), term),
            init,
            |acc, (op, val): (&[u8], f32)| {
                if op[0] as char == '+' {
                    acc + val
                } else {
                    acc - val
                }
            }
        ) >> (res)
    )
);

#[test]
fn float_test() {
    assert_eq!(float(&b"13.37"[..]), IResult::Done(&b""[..], 13.37));
    assert_eq!(float(&b".1337"[..]), IResult::Done(&b""[..], 0.1337));
}

#[test]
fn factor_test() {
    assert_eq!(factor(&b"3.5"[..]), IResult::Done(&b""[..], 3.5f32));
    assert_eq!(factor(&b" 5"[..]), IResult::Done(&b""[..], 5f32));
}

#[test]
fn expr_test() {
    assert_eq!(expr(&b"1 + 1.5"[..]), IResult::Done(&b""[..], 2.5f32))
}

#[test]
fn ident_test() {
    assert_eq!(ident(&"test?"[..]), IResult::Done("", "test?"));
    assert_eq!(ident(&"test!"[..]), IResult::Done("", "test!"));
    assert_eq!(ident(&"test!?"[..]), IResult::Done("", "test!?"));
    assert_eq!(ident(&"_test!"[..]), IResult::Done("", "_test!"));
}

#[test]
fn parens_test() {
    assert_eq!(parens(&b"(1.5 + .5)"[..]), IResult::Done(&b""[..], 2f32));
    assert_eq!(parens(&b"(0 * 2 + 3)"[..]), IResult::Done(&b""[..], 3f32));
    assert_eq!(parens(&b"(1 + 2 * (2 + 1))"[..]), IResult::Done(&b""[..], 7f32));
    assert_eq!(parens(&b"(1 + 3 / (2 + 1))"[..]), IResult::Done(&b""[..], 2f32));
}<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use option::{Some, None};
use rt::io::Reader;
use rt::io::ReaderByteConversions;

use rand::Rng;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// It will fail if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::rt::io::mem;
///
/// fn main() {
///     let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
///     println!("{:x}", rng.gen::<uint>());
/// }
/// ```
pub struct ReaderRng<R> {
    priv reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32_()
        } else {
            self.reader.read_be_u32_()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64_()
        } else {
            self.reader.read_be_u64_()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read(v) {
            Some(n) if n == v.len() => return,
            Some(n) => fail!("ReaderRng.fill_bytes could not fill buffer: \
                              read {} out of {} bytes.", n, v.len()),
            None => fail!("ReaderRng.fill_bytes reached eof.")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rt::io::mem::MemReader;
    use cast;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u64, 2u64, 3u64];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u64(), 1);
        assert_eq!(rng.next_u64(), 2);
        assert_eq!(rng.next_u64(), 3);
    }
    #[test]
    fn test_reader_rng_u32() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u32, 2u32, 3u32];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u32(), 1);
        assert_eq!(rng.next_u32(), 2);
        assert_eq!(rng.next_u32(), 3);
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8, .. 8];

        let mut rng = ReaderRng::new(MemReader::new(v.to_owned()));
        rng.fill_bytes(w);

        assert_eq!(v, w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(~[]));
        let mut v = [0u8, .. 3];
        rng.fill_bytes(v);
    }
}
<commit_msg>Remove the extension traits for Readers/Writers<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! A wrapper around any Reader to treat it as an RNG.

use option::{Some, None};
use rt::io::Reader;

use rand::Rng;

/// An RNG that reads random bytes straight from a `Reader`. This will
/// work best with an infinite reader, but this is not required.
///
/// It will fail if it there is insufficient data to fulfill a request.
///
/// # Example
///
/// ```rust
/// use std::rand::{reader, Rng};
/// use std::rt::io::mem;
///
/// fn main() {
///     let mut rng = reader::ReaderRng::new(mem::MemReader::new(~[1,2,3,4,5,6,7,8]));
///     println!("{:x}", rng.gen::<uint>());
/// }
/// ```
pub struct ReaderRng<R> {
    priv reader: R
}

impl<R: Reader> ReaderRng<R> {
    /// Create a new `ReaderRng` from a `Reader`.
    pub fn new(r: R) -> ReaderRng<R> {
        ReaderRng {
            reader: r
        }
    }
}

impl<R: Reader> Rng for ReaderRng<R> {
    fn next_u32(&mut self) -> u32 {
        // This is designed for speed: reading a LE integer on a LE
        // platform just involves blitting the bytes into the memory
        // of the u32, similarly for BE on BE; avoiding byteswapping.
        if cfg!(target_endian="little") {
            self.reader.read_le_u32()
        } else {
            self.reader.read_be_u32()
        }
    }
    fn next_u64(&mut self) -> u64 {
        // see above for explanation.
        if cfg!(target_endian="little") {
            self.reader.read_le_u64()
        } else {
            self.reader.read_be_u64()
        }
    }
    fn fill_bytes(&mut self, v: &mut [u8]) {
        if v.len() == 0 { return }
        match self.reader.read(v) {
            Some(n) if n == v.len() => return,
            Some(n) => fail!("ReaderRng.fill_bytes could not fill buffer: \
                              read {} out of {} bytes.", n, v.len()),
            None => fail!("ReaderRng.fill_bytes reached eof.")
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use rt::io::mem::MemReader;
    use cast;

    #[test]
    fn test_reader_rng_u64() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u64, 2u64, 3u64];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u64(), 1);
        assert_eq!(rng.next_u64(), 2);
        assert_eq!(rng.next_u64(), 3);
    }
    #[test]
    fn test_reader_rng_u32() {
        // transmute from the target to avoid endianness concerns.
        let v = ~[1u32, 2u32, 3u32];
        let bytes: ~[u8] = unsafe {cast::transmute(v)};
        let mut rng = ReaderRng::new(MemReader::new(bytes));

        assert_eq!(rng.next_u32(), 1);
        assert_eq!(rng.next_u32(), 2);
        assert_eq!(rng.next_u32(), 3);
    }
    #[test]
    fn test_reader_rng_fill_bytes() {
        let v = [1u8, 2, 3, 4, 5, 6, 7, 8];
        let mut w = [0u8, .. 8];

        let mut rng = ReaderRng::new(MemReader::new(v.to_owned()));
        rng.fill_bytes(w);

        assert_eq!(v, w);
    }

    #[test]
    #[should_fail]
    fn test_reader_rng_insufficient_bytes() {
        let mut rng = ReaderRng::new(MemReader::new(~[]));
        let mut v = [0u8, .. 3];
        rng.fill_bytes(v);
    }
}
<|endoftext|>