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