1use std::fmt;
12use std::str::FromStr;
13
14use serde::Deserialize;
15use serde::Serialize;
16
17use crate::channel::ChannelAddr;
18use crate::id::ActorId;
19use crate::id::IdParseError;
20use crate::id::PortId;
21use crate::id::ProcId;
22
23#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
25pub struct Location(ChannelAddr);
26
27impl Location {
28 pub fn addr(&self) -> &ChannelAddr {
30 &self.0
31 }
32}
33
34impl From<ChannelAddr> for Location {
35 fn from(addr: ChannelAddr) -> Self {
36 Self(addr)
37 }
38}
39
40impl fmt::Display for Location {
41 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42 f.write_str(&self.0.to_zmq_url())
43 }
44}
45
46impl fmt::Debug for Location {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 fmt::Display::fmt(self, f)
49 }
50}
51
52impl FromStr for Location {
53 type Err = anyhow::Error;
54
55 fn from_str(s: &str) -> Result<Self, Self::Err> {
56 ChannelAddr::from_zmq_url(s).map(Self)
57 }
58}
59
60#[derive(Debug, thiserror::Error)]
62pub enum RefParseError {
63 #[error("missing '@' separator between id and location")]
65 MissingSeparator,
66 #[error("invalid id: {0}")]
68 InvalidId(#[from] IdParseError),
69 #[error("invalid location: {0}")]
71 InvalidLocation(#[source] anyhow::Error),
72}
73
74#[derive(Clone, Serialize, Deserialize)]
76pub struct ProcRef {
77 id: ProcId,
78 location: Location,
79}
80
81impl ProcRef {
82 pub fn new(id: ProcId, location: Location) -> Self {
84 Self { id, location }
85 }
86
87 pub fn id(&self) -> &ProcId {
89 &self.id
90 }
91
92 pub fn location(&self) -> &Location {
94 &self.location
95 }
96}
97
98impl PartialEq for ProcRef {
99 fn eq(&self, other: &Self) -> bool {
100 self.id == other.id && self.location == other.location
101 }
102}
103
104impl Eq for ProcRef {}
105
106impl std::hash::Hash for ProcRef {
107 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
108 self.id.hash(state);
109 self.location.hash(state);
110 }
111}
112
113impl PartialOrd for ProcRef {
114 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
115 Some(self.cmp(other))
116 }
117}
118
119impl Ord for ProcRef {
120 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
121 self.id
122 .cmp(&other.id)
123 .then_with(|| self.location.cmp(&other.location))
124 }
125}
126
127impl fmt::Display for ProcRef {
128 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
129 write!(f, "{}@{}", self.id, self.location)
130 }
131}
132
133impl fmt::Debug for ProcRef {
134 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
135 match self.id.label() {
136 Some(label) => write!(f, "<'{}' {}@{}>", label, self.id, self.location),
137 None => write!(f, "<{}@{}>", self.id, self.location),
138 }
139 }
140}
141
142impl FromStr for ProcRef {
143 type Err = RefParseError;
144
145 fn from_str(s: &str) -> Result<Self, Self::Err> {
146 let at = s.find('@').ok_or(RefParseError::MissingSeparator)?;
147 let id: ProcId = s[..at].parse()?;
148 let location: Location = s[at + 1..]
149 .parse()
150 .map_err(RefParseError::InvalidLocation)?;
151 Ok(Self { id, location })
152 }
153}
154
155#[derive(Clone, Serialize, Deserialize)]
157pub struct ActorRef {
158 id: ActorId,
159 location: Location,
160}
161
162impl ActorRef {
163 pub fn new(id: ActorId, location: Location) -> Self {
165 Self { id, location }
166 }
167
168 pub fn id(&self) -> &ActorId {
170 &self.id
171 }
172
173 pub fn location(&self) -> &Location {
175 &self.location
176 }
177}
178
179impl PartialEq for ActorRef {
180 fn eq(&self, other: &Self) -> bool {
181 self.id == other.id && self.location == other.location
182 }
183}
184
185impl Eq for ActorRef {}
186
187impl std::hash::Hash for ActorRef {
188 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
189 self.id.hash(state);
190 self.location.hash(state);
191 }
192}
193
194impl PartialOrd for ActorRef {
195 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
196 Some(self.cmp(other))
197 }
198}
199
200impl Ord for ActorRef {
201 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
202 self.id
203 .cmp(&other.id)
204 .then_with(|| self.location.cmp(&other.location))
205 }
206}
207
208impl fmt::Display for ActorRef {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 write!(f, "{}@{}", self.id, self.location)
211 }
212}
213
214impl fmt::Debug for ActorRef {
215 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
216 match (self.id.label(), self.id.proc_id().label()) {
217 (Some(actor_label), Some(proc_label)) => {
218 write!(
219 f,
220 "<'{}.{}' {}@{}>",
221 actor_label, proc_label, self.id, self.location
222 )
223 }
224 (Some(actor_label), None) => {
225 write!(f, "<'{}' {}@{}>", actor_label, self.id, self.location)
226 }
227 (None, Some(proc_label)) => {
228 write!(f, "<'.{}' {}@{}>", proc_label, self.id, self.location)
229 }
230 (None, None) => {
231 write!(f, "<{}@{}>", self.id, self.location)
232 }
233 }
234 }
235}
236
237impl FromStr for ActorRef {
238 type Err = RefParseError;
239
240 fn from_str(s: &str) -> Result<Self, Self::Err> {
241 let at = s.find('@').ok_or(RefParseError::MissingSeparator)?;
242 let id: ActorId = s[..at].parse()?;
243 let location: Location = s[at + 1..]
244 .parse()
245 .map_err(RefParseError::InvalidLocation)?;
246 Ok(Self { id, location })
247 }
248}
249
250#[derive(Clone, Serialize, Deserialize)]
252pub struct PortRef {
253 id: PortId,
254 location: Location,
255}
256
257impl PortRef {
258 pub fn new(id: PortId, location: Location) -> Self {
260 Self { id, location }
261 }
262
263 pub fn id(&self) -> &PortId {
265 &self.id
266 }
267
268 pub fn location(&self) -> &Location {
270 &self.location
271 }
272
273 pub fn actor_id(&self) -> &ActorId {
275 self.id.actor_id()
276 }
277}
278
279impl PartialEq for PortRef {
280 fn eq(&self, other: &Self) -> bool {
281 self.id == other.id && self.location == other.location
282 }
283}
284
285impl Eq for PortRef {}
286
287impl std::hash::Hash for PortRef {
288 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
289 self.id.hash(state);
290 self.location.hash(state);
291 }
292}
293
294impl PartialOrd for PortRef {
295 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
296 Some(self.cmp(other))
297 }
298}
299
300impl Ord for PortRef {
301 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
302 self.id
303 .cmp(&other.id)
304 .then_with(|| self.location.cmp(&other.location))
305 }
306}
307
308impl fmt::Display for PortRef {
309 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
310 write!(f, "{}@{}", self.id, self.location)
311 }
312}
313
314impl fmt::Debug for PortRef {
315 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
316 match (
317 self.id.actor_id().label(),
318 self.id.actor_id().proc_id().label(),
319 ) {
320 (Some(actor_label), Some(proc_label)) => {
321 write!(
322 f,
323 "<'{}.{}' {}@{}>",
324 actor_label, proc_label, self.id, self.location
325 )
326 }
327 (Some(actor_label), None) => {
328 write!(f, "<'{}' {}@{}>", actor_label, self.id, self.location)
329 }
330 (None, Some(proc_label)) => {
331 write!(f, "<'.{}' {}@{}>", proc_label, self.id, self.location)
332 }
333 (None, None) => {
334 write!(f, "<{}@{}>", self.id, self.location)
335 }
336 }
337 }
338}
339
340impl FromStr for PortRef {
341 type Err = RefParseError;
342
343 fn from_str(s: &str) -> Result<Self, Self::Err> {
344 let at = s.find('@').ok_or(RefParseError::MissingSeparator)?;
345 let id: PortId = s[..at].parse()?;
346 let location: Location = s[at + 1..]
347 .parse()
348 .map_err(RefParseError::InvalidLocation)?;
349 Ok(Self { id, location })
350 }
351}
352
353#[cfg(test)]
354mod tests {
355 use std::hash::Hash;
356
357 use super::*;
358 use crate::id::Label;
359 use crate::id::Uid;
360 use crate::port::Port;
361
362 #[test]
363 fn test_location_display_fromstr_roundtrip() {
364 let loc: Location = ChannelAddr::Local(42).into();
365 let s = loc.to_string();
366 assert_eq!(s, "inproc://42");
367 let parsed: Location = s.parse().unwrap();
368 assert_eq!(loc, parsed);
369 }
370
371 #[test]
372 fn test_location_tcp() {
373 let addr: ChannelAddr = "tcp:127.0.0.1:8080".parse().unwrap();
374 let loc = Location::from(addr.clone());
375 assert_eq!(loc.to_string(), "tcp://127.0.0.1:8080");
376 assert_eq!(loc.addr(), &addr);
377 }
378
379 #[test]
380 fn test_location_debug_same_as_display() {
381 let loc: Location = ChannelAddr::Local(7).into();
382 assert_eq!(format!("{:?}", loc), format!("{}", loc));
383 }
384
385 #[test]
386 fn test_proc_ref_display() {
387 let pid = ProcId::new(
388 Uid::Instance(0xabc123),
389 Some(Label::new("my-proc").unwrap()),
390 );
391 let loc: Location = ChannelAddr::Local(42).into();
392 let pref = ProcRef::new(pid, loc);
393 assert_eq!(pref.to_string(), "0000000000abc123@inproc://42");
394 }
395
396 #[test]
397 fn test_proc_ref_debug_with_label() {
398 let pid = ProcId::new(
399 Uid::Instance(0xabc123),
400 Some(Label::new("my-proc").unwrap()),
401 );
402 let loc: Location = ChannelAddr::Local(42).into();
403 let pref = ProcRef::new(pid, loc);
404 assert_eq!(
405 format!("{:?}", pref),
406 "<'my-proc' 0000000000abc123@inproc://42>"
407 );
408 }
409
410 #[test]
411 fn test_proc_ref_debug_without_label() {
412 let pid = ProcId::new(Uid::Instance(0xabc123), None);
413 let loc: Location = ChannelAddr::Local(42).into();
414 let pref = ProcRef::new(pid, loc);
415 assert_eq!(format!("{:?}", pref), "<0000000000abc123@inproc://42>");
416 }
417
418 #[test]
419 fn test_proc_ref_fromstr_roundtrip() {
420 let pid = ProcId::new(
421 Uid::Instance(0xabc123),
422 Some(Label::new("my-proc").unwrap()),
423 );
424 let loc: Location = ChannelAddr::Local(42).into();
425 let pref = ProcRef::new(pid, loc);
426 let s = pref.to_string();
427 let parsed: ProcRef = s.parse().unwrap();
428 assert_eq!(pref, parsed);
429 }
430
431 #[test]
432 fn test_proc_ref_fromstr_tcp() {
433 let parsed: ProcRef = "0000000000abc123@tcp://127.0.0.1:8080".parse().unwrap();
434 assert_eq!(*parsed.id().uid(), Uid::Instance(0xabc123));
435 assert_eq!(
436 *parsed.location().addr(),
437 "tcp:127.0.0.1:8080".parse::<ChannelAddr>().unwrap()
438 );
439 }
440
441 #[test]
442 fn test_proc_ref_fromstr_missing_separator() {
443 let err = "0000000000abc123".parse::<ProcRef>().unwrap_err();
444 assert!(matches!(err, RefParseError::MissingSeparator));
445 }
446
447 #[test]
448 fn test_actor_ref_display() {
449 let aid = ActorId::new(
450 Uid::Instance(0xabc123),
451 ProcId::new(
452 Uid::Instance(0xdef456),
453 Some(Label::new("my-proc").unwrap()),
454 ),
455 Some(Label::new("my-actor").unwrap()),
456 );
457 let loc: Location = ChannelAddr::Local(42).into();
458 let aref = ActorRef::new(aid, loc);
459 assert_eq!(
460 aref.to_string(),
461 "0000000000abc123.0000000000def456@inproc://42"
462 );
463 }
464
465 #[test]
466 fn test_actor_ref_debug_all_labels() {
467 let aid = ActorId::new(
468 Uid::Instance(0xabc123),
469 ProcId::new(
470 Uid::Instance(0xdef456),
471 Some(Label::new("my-proc").unwrap()),
472 ),
473 Some(Label::new("my-actor").unwrap()),
474 );
475 let loc: Location = ChannelAddr::Local(42).into();
476 let aref = ActorRef::new(aid, loc);
477 assert_eq!(
478 format!("{:?}", aref),
479 "<'my-actor.my-proc' 0000000000abc123.0000000000def456@inproc://42>"
480 );
481 }
482
483 #[test]
484 fn test_actor_ref_debug_no_labels() {
485 let aid = ActorId::new(
486 Uid::Instance(0xabc123),
487 ProcId::new(Uid::Instance(0xdef456), None),
488 None,
489 );
490 let loc: Location = ChannelAddr::Local(42).into();
491 let aref = ActorRef::new(aid, loc);
492 assert_eq!(
493 format!("{:?}", aref),
494 "<0000000000abc123.0000000000def456@inproc://42>"
495 );
496 }
497
498 #[test]
499 fn test_actor_ref_debug_actor_label_only() {
500 let aid = ActorId::new(
501 Uid::Instance(0xabc123),
502 ProcId::new(Uid::Instance(0xdef456), None),
503 Some(Label::new("my-actor").unwrap()),
504 );
505 let loc: Location = ChannelAddr::Local(42).into();
506 let aref = ActorRef::new(aid, loc);
507 assert_eq!(
508 format!("{:?}", aref),
509 "<'my-actor' 0000000000abc123.0000000000def456@inproc://42>"
510 );
511 }
512
513 #[test]
514 fn test_actor_ref_debug_proc_label_only() {
515 let aid = ActorId::new(
516 Uid::Instance(0xabc123),
517 ProcId::new(
518 Uid::Instance(0xdef456),
519 Some(Label::new("my-proc").unwrap()),
520 ),
521 None,
522 );
523 let loc: Location = ChannelAddr::Local(42).into();
524 let aref = ActorRef::new(aid, loc);
525 assert_eq!(
526 format!("{:?}", aref),
527 "<'.my-proc' 0000000000abc123.0000000000def456@inproc://42>"
528 );
529 }
530
531 #[test]
532 fn test_actor_ref_fromstr_roundtrip() {
533 let aid = ActorId::new(
534 Uid::Instance(0xabc123),
535 ProcId::new(
536 Uid::Instance(0xdef456),
537 Some(Label::new("my-proc").unwrap()),
538 ),
539 Some(Label::new("my-actor").unwrap()),
540 );
541 let loc: Location = ChannelAddr::Local(42).into();
542 let aref = ActorRef::new(aid, loc);
543 let s = aref.to_string();
544 let parsed: ActorRef = s.parse().unwrap();
545 assert_eq!(aref, parsed);
546 }
547
548 #[test]
549 fn test_actor_ref_fromstr_missing_separator() {
550 let err = "0000000000abc123.0000000000def456"
551 .parse::<ActorRef>()
552 .unwrap_err();
553 assert!(matches!(err, RefParseError::MissingSeparator));
554 }
555
556 #[test]
557 fn test_proc_ref_eq_and_hash() {
558 use std::collections::hash_map::DefaultHasher;
559 use std::hash::Hasher;
560
561 let pid = ProcId::new(Uid::Instance(0x42), Some(Label::new("proc").unwrap()));
562 let loc: Location = ChannelAddr::Local(1).into();
563 let a = ProcRef::new(pid.clone(), loc.clone());
564 let b = ProcRef::new(pid, loc);
565 assert_eq!(a, b);
566
567 let hash = |r: &ProcRef| {
568 let mut h = DefaultHasher::new();
569 r.hash(&mut h);
570 h.finish()
571 };
572 assert_eq!(hash(&a), hash(&b));
573 }
574
575 #[test]
576 fn test_proc_ref_neq_different_location() {
577 let pid = ProcId::new(Uid::Instance(0x42), Some(Label::new("proc").unwrap()));
578 let a = ProcRef::new(pid.clone(), ChannelAddr::Local(1).into());
579 let b = ProcRef::new(pid, ChannelAddr::Local(2).into());
580 assert_ne!(a, b);
581 }
582
583 #[test]
584 fn test_actor_ref_eq_and_hash() {
585 use std::collections::hash_map::DefaultHasher;
586 use std::hash::Hasher;
587
588 let aid = ActorId::new(
589 Uid::Instance(0x42),
590 ProcId::new(Uid::Instance(0x99), Some(Label::new("proc").unwrap())),
591 Some(Label::new("actor").unwrap()),
592 );
593 let loc: Location = ChannelAddr::Local(1).into();
594 let a = ActorRef::new(aid.clone(), loc.clone());
595 let b = ActorRef::new(aid, loc);
596 assert_eq!(a, b);
597
598 let hash = |r: &ActorRef| {
599 let mut h = DefaultHasher::new();
600 r.hash(&mut h);
601 h.finish()
602 };
603 assert_eq!(hash(&a), hash(&b));
604 }
605
606 #[test]
607 fn test_proc_ref_singleton() {
608 let pid = ProcId::new(
609 Uid::singleton(Label::new("my-proc").unwrap()),
610 Some(Label::new("my-proc").unwrap()),
611 );
612 let loc: Location = ChannelAddr::Local(0).into();
613 let pref = ProcRef::new(pid, loc);
614 let s = pref.to_string();
615 assert_eq!(s, "_my-proc@inproc://0");
616 let parsed: ProcRef = s.parse().unwrap();
617 assert_eq!(pref, parsed);
618 }
619
620 #[test]
621 fn test_location_serde_roundtrip() {
622 let loc: Location = ChannelAddr::Local(42).into();
623 let json = serde_json::to_string(&loc).unwrap();
624 let parsed: Location = serde_json::from_str(&json).unwrap();
625 assert_eq!(loc, parsed);
626 }
627
628 #[test]
629 fn test_proc_ref_serde_roundtrip() {
630 let pid = ProcId::new(
631 Uid::Instance(0xabcdef),
632 Some(Label::new("my-proc").unwrap()),
633 );
634 let loc: Location = ChannelAddr::Local(42).into();
635 let pref = ProcRef::new(pid, loc);
636 let json = serde_json::to_string(&pref).unwrap();
637 let parsed: ProcRef = serde_json::from_str(&json).unwrap();
638 assert_eq!(pref, parsed);
639 }
640
641 #[test]
642 fn test_actor_ref_serde_roundtrip() {
643 let aid = ActorId::new(
644 Uid::Instance(0xabcdef),
645 ProcId::new(
646 Uid::Instance(0x123456),
647 Some(Label::new("my-proc").unwrap()),
648 ),
649 Some(Label::new("my-actor").unwrap()),
650 );
651 let loc: Location = ChannelAddr::Local(42).into();
652 let aref = ActorRef::new(aid, loc);
653 let json = serde_json::to_string(&aref).unwrap();
654 let parsed: ActorRef = serde_json::from_str(&json).unwrap();
655 assert_eq!(aref, parsed);
656 }
657
658 #[test]
659 fn test_proc_ref_with_metatls_location() {
660 use crate::channel::TlsAddr;
661
662 let pid = ProcId::new(Uid::Instance(0x42), None);
663 let loc: Location = ChannelAddr::MetaTls(TlsAddr::new("example.com", 443)).into();
664 let pref = ProcRef::new(pid, loc);
665 let s = pref.to_string();
666 assert_eq!(s, "0000000000000042@metatls://example.com:443");
667 let parsed: ProcRef = s.parse().unwrap();
668 assert_eq!(pref, parsed);
669 }
670
671 #[test]
672 fn test_port_ref_construction_and_accessors() {
673 let aid = ActorId::new(
674 Uid::Instance(0xabc123),
675 ProcId::new(
676 Uid::Instance(0xdef456),
677 Some(Label::new("my-proc").unwrap()),
678 ),
679 Some(Label::new("my-actor").unwrap()),
680 );
681 let port_id = PortId::new(aid.clone(), Port::from(42));
682 let loc: Location = ChannelAddr::Local(7).into();
683 let pref = PortRef::new(port_id.clone(), loc.clone());
684 assert_eq!(pref.id(), &port_id);
685 assert_eq!(pref.location(), &loc);
686 assert_eq!(pref.actor_id(), &aid);
687 }
688
689 #[test]
690 fn test_port_ref_display() {
691 let aid = ActorId::new(
692 Uid::Instance(0xabc123),
693 ProcId::new(
694 Uid::Instance(0xdef456),
695 Some(Label::new("my-proc").unwrap()),
696 ),
697 Some(Label::new("my-actor").unwrap()),
698 );
699 let port_id = PortId::new(aid, Port::from(42));
700 let loc: Location = ChannelAddr::Local(7).into();
701 let pref = PortRef::new(port_id, loc);
702 assert_eq!(
703 pref.to_string(),
704 "0000000000abc123.0000000000def456:42@inproc://7"
705 );
706 }
707
708 #[test]
709 fn test_port_ref_debug_all_labels() {
710 let aid = ActorId::new(
711 Uid::Instance(0xabc123),
712 ProcId::new(
713 Uid::Instance(0xdef456),
714 Some(Label::new("my-proc").unwrap()),
715 ),
716 Some(Label::new("my-actor").unwrap()),
717 );
718 let port_id = PortId::new(aid, Port::from(42));
719 let loc: Location = ChannelAddr::Local(7).into();
720 let pref = PortRef::new(port_id, loc);
721 assert_eq!(
722 format!("{:?}", pref),
723 "<'my-actor.my-proc' 0000000000abc123.0000000000def456:42@inproc://7>"
724 );
725 }
726
727 #[test]
728 fn test_port_ref_debug_no_labels() {
729 let aid = ActorId::new(
730 Uid::Instance(0xabc123),
731 ProcId::new(Uid::Instance(0xdef456), None),
732 None,
733 );
734 let port_id = PortId::new(aid, Port::from(42));
735 let loc: Location = ChannelAddr::Local(7).into();
736 let pref = PortRef::new(port_id, loc);
737 assert_eq!(
738 format!("{:?}", pref),
739 "<0000000000abc123.0000000000def456:42@inproc://7>"
740 );
741 }
742
743 #[test]
744 fn test_port_ref_debug_actor_label_only() {
745 let aid = ActorId::new(
746 Uid::Instance(0xabc123),
747 ProcId::new(Uid::Instance(0xdef456), None),
748 Some(Label::new("my-actor").unwrap()),
749 );
750 let port_id = PortId::new(aid, Port::from(42));
751 let loc: Location = ChannelAddr::Local(7).into();
752 let pref = PortRef::new(port_id, loc);
753 assert_eq!(
754 format!("{:?}", pref),
755 "<'my-actor' 0000000000abc123.0000000000def456:42@inproc://7>"
756 );
757 }
758
759 #[test]
760 fn test_port_ref_debug_proc_label_only() {
761 let aid = ActorId::new(
762 Uid::Instance(0xabc123),
763 ProcId::new(
764 Uid::Instance(0xdef456),
765 Some(Label::new("my-proc").unwrap()),
766 ),
767 None,
768 );
769 let port_id = PortId::new(aid, Port::from(42));
770 let loc: Location = ChannelAddr::Local(7).into();
771 let pref = PortRef::new(port_id, loc);
772 assert_eq!(
773 format!("{:?}", pref),
774 "<'.my-proc' 0000000000abc123.0000000000def456:42@inproc://7>"
775 );
776 }
777
778 #[test]
779 fn test_port_ref_fromstr_roundtrip() {
780 let aid = ActorId::new(
781 Uid::Instance(0xabc123),
782 ProcId::new(
783 Uid::Instance(0xdef456),
784 Some(Label::new("my-proc").unwrap()),
785 ),
786 Some(Label::new("my-actor").unwrap()),
787 );
788 let port_id = PortId::new(aid, Port::from(42));
789 let loc: Location = ChannelAddr::Local(7).into();
790 let pref = PortRef::new(port_id, loc);
791 let s = pref.to_string();
792 let parsed: PortRef = s.parse().unwrap();
793 assert_eq!(pref, parsed);
794 }
795
796 #[test]
797 fn test_port_ref_fromstr_missing_separator() {
798 let err = "0000000000abc123.0000000000def456:42"
799 .parse::<PortRef>()
800 .unwrap_err();
801 assert!(matches!(err, RefParseError::MissingSeparator));
802 }
803
804 #[test]
805 fn test_port_ref_eq_and_hash() {
806 use std::collections::hash_map::DefaultHasher;
807 use std::hash::Hasher;
808
809 let aid = ActorId::new(
810 Uid::Instance(0x42),
811 ProcId::new(Uid::Instance(0x99), Some(Label::new("proc").unwrap())),
812 Some(Label::new("actor").unwrap()),
813 );
814 let port_id = PortId::new(aid, Port::from(10));
815 let loc: Location = ChannelAddr::Local(1).into();
816 let a = PortRef::new(port_id.clone(), loc.clone());
817 let b = PortRef::new(port_id, loc);
818 assert_eq!(a, b);
819
820 let hash = |r: &PortRef| {
821 let mut h = DefaultHasher::new();
822 r.hash(&mut h);
823 h.finish()
824 };
825 assert_eq!(hash(&a), hash(&b));
826 }
827
828 #[test]
829 fn test_port_ref_neq_different_location() {
830 let aid = ActorId::new(
831 Uid::Instance(0x42),
832 ProcId::new(Uid::Instance(0x99), Some(Label::new("proc").unwrap())),
833 Some(Label::new("actor").unwrap()),
834 );
835 let port_id = PortId::new(aid, Port::from(10));
836 let a = PortRef::new(port_id.clone(), ChannelAddr::Local(1).into());
837 let b = PortRef::new(port_id, ChannelAddr::Local(2).into());
838 assert_ne!(a, b);
839 }
840
841 #[test]
842 fn test_port_ref_serde_roundtrip() {
843 let aid = ActorId::new(
844 Uid::Instance(0xabcdef),
845 ProcId::new(
846 Uid::Instance(0x123456),
847 Some(Label::new("my-proc").unwrap()),
848 ),
849 Some(Label::new("my-actor").unwrap()),
850 );
851 let port_id = PortId::new(aid, Port::from(42));
852 let loc: Location = ChannelAddr::Local(7).into();
853 let pref = PortRef::new(port_id, loc);
854 let json = serde_json::to_string(&pref).unwrap();
855 let parsed: PortRef = serde_json::from_str(&json).unwrap();
856 assert_eq!(pref, parsed);
857 }
858}