├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── src ├── ecs │ ├── Cargo.toml │ └── lib.rs ├── id │ ├── Cargo.toml │ └── lib.rs ├── lib.rs └── secs │ ├── Cargo.toml │ └── lib.rs └── tests ├── ecs.rs └── secs.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled files 2 | *.o 3 | *.so 4 | *.rlib 5 | *.dll 6 | 7 | # Executables 8 | *.exe 9 | 10 | # Generated by Cargo 11 | /target/ 12 | /Cargo.lock 13 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "simplecs" 3 | version = "0.0.1" 4 | license = "Apache-2.0" 5 | keywords = ["gamedev"] 6 | authors = [ 7 | "Dzmitry Malyshau ", 8 | ] 9 | 10 | [lib] 11 | name = "simplecs" 12 | 13 | [dependencies.id] 14 | path = "src/id" 15 | 16 | [dependencies.ecs] 17 | path = "src/ecs" 18 | plugin = true 19 | 20 | [dependencies.secs] 21 | path = "src/secs" 22 | plugin = true 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/kvark/simplecs.png?branch=master)](https://travis-ci.org/kvark/simplecs) 2 | 3 | Simple Entity-Component System in Rust: 4 | - minimal functionality 5 | - perfect data representation 6 | - no unsafe code 7 | 8 | Crates: 9 | - `id`: provides `Id` and `Array` helpers for safer addressing 10 | - `ecs`: simple macro-generated ECS 11 | - `secs` syntax extension (struct decorator) doing the same but properly handing visibility and generics 12 | -------------------------------------------------------------------------------- /src/ecs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ecs" 3 | version = "0.0.1" 4 | description = "Simple ECS macro" 5 | 6 | [lib] 7 | name = "ecs" 8 | path = "lib.rs" 9 | -------------------------------------------------------------------------------- /src/ecs/lib.rs: -------------------------------------------------------------------------------- 1 | #![crate_type = "dylib"] 2 | 3 | #[macro_export] 4 | macro_rules! world { 5 | ($space:ident ($param:ty), $($name:ident : $component:ty,)*) => { 6 | /// A collection of pointers to components 7 | #[derive(Clone, Debug, Eq, PartialEq)] 8 | pub struct Entity { 9 | $( 10 | pub $name: Option<$space::Id<$component>>, 11 | )* 12 | } 13 | 14 | impl Entity { 15 | pub fn new() -> Entity { 16 | Entity { 17 | $( 18 | $name: None, 19 | )* 20 | } 21 | } 22 | } 23 | 24 | /// A collection of component arrays 25 | pub struct Components { 26 | $( 27 | pub $name: $space::Array<$component>, 28 | )* 29 | } 30 | 31 | /// Component add_to() wrapper 32 | pub struct Adder<'d> { 33 | pub entity: Entity, 34 | data: &'d mut Components, 35 | } 36 | impl<'d> Adder<'d> { 37 | $( 38 | pub fn $name(mut self, value: $component) -> Adder<'d> { 39 | debug_assert!(self.entity.$name.is_none()); 40 | let id = self.data.$name.add(value); 41 | self.entity.$name = Some(id); 42 | self 43 | } 44 | )* 45 | } 46 | 47 | impl Components { 48 | pub fn new() -> Components { 49 | Components { 50 | $( 51 | $name: $space::Array::new(), 52 | )* 53 | } 54 | } 55 | pub fn add<'d>(&'d mut self) -> Adder<'d> { 56 | Adder {entity: Entity::new(), data: self,} 57 | } 58 | } 59 | 60 | /// A system responsible for some aspect (physics, rendering, etc) 61 | pub trait System: Send { 62 | fn process(&mut self, &mut $param, &mut Components, &mut Vec); 63 | } 64 | 65 | /// A top level union of entities, their data, and systems 66 | pub struct World { 67 | pub data: Components, 68 | pub entities: Vec, 69 | pub systems: Vec>, 70 | } 71 | 72 | impl World { 73 | pub fn new() -> World { 74 | World { 75 | data: Components::new(), 76 | entities: Vec::new(), 77 | systems: Vec::new(), 78 | } 79 | } 80 | pub fn add_system(&mut self, system: S) { 81 | self.systems.push(Box::new(system)); 82 | } 83 | pub fn update(&mut self, param: &mut $param) { 84 | for sys in self.systems.iter_mut() { 85 | sys.process(param, &mut self.data, &mut self.entities); 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/id/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "id" 3 | version = "0.0.1" 4 | description = "Simple typed IDs" 5 | 6 | [lib] 7 | name = "id" 8 | path = "lib.rs" 9 | -------------------------------------------------------------------------------- /src/id/lib.rs: -------------------------------------------------------------------------------- 1 | //! ID type 2 | 3 | use std::{fmt, slice}; 4 | use std::cmp::Ordering; 5 | use std::marker::PhantomData; 6 | 7 | type IdType = u32; 8 | 9 | // Deriving forces T to have the same properties, we can't afford that. 10 | //#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] 11 | pub struct Id(IdType, PhantomData); 12 | 13 | impl Copy for Id {} 14 | 15 | impl Clone for Id { 16 | fn clone(&self) -> Id { 17 | Id(self.0, PhantomData) 18 | } 19 | } 20 | 21 | impl Eq for Id {} 22 | 23 | impl Ord for Id { 24 | fn cmp(&self, other: &Id) -> Ordering { 25 | self.0.cmp(&other.0) 26 | } 27 | } 28 | 29 | impl PartialEq for Id { 30 | fn eq(&self, other: &Id) -> bool { 31 | self.0 == other.0 32 | } 33 | } 34 | 35 | impl PartialOrd for Id { 36 | fn partial_cmp(&self, other: &Id) -> Option { 37 | self.0.partial_cmp(&other.0) 38 | } 39 | } 40 | 41 | impl fmt::Debug for Id { 42 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 43 | write!(f, "Id({})", self.0) 44 | } 45 | } 46 | 47 | /// Abstract read-only storage 48 | pub trait Storage { 49 | fn get(&self, Id) -> &T; 50 | fn get_opt(&self, opt: Option>) -> Option<&T> { 51 | opt.map(|id| self.get(id)) 52 | } 53 | fn find_id bool>(&self, F) -> Option>; 54 | } 55 | 56 | /// A wrapper around `Vec` that can only be grown up 57 | /// and implements `Storage` 58 | #[derive(Debug)] 59 | pub struct Array(Vec); 60 | 61 | impl Array { 62 | pub fn new() -> Array { 63 | Array(Vec::new()) 64 | } 65 | 66 | pub fn add(&mut self, value: T) -> Id { 67 | self.0.push(value); 68 | Id(self.0.len() as IdType - 1, PhantomData) 69 | } 70 | 71 | pub fn get_mut(&mut self, Id(i, _): Id) -> &mut T { 72 | self.0.get_mut(i as usize).unwrap() 73 | } 74 | 75 | pub fn iter<'a>(&'a self) -> slice::Iter<'a, T> { 76 | self.0.iter() 77 | } 78 | 79 | pub fn iter_mut<'a>(&'a mut self) -> slice::IterMut<'a, T> { 80 | self.0.iter_mut() 81 | } 82 | 83 | pub fn walk_looking_back, &mut T)>(&mut self, fun: F) { 84 | for i in 0.. self.0.len() { 85 | let (left, right) = self.0.split_at_mut(i); 86 | fun(Slice(left), &mut right[0]) 87 | } 88 | } 89 | } 90 | 91 | impl Storage for Array { 92 | fn get(&self, Id(i, _): Id) -> &T { 93 | &self.0[i as usize] 94 | } 95 | 96 | fn find_id bool>(&self, fun: F) -> Option> { 97 | self.0.iter().position(fun).map(|i| Id(i as IdType, PhantomData)) 98 | } 99 | } 100 | 101 | /// Wrapper around a slice that implements `Storage` 102 | #[derive(Debug)] 103 | pub struct Slice<'a, T: 'a>(&'a [T]); 104 | 105 | impl<'a, T> Storage for Slice<'a, T> { 106 | fn get(&self, Id(i, _): Id) -> &T { 107 | &self.0[i as usize] 108 | } 109 | 110 | fn find_id bool>(&self, fun: F) -> Option> { 111 | self.0.iter().position(fun).map(|i| Id(i as IdType, PhantomData)) 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kvark/simplecs/d2c42d18bea1f7b3761374d793d25091635bfe24/src/lib.rs -------------------------------------------------------------------------------- /src/secs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "secs" 3 | version = "0.0.1" 4 | description = "Syntactic ECS" 5 | keywords = ["gamedev"] 6 | license = "Apache-2.0" 7 | authors = [ 8 | "Dzmitry Malyshau ", 9 | ] 10 | 11 | [lib] 12 | name = "secs" 13 | path = "lib.rs" 14 | plugin = true 15 | crate-type = [ "dylib" ] 16 | -------------------------------------------------------------------------------- /src/secs/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(plugin_registrar, quote, rustc_private)] 2 | 3 | extern crate rustc; 4 | extern crate syntax; 5 | 6 | use std::ops::Deref; 7 | use syntax::{ast, ext, codemap}; 8 | use syntax::abi::Abi; 9 | use syntax::owned_slice::OwnedSlice; 10 | use syntax::parse::token; 11 | use syntax::ptr::P; 12 | 13 | #[plugin_registrar] 14 | pub fn registrar(reg: &mut rustc::plugin::Registry) { 15 | reg.register_syntax_extension(token::intern("secs"), 16 | ext::base::Decorator(Box::new(SyntaxEcs)) 17 | ); 18 | } 19 | 20 | #[derive(Clone, Copy)] 21 | pub struct SyntaxEcs; 22 | 23 | impl ext::base::ItemDecorator for SyntaxEcs { 24 | fn expand(&self, context: &mut ext::base::ExtCtxt, span: codemap::Span, 25 | meta_item: &ast::MetaItem, item: &ast::Item, 26 | push: &mut FnMut(P)) 27 | { 28 | use syntax::ext::build::AstBuilder; 29 | //1: extract the path to `id` crate 30 | let id_path = match meta_item.node { 31 | ast::MetaList(_, ref list) if list.len() == 1 => { 32 | match list[0].node { 33 | ast::MetaWord(ref path) => context.ident_of(path.deref()), 34 | _ => { 35 | context.span_err(span, "the `id` path should be a word"); 36 | return 37 | } 38 | } 39 | }, 40 | _ => { 41 | context.span_err(span, "use as `#[secs(id_path)]`"); 42 | return 43 | } 44 | }; 45 | //2: extract the prototype definition 46 | let (definition, generics) = match item.node { 47 | ast::ItemStruct(ref def, ref gen) => (def.deref(), gen), 48 | _ => { 49 | context.span_err(span, "#[secs] only works with structs"); 50 | return 51 | } 52 | }; 53 | let gen_types: Vec<_> = generics.ty_params.as_slice().iter().map(|typaram| 54 | context.ty_ident(typaram.span, typaram.ident) 55 | ).collect(); 56 | //3: generate `struct Entity` 57 | let entity_ident = context.ident_of("Entity"); 58 | let entity_ty = context.ty_path(context.path_all(span, false, 59 | vec![entity_ident], Vec::new(), gen_types.clone(), Vec::new() 60 | )); 61 | push(P(ast::Item { 62 | ident: entity_ident, 63 | attrs: Vec::new(), 64 | id: ast::DUMMY_NODE_ID, 65 | node: ast::ItemStruct( 66 | P(ast::StructDef { 67 | fields: definition.fields.iter().map(|field| codemap::Spanned { 68 | node: ast::StructField_ { 69 | kind: ast::StructFieldKind::NamedField( 70 | field.node.ident().unwrap(), 71 | ast::Visibility::Public 72 | ), 73 | id: field.node.id, 74 | ty: { 75 | let ty = field.node.ty.deref(); 76 | quote_ty!(context, Option<$id_path::Id<$ty>>) 77 | }, 78 | attrs: Vec::new(), 79 | }, 80 | span: field.span, 81 | }).collect(), 82 | ctor_id: None, 83 | }), 84 | generics.clone() 85 | ), 86 | vis: item.vis, 87 | span: span, 88 | })); 89 | //4a: generate `struct Components` 90 | let comp_ident = context.ident_of("Components"); 91 | let comp_ty = context.ty_path(context.path_all(span, false, 92 | vec![comp_ident], Vec::new(), gen_types.clone(), Vec::new() 93 | )); 94 | push(P(ast::Item { 95 | ident: comp_ident, 96 | attrs: Vec::new(), 97 | id: ast::DUMMY_NODE_ID, 98 | node: ast::ItemStruct( 99 | P(ast::StructDef { 100 | fields: definition.fields.iter().map(|field| codemap::Spanned { 101 | node: ast::StructField_ { 102 | kind: ast::StructFieldKind::NamedField( 103 | field.node.ident().unwrap(), 104 | ast::Visibility::Public 105 | ), 106 | id: field.node.id, 107 | ty: { 108 | let ty = field.node.ty.deref(); 109 | quote_ty!(context, $id_path::Array<$ty>>) 110 | }, 111 | attrs: Vec::new(), 112 | }, 113 | span: field.span, 114 | }).collect(), 115 | ctor_id: None, 116 | }), 117 | generics.clone() 118 | ), 119 | vis: item.vis, 120 | span: span, 121 | })); 122 | //4b: generate `impl Components` 123 | let new_array = quote_expr!(context, $id_path::Array::new()); 124 | fn make_generics() -> ast::Generics { 125 | ast::Generics { 126 | lifetimes: Vec::new(), 127 | ty_params: OwnedSlice::empty(), 128 | where_clause: ast::WhereClause { 129 | id: ast::DUMMY_NODE_ID, 130 | predicates: Vec::new(), 131 | }, 132 | } 133 | } 134 | push(P(ast::Item { 135 | ident: comp_ident, 136 | attrs: Vec::new(), 137 | id: ast::DUMMY_NODE_ID, 138 | node: ast::ItemImpl( 139 | ast::Unsafety::Normal, 140 | ast::ImplPolarity::Positive, 141 | generics.clone(), 142 | None, //TraitRef 143 | comp_ty.clone(), 144 | //method ::new() -> Components 145 | Some(P(ast::ImplItem { 146 | id: ast::DUMMY_NODE_ID, 147 | ident: context.ident_of("new"), 148 | vis: item.vis, 149 | attrs: Vec::new(), 150 | node: ast::MethodImplItem( 151 | ast::MethodSig { 152 | unsafety: ast::Unsafety::Normal, 153 | abi: Abi::Rust, 154 | decl: context.fn_decl(Vec::new(), comp_ty.clone()), 155 | generics: make_generics(), 156 | explicit_self: codemap::Spanned { 157 | node: ast::SelfStatic, 158 | span: span, 159 | }, 160 | }, 161 | context.block_expr(context.expr_struct_ident( 162 | span, comp_ident, definition.fields.iter().map(|field| 163 | context.field_imm(field.span, field.node.ident().unwrap(), new_array.clone()) 164 | ).collect() 165 | )) 166 | ), 167 | span: span, 168 | })).into_iter() 169 | //TODO: add_X, get_X, mut_X, etc 170 | .collect() 171 | ), 172 | vis: ast::Visibility::Inherited, 173 | span: span, 174 | })); 175 | //5a: generate `struct World` 176 | let world_ident = context.ident_of("World"); 177 | let world_ty = context.ty_path(context.path_all(span, false, 178 | vec![world_ident], Vec::new(), gen_types.clone(), Vec::new() 179 | )); 180 | push(P(ast::Item { 181 | ident: world_ident, 182 | attrs: Vec::new(), 183 | id: ast::DUMMY_NODE_ID, 184 | node: ast::ItemStruct( 185 | P(ast::StructDef { 186 | fields: vec![ 187 | codemap::Spanned { 188 | node: ast::StructField_ { 189 | kind: ast::StructFieldKind::NamedField( 190 | context.ident_of("data"), 191 | ast::Visibility::Public, 192 | ), 193 | id: ast::DUMMY_NODE_ID, 194 | ty: comp_ty, 195 | attrs: Vec::new(), 196 | }, 197 | span: span, 198 | }, 199 | codemap::Spanned { 200 | node: ast::StructField_ { 201 | kind: ast::StructFieldKind::NamedField( 202 | context.ident_of("entities"), 203 | ast::Visibility::Public, 204 | ), 205 | id: ast::DUMMY_NODE_ID, 206 | ty: quote_ty!(context, Vec<$entity_ty>), 207 | attrs: Vec::new(), 208 | }, 209 | span: span, 210 | }, 211 | ], 212 | ctor_id: None, 213 | }), 214 | generics.clone() 215 | ), 216 | vis: item.vis, 217 | span: span, 218 | })); 219 | //5b: generate `impl World` 220 | push(P(ast::Item { 221 | ident: world_ident, 222 | attrs: Vec::new(), 223 | id: ast::DUMMY_NODE_ID, 224 | node: ast::ItemImpl( 225 | ast::Unsafety::Normal, 226 | ast::ImplPolarity::Positive, 227 | generics.clone(), 228 | None, //TraitRef 229 | world_ty.clone(), 230 | vec![ 231 | P(ast::ImplItem { 232 | id: ast::DUMMY_NODE_ID, 233 | ident: context.ident_of("new"), 234 | vis: item.vis, 235 | attrs: Vec::new(), 236 | node: ast::MethodImplItem( 237 | ast::MethodSig { 238 | unsafety: ast::Unsafety::Normal, 239 | abi: Abi::Rust, 240 | decl: context.fn_decl(Vec::new(), world_ty.clone()), 241 | generics: make_generics(), 242 | explicit_self: codemap::Spanned { 243 | node: ast::SelfStatic, 244 | span: span, 245 | }, 246 | }, 247 | context.block_expr(quote_expr!(context, 248 | World { 249 | data: Components::new(), 250 | entities: Vec::new(), 251 | } 252 | )) 253 | ), 254 | span: span, 255 | }) 256 | ] 257 | ), 258 | vis: ast::Visibility::Inherited, 259 | span: span, 260 | })); 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /tests/ecs.rs: -------------------------------------------------------------------------------- 1 | extern crate id; 2 | #[macro_use] 3 | extern crate ecs; 4 | 5 | use id::Storage; 6 | 7 | pub type SimpleComponent = i32; 8 | world! { id (()), 9 | simple : SimpleComponent, 10 | } 11 | 12 | #[test] 13 | fn test_component() { 14 | let mut hub = Components::new(); 15 | let ent = hub.add().simple(4).entity; 16 | let value = hub.simple.get(ent.simple.unwrap()); 17 | assert_eq!(*value, 4); 18 | } 19 | 20 | struct Sys; 21 | impl System for Sys { 22 | fn process(&mut self, _param: &mut (), _data: &mut Components, _entities: &mut Vec) {} 23 | } 24 | 25 | #[test] 26 | fn test_system() { 27 | let mut world = World::new(); 28 | world.add_system(Sys); 29 | world.update(&mut ()); 30 | } 31 | -------------------------------------------------------------------------------- /tests/secs.rs: -------------------------------------------------------------------------------- 1 | #![feature(custom_attribute, plugin)] 2 | #![plugin(secs)] 3 | 4 | use std::marker::PhantomData; 5 | extern crate id; 6 | 7 | #[secs(id)] 8 | struct Proto { 9 | x: i8, 10 | y: PhantomData, 11 | } 12 | 13 | #[test] 14 | fn test_macro() {} 15 | 16 | #[test] 17 | fn test_world() { 18 | let _ = World::::new(); 19 | } 20 | --------------------------------------------------------------------------------