1use std::cmp::Ordering;
38use std::collections::hash_map::DefaultHasher;
39use std::fmt;
40use std::hash::Hash;
41use std::hash::Hasher;
42use std::path::Path;
43use std::path::PathBuf;
44use std::str::FromStr;
45
46use enum_as_inner::EnumAsInner;
47use serde::Deserialize;
48use serde::Serialize;
49use serde::de::EnumAccess;
50use serde::de::SeqAccess;
51use serde::de::VariantAccess;
52use serde::de::Visitor;
53use serde::ser::SerializeTupleVariant;
54use smol_str::SmolStr;
55
56use crate::addr::ActorAddr;
57use crate::addr::Addr;
58use crate::addr::Location;
59use crate::addr::PortAddr;
60use crate::addr::ProcAddr;
61use crate::parse::id::encode_base58_uid;
62use crate::port::Port;
63
64const MAX_LABEL_LEN: usize = 63;
66
67#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
71pub struct Label(SmolStr);
72
73#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
75pub enum LabelError {
76 #[error("label must not be empty")]
78 Empty,
79 #[error("label exceeds 63 characters")]
81 TooLong,
82 #[error("label must start with a lowercase letter")]
84 InvalidStart,
85 #[error("label must end with a lowercase letter or digit")]
87 InvalidEnd,
88 #[error("label contains invalid character '{0}'")]
91 InvalidChar(char),
92}
93
94impl Label {
95 pub fn new(s: &str) -> Result<Self, LabelError> {
97 if s.is_empty() {
98 return Err(LabelError::Empty);
99 }
100 if s.len() > MAX_LABEL_LEN {
101 return Err(LabelError::TooLong);
102 }
103 let first = s.as_bytes()[0];
104 if !first.is_ascii_lowercase() {
105 return Err(LabelError::InvalidStart);
106 }
107 let last = s.as_bytes()[s.len() - 1];
108 if !last.is_ascii_lowercase() && !last.is_ascii_digit() {
109 return Err(LabelError::InvalidEnd);
110 }
111 for ch in s.chars() {
112 if !ch.is_ascii_lowercase() && !ch.is_ascii_digit() && ch != '-' && ch != '_' {
113 return Err(LabelError::InvalidChar(ch));
114 }
115 }
116 Ok(Self(SmolStr::new(s)))
117 }
118
119 pub fn strip(s: &str) -> Self {
125 let lowered: String = s
126 .chars()
127 .filter_map(|ch| {
128 let ch = ch.to_ascii_lowercase();
129 if ch.is_ascii_lowercase() || ch.is_ascii_digit() || ch == '-' || ch == '_' {
130 Some(ch)
131 } else {
132 None
133 }
134 })
135 .collect();
136
137 let trimmed = lowered.trim_start_matches(|c: char| !c.is_ascii_lowercase());
139 let trimmed =
141 trimmed.trim_end_matches(|c: char| !c.is_ascii_lowercase() && !c.is_ascii_digit());
142
143 if trimmed.is_empty() {
144 return Self(SmolStr::new("nil"));
145 }
146
147 let truncated = if trimmed.len() > MAX_LABEL_LEN {
148 let t = &trimmed[..MAX_LABEL_LEN];
150 t.trim_end_matches(|c: char| !c.is_ascii_lowercase() && !c.is_ascii_digit())
151 } else {
152 trimmed
153 };
154
155 if truncated.is_empty() {
156 Self(SmolStr::new("nil"))
157 } else {
158 Self(SmolStr::new(truncated))
159 }
160 }
161
162 pub fn as_str(&self) -> &str {
164 &self.0
165 }
166}
167
168impl fmt::Debug for Label {
169 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
170 write!(f, "Label({:?})", self.0.as_str())
171 }
172}
173
174impl fmt::Display for Label {
175 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
176 f.write_str(&self.0)
177 }
178}
179
180impl FromStr for Label {
181 type Err = LabelError;
182
183 fn from_str(s: &str) -> Result<Self, Self::Err> {
184 Self::new(s)
185 }
186}
187
188impl Serialize for Label {
189 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
190 self.0.as_str().serialize(serializer)
191 }
192}
193
194impl<'de> Deserialize<'de> for Label {
195 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
196 let s = String::deserialize(deserializer)?;
197 Label::new(&s).map_err(serde::de::Error::custom)
198 }
199}
200
201#[derive(Clone, EnumAsInner)]
206pub enum Uid {
207 Singleton(Label),
209 Instance(u64, Option<Label>),
211}
212
213#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
215pub enum UidParseError {
216 #[error("invalid uid syntax: {0}")]
218 InvalidSyntax(String),
219 #[error("invalid label: {0}")]
221 InvalidLabel(#[from] LabelError),
222 #[error("invalid base58 uid: {0}")]
224 InvalidBase58(String),
225}
226
227impl Uid {
228 pub fn anonymous() -> Self {
230 Uid::Instance(rand::random(), None)
231 }
232
233 pub fn instance(label: Label) -> Self {
235 Uid::Instance(rand::random(), Some(label))
236 }
237
238 pub fn singleton(label: Label) -> Self {
240 Uid::Singleton(label)
241 }
242
243 pub fn label(&self) -> Option<&Label> {
248 match self {
249 Uid::Singleton(label) => Some(label),
250 Uid::Instance(_, label) => label.as_ref(),
251 }
252 }
253
254 pub fn instance_uid_base58(&self) -> Option<String> {
256 match self {
257 Uid::Singleton(_) => None,
258 Uid::Instance(uid, _) => Some(encode_base58_uid(*uid)),
259 }
260 }
261
262 pub fn instance_value(&self) -> Option<u64> {
264 match self {
265 Uid::Singleton(_) => None,
266 Uid::Instance(uid, _) => Some(*uid),
267 }
268 }
269
270 pub fn parse_instance_uid_base58(s: &str) -> Result<u64, UidParseError> {
272 parse_base58_uid(s)
273 }
274
275 pub fn with_label(self, label: Option<Label>) -> Self {
279 match self {
280 Uid::Singleton(label) => Uid::Singleton(label),
281 Uid::Instance(uid, existing) => Uid::Instance(uid, label.or(existing)),
282 }
283 }
284}
285
286impl PartialEq for Uid {
287 fn eq(&self, other: &Self) -> bool {
288 match (self, other) {
289 (Uid::Singleton(a), Uid::Singleton(b)) => a == b,
290 (Uid::Instance(a, _), Uid::Instance(b, _)) => a == b,
291 _ => false,
292 }
293 }
294}
295
296impl Eq for Uid {}
297
298impl Hash for Uid {
299 fn hash<H: Hasher>(&self, state: &mut H) {
300 std::mem::discriminant(self).hash(state);
301 match self {
302 Uid::Singleton(label) => label.hash(state),
303 Uid::Instance(uid, _) => uid.hash(state),
304 }
305 }
306}
307
308impl PartialOrd for Uid {
309 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
310 Some(self.cmp(other))
311 }
312}
313
314impl Ord for Uid {
315 fn cmp(&self, other: &Self) -> Ordering {
316 match (self, other) {
317 (Uid::Singleton(a), Uid::Singleton(b)) => a.cmp(b),
318 (Uid::Singleton(_), Uid::Instance(_, _)) => Ordering::Less,
319 (Uid::Instance(_, _), Uid::Singleton(_)) => Ordering::Greater,
320 (Uid::Instance(a, _), Uid::Instance(b, _)) => a.cmp(b),
321 }
322 }
323}
324
325impl fmt::Debug for Uid {
328 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
329 match self {
330 Uid::Singleton(label) => write!(f, "Uid({})", label),
331 Uid::Instance(uid, Some(label)) => {
332 write!(f, "Uid({}<{}>)", label, encode_base58_uid(*uid))
333 }
334 Uid::Instance(uid, None) => write!(f, "Uid(<{}>)", encode_base58_uid(*uid)),
335 }
336 }
337}
338
339impl fmt::Display for Uid {
340 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
341 match self {
342 Uid::Singleton(label) => write!(f, "{label}"),
343 Uid::Instance(uid, Some(label)) => write!(f, "{}<{}>", label, encode_base58_uid(*uid)),
344 Uid::Instance(uid, None) => write!(f, "<{}>", encode_base58_uid(*uid)),
345 }
346 }
347}
348
349impl FromStr for Uid {
352 type Err = UidParseError;
353
354 fn from_str(s: &str) -> Result<Self, Self::Err> {
355 crate::parse::id::parse_uid_str(s)
356 .map_err(|err| UidParseError::InvalidSyntax(err.to_string()))
357 }
358}
359
360fn parse_base58_uid(s: &str) -> Result<u64, UidParseError> {
361 crate::parse::id::decode_base58_uid(s).map_err(|_| UidParseError::InvalidBase58(s.to_string()))
362}
363
364impl Serialize for Uid {
365 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
366 if serializer.is_human_readable() {
367 serializer.serialize_str(&self.to_string())
368 } else {
369 match self {
370 Uid::Singleton(label) => {
371 serializer.serialize_newtype_variant("Uid", 0, "Singleton", label)
372 }
373 Uid::Instance(uid, label) => {
374 let mut variant =
375 serializer.serialize_tuple_variant("Uid", 1, "Instance", 2)?;
376 variant.serialize_field(uid)?;
377 variant.serialize_field(label)?;
378 variant.end()
379 }
380 }
381 }
382 }
383}
384
385impl<'de> Deserialize<'de> for Uid {
386 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
387 if deserializer.is_human_readable() {
388 let s = String::deserialize(deserializer)?;
389 Uid::from_str(&s).map_err(serde::de::Error::custom)
390 } else {
391 deserializer.deserialize_enum("Uid", &["Singleton", "Instance"], UidVisitor)
392 }
393 }
394}
395
396struct UidVisitor;
397
398impl<'de> Visitor<'de> for UidVisitor {
399 type Value = Uid;
400
401 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
402 f.write_str("a uid enum")
403 }
404
405 fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
406 where
407 A: EnumAccess<'de>,
408 {
409 match data.variant()? {
410 (UidVariant::Singleton, variant) => variant.newtype_variant().map(Uid::Singleton),
411 (UidVariant::Instance, variant) => {
412 let (uid, label) = variant.tuple_variant(2, UidInstanceVisitor)?;
413 Ok(Uid::Instance(uid, label))
414 }
415 }
416 }
417}
418
419#[derive(Deserialize)]
420#[serde(field_identifier)]
421enum UidVariant {
422 Singleton,
423 Instance,
424}
425
426struct UidInstanceVisitor;
427
428impl<'de> Visitor<'de> for UidInstanceVisitor {
429 type Value = (u64, Option<Label>);
430
431 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
432 f.write_str("a uid instance tuple")
433 }
434
435 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
436 where
437 A: SeqAccess<'de>,
438 {
439 let uid = seq
440 .next_element()?
441 .ok_or_else(|| serde::de::Error::invalid_length(0, &self))?;
442 let label = seq
443 .next_element()?
444 .ok_or_else(|| serde::de::Error::invalid_length(1, &self))?;
445 Ok((uid, label))
446 }
447}
448
449#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
451pub enum IdParseError {
452 #[error("invalid proc id: {0}")]
454 InvalidProcId(#[from] UidParseError),
455 #[error("invalid actor id: expected format `<actor>.<proc>`")]
457 InvalidActorIdFormat,
458 #[error("invalid actor uid: {0}")]
460 InvalidActorUid(UidParseError),
461 #[error("invalid proc uid in actor id: {0}")]
463 InvalidActorProcUid(UidParseError),
464 #[error("invalid port id: expected format `<actor>:<port>`")]
466 InvalidPortIdFormat,
467 #[error("invalid port: {0}")]
469 InvalidPort(String),
470}
471
472#[derive(Clone, Serialize, Deserialize)]
476pub struct ProcId {
477 uid: Uid,
478}
479
480impl ProcId {
481 pub fn new(uid: Uid, label: Option<Label>) -> Self {
483 Self {
484 uid: uid.with_label(label),
485 }
486 }
487
488 pub fn anonymous() -> Self {
490 Self {
491 uid: Uid::anonymous(),
492 }
493 }
494
495 pub fn singleton(label: Label) -> Self {
497 Self {
498 uid: Uid::Singleton(label),
499 }
500 }
501
502 pub fn instance(label: Label) -> Self {
504 Self {
505 uid: Uid::instance(label),
506 }
507 }
508
509 pub fn uid(&self) -> &Uid {
511 &self.uid
512 }
513
514 pub fn label(&self) -> Option<&Label> {
516 self.uid.label()
517 }
518
519 pub fn to_path_elem(&self, base_dir: &Path) -> PathBuf {
528 let pseudo_id = self.pseudo_uid();
529 let tag = match pseudo_id {
530 Uid::Singleton(label) => {
531 panic!("pseudo uid should never be a singleton, but got: {}", label)
532 }
533 Uid::Instance(uid, _) => encode_base58_uid(uid).to_string(),
534 };
535 base_dir.join(tag)
536 }
537
538 pub fn pseudo_uid(&self) -> Uid {
547 match &self.uid {
548 Uid::Instance(_, _) => self.uid.clone(),
549 Uid::Singleton(label) => {
550 let mut h = DefaultHasher::new();
551 label.hash(&mut h);
552 Uid::Instance(h.finish(), None)
553 }
554 }
555 }
556}
557
558impl PartialEq for ProcId {
559 fn eq(&self, other: &Self) -> bool {
560 self.uid == other.uid
561 }
562}
563
564impl Eq for ProcId {}
565
566impl Hash for ProcId {
567 fn hash<H: Hasher>(&self, state: &mut H) {
568 self.uid.hash(state);
569 }
570}
571
572impl PartialOrd for ProcId {
573 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
574 Some(self.cmp(other))
575 }
576}
577
578impl Ord for ProcId {
579 fn cmp(&self, other: &Self) -> Ordering {
580 self.uid.cmp(&other.uid)
581 }
582}
583
584impl fmt::Display for ProcId {
585 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
586 fmt::Display::fmt(&self.uid, f)
587 }
588}
589
590impl fmt::Debug for ProcId {
591 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
592 match self.label() {
593 Some(label) => write!(f, "<'{}' {}>", label, self.uid),
594 None => write!(f, "<{}>", self.uid),
595 }
596 }
597}
598
599impl FromStr for ProcId {
600 type Err = IdParseError;
601
602 fn from_str(s: &str) -> Result<Self, Self::Err> {
603 crate::parse::id::parse_proc_id(s).map_err(|err| {
604 IdParseError::InvalidProcId(UidParseError::InvalidSyntax(err.to_string()))
605 })
606 }
607}
608
609#[derive(Clone, Serialize, Deserialize)]
613pub struct ActorId {
614 uid: Uid,
615 proc_id: ProcId,
616}
617
618impl ActorId {
619 pub fn new(uid: Uid, proc_id: ProcId, label: Option<Label>) -> Self {
621 Self {
622 uid: uid.with_label(label),
623 proc_id,
624 }
625 }
626
627 pub fn singleton(label: Label, proc_id: ProcId) -> Self {
629 Self {
630 uid: Uid::Singleton(label),
631 proc_id,
632 }
633 }
634
635 pub fn anonymous(proc_id: ProcId) -> Self {
637 Self {
638 uid: Uid::anonymous(),
639 proc_id,
640 }
641 }
642
643 pub fn instance(label: Label, proc_id: ProcId) -> Self {
645 Self {
646 uid: Uid::instance(label),
647 proc_id,
648 }
649 }
650
651 pub fn uid(&self) -> &Uid {
653 &self.uid
654 }
655
656 pub fn proc_id(&self) -> &ProcId {
658 &self.proc_id
659 }
660
661 pub fn label(&self) -> Option<&Label> {
663 self.uid.label()
664 }
665}
666
667impl PartialEq for ActorId {
668 fn eq(&self, other: &Self) -> bool {
669 self.proc_id == other.proc_id && self.uid == other.uid
670 }
671}
672
673impl Eq for ActorId {}
674
675impl Hash for ActorId {
676 fn hash<H: Hasher>(&self, state: &mut H) {
677 self.proc_id.hash(state);
678 self.uid.hash(state);
679 }
680}
681
682impl PartialOrd for ActorId {
683 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
684 Some(self.cmp(other))
685 }
686}
687
688impl Ord for ActorId {
689 fn cmp(&self, other: &Self) -> Ordering {
690 self.proc_id
691 .cmp(&other.proc_id)
692 .then_with(|| self.uid.cmp(&other.uid))
693 }
694}
695
696impl fmt::Display for ActorId {
697 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
698 fmt::Display::fmt(&self.uid, f)?;
699 write!(f, ".{}", self.proc_id)
700 }
701}
702
703impl fmt::Debug for ActorId {
704 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
705 match (self.label(), self.proc_id.label()) {
706 (Some(actor_label), Some(proc_label)) => {
707 write!(
708 f,
709 "<'{}.{}' {}.{}>",
710 actor_label, proc_label, self.uid, self.proc_id.uid
711 )
712 }
713 (Some(actor_label), None) => {
714 write!(f, "<'{}' {}.{}>", actor_label, self.uid, self.proc_id.uid)
715 }
716 (None, Some(proc_label)) => {
717 write!(f, "<'.{}' {}.{}>", proc_label, self.uid, self.proc_id.uid)
718 }
719 (None, None) => {
720 write!(f, "<{}.{}>", self.uid, self.proc_id.uid)
721 }
722 }
723 }
724}
725
726impl FromStr for ActorId {
727 type Err = IdParseError;
728
729 fn from_str(s: &str) -> Result<Self, Self::Err> {
730 crate::parse::id::parse_actor_id(s).map_err(|_| legacy_parse_actor_id(s))
731 }
732}
733
734#[derive(Clone, Serialize, Deserialize)]
738pub struct PortId {
739 actor_id: ActorId,
740 port: Port,
741}
742
743impl PortId {
744 pub fn new(actor_id: ActorId, port: Port) -> Self {
746 Self { actor_id, port }
747 }
748
749 pub fn actor_id(&self) -> &ActorId {
751 &self.actor_id
752 }
753
754 pub fn port(&self) -> Port {
756 self.port.clone()
757 }
758
759 pub fn proc_id(&self) -> &ProcId {
761 self.actor_id.proc_id()
762 }
763}
764
765impl PartialEq for PortId {
766 fn eq(&self, other: &Self) -> bool {
767 self.actor_id == other.actor_id && self.port == other.port
768 }
769}
770
771impl Eq for PortId {}
772
773impl Hash for PortId {
774 fn hash<H: Hasher>(&self, state: &mut H) {
775 self.actor_id.hash(state);
776 self.port.hash(state);
777 }
778}
779
780impl PartialOrd for PortId {
781 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
782 Some(self.cmp(other))
783 }
784}
785
786impl Ord for PortId {
787 fn cmp(&self, other: &Self) -> Ordering {
788 self.actor_id
789 .cmp(&other.actor_id)
790 .then_with(|| self.port.cmp(&other.port))
791 }
792}
793
794impl fmt::Display for PortId {
795 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
796 match &self.port {
797 Port::Control(port) => write!(f, "{}!{}", self.actor_id, port),
798 _ => write!(f, "{}:{}", self.actor_id, self.port),
799 }
800 }
801}
802
803impl fmt::Debug for PortId {
804 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
805 match (self.actor_id.label(), self.actor_id.proc_id().label()) {
806 (Some(actor_label), Some(proc_label)) => {
807 write!(f, "<'{}.{}' {}>", actor_label, proc_label, self)
808 }
809 (Some(actor_label), None) => {
810 write!(f, "<'{}' {}>", actor_label, self)
811 }
812 (None, Some(proc_label)) => {
813 write!(f, "<'.{}' {}>", proc_label, self)
814 }
815 (None, None) => {
816 write!(f, "<{}>", self)
817 }
818 }
819 }
820}
821
822impl FromStr for PortId {
823 type Err = IdParseError;
824
825 fn from_str(s: &str) -> Result<Self, Self::Err> {
826 crate::parse::id::parse_port_id(s).map_err(|_| legacy_port_parse_error(s))
827 }
828}
829
830#[derive(
832 Clone,
833 EnumAsInner,
834 PartialEq,
835 Eq,
836 Hash,
837 PartialOrd,
838 Ord,
839 Serialize,
840 Deserialize
841)]
842pub enum Id {
843 Proc(ProcId),
845 Actor(ActorId),
847 Port(PortId),
849}
850
851impl Id {
852 pub fn addr(self, location: Location) -> Addr {
854 match self {
855 Self::Proc(id) => Addr::Proc(ProcAddr::new(id, location)),
856 Self::Actor(id) => Addr::Actor(ActorAddr::new(id, location)),
857 Self::Port(id) => Addr::Port(PortAddr::new(id, location)),
858 }
859 }
860}
861
862impl fmt::Display for Id {
863 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
864 match self {
865 Self::Proc(id) => fmt::Display::fmt(id, f),
866 Self::Actor(id) => fmt::Display::fmt(id, f),
867 Self::Port(id) => fmt::Display::fmt(id, f),
868 }
869 }
870}
871
872impl fmt::Debug for Id {
873 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
874 match self {
875 Self::Proc(id) => fmt::Debug::fmt(id, f),
876 Self::Actor(id) => fmt::Debug::fmt(id, f),
877 Self::Port(id) => fmt::Debug::fmt(id, f),
878 }
879 }
880}
881
882impl FromStr for Id {
883 type Err = IdParseError;
884
885 fn from_str(s: &str) -> Result<Self, Self::Err> {
886 crate::parse::id::parse_id(s).map_err(|_| legacy_parse_id(s))
887 }
888}
889
890fn legacy_parse_id_component(s: &str) -> Result<(Uid, Option<Label>), UidParseError> {
891 if let Some(inner) = s
892 .strip_prefix('<')
893 .and_then(|inner| inner.strip_suffix('>'))
894 {
895 let uid = parse_base58_uid(inner)?;
896 return Ok((Uid::Instance(uid, None), None));
897 }
898
899 if let Some(open) = s.find('<')
900 && s.ends_with('>')
901 {
902 let label = Label::new(&s[..open])?;
903 let uid = parse_base58_uid(&s[open + 1..s.len() - 1])?;
904 return Ok((Uid::Instance(uid, Some(label.clone())), Some(label)));
905 }
906
907 let label = Label::new(s)?;
908 Ok((Uid::Singleton(label.clone()), Some(label)))
909}
910
911fn legacy_parse_id(s: &str) -> IdParseError {
912 if s.contains(':') {
913 legacy_port_parse_error(s)
914 } else if s.contains('.') {
915 legacy_parse_actor_id(s)
916 } else {
917 legacy_parse_id_component(s)
918 .err()
919 .map(IdParseError::InvalidProcId)
920 .unwrap_or(IdParseError::InvalidActorIdFormat)
921 }
922}
923
924fn legacy_parse_actor_id(s: &str) -> IdParseError {
925 let Some((actor_part, proc_part)) = s.split_once('.') else {
926 return IdParseError::InvalidActorIdFormat;
927 };
928
929 if let Err(err) = legacy_parse_id_component(actor_part) {
930 return IdParseError::InvalidActorUid(err);
931 }
932
933 if let Err(err) = legacy_parse_id_component(proc_part) {
934 return IdParseError::InvalidActorProcUid(err);
935 }
936
937 IdParseError::InvalidActorIdFormat
938}
939
940fn legacy_port_parse_error(s: &str) -> IdParseError {
941 let Some((actor_part, port_part)) = s.split_once(':') else {
942 return IdParseError::InvalidPortIdFormat;
943 };
944
945 if crate::parse::id::parse_actor_id(actor_part).is_ok() {
946 return IdParseError::InvalidPort(port_part.to_string());
947 }
948
949 IdParseError::InvalidPortIdFormat
950}
951
952#[cfg(test)]
953mod tests {
954 use super::*;
955
956 #[test]
957 fn test_label_valid() {
958 assert!(Label::new("a").is_ok());
959 assert!(Label::new("abc").is_ok());
960 assert!(Label::new("my-service").is_ok());
961 assert!(Label::new("a1").is_ok());
962 assert!(Label::new("abc123").is_ok());
963 assert!(Label::new("a-b-c").is_ok());
964 }
965
966 #[test]
967 fn test_label_invalid_empty() {
968 assert_eq!(Label::new(""), Err(LabelError::Empty));
969 }
970
971 #[test]
972 fn test_label_invalid_too_long() {
973 let long = "a".repeat(64);
974 assert_eq!(Label::new(&long), Err(LabelError::TooLong));
975 let exact = "a".repeat(63);
977 assert!(Label::new(&exact).is_ok());
978 }
979
980 #[test]
981 fn test_label_invalid_bad_start() {
982 assert_eq!(Label::new("1abc"), Err(LabelError::InvalidStart));
983 assert_eq!(Label::new("-abc"), Err(LabelError::InvalidStart));
984 assert_eq!(Label::new("Abc"), Err(LabelError::InvalidStart));
985 }
986
987 #[test]
988 fn test_label_invalid_bad_end() {
989 assert_eq!(Label::new("abc-"), Err(LabelError::InvalidEnd));
990 }
991
992 #[test]
993 fn test_label_invalid_char() {
994 assert_eq!(Label::new("ab.c"), Err(LabelError::InvalidChar('.')));
995 assert_eq!(Label::new("aBc"), Err(LabelError::InvalidChar('B')));
996 }
997
998 #[test]
999 fn test_label_allows_underscores() {
1000 assert!(Label::new("ab_c").is_ok());
1001 assert!(Label::new("proc_agent").is_ok());
1002 assert!(Label::new("host_agent").is_ok());
1003 }
1004
1005 #[test]
1006 fn test_label_strip() {
1007 assert_eq!(Label::strip("Hello-World").as_str(), "hello-world");
1008 assert_eq!(Label::strip("123abc").as_str(), "abc");
1009 assert_eq!(Label::strip("---abc---").as_str(), "abc");
1010 assert_eq!(Label::strip("").as_str(), "nil");
1011 assert_eq!(Label::strip("123").as_str(), "nil");
1012 assert_eq!(Label::strip("My_Service!").as_str(), "my_service");
1013 }
1014
1015 #[test]
1016 fn test_label_strip_truncation() {
1017 let long = format!("a{}", "b".repeat(100));
1018 let stripped = Label::strip(&long);
1019 assert!(stripped.as_str().len() <= MAX_LABEL_LEN);
1020 }
1021
1022 #[test]
1023 fn test_label_display_fromstr_roundtrip() {
1024 let label = Label::new("my-service").unwrap();
1025 let s = label.to_string();
1026 assert_eq!(s, "my-service");
1027 let parsed: Label = s.parse().unwrap();
1028 assert_eq!(label, parsed);
1029 }
1030
1031 #[test]
1032 fn test_label_serde_roundtrip() {
1033 let label = Label::new("my-service").unwrap();
1034 let json = serde_json::to_string(&label).unwrap();
1035 assert_eq!(json, "\"my-service\"");
1036 let parsed: Label = serde_json::from_str(&json).unwrap();
1037 assert_eq!(label, parsed);
1038 }
1039
1040 #[test]
1041 fn test_singleton_display_parse() {
1042 let uid = Uid::singleton(Label::new("my-actor").unwrap());
1043 let s = uid.to_string();
1044 assert_eq!(s, "my-actor");
1045 let parsed: Uid = s.parse().unwrap();
1046 assert_eq!(uid, parsed);
1047 }
1048
1049 #[test]
1050 fn test_instance_display_parse() {
1051 let uid = Uid::Instance(0xd5d54d7201103869, None);
1052 let s = uid.to_string();
1053 assert_eq!(s, format!("<{}>", encode_base58_uid(0xd5d54d7201103869)));
1054 assert_eq!(
1055 uid.instance_uid_base58(),
1056 Some(encode_base58_uid(0xd5d54d7201103869))
1057 );
1058 assert_eq!(
1059 Uid::parse_instance_uid_base58(&encode_base58_uid(0xd5d54d7201103869)),
1060 Ok(0xd5d54d7201103869)
1061 );
1062 let parsed: Uid = s.parse().unwrap();
1063 assert_eq!(uid, parsed);
1064 }
1065
1066 #[test]
1067 fn test_singleton_has_no_instance_base58() {
1068 let uid = Uid::singleton(Label::new("my-actor").unwrap());
1069 assert_eq!(uid.instance_uid_base58(), None);
1070 }
1071
1072 #[test]
1073 fn test_labeled_instance_display_parse() {
1074 let label = Label::new("my-actor").unwrap();
1075 let uid = Uid::Instance(0xd5d54d7201103869, Some(label.clone()));
1076 let s = uid.to_string();
1077 assert_eq!(
1078 s,
1079 format!("my-actor<{}>", encode_base58_uid(0xd5d54d7201103869))
1080 );
1081 let parsed: Uid = s.parse().unwrap();
1082 assert_eq!(parsed, uid);
1083 assert_eq!(parsed.label(), Some(&label));
1084 }
1085
1086 #[test]
1087 fn test_labeled_instance_identity_ignores_label() {
1088 let a = Uid::Instance(0x42, Some(Label::new("alpha").unwrap()));
1089 let b = Uid::Instance(0x42, Some(Label::new("beta").unwrap()));
1090 assert_eq!(a, b);
1091 assert_eq!(a.cmp(&b), Ordering::Equal);
1092
1093 use std::collections::hash_map::DefaultHasher;
1094
1095 let hash = |uid: &Uid| {
1096 let mut h = DefaultHasher::new();
1097 uid.hash(&mut h);
1098 h.finish()
1099 };
1100 assert_eq!(hash(&a), hash(&b));
1101 }
1102
1103 #[test]
1104 fn test_ordering_singleton_lt_instance() {
1105 let singleton = Uid::singleton(Label::new("zzz").unwrap());
1106 let instance = Uid::Instance(0, None);
1107 assert!(singleton < instance);
1108 }
1109
1110 #[test]
1111 fn test_ordering_singletons() {
1112 let a = Uid::singleton(Label::new("aaa").unwrap());
1113 let b = Uid::singleton(Label::new("bbb").unwrap());
1114 assert!(a < b);
1115 }
1116
1117 #[test]
1118 fn test_ordering_instances() {
1119 let a = Uid::Instance(1, None);
1120 let b = Uid::Instance(2, None);
1121 assert!(a < b);
1122 }
1123
1124 #[test]
1125 fn test_uid_serde_roundtrip() {
1126 let uids = vec![
1127 Uid::singleton(Label::new("my-actor").unwrap()),
1128 Uid::Instance(0xabcdef0123456789, None),
1129 Uid::Instance(1, None),
1130 Uid::Instance(0xd5d54d7201103869, Some(Label::new("my-actor").unwrap())),
1131 ];
1132 for uid in uids {
1133 let json = serde_json::to_string(&uid).unwrap();
1134 assert_eq!(json, format!("\"{}\"", uid));
1135 let parsed: Uid = serde_json::from_str(&json).unwrap();
1136 assert_eq!(uid, parsed);
1137
1138 let encoded = bincode::serde::encode_to_vec(&uid, bincode::config::legacy()).unwrap();
1139 let (parsed, len): (Uid, usize) =
1140 bincode::serde::decode_from_slice(&encoded, bincode::config::legacy()).unwrap();
1141 assert_eq!(len, encoded.len());
1142 assert_eq!(uid, parsed);
1143 }
1144 }
1145
1146 #[test]
1147 fn test_uid_parse_errors() {
1148 assert!("".parse::<Uid>().is_err());
1150 assert!("123bad".parse::<Uid>().is_err());
1152 assert!("<0>".parse::<Uid>().is_err());
1154 assert_eq!(
1156 "<abc".parse::<Uid>().unwrap_err().to_string(),
1157 "invalid uid syntax: expected \">\", found end of input"
1158 );
1159 }
1160
1161 #[test]
1162 fn test_unique_uid_generation() {
1163 let a = Uid::anonymous();
1164 let b = Uid::anonymous();
1165 assert_ne!(a, b);
1166 }
1167
1168 #[test]
1169 fn test_short_hex_parse() {
1170 let parsed: Uid = "<2>".parse().unwrap();
1171 assert_eq!(parsed, Uid::Instance(1, None));
1172 }
1173
1174 #[test]
1175 fn test_proc_id_construction_and_accessors() {
1176 let uid = Uid::Instance(0xabc, None);
1177 let label = Label::new("my-proc").unwrap();
1178 let pid = ProcId::new(uid.clone(), Some(label.clone()));
1179 assert_eq!(pid.uid(), &uid);
1180 assert_eq!(pid.label(), Some(&label));
1181 }
1182
1183 #[test]
1184 fn test_proc_id_eq_ignores_label() {
1185 let uid = Uid::Instance(0x42, None);
1186 let a = ProcId::new(uid.clone(), Some(Label::new("alpha").unwrap()));
1187 let b = ProcId::new(uid, Some(Label::new("beta").unwrap()));
1188 assert_eq!(a, b);
1189 }
1190
1191 #[test]
1192 fn test_proc_id_hash_ignores_label() {
1193 use std::collections::hash_map::DefaultHasher;
1194
1195 let uid = Uid::Instance(0x42, None);
1196 let a = ProcId::new(uid.clone(), Some(Label::new("alpha").unwrap()));
1197 let b = ProcId::new(uid, Some(Label::new("beta").unwrap()));
1198
1199 let hash = |pid: &ProcId| {
1200 let mut h = DefaultHasher::new();
1201 pid.hash(&mut h);
1202 h.finish()
1203 };
1204 assert_eq!(hash(&a), hash(&b));
1205 }
1206
1207 #[test]
1208 fn test_proc_id_ord_ignores_label() {
1209 let a = ProcId::new(Uid::Instance(1, None), Some(Label::new("zzz").unwrap()));
1210 let b = ProcId::new(Uid::Instance(2, None), Some(Label::new("aaa").unwrap()));
1211 assert!(a < b);
1212 }
1213
1214 #[test]
1215 fn test_proc_id_display() {
1216 let pid = ProcId::new(
1217 Uid::Instance(0xd5d54d7201103869, None),
1218 Some(Label::new("my-proc").unwrap()),
1219 );
1220 assert_eq!(
1221 pid.to_string(),
1222 format!("my-proc<{}>", encode_base58_uid(0xd5d54d7201103869))
1223 );
1224
1225 let pid_singleton = ProcId::new(
1226 Uid::singleton(Label::new("my-proc").unwrap()),
1227 Some(Label::new("my-proc").unwrap()),
1228 );
1229 assert_eq!(pid_singleton.to_string(), "my-proc");
1230 }
1231
1232 #[test]
1233 fn test_proc_id_debug() {
1234 let pid = ProcId::new(
1235 Uid::Instance(0xd5d54d7201103869, None),
1236 Some(Label::new("my-proc").unwrap()),
1237 );
1238 assert_eq!(
1239 format!("{:?}", pid),
1240 format!(
1241 "<'my-proc' my-proc<{}>>",
1242 encode_base58_uid(0xd5d54d7201103869)
1243 )
1244 );
1245
1246 let pid_no_label = ProcId::new(Uid::Instance(0xd5d54d7201103869, None), None);
1247 assert_eq!(
1248 format!("{:?}", pid_no_label),
1249 format!("<<{}>>", encode_base58_uid(0xd5d54d7201103869))
1250 );
1251 }
1252
1253 #[test]
1254 fn test_proc_id_fromstr_roundtrip() {
1255 let pid = ProcId::new(
1256 Uid::Instance(0xd5d54d7201103869, None),
1257 Some(Label::new("my-proc").unwrap()),
1258 );
1259 let s = pid.to_string();
1260 let parsed: ProcId = s.parse().unwrap();
1261 assert_eq!(pid, parsed);
1262 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-proc"));
1263 }
1264
1265 #[test]
1266 fn test_proc_id_fromstr_singleton() {
1267 let parsed: ProcId = "my-proc".parse().unwrap();
1268 assert_eq!(
1269 *parsed.uid(),
1270 Uid::singleton(Label::new("my-proc").unwrap())
1271 );
1272 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-proc"));
1273 }
1274
1275 #[test]
1276 fn test_proc_id_fromstr_unlabeled_instance() {
1277 let expected_uid = Uid::Instance(0xabc123, None);
1278 let parsed: ProcId = expected_uid.to_string().parse().unwrap();
1279 assert_eq!(parsed.uid(), &expected_uid);
1280 assert_eq!(parsed.label(), None);
1281 }
1282
1283 #[test]
1284 fn test_proc_id_fromstr_labeled_instance_with_underscore() {
1285 let expected_uid = Uid::Instance(0xabc123, None);
1286 let parsed: ProcId = format!("proc_agent{}", expected_uid).parse().unwrap();
1287 assert_eq!(parsed.uid(), &expected_uid);
1288 assert_eq!(
1289 parsed.label().map(|label| label.as_str()),
1290 Some("proc_agent")
1291 );
1292 }
1293
1294 #[test]
1295 fn test_proc_id_fromstr_errors_are_stable() {
1296 assert_eq!(
1297 "".parse::<ProcId>().unwrap_err().to_string(),
1298 "invalid proc id: invalid uid syntax: expected \"label\" or \"<\", found end of input"
1299 );
1300 assert_eq!(
1301 "controller<2MuAHeDjLCEd"
1302 .parse::<ProcId>()
1303 .unwrap_err()
1304 .to_string(),
1305 "invalid proc id: invalid uid syntax: expected \">\", found end of input"
1306 );
1307 assert_eq!(
1308 "controller@tcp".parse::<ProcId>().unwrap_err().to_string(),
1309 "invalid proc id: invalid uid syntax: expected end of input, found \"@\""
1310 );
1311 }
1312
1313 #[test]
1314 fn test_proc_id_serde_roundtrip() {
1315 let pid = ProcId::new(
1316 Uid::Instance(0xabcdef, None),
1317 Some(Label::new("my-proc").unwrap()),
1318 );
1319 let json = serde_json::to_string(&pid).unwrap();
1320 let parsed: ProcId = serde_json::from_str(&json).unwrap();
1321 assert_eq!(pid, parsed);
1322 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-proc"));
1323
1324 let pid_none = ProcId::new(Uid::Instance(0xabcdef, None), None);
1325 let json_none = serde_json::to_string(&pid_none).unwrap();
1326 let parsed_none: ProcId = serde_json::from_str(&json_none).unwrap();
1327 assert_eq!(parsed_none.label(), None);
1328 }
1329
1330 #[test]
1331 fn test_proc_id_singleton() {
1332 let label = Label::new("my-proc").unwrap();
1333 let pid = ProcId::singleton(label.clone());
1334 assert_eq!(*pid.uid(), Uid::Singleton(label.clone()));
1335 assert_eq!(pid.label(), Some(&label));
1336 }
1337
1338 #[test]
1339 fn test_proc_id_instance() {
1340 let label = Label::new("my-proc").unwrap();
1341 let pid = ProcId::instance(label.clone());
1342 assert!(pid.uid().is_instance());
1343 assert_eq!(pid.label(), Some(&label));
1344 let pid2 = ProcId::instance(label);
1345 assert_ne!(pid, pid2);
1346 }
1347
1348 #[test]
1349 fn test_proc_id_pseudo_uid_instance_returns_real_uid() {
1350 let uid = Uid::Instance(0xd5d54d7201103869, None);
1351 let pid = ProcId::new(uid.clone(), Some(Label::new("my-proc").unwrap()));
1352 assert_eq!(pid.pseudo_uid(), uid);
1353 }
1354
1355 #[test]
1356 fn test_proc_id_pseudo_uid_singleton_is_instance_form() {
1357 let pid = ProcId::singleton(Label::new("my-proc").unwrap());
1358 assert!(matches!(pid.pseudo_uid(), Uid::Instance(_, _)));
1359 }
1360
1361 #[test]
1362 fn test_proc_id_pseudo_uid_singleton_is_deterministic() {
1363 let a = ProcId::singleton(Label::new("my-proc").unwrap());
1364 let b = ProcId::singleton(Label::new("my-proc").unwrap());
1365 assert_eq!(a.pseudo_uid(), b.pseudo_uid());
1366 }
1367
1368 #[test]
1369 fn test_proc_id_pseudo_uid_singleton_distinct_labels_differ() {
1370 let a = ProcId::singleton(Label::new("alpha").unwrap());
1371 let b = ProcId::singleton(Label::new("beta").unwrap());
1372 assert_ne!(a.pseudo_uid(), b.pseudo_uid());
1373 }
1374
1375 #[test]
1376 fn test_proc_id_pseudo_uid_displays_as_short_base58() {
1377 let pid = ProcId::singleton(Label::new("my-proc").unwrap());
1378 let s = pid.pseudo_uid().to_string();
1379 assert!(s.starts_with('<') && s.ends_with('>'), "got: {s}");
1380 assert!(s.len() <= 13, "expected short base58 form, got: {s}");
1382 }
1383
1384 #[test]
1385 fn test_actor_id_singleton() {
1386 let label = Label::new("my-actor").unwrap();
1387 let proc_id = ProcId::singleton(Label::new("my-proc").unwrap());
1388 let aid = ActorId::singleton(label.clone(), proc_id.clone());
1389 assert_eq!(*aid.uid(), Uid::Singleton(label.clone()));
1390 assert_eq!(aid.proc_id(), &proc_id);
1391 assert_eq!(aid.label(), Some(&label));
1392 }
1393
1394 #[test]
1395 fn test_actor_id_anonymous() {
1396 let proc_id = ProcId::singleton(Label::new("my-proc").unwrap());
1397 let aid = ActorId::anonymous(proc_id.clone());
1398 assert!(aid.uid().is_instance());
1399 assert_eq!(aid.proc_id(), &proc_id);
1400 assert_eq!(aid.label(), None);
1401 let aid2 = ActorId::anonymous(proc_id);
1402 assert_ne!(aid, aid2);
1403 }
1404
1405 #[test]
1406 fn test_actor_id_instance() {
1407 let label = Label::new("my-actor").unwrap();
1408 let proc_id = ProcId::singleton(Label::new("my-proc").unwrap());
1409 let aid = ActorId::instance(label.clone(), proc_id.clone());
1410 assert!(aid.uid().is_instance());
1411 assert_eq!(aid.proc_id(), &proc_id);
1412 assert_eq!(aid.label(), Some(&label));
1413 let aid2 = ActorId::instance(label, proc_id);
1414 assert_ne!(aid, aid2);
1415 }
1416
1417 #[test]
1418 fn test_actor_id_construction_and_accessors() {
1419 let actor_uid = Uid::Instance(0xabc, None);
1420 let proc_id = ProcId::new(
1421 Uid::Instance(0xdef, None),
1422 Some(Label::new("my-proc").unwrap()),
1423 );
1424 let label = Label::new("my-actor").unwrap();
1425 let aid = ActorId::new(actor_uid.clone(), proc_id.clone(), Some(label.clone()));
1426 assert_eq!(aid.uid(), &actor_uid);
1427 assert_eq!(aid.proc_id(), &proc_id);
1428 assert_eq!(aid.label(), Some(&label));
1429 }
1430
1431 #[test]
1432 fn test_actor_id_eq_ignores_label() {
1433 let actor_uid = Uid::Instance(0x42, None);
1434 let proc_id = ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap()));
1435 let a = ActorId::new(
1436 actor_uid.clone(),
1437 proc_id.clone(),
1438 Some(Label::new("alpha").unwrap()),
1439 );
1440 let b = ActorId::new(actor_uid, proc_id, Some(Label::new("beta").unwrap()));
1441 assert_eq!(a, b);
1442 }
1443
1444 #[test]
1445 fn test_actor_id_neq_different_proc() {
1446 let actor_uid = Uid::Instance(0x42, None);
1447 let proc_a = ProcId::new(Uid::Instance(1, None), Some(Label::new("proc").unwrap()));
1448 let proc_b = ProcId::new(Uid::Instance(2, None), Some(Label::new("proc").unwrap()));
1449 let a = ActorId::new(
1450 actor_uid.clone(),
1451 proc_a,
1452 Some(Label::new("actor").unwrap()),
1453 );
1454 let b = ActorId::new(actor_uid, proc_b, Some(Label::new("actor").unwrap()));
1455 assert_ne!(a, b);
1456 }
1457
1458 #[test]
1459 fn test_actor_id_hash_ignores_label() {
1460 use std::collections::hash_map::DefaultHasher;
1461
1462 let actor_uid = Uid::Instance(0x42, None);
1463 let proc_id = ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap()));
1464 let a = ActorId::new(
1465 actor_uid.clone(),
1466 proc_id.clone(),
1467 Some(Label::new("alpha").unwrap()),
1468 );
1469 let b = ActorId::new(actor_uid, proc_id, Some(Label::new("beta").unwrap()));
1470
1471 let hash = |aid: &ActorId| {
1472 let mut h = DefaultHasher::new();
1473 aid.hash(&mut h);
1474 h.finish()
1475 };
1476 assert_eq!(hash(&a), hash(&b));
1477 }
1478
1479 #[test]
1480 fn test_actor_id_ord_proc_first() {
1481 let a = ActorId::new(
1482 Uid::Instance(0xff, None),
1483 ProcId::new(Uid::Instance(1, None), Some(Label::new("p").unwrap())),
1484 Some(Label::new("a").unwrap()),
1485 );
1486 let b = ActorId::new(
1487 Uid::Instance(0x01, None),
1488 ProcId::new(Uid::Instance(2, None), Some(Label::new("p").unwrap())),
1489 Some(Label::new("a").unwrap()),
1490 );
1491 assert!(a < b, "proc_id should be compared first");
1492 }
1493
1494 #[test]
1495 fn test_actor_id_ord_then_uid() {
1496 let proc_id = ProcId::new(Uid::Instance(1, None), Some(Label::new("p").unwrap()));
1497 let a = ActorId::new(
1498 Uid::Instance(1, None),
1499 proc_id.clone(),
1500 Some(Label::new("a").unwrap()),
1501 );
1502 let b = ActorId::new(
1503 Uid::Instance(2, None),
1504 proc_id,
1505 Some(Label::new("a").unwrap()),
1506 );
1507 assert!(a < b);
1508 }
1509
1510 #[test]
1511 fn test_actor_id_display() {
1512 let aid = ActorId::new(
1513 Uid::Instance(0xabc123, None),
1514 ProcId::new(
1515 Uid::Instance(0xdef456, None),
1516 Some(Label::new("my-proc").unwrap()),
1517 ),
1518 Some(Label::new("my-actor").unwrap()),
1519 );
1520 assert_eq!(
1521 aid.to_string(),
1522 format!(
1523 "my-actor<{}>.my-proc<{}>",
1524 encode_base58_uid(0xabc123),
1525 encode_base58_uid(0xdef456)
1526 )
1527 );
1528 }
1529
1530 #[test]
1531 fn test_actor_id_debug() {
1532 let aid = ActorId::new(
1533 Uid::Instance(0xabc123, None),
1534 ProcId::new(
1535 Uid::Instance(0xdef456, None),
1536 Some(Label::new("my-proc").unwrap()),
1537 ),
1538 Some(Label::new("my-actor").unwrap()),
1539 );
1540 assert_eq!(
1541 format!("{:?}", aid),
1542 format!(
1543 "<'my-actor.my-proc' my-actor<{}>.my-proc<{}>>",
1544 encode_base58_uid(0xabc123),
1545 encode_base58_uid(0xdef456)
1546 )
1547 );
1548
1549 let aid_no_labels = ActorId::new(
1550 Uid::Instance(0xabc123, None),
1551 ProcId::new(Uid::Instance(0xdef456, None), None),
1552 None,
1553 );
1554 assert_eq!(
1555 format!("{:?}", aid_no_labels),
1556 format!(
1557 "<<{}>.<{}>>",
1558 encode_base58_uid(0xabc123),
1559 encode_base58_uid(0xdef456)
1560 )
1561 );
1562 }
1563
1564 #[test]
1565 fn test_actor_id_fromstr_roundtrip() {
1566 let aid = ActorId::new(
1567 Uid::Instance(0xabc123, None),
1568 ProcId::new(
1569 Uid::Instance(0xdef456, None),
1570 Some(Label::new("my-proc").unwrap()),
1571 ),
1572 Some(Label::new("my-actor").unwrap()),
1573 );
1574 let s = aid.to_string();
1575 let parsed: ActorId = s.parse().unwrap();
1576 assert_eq!(aid, parsed);
1577 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-actor"));
1578 assert_eq!(
1579 parsed.proc_id().label().map(|l| l.as_str()),
1580 Some("my-proc")
1581 );
1582 }
1583
1584 #[test]
1585 fn test_actor_id_fromstr_with_singletons() {
1586 let parsed: ActorId = "my-actor.my-proc".parse().unwrap();
1587 assert_eq!(
1588 *parsed.uid(),
1589 Uid::singleton(Label::new("my-actor").unwrap())
1590 );
1591 assert_eq!(
1592 *parsed.proc_id().uid(),
1593 Uid::singleton(Label::new("my-proc").unwrap())
1594 );
1595 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-actor"));
1596 assert_eq!(
1597 parsed.proc_id().label().map(|l| l.as_str()),
1598 Some("my-proc")
1599 );
1600 }
1601
1602 #[test]
1603 fn test_actor_id_fromstr_mixed_examples() {
1604 let proc_uid = Uid::Instance(0xabc123, None);
1605 let parsed: ActorId = format!("controller.some-proc-123{}", proc_uid)
1606 .parse()
1607 .unwrap();
1608 assert_eq!(
1609 parsed.uid(),
1610 &Uid::singleton(Label::new("controller").unwrap())
1611 );
1612 assert_eq!(parsed.proc_id().uid(), &proc_uid);
1613 assert_eq!(
1614 parsed.label().map(|label| label.as_str()),
1615 Some("controller")
1616 );
1617 assert_eq!(
1618 parsed.proc_id().label().map(|label| label.as_str()),
1619 Some("some-proc-123")
1620 );
1621
1622 let expected_actor_uid = Uid::Instance(0xabc123, None);
1623 let expected_proc_uid = Uid::Instance(0xdef456, None);
1624 let parsed: ActorId = format!("{}.{}", expected_actor_uid, expected_proc_uid)
1625 .parse()
1626 .unwrap();
1627 assert_eq!(parsed.uid(), &expected_actor_uid);
1628 assert_eq!(parsed.proc_id().uid(), &expected_proc_uid);
1629 assert_eq!(parsed.label(), None);
1630 assert_eq!(parsed.proc_id().label(), None);
1631
1632 let expected_actor_uid = Uid::Instance(0xabc123, None);
1633 let parsed: ActorId = format!("controller{}.local", expected_actor_uid)
1634 .parse()
1635 .unwrap();
1636 assert_eq!(parsed.uid(), &expected_actor_uid);
1637 assert_eq!(
1638 parsed.proc_id().uid(),
1639 &Uid::singleton(Label::new("local").unwrap())
1640 );
1641 assert_eq!(
1642 parsed.label().map(|label| label.as_str()),
1643 Some("controller")
1644 );
1645 assert_eq!(
1646 parsed.proc_id().label().map(|label| label.as_str()),
1647 Some("local")
1648 );
1649 }
1650
1651 #[test]
1652 fn test_actor_id_fromstr_errors() {
1653 assert!("no-dot-here".parse::<ActorId>().is_err());
1654 assert!(".".parse::<ActorId>().is_err());
1655 assert!("abc.".parse::<ActorId>().is_err());
1656 assert!(".abc".parse::<ActorId>().is_err());
1657 }
1658
1659 #[test]
1660 fn test_actor_id_fromstr_errors_are_stable() {
1661 assert_eq!(
1662 "local".parse::<ActorId>().unwrap_err().to_string(),
1663 "invalid actor id: expected format `<actor>.<proc>`"
1664 );
1665 assert_eq!(
1666 ".local".parse::<ActorId>().unwrap_err().to_string(),
1667 "invalid actor uid: invalid label: label must not be empty"
1668 );
1669 assert_eq!(
1670 "local.".parse::<ActorId>().unwrap_err().to_string(),
1671 "invalid proc uid in actor id: invalid label: label must not be empty"
1672 );
1673 assert_eq!(
1674 "local.<bad!>".parse::<ActorId>().unwrap_err().to_string(),
1675 "invalid proc uid in actor id: invalid base58 uid: bad!"
1676 );
1677 }
1678
1679 #[test]
1680 fn test_actor_id_serde_roundtrip() {
1681 let aid = ActorId::new(
1682 Uid::Instance(0xabcdef, None),
1683 ProcId::new(
1684 Uid::Instance(0x123456, None),
1685 Some(Label::new("my-proc").unwrap()),
1686 ),
1687 Some(Label::new("my-actor").unwrap()),
1688 );
1689 let json = serde_json::to_string(&aid).unwrap();
1690 let parsed: ActorId = serde_json::from_str(&json).unwrap();
1691 assert_eq!(aid, parsed);
1692 assert_eq!(parsed.label().map(|l| l.as_str()), Some("my-actor"));
1693 assert_eq!(
1694 parsed.proc_id().label().map(|l| l.as_str()),
1695 Some("my-proc")
1696 );
1697 }
1698
1699 #[test]
1700 fn test_port_id_construction_and_accessors() {
1701 let actor_uid = Uid::Instance(0xabc, None);
1702 let proc_id = ProcId::new(
1703 Uid::Instance(0xdef, None),
1704 Some(Label::new("my-proc").unwrap()),
1705 );
1706 let actor_id = ActorId::new(
1707 actor_uid,
1708 proc_id.clone(),
1709 Some(Label::new("my-actor").unwrap()),
1710 );
1711 let port = Port::from(42);
1712 let pid = PortId::new(actor_id.clone(), port.clone());
1713 assert_eq!(pid.actor_id(), &actor_id);
1714 assert_eq!(pid.port(), port);
1715 assert_eq!(pid.proc_id(), &proc_id);
1716 }
1717
1718 #[test]
1719 fn test_port_id_eq() {
1720 let actor_id = ActorId::new(
1721 Uid::Instance(0x42, None),
1722 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1723 Some(Label::new("actor").unwrap()),
1724 );
1725 let a = PortId::new(actor_id.clone(), Port::from(10));
1726 let b = PortId::new(actor_id, Port::from(10));
1727 assert_eq!(a, b);
1728 }
1729
1730 #[test]
1731 fn test_port_id_neq_different_port() {
1732 let actor_id = ActorId::new(
1733 Uid::Instance(0x42, None),
1734 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1735 Some(Label::new("actor").unwrap()),
1736 );
1737 let a = PortId::new(actor_id.clone(), Port::from(10));
1738 let b = PortId::new(actor_id, Port::from(20));
1739 assert_ne!(a, b);
1740 }
1741
1742 #[test]
1743 fn test_port_id_hash() {
1744 use std::collections::hash_map::DefaultHasher;
1745
1746 let actor_id = ActorId::new(
1747 Uid::Instance(0x42, None),
1748 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1749 Some(Label::new("actor").unwrap()),
1750 );
1751 let a = PortId::new(actor_id.clone(), Port::from(10));
1752 let b = PortId::new(actor_id, Port::from(10));
1753 let hash = |pid: &PortId| {
1754 let mut h = DefaultHasher::new();
1755 pid.hash(&mut h);
1756 h.finish()
1757 };
1758 assert_eq!(hash(&a), hash(&b));
1759 }
1760
1761 #[test]
1762 fn test_port_id_ord() {
1763 let actor_id = ActorId::new(
1764 Uid::Instance(0x42, None),
1765 ProcId::new(Uid::Instance(0x99, None), Some(Label::new("proc").unwrap())),
1766 Some(Label::new("actor").unwrap()),
1767 );
1768 let a = PortId::new(actor_id.clone(), Port::from(1));
1769 let b = PortId::new(actor_id, Port::from(2));
1770 assert!(a < b);
1771 }
1772
1773 #[test]
1774 fn test_port_id_ord_actor_first() {
1775 let a = PortId::new(
1776 ActorId::new(
1777 Uid::Instance(0x01, None),
1778 ProcId::new(Uid::Instance(1, None), Some(Label::new("p").unwrap())),
1779 Some(Label::new("a").unwrap()),
1780 ),
1781 Port::from(99),
1782 );
1783 let b = PortId::new(
1784 ActorId::new(
1785 Uid::Instance(0x02, None),
1786 ProcId::new(Uid::Instance(1, None), Some(Label::new("p").unwrap())),
1787 Some(Label::new("a").unwrap()),
1788 ),
1789 Port::from(1),
1790 );
1791 assert!(a < b, "actor_id should be compared first");
1792 }
1793
1794 #[test]
1795 fn test_port_id_display() {
1796 let aid = ActorId::new(
1797 Uid::Instance(0xabc123, None),
1798 ProcId::new(
1799 Uid::Instance(0xdef456, None),
1800 Some(Label::new("my-proc").unwrap()),
1801 ),
1802 Some(Label::new("my-actor").unwrap()),
1803 );
1804 let pid = PortId::new(aid, Port::from(42));
1805 assert_eq!(
1806 pid.to_string(),
1807 format!(
1808 "my-actor<{}>.my-proc<{}>:42",
1809 encode_base58_uid(0xabc123),
1810 encode_base58_uid(0xdef456)
1811 )
1812 );
1813 }
1814
1815 #[test]
1816 fn test_port_id_fromstr_examples() {
1817 let parsed: PortId = "local.local:0".parse().unwrap();
1818 assert_eq!(
1819 parsed.actor_id().uid(),
1820 &Uid::singleton(Label::new("local").unwrap())
1821 );
1822 assert_eq!(
1823 parsed.proc_id().uid(),
1824 &Uid::singleton(Label::new("local").unwrap())
1825 );
1826 assert_eq!(parsed.port(), Port::from(0));
1827
1828 let expected_actor_uid = Uid::Instance(0xabc123, None);
1829 let parsed: PortId = format!("controller{}.local:42", expected_actor_uid)
1830 .parse()
1831 .unwrap();
1832 assert_eq!(parsed.actor_id().uid(), &expected_actor_uid);
1833 assert_eq!(
1834 parsed.actor_id().label().map(|label| label.as_str()),
1835 Some("controller")
1836 );
1837 assert_eq!(
1838 parsed.proc_id().uid(),
1839 &Uid::singleton(Label::new("local").unwrap())
1840 );
1841 assert_eq!(parsed.port(), Port::from(42));
1842
1843 let expected_actor_uid = Uid::Instance(0xabc123, None);
1844 let expected_proc_uid = Uid::Instance(0xdef456, None);
1845 let parsed: PortId = format!("{}.{}:7", expected_actor_uid, expected_proc_uid)
1846 .parse()
1847 .unwrap();
1848 assert_eq!(parsed.actor_id().uid(), &expected_actor_uid);
1849 assert_eq!(parsed.proc_id().uid(), &expected_proc_uid);
1850 assert_eq!(parsed.port(), Port::from(7));
1851 }
1852
1853 #[test]
1854 fn test_port_id_debug_all_labels() {
1855 let aid = ActorId::new(
1856 Uid::Instance(0xabc123, None),
1857 ProcId::new(
1858 Uid::Instance(0xdef456, None),
1859 Some(Label::new("my-proc").unwrap()),
1860 ),
1861 Some(Label::new("my-actor").unwrap()),
1862 );
1863 let pid = PortId::new(aid, Port::from(42));
1864 assert_eq!(
1865 format!("{:?}", pid),
1866 format!(
1867 "<'my-actor.my-proc' my-actor<{}>.my-proc<{}>:42>",
1868 encode_base58_uid(0xabc123),
1869 encode_base58_uid(0xdef456)
1870 )
1871 );
1872 }
1873
1874 #[test]
1875 fn test_port_id_debug_no_labels() {
1876 let aid = ActorId::new(
1877 Uid::Instance(0xabc123, None),
1878 ProcId::new(Uid::Instance(0xdef456, None), None),
1879 None,
1880 );
1881 let pid = PortId::new(aid, Port::from(42));
1882 assert_eq!(
1883 format!("{:?}", pid),
1884 format!(
1885 "<<{}>.<{}>:42>",
1886 encode_base58_uid(0xabc123),
1887 encode_base58_uid(0xdef456)
1888 )
1889 );
1890 }
1891
1892 #[test]
1893 fn test_port_id_debug_actor_label_only() {
1894 let aid = ActorId::new(
1895 Uid::Instance(0xabc123, None),
1896 ProcId::new(Uid::Instance(0xdef456, None), None),
1897 Some(Label::new("my-actor").unwrap()),
1898 );
1899 let pid = PortId::new(aid, Port::from(42));
1900 assert_eq!(
1901 format!("{:?}", pid),
1902 format!(
1903 "<'my-actor' my-actor<{}>.<{}>:42>",
1904 encode_base58_uid(0xabc123),
1905 encode_base58_uid(0xdef456)
1906 )
1907 );
1908 }
1909
1910 #[test]
1911 fn test_port_id_debug_proc_label_only() {
1912 let aid = ActorId::new(
1913 Uid::Instance(0xabc123, None),
1914 ProcId::new(
1915 Uid::Instance(0xdef456, None),
1916 Some(Label::new("my-proc").unwrap()),
1917 ),
1918 None,
1919 );
1920 let pid = PortId::new(aid, Port::from(42));
1921 assert_eq!(
1922 format!("{:?}", pid),
1923 format!(
1924 "<'.my-proc' <{}>.my-proc<{}>:42>",
1925 encode_base58_uid(0xabc123),
1926 encode_base58_uid(0xdef456)
1927 )
1928 );
1929 }
1930
1931 #[test]
1932 fn test_port_id_fromstr_roundtrip() {
1933 let aid = ActorId::new(
1934 Uid::Instance(0xabc123, None),
1935 ProcId::new(
1936 Uid::Instance(0xdef456, None),
1937 Some(Label::new("my-proc").unwrap()),
1938 ),
1939 Some(Label::new("my-actor").unwrap()),
1940 );
1941 let pid = PortId::new(aid, Port::from(42));
1942 let s = pid.to_string();
1943 let parsed: PortId = s.parse().unwrap();
1944 assert_eq!(pid, parsed);
1945 assert_eq!(
1946 parsed.actor_id().label().map(|l| l.as_str()),
1947 Some("my-actor")
1948 );
1949 assert_eq!(
1950 parsed.actor_id().proc_id().label().map(|l| l.as_str()),
1951 Some("my-proc")
1952 );
1953 }
1954
1955 #[test]
1956 fn test_port_id_fromstr_errors_are_stable() {
1957 assert_eq!(
1958 "local.local".parse::<PortId>().unwrap_err().to_string(),
1959 "invalid port id: expected format `<actor>:<port>`"
1960 );
1961 assert_eq!(
1962 "local.local:".parse::<PortId>().unwrap_err().to_string(),
1963 "invalid port: "
1964 );
1965 assert_eq!(
1966 "local.local:not-a-port"
1967 .parse::<PortId>()
1968 .unwrap_err()
1969 .to_string(),
1970 "invalid port: not-a-port"
1971 );
1972 assert_eq!(
1973 "local.local:7@tcp://127.0.0.1:1"
1974 .parse::<PortId>()
1975 .unwrap_err()
1976 .to_string(),
1977 "invalid port: 7@tcp://127.0.0.1:1"
1978 );
1979 }
1980
1981 #[test]
1982 fn test_port_id_fromstr_errors() {
1983 assert!("<abc>.<def>".parse::<PortId>().is_err());
1985 assert!("actor.proc:notanumber".parse::<PortId>().is_err());
1987 }
1988
1989 #[test]
1990 fn test_port_id_serde_roundtrip() {
1991 let aid = ActorId::new(
1992 Uid::Instance(0xabcdef, None),
1993 ProcId::new(
1994 Uid::Instance(0x123456, None),
1995 Some(Label::new("my-proc").unwrap()),
1996 ),
1997 Some(Label::new("my-actor").unwrap()),
1998 );
1999 let pid = PortId::new(aid, Port::from(42));
2000 let json = serde_json::to_string(&pid).unwrap();
2001 let parsed: PortId = serde_json::from_str(&json).unwrap();
2002 assert_eq!(pid, parsed);
2003 }
2004}