Skip to content

Commit 3714ce8

Browse files
authored
feat: implement Hash for MessageChannel (Restioson#226)
1 parent 539252a commit 3714ce8

File tree

1 file changed

+111
-0
lines changed

1 file changed

+111
-0
lines changed

xtra/src/message_channel.rs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//! the message type rather than the actor type.
44
55
use std::fmt;
6+
use std::hash::{Hash, Hasher};
67

78
use crate::address::{ActorJoinHandle, Address};
89
use crate::chan::RefCounter;
@@ -207,6 +208,17 @@ where
207208
}
208209
}
209210

211+
impl<M, R, Rc: RefCounter> Hash for MessageChannel<M, R, Rc>
212+
where
213+
M: Send + 'static,
214+
R: Send + 'static,
215+
Rc: Send + 'static,
216+
{
217+
fn hash<H: Hasher>(&self, state: &mut H) {
218+
self.inner.hash(state)
219+
}
220+
}
221+
210222
impl<M, R, Rc> Clone for MessageChannel<M, R, Rc>
211223
where
212224
R: Send + 'static,
@@ -295,6 +307,8 @@ trait MessageChannelTrait<M, Rc> {
295307
fn to_either(
296308
&self,
297309
) -> Box<dyn MessageChannelTrait<M, Either, Return = Self::Return> + Send + Sync + 'static>;
310+
311+
fn hash(&self, state: &mut dyn Hasher);
298312
}
299313

300314
impl<A, R, M, Rc: RefCounter> MessageChannelTrait<M, Rc> for Address<A, Rc>
@@ -366,4 +380,101 @@ where
366380
{
367381
Box::new(Address(self.0.to_tx_either()))
368382
}
383+
384+
fn hash(&self, state: &mut dyn Hasher) {
385+
state.write_usize(self.0.inner_ptr() as *const _ as usize);
386+
state.write_u8(self.0.is_strong() as u8);
387+
state.finish();
388+
}
389+
}
390+
391+
#[cfg(test)]
392+
mod test {
393+
use std::hash::{Hash, Hasher};
394+
395+
use crate::{Actor, Handler, Mailbox};
396+
397+
type TestMessageChannel = super::MessageChannel<TestMessage, ()>;
398+
399+
struct TestActor;
400+
struct TestMessage;
401+
402+
impl Actor for TestActor {
403+
type Stop = ();
404+
405+
async fn stopped(self) -> Self::Stop {}
406+
}
407+
408+
impl Handler<TestMessage> for TestActor {
409+
type Return = ();
410+
411+
async fn handle(&mut self, _: TestMessage, _: &mut crate::Context<Self>) -> Self::Return {}
412+
}
413+
414+
struct RecordingHasher(Vec<u8>);
415+
416+
impl RecordingHasher {
417+
fn record_hash<H: Hash>(value: &H) -> Vec<u8> {
418+
let mut h = Self(Vec::new());
419+
value.hash(&mut h);
420+
assert!(!h.0.is_empty(), "the hash data not be empty");
421+
h.0
422+
}
423+
}
424+
425+
impl Hasher for RecordingHasher {
426+
fn finish(&self) -> u64 {
427+
0
428+
}
429+
430+
fn write(&mut self, bytes: &[u8]) {
431+
self.0.extend_from_slice(bytes)
432+
}
433+
}
434+
435+
#[test]
436+
fn hashcode() {
437+
let (a1, _) = Mailbox::<TestActor>::unbounded();
438+
let c1 = TestMessageChannel::new(a1.clone());
439+
440+
let h1 = RecordingHasher::record_hash(&c1);
441+
let h2 = RecordingHasher::record_hash(&c1.clone());
442+
let h3 = RecordingHasher::record_hash(&TestMessageChannel::new(a1));
443+
444+
assert_eq!(h1, h2, "hashes from cloned channels should match");
445+
assert_eq!(
446+
h1, h3,
447+
"hashes channels created against the same address should match"
448+
);
449+
450+
let h4 = RecordingHasher::record_hash(&TestMessageChannel::new(
451+
Mailbox::<TestActor>::unbounded().0,
452+
));
453+
454+
assert_ne!(
455+
h1, h4,
456+
"hashes from channels created against different addresses should differ"
457+
);
458+
}
459+
460+
#[test]
461+
fn partial_eq() {
462+
let (a1, _) = Mailbox::<TestActor>::unbounded();
463+
let c1 = TestMessageChannel::new(a1.clone());
464+
let c2 = c1.clone();
465+
let c3 = TestMessageChannel::new(a1);
466+
467+
assert_eq!(c1, c2, "cloned channels should match");
468+
assert_eq!(
469+
c1, c3,
470+
"channels created against the same address should match"
471+
);
472+
473+
let c4 = TestMessageChannel::new(Mailbox::<TestActor>::unbounded().0);
474+
475+
assert_ne!(
476+
c1, c4,
477+
"channels created against different addresses should differ"
478+
);
479+
}
369480
}

0 commit comments

Comments
 (0)