Skip to content

Commit 72fcd72

Browse files
authored
Add derive macro for DeliveryLabel (#30)
Signed-off-by: Michael X. Grey <greyxmike@gmail.com>
1 parent 57aeff2 commit 72fcd72

File tree

5 files changed

+133
-34
lines changed

5 files changed

+133
-34
lines changed

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bevy_impulse"
3-
version = "0.0.1"
3+
version = "0.0.2"
44
edition = "2021"
55
authors = ["Grey <mxgrey@intrinsic.ai>"]
66
license = "Apache-2.0"
@@ -11,7 +11,7 @@ keywords = ["reactive", "workflow", "behavior", "agent", "bevy"]
1111
categories = ["science::robotics", "asynchronous", "concurrency", "game-development"]
1212

1313
[dependencies]
14-
bevy_impulse_derive = { path = "macros", version = "0.0.1" }
14+
bevy_impulse_derive = { path = "macros", version = "0.0.2" }
1515
bevy_ecs = "0.12"
1616
bevy_utils = "0.12"
1717
bevy_hierarchy = "0.12"

macros/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "bevy_impulse_derive"
3-
version = "0.0.1"
3+
version = "0.0.2"
44
edition = "2021"
55
authors = ["Grey <mxgrey@intrinsic.ai>"]
66
license = "Apache-2.0"

macros/src/lib.rs

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,37 @@ pub fn simple_stream_macro(item: TokenStream) -> TokenStream {
2424
let ast: DeriveInput = syn::parse(item).unwrap();
2525
let struct_name = &ast.ident;
2626
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
27-
// let bevy_impulse_path: Path =
2827

2928
quote! {
30-
impl #impl_generics Stream for #struct_name #type_generics #where_clause {
29+
impl #impl_generics ::bevy_impulse::Stream for #struct_name #type_generics #where_clause {
3130
type Container = ::bevy_impulse::DefaultStreamContainer<Self>;
3231
}
3332
}
3433
.into()
3534
}
35+
36+
#[proc_macro_derive(DeliveryLabel)]
37+
pub fn delivery_label_macro(item: TokenStream) -> TokenStream {
38+
let ast: DeriveInput = syn::parse(item).unwrap();
39+
let struct_name = &ast.ident;
40+
let (impl_generics, type_generics, where_clause) = &ast.generics.split_for_impl();
41+
42+
quote! {
43+
impl #impl_generics ::bevy_impulse::DeliveryLabel for #struct_name #type_generics #where_clause {
44+
fn dyn_clone(&self) -> Box<dyn DeliveryLabel> {
45+
::std::boxed::Box::new(::std::clone::Clone::clone(self))
46+
}
47+
48+
fn as_dyn_eq(&self) -> &dyn ::bevy_impulse::utils::DynEq {
49+
self
50+
}
51+
52+
fn dyn_hash(&self, mut state: &mut dyn ::std::hash::Hasher) {
53+
let ty_id = ::std::any::TypeId::of::<Self>();
54+
::std::hash::Hash::hash(&ty_id, &mut state);
55+
::std::hash::Hash::hash(self, &mut state);
56+
}
57+
}
58+
}
59+
.into()
60+
}

src/impulse.rs

Lines changed: 97 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -485,11 +485,30 @@ mod tests {
485485
}
486486

487487
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
488-
struct TestLabel;
488+
struct UnitLabel;
489489

490-
// TODO(luca) create a proc-macro for this, because library crates can't
491-
// export proc macros we will need to create a new macros only crate
492-
impl DeliveryLabel for TestLabel {
490+
// TODO(@mxgrey) Figure out how to make the DeliveryLabel macro usable
491+
// within the core bevy_impulse library
492+
impl DeliveryLabel for UnitLabel {
493+
fn dyn_clone(&self) -> Box<dyn DeliveryLabel> {
494+
Box::new(self.clone())
495+
}
496+
497+
fn as_dyn_eq(&self) -> &dyn DynEq {
498+
self
499+
}
500+
501+
fn dyn_hash(&self, mut state: &mut dyn std::hash::Hasher) {
502+
let ty_id = std::any::TypeId::of::<Self>();
503+
std::hash::Hash::hash(&ty_id, &mut state);
504+
std::hash::Hash::hash(self, &mut state);
505+
}
506+
}
507+
508+
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
509+
struct StatefulLabel(u64);
510+
511+
impl DeliveryLabel for StatefulLabel {
493512
fn dyn_clone(&self) -> Box<dyn DeliveryLabel> {
494513
Box::new(self.clone())
495514
}
@@ -534,9 +553,38 @@ mod tests {
534553
service: Service<Arc<Mutex<u64>>, ()>,
535554
context: &mut TestingContext,
536555
) {
537-
let queuing_service = service.instruct(TestLabel);
538-
let preempting_service = service.instruct(TestLabel.preempt());
556+
// Test for a unit struct
557+
verify_preemption_matrix(
558+
service.instruct(UnitLabel),
559+
service.instruct(UnitLabel.preempt()),
560+
context,
561+
);
562+
563+
// Test for a stateful struct
564+
verify_preemption_matrix(
565+
service.instruct(StatefulLabel(5)),
566+
service.instruct(StatefulLabel(5).preempt()),
567+
context,
568+
);
539569

570+
// Test for a unit struct
571+
verify_queuing_matrix(service.instruct(UnitLabel), context);
572+
573+
// Test for a stateful struct
574+
verify_queuing_matrix(service.instruct(StatefulLabel(7)), context);
575+
576+
// Test for a unit struct
577+
verify_ensured_matrix(service, UnitLabel, context);
578+
579+
// Test for a stateful struct
580+
verify_ensured_matrix(service, StatefulLabel(2), context);
581+
}
582+
583+
fn verify_preemption_matrix(
584+
queuing_service: Service<Arc<Mutex<u64>>, ()>,
585+
preempting_service: Service<Arc<Mutex<u64>>, ()>,
586+
context: &mut TestingContext,
587+
) {
540588
// Test by queuing up a bunch of requests before preempting them all at once.
541589
verify_preemption(1, queuing_service, preempting_service, context);
542590
verify_preemption(2, queuing_service, preempting_service, context);
@@ -548,25 +596,6 @@ mod tests {
548596
verify_preemption(2, preempting_service, preempting_service, context);
549597
verify_preemption(3, preempting_service, preempting_service, context);
550598
verify_preemption(4, preempting_service, preempting_service, context);
551-
552-
// Test by queuing up a bunch of requests and making sure they all get
553-
// delivered.
554-
verify_queuing(2, queuing_service, context);
555-
verify_queuing(3, queuing_service, context);
556-
verify_queuing(4, queuing_service, context);
557-
verify_queuing(5, queuing_service, context);
558-
559-
// Test by queuing up a mix of ensured and unensured requests, and then
560-
// sending in one that preempts them all. The ensured requests should
561-
// remain in the queue and execute despite the preempter. The unensured
562-
// requests should all be cancelled.
563-
verify_ensured([false, true, false, true], service, context);
564-
verify_ensured([true, false, false, false], service, context);
565-
verify_ensured([true, true, false, false], service, context);
566-
verify_ensured([false, false, true, true], service, context);
567-
verify_ensured([true, false, false, true], service, context);
568-
verify_ensured([false, false, false, false], service, context);
569-
verify_ensured([true, true, true, true], service, context);
570599
}
571600

572601
fn verify_preemption(
@@ -603,6 +632,18 @@ mod tests {
603632
assert!(context.no_unhandled_errors());
604633
}
605634

635+
fn verify_queuing_matrix(
636+
queuing_service: Service<Arc<Mutex<u64>>, ()>,
637+
context: &mut TestingContext,
638+
) {
639+
// Test by queuing up a bunch of requests and making sure they all get
640+
// delivered.
641+
verify_queuing(2, queuing_service, context);
642+
verify_queuing(3, queuing_service, context);
643+
verify_queuing(4, queuing_service, context);
644+
verify_queuing(5, queuing_service, context);
645+
}
646+
606647
fn verify_queuing(
607648
queue_size: usize,
608649
queuing_service: Service<Arc<Mutex<u64>>, ()>,
@@ -628,9 +669,33 @@ mod tests {
628669
assert!(context.no_unhandled_errors());
629670
}
630671

631-
fn verify_ensured(
672+
fn verify_ensured_matrix<L: DeliveryLabel + Clone>(
673+
service: Service<Arc<Mutex<u64>>, ()>,
674+
label: L,
675+
context: &mut TestingContext,
676+
) {
677+
// Test by queuing up a mix of ensured and unensured requests, and then
678+
// sending in one that preempts them all. The ensured requests should
679+
// remain in the queue and execute despite the preempter. The unensured
680+
// requests should all be cancelled.
681+
verify_ensured([false, true, false, true], service, label.clone(), context);
682+
verify_ensured([true, false, false, false], service, label.clone(), context);
683+
verify_ensured([true, true, false, false], service, label.clone(), context);
684+
verify_ensured([false, false, true, true], service, label.clone(), context);
685+
verify_ensured([true, false, false, true], service, label.clone(), context);
686+
verify_ensured(
687+
[false, false, false, false],
688+
service,
689+
label.clone(),
690+
context,
691+
);
692+
verify_ensured([true, true, true, true], service, label.clone(), context);
693+
}
694+
695+
fn verify_ensured<L: DeliveryLabel + Clone>(
632696
queued: impl IntoIterator<Item = bool>,
633697
service: Service<Arc<Mutex<u64>>, ()>,
698+
label: L,
634699
context: &mut TestingContext,
635700
) {
636701
let counter = Arc::new(Mutex::new(0_u64));
@@ -640,9 +705,9 @@ mod tests {
640705
for ensured in queued {
641706
let srv = if ensured {
642707
expected_count += 1;
643-
service.instruct(TestLabel.ensure())
708+
service.instruct(label.clone().ensure())
644709
} else {
645-
service.instruct(TestLabel)
710+
service.instruct(label.clone())
646711
};
647712

648713
let promise = context
@@ -653,7 +718,10 @@ mod tests {
653718

654719
let mut preempter = context.command(|commands| {
655720
commands
656-
.request(Arc::clone(&counter), service.instruct(TestLabel.preempt()))
721+
.request(
722+
Arc::clone(&counter),
723+
service.instruct(label.clone().preempt()),
724+
)
657725
.take_response()
658726
});
659727

src/service.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use bevy_ecs::{
2626
prelude::{Commands, Component, Entity, Event, World},
2727
schedule::ScheduleLabel,
2828
};
29+
pub use bevy_impulse_derive::DeliveryLabel;
2930
use bevy_utils::{define_label, intern::Interned};
3031
use std::{any::TypeId, collections::HashSet};
3132
use thiserror::Error as ThisError;
@@ -196,6 +197,11 @@ define_label!(
196197
DELIVERY_LABEL_INTERNER
197198
);
198199

200+
pub mod utils {
201+
/// Used by the procedural macro for DeliveryLabel
202+
pub use bevy_utils::label::DynEq;
203+
}
204+
199205
/// When using a service, you can bundle in delivery instructions that affect
200206
/// how multiple requests to the same service may interact with each other.
201207
///

0 commit comments

Comments
 (0)