use std::convert::AsRef;
use std::error::Error;
use std::io::Write;
use std::marker::PhantomData;
use crossbeam::sync::MsQueue;
use futures::executor::{spawn, Notify, Spawn};
use futures::{Async, Future};
use error::BoxedErr;
use join::Join;
use shred::{Read, RunningTime, System};
use storage::WriteStorage;
use world::{Component, Entities, Entity};
pub type BoxedFuture<T> = Box<Future<Item = T, Error = BoxedErr> + Send + Sync + 'static>;
#[derive(Debug)]
pub struct DrainErrors<'a> {
queue: &'a mut MsQueue<BoxedErr>,
}
impl<'a> Iterator for DrainErrors<'a> {
type Item = BoxedErr;
fn next(&mut self) -> Option<Self::Item> {
self.queue.try_pop()
}
}
#[derive(Debug)]
pub struct Errors {
errors: MsQueue<BoxedErr>,
}
impl Default for Errors {
fn default() -> Self {
Errors::new()
}
}
impl Errors {
pub fn new() -> Self {
Errors {
errors: MsQueue::new(),
}
}
pub fn add(&self, error: BoxedErr) {
self.errors.push(error);
}
pub fn execute<E, F>(&self, f: F)
where
E: Error + Send + Sync + 'static,
F: FnOnce() -> Result<(), E>,
{
if let Err(e) = f() {
self.add(BoxedErr::new(e));
}
}
pub fn has_error(&self) -> bool {
!self.errors.is_empty()
}
pub fn pop_err(&mut self) -> Option<BoxedErr> {
self.errors.try_pop()
}
pub fn drain(&mut self) -> DrainErrors {
DrainErrors {
queue: &mut self.errors,
}
}
pub fn collect(&mut self) -> Vec<BoxedErr> {
self.drain().collect()
}
pub fn print_and_exit(&mut self) {
use std::io::stderr;
use std::process::exit;
if self.errors.is_empty() {
return;
}
let mut errors = self.collect();
let stderr = stderr();
let mut stderr = stderr.lock();
writeln!(
&mut stderr,
"Exiting program because of {} errors...",
errors.len()
).unwrap();
for (ind, error) in errors.drain(..).enumerate() {
let error = error.as_ref();
writeln!(&mut stderr, "{}: {}", ind, error).unwrap();
}
exit(1);
}
}
#[derive(Derivative)]
#[derivative(Default(bound = ""))]
pub struct Merge<F> {
#[derivative(Default(value = "PhantomData"))]
future_type: PhantomData<F>,
spawns: Vec<(Entity, Spawn<F>)>,
}
impl<F> Merge<F> {
pub fn new() -> Self {
Default::default()
}
}
impl<'a, T, F> System<'a> for Merge<F>
where
T: Component + Send + Sync + 'static,
F: Future<Item = T, Error = BoxedErr> + Component + Send + Sync,
{
type SystemData = (
Entities<'a>,
Read<'a, Errors>,
WriteStorage<'a, F>,
WriteStorage<'a, T>,
);
fn run(&mut self, (entities, errors, mut futures, mut pers): Self::SystemData) {
for (e, future) in (&*entities, futures.drain()).join() {
self.spawns.push((e, spawn(future)));
}
retain_mut(&mut self.spawns, |spawn| {
match spawn.1.poll_future_notify(NOTIFY_IGNORE, 0) {
Ok(Async::NotReady) => true,
Ok(Async::Ready(value)) => {
pers.insert(spawn.0, value);
false
}
Err(err) => {
errors.add(err);
false
}
}
});
}
fn running_time(&self) -> RunningTime {
RunningTime::Short
}
}
struct NotifyIgnore;
impl Notify for NotifyIgnore {
fn notify(&self, _: usize) {
}
}
static NOTIFY_IGNORE: &&NotifyIgnore = &&NotifyIgnore;
fn retain_mut<T, F>(vec: &mut Vec<T>, mut f: F)
where
F: FnMut(&mut T) -> bool,
{
let len = vec.len();
let mut del = 0;
{
let v = &mut **vec;
for i in 0..len {
if !f(&mut v[i]) {
del += 1;
} else if del > 0 {
v.swap(i - del, i);
}
}
}
if del > 0 {
vec.truncate(len - del);
}
}
#[cfg(test)]
mod test {
use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};
use futures::future::{result, Future, FutureResult};
use futures::task;
use futures::Poll;
use common::{BoxedErr, Errors, Merge};
use shred::DispatcherBuilder;
use storage::{NullStorage, VecStorage};
use world::{Component, World};
#[test]
fn test_merge() {
#[derive(Default)]
struct TestComponent;
impl Component for TestComponent {
type Storage = NullStorage<Self>;
}
struct TestFuture {
result: FutureResult<TestComponent, BoxedErr>,
}
impl Future for TestFuture {
type Item = TestComponent;
type Error = BoxedErr;
fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
task::current();
self.result.poll()
}
}
impl Component for TestFuture {
type Storage = VecStorage<Self>;
}
#[derive(Debug)]
struct TestError;
impl Display for TestError {
fn fmt(&self, fmt: &mut Formatter) -> FmtResult {
fmt.write_str("TestError")
}
}
impl Error for TestError {
fn description(&self) -> &str {
"An error used for testing"
}
fn cause(&self) -> Option<&Error> {
None
}
}
let mut world = World::new();
world.add_resource(Errors::new());
world.register::<TestComponent>();
world.register::<TestFuture>();
let success = world
.create_entity()
.with(TestFuture {
result: result(Ok(TestComponent)),
})
.build();
let error = world
.create_entity()
.with(TestFuture {
result: result(Err(BoxedErr::new(TestError))),
})
.build();
let system: Merge<TestFuture> = Merge::new();
let mut dispatcher = DispatcherBuilder::new().with(system, "merge", &[]).build();
dispatcher.dispatch_seq(&mut world.res);
let components = world.read::<TestComponent>();
assert!(components.get(success).is_some());
assert!(components.get(error).is_none());
assert_eq!(
world.read_resource::<Errors>().errors.pop().description(),
"An error used for testing"
);
assert!(world.read_resource::<Errors>().errors.try_pop().is_none());
}
}