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