ecsact/
lib.rs

1use std::collections::HashMap;
2use std::convert::From;
3use std::ffi::{c_char, c_void};
4use std::fmt::{Error, Write};
5
6pub mod support;
7
8/// Creates a "typed ID". A typed ID is an opaque `i32`. The purpose is for
9/// type safety when passing ids to the Ecsact API.
10macro_rules! typed_id {
11	($type_name:ident) => {
12		#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
13		#[repr(transparent)]
14		pub struct $type_name(i32);
15
16		impl $type_name {
17			/// # Safety
18			///
19			/// Ecsact IDs may only be set by the code generator
20			pub const unsafe fn new(id: i32) -> Self {
21				Self(id)
22			}
23		}
24
25		impl From<i32> for $type_name {
26			fn from(id: i32) -> Self {
27				Self(id)
28			}
29		}
30
31		impl From<$type_name> for i32 {
32			fn from(id: $type_name) -> i32 {
33				id.0
34			}
35		}
36	};
37}
38
39typed_id!(PackageId);
40typed_id!(SystemId);
41typed_id!(ActionId);
42typed_id!(ComponentId);
43typed_id!(TransientId);
44typed_id!(EnumId);
45typed_id!(EnumValueId);
46typed_id!(FieldId);
47typed_id!(VariantId);
48typed_id!(RegistryId);
49typed_id!(EntityId);
50typed_id!(SystemGeneratesId);
51typed_id!(AsyncRequestId);
52
53typed_id!(DeclId);
54typed_id!(CompositeId);
55typed_id!(SystemLikeId);
56typed_id!(ComponentLikeId);
57
58/// Implements From trait for typed ID structs from the `typed_id!` macro. Some
59/// Ecsact IDs are supposed to be easily converted from one to the other.
60macro_rules! typed_id_convert {
61	($type1:ident, $type2:ident) => {
62		impl From<$type1> for $type2 {
63			fn from(id: $type1) -> Self {
64				Self(id.into())
65			}
66		}
67	};
68}
69
70typed_id_convert!(SystemId, SystemLikeId);
71typed_id_convert!(ActionId, SystemLikeId);
72typed_id_convert!(ActionId, CompositeId);
73typed_id_convert!(ComponentId, CompositeId);
74typed_id_convert!(TransientId, CompositeId);
75typed_id_convert!(ComponentLikeId, CompositeId);
76
77typed_id_convert!(ComponentId, DeclId);
78typed_id_convert!(TransientId, DeclId);
79typed_id_convert!(SystemId, DeclId);
80typed_id_convert!(ActionId, DeclId);
81typed_id_convert!(VariantId, DeclId);
82typed_id_convert!(SystemLikeId, DeclId);
83typed_id_convert!(CompositeId, DeclId);
84typed_id_convert!(ComponentLikeId, DeclId);
85
86typed_id_convert!(ComponentId, ComponentLikeId);
87typed_id_convert!(TransientId, ComponentLikeId);
88
89pub enum IntType {
90	I8,
91	U8,
92	I16,
93	U16,
94	I32,
95	U32,
96}
97
98pub enum FieldType {
99	Bool { length: i32 },
100	I8 { length: i32 },
101	U8 { length: i32 },
102	I16 { length: i32 },
103	U16 { length: i32 },
104	I32 { length: i32 },
105	U32 { length: i32 },
106	F32 { length: i32 },
107	Entity { length: i32 },
108	Enum { id: EnumId, length: i32 },
109}
110
111impl From<IntType> for FieldType {
112	fn from(int_type: IntType) -> FieldType {
113		match int_type {
114			IntType::I8 => FieldType::I8 { length: 1 },
115			IntType::U8 => FieldType::U8 { length: 1 },
116			IntType::I16 => FieldType::I16 { length: 1 },
117			IntType::U16 => FieldType::U16 { length: 1 },
118			IntType::I32 => FieldType::I32 { length: 1 },
119			IntType::U32 => FieldType::U32 { length: 1 },
120		}
121	}
122}
123
124pub struct CodegenPluginContext {
125	package_id: i32,
126	write_fn: extern "C" fn(*const ::std::ffi::c_char, i32),
127}
128
129impl CodegenPluginContext {
130	pub fn new(
131		package_id: i32,
132		write_fn: extern "C" fn(*const ::std::ffi::c_char, i32),
133	) -> Self {
134		Self {
135			package_id,
136			write_fn,
137		}
138	}
139
140	pub fn package_id(&self) -> PackageId {
141		self.package_id.into()
142	}
143}
144
145impl Write for CodegenPluginContext {
146	fn write_str(&mut self, s: &str) -> Result<(), Error> {
147		(self.write_fn)(
148			s.as_ptr() as *const c_char,
149			s.len().try_into().unwrap(),
150		);
151		Ok(())
152	}
153}
154
155pub enum SystemCapability {
156	Readonly { optional: bool },
157	Writeonly { optional: bool },
158	Readwrite { optional: bool },
159	Include,
160	Exclude,
161	Adds,
162	Removes,
163}
164
165// TODO(zaucy): Mark these traits as unsafe because of the repr(C) and ID
166// consider the ID constructor to be unsafe
167// consider a IsReprC unsafe trait
168
169pub trait Component: ComponentLike + Composite {
170	const ID: ComponentId;
171}
172
173pub trait Transient: ComponentLike + Composite {
174	const ID: TransientId;
175}
176
177pub trait Composite {
178	const ID: CompositeId;
179}
180
181pub trait ComponentLike: Composite {
182	const ID: ComponentLikeId;
183}
184
185pub trait Action: Composite + SystemLike {
186	const ID: ActionId;
187}
188
189pub trait System: SystemLike {
190	const ID: SystemId;
191}
192
193pub trait SystemLike {
194	const ID: SystemLikeId;
195}
196
197type ComponentVoidDataCallback<'a> = Box<dyn Fn(EntityId, *const c_void) + 'a>;
198
199#[derive(Default)]
200pub struct ExecutionEventsCollector<'a> {
201	pub init_callbacks:
202		HashMap<ComponentId, Vec<ComponentVoidDataCallback<'a>>>,
203	pub update_callbacks:
204		HashMap<ComponentId, Vec<ComponentVoidDataCallback<'a>>>,
205	pub remove_callbacks:
206		HashMap<ComponentId, Vec<ComponentVoidDataCallback<'a>>>,
207}
208
209impl<'a> ExecutionEventsCollector<'a> {
210	pub fn on_init<C: Component>(
211		mut self,
212		callback: &'a dyn Fn(EntityId, &C),
213	) -> Self {
214		self.init_callbacks.entry(<C as Component>::ID).or_default();
215		self.init_callbacks
216			.get_mut(&<C as Component>::ID)
217			.unwrap()
218			.push(Box::new(|e, comp_data| unsafe {
219				let comp: &C = &*(comp_data as *const _ as *const C);
220				callback(e, comp);
221			}));
222		self
223	}
224
225	pub fn on_update<C: Component>(
226		mut self,
227		callback: &'a dyn Fn(EntityId, &C),
228	) -> Self {
229		self.update_callbacks
230			.entry(<C as Component>::ID)
231			.or_default();
232		self.update_callbacks
233			.get_mut(&<C as Component>::ID)
234			.unwrap()
235			.push(Box::new(|e, comp_data| unsafe {
236				let comp: &C = &*(comp_data as *const _ as *const C);
237				callback(e, comp);
238			}));
239		self
240	}
241
242	pub fn on_remove<C: Component>(
243		mut self,
244		callback: &'a dyn Fn(EntityId, &C),
245	) -> Self {
246		self.remove_callbacks
247			.entry(<C as Component>::ID)
248			.or_default();
249		self.remove_callbacks
250			.get_mut(&<C as Component>::ID)
251			.unwrap()
252			.push(Box::new(|e, comp_data| unsafe {
253				let comp: &C = &*(comp_data as *const _ as *const C);
254				callback(e, comp);
255			}));
256		self
257	}
258}