1use std::fmt;
36use std::str::FromStr;
37
38use enum_as_inner::EnumAsInner;
39use serde::Deserialize;
40use serde::Serialize;
41
42use crate::channel::ChannelAddr;
43use crate::context::MailboxExt;
44use crate::id;
45use crate::id::ActorId;
46use crate::id::IdParseError;
47use crate::id::Label;
48use crate::id::PortId;
49use crate::id::ProcId;
50use crate::id::Uid;
51use crate::introspect::IntrospectMessage;
52use crate::parse;
53use crate::port::ControlPort;
54use crate::port::Port;
55use crate::proc::StatusMessage;
56use crate::ref_::PortRef;
57
58#[derive(
86 Clone,
87 EnumAsInner,
88 PartialEq,
89 Eq,
90 Hash,
91 PartialOrd,
92 Ord,
93 Serialize,
94 Deserialize
95)]
96pub enum Location {
97 Addr(ChannelAddr),
99 Via(Uid, Box<Location>),
103}
104
105impl Location {
106 pub fn addr(&self) -> &ChannelAddr {
108 match self {
109 Location::Addr(addr) => addr,
110 Location::Via(_, inner) => inner.addr(),
111 }
112 }
113
114 pub fn with_via(self, uid: Uid) -> Self {
118 Location::Via(uid, Box::new(self))
119 }
120
121 pub fn append_via(self, uid: Uid) -> Self {
124 match self {
125 Location::Addr(addr) => Location::Addr(addr).with_via(uid),
126 Location::Via(outer_uid, inner) => {
127 Location::Via(outer_uid, Box::new(inner.append_via(uid)))
128 }
129 }
130 }
131
132 pub fn pop_via(self) -> Result<(Uid, Location), Location> {
136 match self {
137 Location::Via(uid, inner) => Ok((uid, *inner)),
138 addr @ Location::Addr(_) => Err(addr),
139 }
140 }
141}
142
143impl From<ChannelAddr> for Location {
144 fn from(addr: ChannelAddr) -> Self {
145 Location::Addr(addr)
146 }
147}
148
149impl fmt::Display for Location {
150 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
151 match self {
152 Location::Addr(addr) => f.write_str(&addr.to_zmq_url()),
153 Location::Via(uid, inner) => write!(f, "{uid}.{inner}"),
154 }
155 }
156}
157
158impl fmt::Debug for Location {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160 fmt::Display::fmt(self, f)
161 }
162}
163
164impl FromStr for Location {
165 type Err = anyhow::Error;
166
167 fn from_str(s: &str) -> Result<Self, Self::Err> {
168 parse::addr::parse_location_str(s).map_err(|err| anyhow::anyhow!("{err}"))
169 }
170}
171
172#[derive(Debug, thiserror::Error)]
174pub enum AddrParseError {
175 #[error("missing '@' separator between id and location")]
177 MissingSeparator,
178 #[error("invalid id: {0}")]
180 InvalidId(#[from] IdParseError),
181 #[error("invalid location: {0}")]
183 InvalidLocation(#[source] anyhow::Error),
184}
185
186#[derive(Clone, Serialize, Deserialize, typeuri::Named)]
188pub struct ProcAddr {
189 id: ProcId,
190 location: Location,
191}
192
193impl ProcAddr {
194 pub fn new(id: ProcId, location: Location) -> Self {
196 Self { id, location }
197 }
198
199 pub fn id(&self) -> &ProcId {
201 &self.id
202 }
203
204 pub fn location(&self) -> &Location {
206 &self.location
207 }
208
209 pub fn addr(&self) -> &ChannelAddr {
211 self.location.addr()
212 }
213
214 pub fn uid(&self) -> &Uid {
216 self.id.uid()
217 }
218
219 pub fn label(&self) -> Option<&Label> {
222 self.id.label()
223 }
224
225 pub fn anonymous(addr: ChannelAddr) -> Self {
227 Self::new(id::ProcId::anonymous(), Location::from(addr))
228 }
229
230 pub fn instance(addr: ChannelAddr, base_name: impl AsRef<str>) -> Self {
232 let label = Label::strip(base_name.as_ref());
233 Self::new(id::ProcId::instance(label), Location::from(addr))
234 }
235
236 pub fn singleton(addr: ChannelAddr, name: impl AsRef<str>) -> Self {
238 Self::new(
239 id::ProcId::singleton(Label::strip(name.as_ref())),
240 Location::from(addr),
241 )
242 }
243
244 pub fn actor_addr(&self, name: impl AsRef<str>) -> ActorAddr {
246 let uid = Uid::singleton(Label::strip(name.as_ref()));
247 self.actor_addr_uid(uid)
248 }
249
250 pub fn actor_addr_uid(&self, uid: Uid) -> ActorAddr {
252 ActorAddr::new_from_uid(self.clone(), uid)
253 }
254
255 pub fn log_name(&self) -> &str {
257 self.label().map(|l| l.as_str()).unwrap_or("?")
258 }
259}
260
261impl PartialEq for ProcAddr {
262 fn eq(&self, other: &Self) -> bool {
263 self.id == other.id && self.location == other.location
264 }
265}
266
267impl Eq for ProcAddr {}
268
269impl std::hash::Hash for ProcAddr {
270 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
271 self.id.hash(state);
272 self.location.hash(state);
273 }
274}
275
276impl PartialOrd for ProcAddr {
277 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
278 Some(self.cmp(other))
279 }
280}
281
282impl Ord for ProcAddr {
283 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
284 self.id
285 .cmp(&other.id)
286 .then_with(|| self.location.cmp(&other.location))
287 }
288}
289
290impl fmt::Display for ProcAddr {
291 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292 write!(f, "{}@{}", self.id, self.location)
293 }
294}
295
296impl fmt::Debug for ProcAddr {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 match self.id.label() {
299 Some(label) => write!(f, "<'{}' {}@{}>", label, self.id, self.location),
300 None => write!(f, "<{}@{}>", self.id, self.location),
301 }
302 }
303}
304
305impl FromStr for ProcAddr {
306 type Err = AddrParseError;
307
308 fn from_str(s: &str) -> Result<Self, Self::Err> {
309 parse::addr::parse_proc_addr(s).map_err(|_| legacy_parse_proc_ref(s))
310 }
311}
312
313#[derive(Clone, Serialize, Deserialize, typeuri::Named)]
315pub struct ActorAddr {
316 id: ActorId,
317 location: Location,
318}
319
320hyperactor_config::impl_attrvalue!(ActorAddr);
321
322impl ActorAddr {
323 pub fn new(id: ActorId, location: Location) -> Self {
325 Self { id, location }
326 }
327
328 pub fn new_from_uid(proc_ref: ProcAddr, uid: Uid) -> Self {
330 let actor_id = id::ActorId::new(uid, proc_ref.id.clone(), None);
331 Self::new(actor_id, proc_ref.location)
332 }
333
334 pub fn id(&self) -> &ActorId {
336 &self.id
337 }
338
339 pub fn proc_id(&self) -> &ProcId {
341 self.id.proc_id()
342 }
343
344 pub fn location(&self) -> &Location {
346 &self.location
347 }
348
349 pub fn addr(&self) -> &ChannelAddr {
351 self.location.addr()
352 }
353
354 pub fn uid(&self) -> &Uid {
356 self.id.uid()
357 }
358
359 pub fn label(&self) -> Option<&Label> {
362 self.id.label()
363 }
364
365 pub fn proc_addr(&self) -> ProcAddr {
367 ProcAddr::new(self.id.proc_id().clone(), self.location.clone())
368 }
369
370 pub fn port_addr(&self, port: Port) -> PortAddr {
372 PortAddr::new(
373 id::PortId::new(self.id.clone(), port),
374 self.location.clone(),
375 )
376 }
377
378 pub fn introspect_port(&self) -> PortRef<IntrospectMessage> {
380 PortRef::attest(self.port_addr(Port::control(ControlPort::Introspect)))
381 }
382
383 pub fn status_port(&self) -> PortRef<StatusMessage> {
385 PortRef::attest(self.port_addr(Port::control(ControlPort::Status)))
386 }
387
388 pub fn root(proc_ref: ProcAddr, label: impl Into<Label>) -> Self {
390 let label = label.into();
391 let actor_id = id::ActorId::singleton(label, proc_ref.id.clone());
392 Self::new(actor_id, proc_ref.location)
393 }
394
395 pub fn anonymous_child(&self) -> Self {
397 let child_id = id::ActorId::anonymous(self.id.proc_id().clone());
398 Self::new(child_id, self.location.clone())
399 }
400
401 pub fn is_root(&self) -> bool {
403 self.id.uid().is_singleton()
404 }
405
406 pub fn log_name(&self) -> &str {
408 self.label().map(|l| l.as_str()).unwrap_or("?")
409 }
410}
411
412impl PartialEq for ActorAddr {
413 fn eq(&self, other: &Self) -> bool {
414 self.id == other.id && self.location == other.location
415 }
416}
417
418impl Eq for ActorAddr {}
419
420impl std::hash::Hash for ActorAddr {
421 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
422 self.id.hash(state);
423 self.location.hash(state);
424 }
425}
426
427impl PartialOrd for ActorAddr {
428 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
429 Some(self.cmp(other))
430 }
431}
432
433impl Ord for ActorAddr {
434 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
435 self.id
436 .cmp(&other.id)
437 .then_with(|| self.location.cmp(&other.location))
438 }
439}
440
441impl fmt::Display for ActorAddr {
442 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
443 write!(f, "{}@{}", self.id, self.location)
444 }
445}
446
447impl fmt::Debug for ActorAddr {
448 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
449 match (self.id.label(), self.id.proc_id().label()) {
450 (Some(actor_label), Some(proc_label)) => {
451 write!(
452 f,
453 "<'{}.{}' {}@{}>",
454 actor_label, proc_label, self.id, self.location
455 )
456 }
457 (Some(actor_label), None) => {
458 write!(f, "<'{}' {}@{}>", actor_label, self.id, self.location)
459 }
460 (None, Some(proc_label)) => {
461 write!(f, "<'.{}' {}@{}>", proc_label, self.id, self.location)
462 }
463 (None, None) => {
464 write!(f, "<{}@{}>", self.id, self.location)
465 }
466 }
467 }
468}
469
470impl FromStr for ActorAddr {
471 type Err = AddrParseError;
472
473 fn from_str(s: &str) -> Result<Self, Self::Err> {
474 parse::addr::parse_actor_addr(s).map_err(|_| legacy_parse_actor_ref(s))
475 }
476}
477
478#[derive(Clone, Serialize, Deserialize, typeuri::Named)]
480pub struct PortAddr {
481 id: PortId,
482 location: Location,
483}
484
485impl PortAddr {
486 pub fn new(id: PortId, location: Location) -> Self {
488 Self { id, location }
489 }
490
491 pub fn id(&self) -> &PortId {
493 &self.id
494 }
495
496 pub fn location(&self) -> &Location {
498 &self.location
499 }
500
501 pub fn actor_id(&self) -> &ActorId {
503 self.id.actor_id()
504 }
505
506 pub fn proc_id(&self) -> &ProcId {
508 self.id.actor_id().proc_id()
509 }
510
511 pub(crate) fn is_handler_port(&self) -> bool {
513 self.id.port().is_handler()
514 }
515
516 pub(crate) fn is_control_port_kind(&self, port: ControlPort) -> bool {
518 self.id.port() == Port::control(port)
519 }
520
521 pub fn port(&self) -> Port {
523 self.id.port()
524 }
525
526 pub fn index(&self) -> u64 {
528 self.id.port().as_u64()
529 }
530
531 pub fn ephemeral_index(&self) -> Option<u64> {
533 self.id.port().ephemeral_index()
534 }
535
536 pub fn actor_addr(&self) -> ActorAddr {
538 ActorAddr::new(self.id.actor_id().clone(), self.location.clone())
539 }
540
541 pub fn actor_ref(&self) -> ActorAddr {
543 self.actor_addr()
544 }
545
546 pub fn send(&self, cx: &impl crate::context::Actor, serialized: wirevalue::Any) {
548 let mut headers = hyperactor_config::Flattrs::new();
549 crate::mailbox::headers::set_send_timestamp(&mut headers);
550 cx.post(
551 self.clone(),
552 headers,
553 serialized,
554 true,
555 crate::context::SeqInfoPolicy::AssignNew,
556 );
557 }
558
559 pub fn send_with_headers(
561 &self,
562 cx: &impl crate::context::Actor,
563 serialized: wirevalue::Any,
564 mut headers: hyperactor_config::Flattrs,
565 ) {
566 crate::mailbox::headers::set_send_timestamp(&mut headers);
567 cx.post(
568 self.clone(),
569 headers,
570 serialized,
571 true,
572 crate::context::SeqInfoPolicy::AssignNew,
573 );
574 }
575
576 pub fn split(
578 &self,
579 cx: &impl crate::context::Actor,
580 reducer_spec: Option<crate::accum::ReducerSpec>,
581 reducer_mode: crate::accum::ReducerMode,
582 return_undeliverable: bool,
583 ) -> anyhow::Result<PortAddr> {
584 cx.split(
585 self.clone(),
586 reducer_spec,
587 reducer_mode,
588 return_undeliverable,
589 )
590 }
591}
592
593impl PartialEq for PortAddr {
594 fn eq(&self, other: &Self) -> bool {
595 self.id == other.id && self.location == other.location
596 }
597}
598
599impl Eq for PortAddr {}
600
601impl std::hash::Hash for PortAddr {
602 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
603 self.id.hash(state);
604 self.location.hash(state);
605 }
606}
607
608impl PartialOrd for PortAddr {
609 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
610 Some(self.cmp(other))
611 }
612}
613
614impl Ord for PortAddr {
615 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
616 self.id
617 .cmp(&other.id)
618 .then_with(|| self.location.cmp(&other.location))
619 }
620}
621
622impl fmt::Display for PortAddr {
623 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624 write!(f, "{}@{}", self.id, self.location)
625 }
626}
627
628impl fmt::Debug for PortAddr {
629 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
630 match (
631 self.id.actor_id().label(),
632 self.id.actor_id().proc_id().label(),
633 ) {
634 (Some(actor_label), Some(proc_label)) => {
635 write!(
636 f,
637 "<'{}.{}' {}@{}>",
638 actor_label, proc_label, self.id, self.location
639 )
640 }
641 (Some(actor_label), None) => {
642 write!(f, "<'{}' {}@{}>", actor_label, self.id, self.location)
643 }
644 (None, Some(proc_label)) => {
645 write!(f, "<'.{}' {}@{}>", proc_label, self.id, self.location)
646 }
647 (None, None) => {
648 write!(f, "<{}@{}>", self.id, self.location)
649 }
650 }
651 }
652}
653
654impl FromStr for PortAddr {
655 type Err = AddrParseError;
656
657 fn from_str(s: &str) -> Result<Self, Self::Err> {
658 parse::addr::parse_port_addr(s).map_err(|_| legacy_parse_port_ref(s))
659 }
660}
661
662#[derive(Debug, Clone, EnumAsInner, PartialEq, Eq, Hash, Serialize, Deserialize)]
668pub enum Addr {
669 Proc(ProcAddr),
671 Actor(ActorAddr),
673 Port(PortAddr),
675}
676
677impl Addr {
678 pub fn is_prefix_of(&self, other: &Self) -> bool {
683 match (self, other) {
684 (Self::Proc(p), Self::Actor(a)) => *p == a.proc_addr(),
685 (Self::Proc(p), Self::Port(pt)) => *p == pt.actor_addr().proc_addr(),
686 (Self::Actor(a), Self::Port(pt)) => *a == pt.actor_addr(),
687 (Self::Proc(p1), Self::Proc(p2)) => p1 == p2,
688 (Self::Actor(a1), Self::Actor(a2)) => a1 == a2,
689 (Self::Port(p1), Self::Port(p2)) => p1 == p2,
690 _ => false,
691 }
692 }
693
694 pub fn proc_addr(&self) -> ProcAddr {
696 match self {
697 Self::Proc(p) => p.clone(),
698 Self::Actor(a) => a.proc_addr(),
699 Self::Port(p) => p.actor_addr().proc_addr(),
700 }
701 }
702}
703
704impl PartialOrd for Addr {
705 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
706 Some(self.cmp(other))
707 }
708}
709
710impl Ord for Addr {
711 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
712 let proc_ord = self.proc_addr().cmp(&other.proc_addr());
714 if proc_ord != std::cmp::Ordering::Equal {
715 return proc_ord;
716 }
717 let self_actor_uid = match self {
718 Self::Proc(_) => None,
719 Self::Actor(a) => Some(a.uid()),
720 Self::Port(p) => Some(p.actor_id().uid()),
721 };
722 let other_actor_uid = match other {
723 Self::Proc(_) => None,
724 Self::Actor(a) => Some(a.uid()),
725 Self::Port(p) => Some(p.actor_id().uid()),
726 };
727 let actor_ord = self_actor_uid.cmp(&other_actor_uid);
728 if actor_ord != std::cmp::Ordering::Equal {
729 return actor_ord;
730 }
731 let self_port = match self {
732 Self::Port(p) => Some(p.id().port()),
733 _ => None,
734 };
735 let other_port = match other {
736 Self::Port(p) => Some(p.id().port()),
737 _ => None,
738 };
739 self_port.cmp(&other_port)
740 }
741}
742
743impl fmt::Display for Addr {
744 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745 match self {
746 Self::Proc(p) => fmt::Display::fmt(p, f),
747 Self::Actor(a) => fmt::Display::fmt(a, f),
748 Self::Port(p) => fmt::Display::fmt(p, f),
749 }
750 }
751}
752
753impl FromStr for Addr {
754 type Err = AddrParseError;
755
756 fn from_str(s: &str) -> Result<Self, Self::Err> {
757 parse::addr::parse_addr(s).map_err(|_| legacy_parse_reference(s))
758 }
759}
760
761fn split_ref_input(s: &str) -> Result<(&str, &str), AddrParseError> {
762 let Some((id_text, location_text)) = s.split_once('@') else {
763 return Err(AddrParseError::MissingSeparator);
764 };
765 Ok((id_text, location_text))
766}
767
768fn legacy_parse_proc_ref(s: &str) -> AddrParseError {
769 let Ok((id_text, location_text)) = split_ref_input(s) else {
770 return AddrParseError::MissingSeparator;
771 };
772 if let Err(err) = id_text.parse::<ProcId>() {
773 return AddrParseError::InvalidId(err);
774 }
775 let location = if location_text.is_empty() {
776 "@"
777 } else {
778 location_text
779 };
780 let err = location.parse::<Location>().unwrap_err();
781 AddrParseError::InvalidLocation(err)
782}
783
784fn legacy_parse_actor_ref(s: &str) -> AddrParseError {
785 let Ok((id_text, location_text)) = split_ref_input(s) else {
786 return AddrParseError::MissingSeparator;
787 };
788 if let Err(err) = id_text.parse::<ActorId>() {
789 return AddrParseError::InvalidId(err);
790 }
791 let location = if location_text.is_empty() {
792 "@"
793 } else {
794 location_text
795 };
796 let err = location.parse::<Location>().unwrap_err();
797 AddrParseError::InvalidLocation(err)
798}
799
800fn legacy_parse_port_ref(s: &str) -> AddrParseError {
801 let Ok((id_text, location_text)) = split_ref_input(s) else {
802 return AddrParseError::MissingSeparator;
803 };
804 if let Err(err) = id_text.parse::<PortId>() {
805 return AddrParseError::InvalidId(err);
806 }
807 let location = if location_text.is_empty() {
808 "@"
809 } else {
810 location_text
811 };
812 let err = location.parse::<Location>().unwrap_err();
813 AddrParseError::InvalidLocation(err)
814}
815
816fn legacy_parse_reference(s: &str) -> AddrParseError {
817 let Ok((id_text, location_text)) = split_ref_input(s) else {
818 return AddrParseError::MissingSeparator;
819 };
820 let location = if location_text.is_empty() {
821 "@"
822 } else {
823 location_text
824 };
825 let location_err = || {
826 let err = location.parse::<Location>().unwrap_err();
827 AddrParseError::InvalidLocation(err)
828 };
829
830 let port_result = id_text.parse::<PortId>();
831 if port_result.is_ok() {
832 return location_err();
833 }
834 let actor_result = id_text.parse::<ActorId>();
835 if actor_result.is_ok() {
836 return location_err();
837 }
838 let proc_result = id_text.parse::<ProcId>();
839 if proc_result.is_ok() {
840 return location_err();
841 }
842
843 if id_text.contains(':') {
844 return AddrParseError::InvalidId(port_result.unwrap_err());
845 }
846 if id_text.contains('.') {
847 return AddrParseError::InvalidId(actor_result.unwrap_err());
848 }
849
850 AddrParseError::InvalidId(proc_result.unwrap_err())
851}
852
853impl From<ProcAddr> for Addr {
854 fn from(p: ProcAddr) -> Self {
855 Self::Proc(p)
856 }
857}
858
859impl From<ActorAddr> for Addr {
860 fn from(a: ActorAddr) -> Self {
861 Self::Actor(a)
862 }
863}
864
865impl From<PortAddr> for Addr {
866 fn from(p: PortAddr) -> Self {
867 Self::Port(p)
868 }
869}
870
871#[cfg(test)]
872mod tests {
873 use std::hash::Hash;
874
875 use super::*;
876 use crate::id::Label;
877 use crate::id::Uid;
878 use crate::port::Port;
879
880 #[test]
881 fn test_location_display_fromstr_roundtrip() {
882 let loc: Location = ChannelAddr::Local(42).into();
883 let s = loc.to_string();
884 assert_eq!(s, "inproc://42");
885 let parsed: Location = s.parse().unwrap();
886 assert_eq!(loc, parsed);
887 }
888
889 #[test]
890 fn test_location_tcp() {
891 let addr: ChannelAddr = "tcp:127.0.0.1:8080".parse().unwrap();
892 let loc = Location::from(addr.clone());
893 assert_eq!(loc.to_string(), "tcp://127.0.0.1:8080");
894 assert_eq!(loc.addr(), &addr);
895 }
896
897 #[test]
898 fn test_location_debug_same_as_display() {
899 let loc: Location = ChannelAddr::Local(7).into();
900 assert_eq!(format!("{:?}", loc), format!("{}", loc));
901 }
902
903 #[test]
904 fn test_location_append_via_preserves_existing_route_order() {
905 let outer = Uid::instance(Label::strip("outer"));
906 let inner = Uid::instance(Label::strip("inner"));
907 let location = Location::from(ChannelAddr::Local(7)).with_via(outer.clone());
908
909 assert_eq!(
910 location.append_via(inner.clone()),
911 Location::Via(
912 outer,
913 Box::new(Location::Via(
914 inner,
915 Box::new(Location::Addr(ChannelAddr::Local(7))),
916 )),
917 )
918 );
919 }
920
921 #[test]
922 fn test_proc_ref_display() {
923 let pid = ProcId::new(
924 Uid::Instance(0xabc123, None),
925 Some(Label::new("my-proc").unwrap()),
926 );
927 let loc: Location = ChannelAddr::Local(42).into();
928 let pref = ProcAddr::new(pid, loc);
929 assert_eq!(pref.to_string(), format!("{}@inproc://42", pref.id()));
930 }
931
932 #[test]
933 fn test_proc_addr_identity_constructors() {
934 let anonymous = ProcAddr::anonymous(ChannelAddr::Local(1));
935 assert!(
936 matches!(anonymous.id().uid(), Uid::Instance(_, None)),
937 "anonymous proc addr must have an unlabeled instance id"
938 );
939 assert_eq!(anonymous.label(), None);
940 assert_eq!(*anonymous.location().addr(), ChannelAddr::Local(1));
941
942 let instance = ProcAddr::instance(ChannelAddr::Local(2), "worker");
943 assert!(
944 matches!(
945 instance.id().uid(),
946 Uid::Instance(_, Some(label)) if label.as_str() == "worker"
947 ),
948 "instance proc addr must have a labeled instance id"
949 );
950 assert_eq!(instance.label().map(|label| label.as_str()), Some("worker"));
951 assert_eq!(*instance.location().addr(), ChannelAddr::Local(2));
952
953 let singleton = ProcAddr::singleton(ChannelAddr::Local(3), "controller");
954 assert!(
955 matches!(
956 singleton.id().uid(),
957 Uid::Singleton(label) if label.as_str() == "controller"
958 ),
959 "singleton proc addr must have a singleton id"
960 );
961 assert_eq!(
962 singleton.label().map(|label| label.as_str()),
963 Some("controller")
964 );
965 assert_eq!(*singleton.location().addr(), ChannelAddr::Local(3));
966 }
967
968 #[test]
969 fn test_proc_ref_debug_with_label() {
970 let pid = ProcId::new(
971 Uid::Instance(0xabc123, None),
972 Some(Label::new("my-proc").unwrap()),
973 );
974 let loc: Location = ChannelAddr::Local(42).into();
975 let pref = ProcAddr::new(pid, loc);
976 assert_eq!(
977 format!("{:?}", pref),
978 format!("<'my-proc' {}@inproc://42>", pref.id())
979 );
980 }
981
982 #[test]
983 fn test_proc_ref_debug_without_label() {
984 let pid = ProcId::new(Uid::Instance(0xabc123, None), None);
985 let loc: Location = ChannelAddr::Local(42).into();
986 let pref = ProcAddr::new(pid, loc);
987 assert_eq!(
988 format!("{:?}", pref),
989 format!("<{}@inproc://42>", pref.id())
990 );
991 }
992
993 #[test]
994 fn test_proc_ref_fromstr_roundtrip() {
995 let pid = ProcId::new(
996 Uid::Instance(0xabc123, None),
997 Some(Label::new("my-proc").unwrap()),
998 );
999 let loc: Location = ChannelAddr::Local(42).into();
1000 let pref = ProcAddr::new(pid, loc);
1001 let s = pref.to_string();
1002 let parsed: ProcAddr = s.parse().unwrap();
1003 assert_eq!(pref, parsed);
1004 }
1005
1006 #[test]
1007 fn test_proc_ref_fromstr_tcp() {
1008 let parsed: ProcAddr = format!(
1009 "{}@tcp://127.0.0.1:8080",
1010 ProcId::new(Uid::Instance(0xabc123, None), None)
1011 )
1012 .parse()
1013 .unwrap();
1014 assert_eq!(*parsed.id().uid(), Uid::Instance(0xabc123, None));
1015 assert_eq!(
1016 *parsed.location().addr(),
1017 "tcp:127.0.0.1:8080".parse::<ChannelAddr>().unwrap()
1018 );
1019 }
1020
1021 #[test]
1022 fn test_proc_ref_fromstr_examples() {
1023 let parsed: ProcAddr = "local@inproc://0".parse().unwrap();
1024 assert_eq!(
1025 parsed.id().uid(),
1026 &Uid::singleton(Label::new("local").unwrap())
1027 );
1028 assert_eq!(*parsed.location().addr(), ChannelAddr::Local(0));
1029
1030 let expected_uid = Uid::Instance(0xabc123, None);
1031 let parsed: ProcAddr = format!("controller{}@tcp://[::1]:2345", expected_uid)
1032 .parse()
1033 .unwrap();
1034 assert_eq!(parsed.id().uid(), &expected_uid);
1035 assert_eq!(
1036 parsed.id().label().map(|label| label.as_str()),
1037 Some("controller")
1038 );
1039 assert_eq!(
1040 *parsed.location().addr(),
1041 "tcp:[::1]:2345".parse::<ChannelAddr>().unwrap()
1042 );
1043 }
1044
1045 #[test]
1046 fn test_proc_ref_fromstr_missing_separator() {
1047 let err = ProcId::new(Uid::Instance(0xabc123, None), None)
1048 .to_string()
1049 .parse::<ProcAddr>()
1050 .unwrap_err();
1051 assert!(matches!(err, AddrParseError::MissingSeparator));
1052 }
1053
1054 #[test]
1055 fn test_proc_ref_fromstr_invalid_location() {
1056 let err = "local@tcp://".parse::<ProcAddr>().unwrap_err();
1057 assert!(matches!(err, AddrParseError::InvalidLocation(_)));
1058 }
1059
1060 #[test]
1061 fn test_actor_ref_display() {
1062 let aid = ActorId::new(
1063 Uid::Instance(0xabc123, None),
1064 ProcId::new(
1065 Uid::Instance(0xdef456, None),
1066 Some(Label::new("my-proc").unwrap()),
1067 ),
1068 Some(Label::new("my-actor").unwrap()),
1069 );
1070 let loc: Location = ChannelAddr::Local(42).into();
1071 let aref = ActorAddr::new(aid, loc);
1072 assert_eq!(aref.to_string(), format!("{}@inproc://42", aref.id()));
1073 }
1074
1075 #[test]
1076 fn test_actor_and_port_proc_id_accessors() {
1077 let proc_id = ProcId::new(
1078 Uid::Instance(0xdef456, None),
1079 Some(Label::new("my-proc").unwrap()),
1080 );
1081 let actor_id = ActorId::new(
1082 Uid::Instance(0xabc123, None),
1083 proc_id.clone(),
1084 Some(Label::new("my-actor").unwrap()),
1085 );
1086 let actor_addr = ActorAddr::new(actor_id, ChannelAddr::Local(42).into());
1087 let port_addr = actor_addr.port_addr(Port::from(7));
1088
1089 assert_eq!(actor_addr.proc_id(), &proc_id);
1090 assert_eq!(port_addr.actor_id(), actor_addr.id());
1091 assert_eq!(port_addr.proc_id(), &proc_id);
1092 }
1093
1094 #[test]
1095 fn test_actor_ref_debug_all_labels() {
1096 let aid = ActorId::new(
1097 Uid::Instance(0xabc123, None),
1098 ProcId::new(
1099 Uid::Instance(0xdef456, None),
1100 Some(Label::new("my-proc").unwrap()),
1101 ),
1102 Some(Label::new("my-actor").unwrap()),
1103 );
1104 let loc: Location = ChannelAddr::Local(42).into();
1105 let aref = ActorAddr::new(aid, loc);
1106 assert_eq!(
1107 format!("{:?}", aref),
1108 format!("<'my-actor.my-proc' {}@inproc://42>", aref.id())
1109 );
1110 }
1111
1112 #[test]
1113 fn test_actor_ref_debug_no_labels() {
1114 let aid = ActorId::new(
1115 Uid::Instance(0xabc123, None),
1116 ProcId::new(Uid::Instance(0xdef456, None), None),
1117 None,
1118 );
1119 let loc: Location = ChannelAddr::Local(42).into();
1120 let aref = ActorAddr::new(aid, loc);
1121 assert_eq!(
1122 format!("{:?}", aref),
1123 format!("<{}@inproc://42>", aref.id())
1124 );
1125 }
1126
1127 #[test]
1128 fn test_actor_ref_debug_actor_label_only() {
1129 let aid = ActorId::new(
1130 Uid::Instance(0xabc123, None),
1131 ProcId::new(Uid::Instance(0xdef456, None), None),
1132 Some(Label::new("my-actor").unwrap()),
1133 );
1134 let loc: Location = ChannelAddr::Local(42).into();
1135 let aref = ActorAddr::new(aid, loc);
1136 assert_eq!(
1137 format!("{:?}", aref),
1138 format!("<'my-actor' {}@inproc://42>", aref.id())
1139 );
1140 }
1141
1142 #[test]
1143 fn test_actor_ref_debug_proc_label_only() {
1144 let aid = ActorId::new(
1145 Uid::Instance(0xabc123, None),
1146 ProcId::new(
1147 Uid::Instance(0xdef456, None),
1148 Some(Label::new("my-proc").unwrap()),
1149 ),
1150 None,
1151 );
1152 let loc: Location = ChannelAddr::Local(42).into();
1153 let aref = ActorAddr::new(aid, loc);
1154 assert_eq!(
1155 format!("{:?}", aref),
1156 format!("<'.my-proc' {}@inproc://42>", aref.id())
1157 );
1158 }
1159
1160 #[test]
1161 fn test_actor_ref_fromstr_roundtrip() {
1162 let aid = ActorId::new(
1163 Uid::Instance(0xabc123, None),
1164 ProcId::new(
1165 Uid::Instance(0xdef456, None),
1166 Some(Label::new("my-proc").unwrap()),
1167 ),
1168 Some(Label::new("my-actor").unwrap()),
1169 );
1170 let loc: Location = ChannelAddr::Local(42).into();
1171 let aref = ActorAddr::new(aid, loc);
1172 let s = aref.to_string();
1173 let parsed: ActorAddr = s.parse().unwrap();
1174 assert_eq!(aref, parsed);
1175 assert_eq!(parsed.id.label().map(|l| l.as_str()), Some("my-actor"));
1176 assert_eq!(
1177 parsed.id.proc_id().label().map(|l| l.as_str()),
1178 Some("my-proc")
1179 );
1180 }
1181
1182 #[test]
1183 fn test_actor_ref_fromstr_examples() {
1184 let expected_actor_uid = Uid::Instance(0xabc123, None);
1185 let parsed: ActorAddr = format!("controller{}.local@inproc://0", expected_actor_uid)
1186 .parse()
1187 .unwrap();
1188 assert_eq!(parsed.id().uid(), &expected_actor_uid);
1189 assert_eq!(
1190 parsed.id().label().map(|label| label.as_str()),
1191 Some("controller")
1192 );
1193 assert_eq!(
1194 parsed.id().proc_id().uid(),
1195 &Uid::singleton(Label::new("local").unwrap())
1196 );
1197 assert_eq!(*parsed.location().addr(), ChannelAddr::Local(0));
1198 }
1199
1200 #[test]
1201 fn test_actor_ref_fromstr_missing_separator() {
1202 let err = ActorId::new(
1203 Uid::Instance(0xabc123, None),
1204 ProcId::new(Uid::Instance(0xdef456, None), None),
1205 None,
1206 )
1207 .to_string()
1208 .parse::<ActorAddr>()
1209 .unwrap_err();
1210 assert!(matches!(err, AddrParseError::MissingSeparator));
1211 }
1212
1213 #[test]
1214 fn test_actor_ref_fromstr_invalid_location() {
1215 let err = "local.local@tcp://".parse::<ActorAddr>().unwrap_err();
1216 assert!(matches!(err, AddrParseError::InvalidLocation(_)));
1217 }
1218
1219 #[test]
1220 fn test_proc_ref_eq_and_hash() {
1221 use std::collections::hash_map::DefaultHasher;
1222 use std::hash::Hasher;
1223
1224 let pid = ProcId::new(Uid::Instance(0x42, None), Some(Label::new("proc").unwrap()));
1225 let loc: Location = ChannelAddr::Local(1).into();
1226 let a = ProcAddr::new(pid.clone(), loc.clone());
1227 let b = ProcAddr::new(pid, loc);
1228 assert_eq!(a, b);
1229
1230 let hash = |r: &ProcAddr| {
1231 let mut h = DefaultHasher::new();
1232 r.hash(&mut h);
1233 h.finish()
1234 };
1235 assert_eq!(hash(&a), hash(&b));
1236 }
1237
1238 #[test]
1239 fn test_proc_ref_neq_different_location() {
1240 let pid = ProcId::new(Uid::Instance(0x42, None), Some(Label::new("proc").unwrap()));
1241 let a = ProcAddr::new(pid.clone(), ChannelAddr::Local(1).into());
1242 let b = ProcAddr::new(pid, ChannelAddr::Local(2).into());
1243 assert_ne!(a, b);
1244 }
1245
1246 #[test]
1247 fn test_actor_ref_eq_and_hash() {
1248 use std::collections::hash_map::DefaultHasher;
1249 use std::hash::Hasher;
1250
1251 let aid = ActorId::new(
1252 Uid::Instance(0x42, None),
1253 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1254 Some(Label::new("actor").unwrap()),
1255 );
1256 let loc: Location = ChannelAddr::Local(1).into();
1257 let a = ActorAddr::new(aid.clone(), loc.clone());
1258 let b = ActorAddr::new(aid, loc);
1259 assert_eq!(a, b);
1260
1261 let hash = |r: &ActorAddr| {
1262 let mut h = DefaultHasher::new();
1263 r.hash(&mut h);
1264 h.finish()
1265 };
1266 assert_eq!(hash(&a), hash(&b));
1267 }
1268
1269 #[test]
1270 fn test_proc_ref_singleton() {
1271 let pid = ProcId::new(
1272 Uid::singleton(Label::new("my-proc").unwrap()),
1273 Some(Label::new("my-proc").unwrap()),
1274 );
1275 let loc: Location = ChannelAddr::Local(0).into();
1276 let pref = ProcAddr::new(pid, loc);
1277 let s = pref.to_string();
1278 assert_eq!(s, "my-proc@inproc://0");
1279 let parsed: ProcAddr = s.parse().unwrap();
1280 assert_eq!(pref, parsed);
1281 }
1282
1283 #[test]
1284 fn test_reference_prefix_relationships() {
1285 let proc_ref = ProcAddr::singleton(ChannelAddr::Local(42), "service");
1286 let actor_ref = proc_ref.actor_addr("host_agent");
1287 let port_ref = actor_ref.port_addr(Port::from(7u64));
1288
1289 assert!(Addr::Proc(proc_ref.clone()).is_prefix_of(&Addr::Actor(actor_ref.clone())));
1290 assert!(Addr::Proc(proc_ref.clone()).is_prefix_of(&Addr::Port(port_ref.clone())));
1291 assert!(Addr::Actor(actor_ref.clone()).is_prefix_of(&Addr::Port(port_ref)));
1292 }
1293
1294 #[test]
1295 fn test_location_serde_roundtrip() {
1296 let loc: Location = ChannelAddr::Local(42).into();
1297 let json = serde_json::to_string(&loc).unwrap();
1298 let parsed: Location = serde_json::from_str(&json).unwrap();
1299 assert_eq!(loc, parsed);
1300 }
1301
1302 #[test]
1303 fn test_proc_ref_serde_roundtrip() {
1304 let pid = ProcId::new(
1305 Uid::Instance(0xabcdef, None),
1306 Some(Label::new("my-proc").unwrap()),
1307 );
1308 let loc: Location = ChannelAddr::Local(42).into();
1309 let pref = ProcAddr::new(pid, loc);
1310 let json = serde_json::to_string(&pref).unwrap();
1311 let parsed: ProcAddr = serde_json::from_str(&json).unwrap();
1312 assert_eq!(pref, parsed);
1313 }
1314
1315 #[test]
1316 fn test_actor_ref_serde_roundtrip() {
1317 let aid = ActorId::new(
1318 Uid::Instance(0xabcdef, None),
1319 ProcId::new(
1320 Uid::Instance(0x123456, None),
1321 Some(Label::new("my-proc").unwrap()),
1322 ),
1323 Some(Label::new("my-actor").unwrap()),
1324 );
1325 let loc: Location = ChannelAddr::Local(42).into();
1326 let aref = ActorAddr::new(aid, loc);
1327 let json = serde_json::to_string(&aref).unwrap();
1328 let parsed: ActorAddr = serde_json::from_str(&json).unwrap();
1329 assert_eq!(aref, parsed);
1330 }
1331
1332 #[test]
1333 fn test_proc_ref_with_metatls_location() {
1334 use crate::channel::TlsAddr;
1335
1336 let pid = ProcId::new(Uid::Instance(0x42, None), None);
1337 let loc: Location = ChannelAddr::MetaTls(TlsAddr::new("example.com", 443)).into();
1338 let pref = ProcAddr::new(pid, loc);
1339 let s = pref.to_string();
1340 assert_eq!(s, format!("{}@metatls://example.com:443", pref.id()));
1341 let parsed: ProcAddr = s.parse().unwrap();
1342 assert_eq!(pref, parsed);
1343 }
1344
1345 #[test]
1346 fn test_port_ref_construction_and_accessors() {
1347 let aid = ActorId::new(
1348 Uid::Instance(0xabc123, None),
1349 ProcId::new(
1350 Uid::Instance(0xdef456, None),
1351 Some(Label::new("my-proc").unwrap()),
1352 ),
1353 Some(Label::new("my-actor").unwrap()),
1354 );
1355 let port_id = PortId::new(aid.clone(), Port::from(42));
1356 let loc: Location = ChannelAddr::Local(7).into();
1357 let pref = PortAddr::new(port_id.clone(), loc.clone());
1358 assert_eq!(pref.id(), &port_id);
1359 assert_eq!(pref.location(), &loc);
1360 assert_eq!(pref.actor_id(), &aid);
1361 }
1362
1363 #[test]
1364 fn test_port_ref_display() {
1365 let aid = ActorId::new(
1366 Uid::Instance(0xabc123, None),
1367 ProcId::new(
1368 Uid::Instance(0xdef456, None),
1369 Some(Label::new("my-proc").unwrap()),
1370 ),
1371 Some(Label::new("my-actor").unwrap()),
1372 );
1373 let port_id = PortId::new(aid, Port::from(42));
1374 let loc: Location = ChannelAddr::Local(7).into();
1375 let pref = PortAddr::new(port_id, loc);
1376 assert_eq!(pref.to_string(), format!("{}@inproc://7", pref.id()));
1377 }
1378
1379 #[test]
1380 fn test_port_ref_debug_all_labels() {
1381 let aid = ActorId::new(
1382 Uid::Instance(0xabc123, None),
1383 ProcId::new(
1384 Uid::Instance(0xdef456, None),
1385 Some(Label::new("my-proc").unwrap()),
1386 ),
1387 Some(Label::new("my-actor").unwrap()),
1388 );
1389 let port_id = PortId::new(aid, Port::from(42));
1390 let loc: Location = ChannelAddr::Local(7).into();
1391 let pref = PortAddr::new(port_id, loc);
1392 assert_eq!(
1393 format!("{:?}", pref),
1394 format!("<'my-actor.my-proc' {}@inproc://7>", pref.id())
1395 );
1396 }
1397
1398 #[test]
1399 fn test_port_ref_debug_no_labels() {
1400 let aid = ActorId::new(
1401 Uid::Instance(0xabc123, None),
1402 ProcId::new(Uid::Instance(0xdef456, None), None),
1403 None,
1404 );
1405 let port_id = PortId::new(aid, Port::from(42));
1406 let loc: Location = ChannelAddr::Local(7).into();
1407 let pref = PortAddr::new(port_id, loc);
1408 assert_eq!(format!("{:?}", pref), format!("<{}@inproc://7>", pref.id()));
1409 }
1410
1411 #[test]
1412 fn test_port_ref_debug_actor_label_only() {
1413 let aid = ActorId::new(
1414 Uid::Instance(0xabc123, None),
1415 ProcId::new(Uid::Instance(0xdef456, None), None),
1416 Some(Label::new("my-actor").unwrap()),
1417 );
1418 let port_id = PortId::new(aid, Port::from(42));
1419 let loc: Location = ChannelAddr::Local(7).into();
1420 let pref = PortAddr::new(port_id, loc);
1421 assert_eq!(
1422 format!("{:?}", pref),
1423 format!("<'my-actor' {}@inproc://7>", pref.id())
1424 );
1425 }
1426
1427 #[test]
1428 fn test_port_ref_debug_proc_label_only() {
1429 let aid = ActorId::new(
1430 Uid::Instance(0xabc123, None),
1431 ProcId::new(
1432 Uid::Instance(0xdef456, None),
1433 Some(Label::new("my-proc").unwrap()),
1434 ),
1435 None,
1436 );
1437 let port_id = PortId::new(aid, Port::from(42));
1438 let loc: Location = ChannelAddr::Local(7).into();
1439 let pref = PortAddr::new(port_id, loc);
1440 assert_eq!(
1441 format!("{:?}", pref),
1442 format!("<'.my-proc' {}@inproc://7>", pref.id())
1443 );
1444 }
1445
1446 #[test]
1447 fn test_port_ref_fromstr_roundtrip() {
1448 let aid = ActorId::new(
1449 Uid::Instance(0xabc123, None),
1450 ProcId::new(
1451 Uid::Instance(0xdef456, None),
1452 Some(Label::new("my-proc").unwrap()),
1453 ),
1454 Some(Label::new("my-actor").unwrap()),
1455 );
1456 let port_id = PortId::new(aid, Port::from(42));
1457 let loc: Location = ChannelAddr::Local(7).into();
1458 let pref = PortAddr::new(port_id, loc);
1459 let s = pref.to_string();
1460 let parsed: PortAddr = s.parse().unwrap();
1461 assert_eq!(pref, parsed);
1462 assert_eq!(
1463 parsed.id.actor_id().label().map(|l| l.as_str()),
1464 Some("my-actor")
1465 );
1466 assert_eq!(
1467 parsed.id.actor_id().proc_id().label().map(|l| l.as_str()),
1468 Some("my-proc")
1469 );
1470 }
1471
1472 #[test]
1473 fn test_port_ref_fromstr_examples() {
1474 let expected_actor_uid = Uid::Instance(0xabc123, None);
1475 let expected_proc_uid = Uid::Instance(0xdef456, None);
1476 let parsed: PortAddr = format!(
1477 "{}.{}:42@tcp://[::1]:2345",
1478 expected_actor_uid, expected_proc_uid
1479 )
1480 .parse()
1481 .unwrap();
1482 assert_eq!(parsed.id().actor_id().uid(), &expected_actor_uid);
1483 assert_eq!(parsed.id().proc_id().uid(), &expected_proc_uid);
1484 assert_eq!(parsed.id().port(), Port::from(42));
1485 assert_eq!(
1486 *parsed.location().addr(),
1487 "tcp:[::1]:2345".parse::<ChannelAddr>().unwrap()
1488 );
1489 }
1490
1491 #[test]
1492 fn test_port_ref_fromstr_missing_separator() {
1493 let err = PortId::new(
1494 ActorId::new(
1495 Uid::Instance(0xabc123, None),
1496 ProcId::new(Uid::Instance(0xdef456, None), None),
1497 None,
1498 ),
1499 Port::from(42),
1500 )
1501 .to_string()
1502 .parse::<PortAddr>()
1503 .unwrap_err();
1504 assert!(matches!(err, AddrParseError::MissingSeparator));
1505 }
1506
1507 #[test]
1508 fn test_port_ref_fromstr_invalid_location() {
1509 let err = "local.local:7@tcp://".parse::<PortAddr>().unwrap_err();
1510 assert!(matches!(err, AddrParseError::InvalidLocation(_)));
1511 }
1512
1513 #[test]
1514 fn test_reference_fromstr_specificity() {
1515 let parsed: Addr = "local@inproc://0".parse().unwrap();
1516 assert!(parsed.is_proc());
1517
1518 let parsed: Addr = "local.local@inproc://0".parse().unwrap();
1519 assert!(parsed.is_actor());
1520
1521 let parsed: Addr = "local.local:7@inproc://0".parse().unwrap();
1522 assert!(parsed.is_port());
1523 }
1524
1525 #[test]
1526 fn test_reference_fromstr_rejects_malformed_specific_forms() {
1527 assert!("local.local:not-a-port@inproc://0".parse::<Addr>().is_err());
1528 assert!("local.<bad!>@inproc://0".parse::<Addr>().is_err());
1529 assert!("local@tcp://".parse::<Addr>().is_err());
1530 }
1531
1532 #[test]
1533 fn test_reference_fromstr_does_not_downcast_malformed_port_ref() {
1534 let err = "local.local:not-a-port@inproc://0"
1535 .parse::<Addr>()
1536 .unwrap_err();
1537 assert!(matches!(
1538 err,
1539 AddrParseError::InvalidId(IdParseError::InvalidPort(_))
1540 ));
1541 }
1542
1543 #[test]
1544 fn test_reference_fromstr_does_not_downcast_malformed_actor_ref() {
1545 let err = "local.<bad!>@inproc://0".parse::<Addr>().unwrap_err();
1546 assert!(matches!(
1547 err,
1548 AddrParseError::InvalidId(IdParseError::InvalidActorProcUid(_))
1549 ));
1550 }
1551
1552 #[test]
1553 fn test_port_ref_eq_and_hash() {
1554 use std::collections::hash_map::DefaultHasher;
1555 use std::hash::Hasher;
1556
1557 let aid = ActorId::new(
1558 Uid::Instance(0x42, None),
1559 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1560 Some(Label::new("actor").unwrap()),
1561 );
1562 let port_id = PortId::new(aid, Port::from(10));
1563 let loc: Location = ChannelAddr::Local(1).into();
1564 let a = PortAddr::new(port_id.clone(), loc.clone());
1565 let b = PortAddr::new(port_id, loc);
1566 assert_eq!(a, b);
1567
1568 let hash = |r: &PortAddr| {
1569 let mut h = DefaultHasher::new();
1570 r.hash(&mut h);
1571 h.finish()
1572 };
1573 assert_eq!(hash(&a), hash(&b));
1574 }
1575
1576 #[test]
1577 fn test_port_ref_neq_different_location() {
1578 let aid = ActorId::new(
1579 Uid::Instance(0x42, None),
1580 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1581 Some(Label::new("actor").unwrap()),
1582 );
1583 let port_id = PortId::new(aid, Port::from(10));
1584 let a = PortAddr::new(port_id.clone(), ChannelAddr::Local(1).into());
1585 let b = PortAddr::new(port_id, ChannelAddr::Local(2).into());
1586 assert_ne!(a, b);
1587 }
1588
1589 #[test]
1590 fn test_port_ref_serde_roundtrip() {
1591 let aid = ActorId::new(
1592 Uid::Instance(0xabcdef, None),
1593 ProcId::new(
1594 Uid::Instance(0x123456, None),
1595 Some(Label::new("my-proc").unwrap()),
1596 ),
1597 Some(Label::new("my-actor").unwrap()),
1598 );
1599 let port_id = PortId::new(aid, Port::from(42));
1600 let loc: Location = ChannelAddr::Local(7).into();
1601 let pref = PortAddr::new(port_id, loc);
1602 let json = serde_json::to_string(&pref).unwrap();
1603 let parsed: PortAddr = serde_json::from_str(&json).unwrap();
1604 assert_eq!(pref, parsed);
1605 }
1606}