From d9bbf96bc94543edfcd9f584d021c027b92c7a67 Mon Sep 17 00:00:00 2001 From: Thomas Schaller Date: Sun, 26 May 2019 09:28:01 +0200 Subject: [PATCH 1/4] Fix multiple doc warnings --- src/dispatch/builder.rs | 8 ++------ src/dispatch/dispatcher.rs | 7 ++----- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/dispatch/builder.rs b/src/dispatch/builder.rs index e7a242fd8..2fd58b9b7 100644 --- a/src/dispatch/builder.rs +++ b/src/dispatch/builder.rs @@ -183,9 +183,7 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> { /// /// Thread-local systems are dispatched in-order. /// - /// Same as - /// [`add_thread_local()`](struct.DispatcherBuilder.html#method. - /// add_thread_local), but returns `self` to enable method chaining. + /// Same as [DispatcherBuilder::add_thread_local], but returns `self` to enable method chaining. pub fn with_thread_local(mut self, system: T) -> Self where T: for<'c> RunNow<'c> + 'b, @@ -217,9 +215,7 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> { /// Thread-local systems are not affected by barriers; /// they're always executed at the end. /// - /// Same as - /// [`add_barrier()`](struct.DispatcherBuilder.html#method.add_barrier), - /// but returns `self` to enable method chaining. + /// Same as [DispatcherBuilder::add_barrier], but returns `self` to enable method chaining. pub fn with_barrier(mut self) -> Self { self.add_barrier(); diff --git a/src/dispatch/dispatcher.rs b/src/dispatch/dispatcher.rs index 046eb738d..6575de8be 100644 --- a/src/dispatch/dispatcher.rs +++ b/src/dispatch/dispatcher.rs @@ -43,16 +43,13 @@ impl<'a, 'b> Dispatcher<'a, 'b> { /// /// This function automatically redirects to /// - /// * [`dispatch_par`] in case it is supported - /// * [`dispatch_seq`] otherwise + /// * [Dispatcher::dispatch_par] in case it is supported + /// * [Dispatcher::dispatch_seq] otherwise /// /// and runs `dispatch_thread_local` afterwards. /// /// Please note that this method assumes that no resource /// is currently borrowed. If that's the case, it panics. - /// - /// [`dispatch_par`]: struct.Dispatcher.html#method.dispatch_par - /// [`dispatch_seq`]: struct.Dispatcher.html#method.dispatch_seq pub fn dispatch(&mut self, world: &World) { #[cfg(feature = "parallel")] self.dispatch_par(world); From b7fe3ab044ae01e2b8289b817a2eb323dba0953e Mon Sep 17 00:00:00 2001 From: Thomas Schaller Date: Sun, 26 May 2019 09:31:02 +0200 Subject: [PATCH 2/4] Formatting --- src/cell.rs | 4 ++-- src/dispatch/builder.rs | 6 ++++-- src/meta.rs | 3 +-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/cell.rs b/src/cell.rs index 72ab67744..3c7837c5d 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -30,7 +30,7 @@ impl Error for InvalidBorrow { /// /// Access the value via `std::ops::Deref` (e.g. `*val`) #[derive(Debug)] -pub struct Ref<'a, T: 'a + ?Sized> { +pub struct Ref<'a, T: ?Sized + 'a> { flag: &'a AtomicUsize, value: &'a T, } @@ -130,7 +130,7 @@ impl<'a, T: ?Sized> Clone for Ref<'a, T> { /// /// Access the value via `std::ops::DerefMut` (e.g. `*val`) #[derive(Debug)] -pub struct RefMut<'a, T: 'a + ?Sized> { +pub struct RefMut<'a, T: ?Sized + 'a> { flag: &'a AtomicUsize, value: &'a mut T, } diff --git a/src/dispatch/builder.rs b/src/dispatch/builder.rs index 2fd58b9b7..d79c02764 100644 --- a/src/dispatch/builder.rs +++ b/src/dispatch/builder.rs @@ -183,7 +183,8 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> { /// /// Thread-local systems are dispatched in-order. /// - /// Same as [DispatcherBuilder::add_thread_local], but returns `self` to enable method chaining. + /// Same as [DispatcherBuilder::add_thread_local], but returns `self` to + /// enable method chaining. pub fn with_thread_local(mut self, system: T) -> Self where T: for<'c> RunNow<'c> + 'b, @@ -215,7 +216,8 @@ impl<'a, 'b> DispatcherBuilder<'a, 'b> { /// Thread-local systems are not affected by barriers; /// they're always executed at the end. /// - /// Same as [DispatcherBuilder::add_barrier], but returns `self` to enable method chaining. + /// Same as [DispatcherBuilder::add_barrier], but returns `self` to enable + /// method chaining. pub fn with_barrier(mut self) -> Self { self.add_barrier(); diff --git a/src/meta.rs b/src/meta.rs index 8d69fa801..e89cfd3a2 100644 --- a/src/meta.rs +++ b/src/meta.rs @@ -3,7 +3,7 @@ use std::{any::TypeId, marker::PhantomData}; use hashbrown::HashMap; use mopa::Any; -use crate::{Resource, World, ResourceId}; +use crate::{Resource, ResourceId, World}; /// This implements `Send` and `Sync` unconditionally. /// (the trait itself doesn't need to have these bounds and the @@ -436,7 +436,6 @@ mod tests { table.register(&ImplementorA(125)); table.register(&ImplementorB(111111)); - { let mut iter = table.iter(&mut world); assert_eq!(iter.next().unwrap().method1(), 3); From 3fea8a62dbfc0243852f886d6b0257641cc23ab8 Mon Sep 17 00:00:00 2001 From: Thomas Schaller Date: Sun, 26 May 2019 10:10:54 +0200 Subject: [PATCH 3/4] Move methods from specs::WorldExt to World * system_data * setup * exec --- Cargo.toml | 2 +- src/world/mod.rs | 151 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 150 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a39c0357f..2060f6be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "shred" -version = "0.8.1" +version = "0.9.0" authors = ["torkleyy "] description = """ Dispatches systems in parallel which need read access to some resources, diff --git a/src/world/mod.rs b/src/world/mod.rs index 30eb3e1de..040aba5b0 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -15,7 +15,10 @@ use std::{ use hashbrown::HashMap; use mopa::Any; -use crate::cell::{Ref, RefMut, TrustCell}; +use crate::{ + cell::{Ref, RefMut, TrustCell}, + SystemData, +}; use self::entry::create_entry; @@ -158,9 +161,25 @@ impl ResourceId { } } -/// A resource container, which provides methods to access to +/// A [Resource] container, which provides methods to insert, access and manage /// the contained resources. /// +/// Many methods take `&self` which works because everything +/// is stored with **interior mutability**. In case you violate +/// the borrowing rules of Rust (multiple reads xor one write), +/// you will get a panic. +/// +/// # Use with Specs +/// +/// If you're using this from the Specs ECS library, there are two things to be +/// aware of: +/// +/// 1. There are many utility methods Specs provides. To use them, you need to +/// import `specs::WorldExt`. +/// 2. You should not use [World::empty], but rather `specs::WorldExt::new`. The +/// latter can simply be called using `World::new()`, as long as `WorldExt` +/// is imported. +/// /// # Resource Ids /// /// Resources are identified by `ResourceId`s, which consist of a `TypeId`. @@ -242,6 +261,134 @@ impl World { create_entry(self.resources.entry(ResourceId::new::())) } + /// Gets `SystemData` `T` from the `World`. This can be used to retrieve + /// data just like in [System](crate::System)s. + /// + /// This will not setup the system data, i.e. resources fetched here must + /// exist already. + /// + /// # Examples + /// + /// ``` + /// # use shred::*; + /// # #[derive(Default)] struct Timer; #[derive(Default)] struct AnotherResource; + /// + /// // NOTE: If you use Specs, use `World::new` instead. + /// let mut world = World::empty(); + /// world.insert(Timer); + /// world.insert(AnotherResource); + /// let system_data: (Read, Read) = world.system_data(); + /// ``` + /// + /// # Panics + /// + /// * Panics if `T` is already borrowed in an incompatible way. + pub fn system_data<'a, T>(&'a self) -> T + where + T: SystemData<'a>, + { + SystemData::fetch(&self) + } + + /// Sets up system data `T` for fetching afterwards. + /// + /// Most `SystemData` implementations will insert a sensible default value, + /// by implementing [SystemData::setup]. However, it is not guaranteed to + /// do that; if there is no sensible default, `setup` might not do anything. + /// + /// # Examples + /// + /// ``` + /// use shred::{Read, World}; + /// + /// #[derive(Default)] + /// struct MyCounter(u32); + /// + /// // NOTE: If you use Specs, use `World::new` instead. + /// let mut world = World::empty(); + /// assert!(!world.has_value::()); + /// + /// // `Read` requires a `Default` implementation, and uses + /// // that to initialize the resource + /// world.setup::>(); + /// assert!(world.has_value::()); + /// ``` + /// + /// Here's another example, showing the case where no resource gets + /// initialized: + /// + /// ``` + /// use shred::{ReadExpect, World}; + /// + /// struct MyCounter(u32); + /// + /// // NOTE: If you use Specs, use `World::new` instead. + /// let mut world = World::empty(); + /// + /// world.setup::>(); + /// ``` + pub fn setup<'a, T: SystemData<'a>>(&mut self) { + T::setup(self); + } + + /// Executes `f` once, right now and with the specified system data. + /// + /// This sets up the system data `f` expects, fetches it and then + /// executes `f`. This is essentially like a one-time [System]. + /// + /// This is especially useful if you either need a lot of system data or, + /// with Specs, if you want to build an entity and for that you need to + /// access resources first - just fetching the resources and building + /// the entity would cause a double borrow. + /// + /// **Calling this method is equivalent to:** + /// + /// ``` + /// # use shred::*; + /// # struct MySystemData; impl MySystemData { fn do_something(&self) {} } + /// # impl<'a> SystemData<'a> for MySystemData { + /// # fn fetch(res: &World) -> Self { MySystemData } + /// # fn reads() -> Vec { vec![] } + /// # fn writes() -> Vec { vec![] } + /// # fn setup(res: &mut World) {} + /// # } + /// # let mut world = World::empty(); + /// { + /// // note the extra scope + /// world.setup::(); + /// let my_data: MySystemData = world.system_data(); + /// my_data.do_something(); + /// } + /// ``` + /// + /// # Examples + /// + /// ``` + /// # use shred::*; + /// // NOTE: If you use Specs, use `World::new` instead. + /// let mut world = World::empty(); + /// + /// #[derive(Default)] + /// struct MyRes { + /// field: i32, + /// } + /// + /// world.exec(|(mut my_res,): (Write,)| { + /// assert_eq!(my_res.field, 0); + /// my_res.field = 5; + /// }); + /// + /// assert_eq!(world.fetch::().field, 5); + /// ``` + pub fn exec<'a, F, R, T>(&'a mut self, f: F) -> R + where + F: FnOnce(T) -> R, + T: SystemData<'a>, + { + self.setup::(); + f(self.system_data()) + } + /// Fetches the resource with the specified type `T` or panics if it doesn't /// exist. /// From e2c3d47f60cf74fe29ced1a32ddd38de9e614eb7 Mon Sep 17 00:00:00 2001 From: Thomas Schaller Date: Mon, 27 May 2019 18:37:33 +0200 Subject: [PATCH 4/4] Add tests --- src/world/mod.rs | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/src/world/mod.rs b/src/world/mod.rs index 040aba5b0..1477ec856 100644 --- a/src/world/mod.rs +++ b/src/world/mod.rs @@ -615,6 +615,60 @@ mod tests { ); } + #[test] + fn system_data() { + let mut world = World::empty(); + + world.insert(5u32); + let x = *world.system_data::>(); + assert_eq!(x, 5); + } + + #[test] + fn setup() { + let mut world = World::empty(); + + world.insert(5u32); + world.setup::>(); + let x = *world.system_data::>(); + assert_eq!(x, 5); + + world.remove::(); + world.setup::>(); + let x = *world.system_data::>(); + assert_eq!(x, 0); + } + + #[test] + fn exec() { + let mut world = World::empty(); + + world.exec(|(float, boolean): (Read, Read)| { + assert_eq!(*float, 0.0); + assert_eq!(*boolean, false); + }); + + world.exec(|(mut float, mut boolean): (Write, Write)| { + *float = 4.3; + *boolean = true; + }); + + world.exec(|(float, boolean): (Read, ReadExpect)| { + assert_eq!(*float, 4.3); + assert_eq!(*boolean, true); + }); + } + + #[test] + #[should_panic] + fn exec_panic() { + let mut world = World::empty(); + + world.exec(|(_float, _boolean): (Write, Write)| { + panic!(); + }); + } + #[test] #[should_panic] fn invalid_fetch_by_id0() {