1use crate::crypto::random;
2use crate::stdlib::format;
3use crate::stdlib::string::String;
4use crate::stdlib::string::ToString;
5use crate::stdlib::vec::Vec;
6use crate::traits::structural_eq::StructuralEq;
7use crate::utils::buffers::buffer_to_hex;
8use crate::values::core_value::CoreValue;
9use crate::values::value_container::{ValueContainer, ValueError};
10use binrw::io::Cursor;
11use binrw::{BinRead, BinWrite};
12use core::fmt::{Debug, Display, Formatter};
13use core::hash::Hash;
14use core::prelude::rust_2024::*;
15use core::result::Result;
16use core::str;
17use core::str::FromStr;
18use hex::decode;
19use serde::{Deserialize, Serialize};
20
21#[derive(
22 BinWrite, BinRead, Debug, Clone, Copy, Hash, PartialEq, Eq, Default,
23)]
24pub enum EndpointInstance {
25 #[default]
28 #[br(magic = 0u16)]
29 #[bw(magic = 0u16)]
30 Any,
31 #[br(magic = 65535u16)]
34 #[bw(magic = 65535u16)]
35 All,
36 Instance(u16),
39}
40
41impl EndpointInstance {
42 pub fn new(instance: u16) -> EndpointInstance {
43 match instance {
44 0 => EndpointInstance::Any,
45 65535 => EndpointInstance::All,
46 _ => EndpointInstance::Instance(instance),
47 }
48 }
49}
50
51#[derive(
53 Debug, Hash, PartialEq, Eq, Clone, Copy, Default, BinWrite, BinRead,
54)]
55#[brw(repr(u8))]
56pub enum EndpointType {
57 #[default]
58 Person = 0,
59 Institution = 1,
60 Anonymous = 2,
61}
62
63#[derive(BinWrite, BinRead, Debug, Clone, Hash, PartialEq, Eq)]
64#[brw(little)]
65pub struct Endpoint {
66 pub type_: EndpointType,
68 pub identifier: [u8; 18],
69 pub instance: EndpointInstance,
70}
71
72impl<T: Into<ValueContainer>> TryFrom<Option<T>> for Endpoint {
74 type Error = ValueError;
75 fn try_from(value: Option<T>) -> Result<Self, Self::Error> {
76 if let Some(value) = value {
77 let container: ValueContainer = value.into();
78 if let Some(endpoint) =
79 container.to_value().borrow().cast_to_endpoint()
80 {
81 return Ok(endpoint);
82 }
83 }
84 Err(ValueError::TypeConversionError)
85 }
86}
87
88impl StructuralEq for Endpoint {
103 fn structural_eq(&self, other: &Self) -> bool {
104 self == other
105 }
106}
107
108impl Default for Endpoint {
109 fn default() -> Self {
110 Endpoint::LOCAL
111 }
112}
113
114impl TryFrom<&str> for Endpoint {
115 type Error = InvalidEndpointError;
116 fn try_from(name: &str) -> Result<Self, Self::Error> {
117 Endpoint::from_string(name)
118 }
119}
120impl TryFrom<String> for Endpoint {
121 type Error = InvalidEndpointError;
122 fn try_from(name: String) -> Result<Self, Self::Error> {
123 Endpoint::from_string(&name)
124 }
125}
126
127impl TryFrom<CoreValue> for Endpoint {
128 type Error = ValueError;
129 fn try_from(value: CoreValue) -> Result<Self, Self::Error> {
130 if let Some(endpoint) = value.cast_to_endpoint() {
131 return Ok(endpoint);
132 }
133 Err(ValueError::TypeConversionError)
134 }
135}
136
137#[derive(PartialEq, Debug, Clone, Eq)]
138pub enum InvalidEndpointError {
139 InvalidCharacters,
140 MaxLengthExceeded,
141 MinLengthNotMet,
142 InvalidInstance,
143 ReservedName,
144}
145impl Display for InvalidEndpointError {
146 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
147 match self {
148 InvalidEndpointError::InvalidCharacters => {
149 core::write!(f, "Endpoint contains invalid characters")
150 }
151 InvalidEndpointError::MaxLengthExceeded => {
152 core::write!(
153 f,
154 "Endpoint name exceeds maximum length of 18 characters"
155 )
156 }
157 InvalidEndpointError::MinLengthNotMet => {
158 core::write!(
159 f,
160 "Endpoint name must be at least 3 characters long"
161 )
162 }
163 InvalidEndpointError::InvalidInstance => {
164 core::write!(f, "Endpoint instance must be between 1 and 65534")
165 }
166 InvalidEndpointError::ReservedName => {
167 core::write!(f, "Endpoint name is reserved")
168 }
169 }
170 }
171}
172
173#[derive(PartialEq, Debug)]
174pub struct EndpointParsingError;
175
176impl Endpoint {
177 const PREFIX_PERSON: &'static str = "@";
178 const PREFIX_INSTITUTION: &'static str = "@+";
179 const PREFIX_ANONYMOUS: &'static str = "@@";
180
181 const ALIAS_LOCAL: &'static str = "local";
182 const ALIAS_ANY: &'static str = "any";
183
184 pub const ANY: Endpoint = Endpoint {
187 type_: EndpointType::Anonymous,
188 identifier: [255; 18],
189 instance: EndpointInstance::Any,
190 };
191
192 pub const ANY_ALL_INSTANCES: Endpoint = Endpoint {
195 type_: EndpointType::Anonymous,
196 identifier: [255; 18],
197 instance: EndpointInstance::All,
198 };
199
200 pub const LOCAL: Endpoint = Endpoint {
203 type_: EndpointType::Anonymous,
204 identifier: [0; 18],
205 instance: EndpointInstance::Any,
206 };
207
208 pub const LOCAL_ALL_INSTANCES: Endpoint = Endpoint {
211 type_: EndpointType::Anonymous,
212 identifier: [0; 18],
213 instance: EndpointInstance::All,
214 };
215
216 pub fn random() -> Endpoint {
218 Self::anonymous(Self::random_anonymous_id(), EndpointInstance::Any)
219 .unwrap()
220 }
221
222 pub fn anonymous(
224 identifier: [u8; 18],
225 instance: EndpointInstance,
226 ) -> Result<Endpoint, InvalidEndpointError> {
227 if identifier == [255; 18] {
229 return if instance == EndpointInstance::Any {
230 Ok(Endpoint::ANY)
231 } else if instance == EndpointInstance::All {
232 Ok(Endpoint::ANY_ALL_INSTANCES)
233 } else {
234 Ok(Endpoint {
235 type_: EndpointType::Anonymous,
236 identifier,
237 instance,
238 })
239 };
240 }
241
242 if identifier == [0; 18] {
244 return if instance == EndpointInstance::Any {
245 Ok(Endpoint::LOCAL)
246 } else if instance == EndpointInstance::All {
247 Ok(Endpoint::LOCAL_ALL_INSTANCES)
248 } else {
249 Ok(Endpoint {
250 type_: EndpointType::Anonymous,
251 identifier,
252 instance,
253 })
254 };
255 }
256
257 Ok(Endpoint {
258 type_: EndpointType::Anonymous,
259 identifier,
260 instance,
261 })
262 }
263
264 pub fn new(name: &str) -> Endpoint {
267 Endpoint::from_string(name).unwrap_or_else(|_| {
268 core::panic!("Failed to convert str {name} to Endpoint")
269 })
270 }
271
272 pub fn person(
274 name: &str,
275 instance: EndpointInstance,
276 ) -> Result<Endpoint, InvalidEndpointError> {
277 Self::named(name, instance, EndpointType::Person)
278 }
279
280 pub fn institution(
282 name: &str,
283 instance: EndpointInstance,
284 ) -> Result<Endpoint, InvalidEndpointError> {
285 Self::named(name, instance, EndpointType::Institution)
286 }
287
288 fn from_string(name: &str) -> Result<Endpoint, InvalidEndpointError> {
290 let name = name.to_string();
291 if name
292 == format!("{}{}", Endpoint::PREFIX_ANONYMOUS, Endpoint::ALIAS_ANY)
293 {
294 return Ok(Endpoint::ANY);
295 } else if name
296 == format!(
297 "{}{}",
298 Endpoint::PREFIX_ANONYMOUS,
299 Endpoint::ALIAS_LOCAL
300 )
301 {
302 return Ok(Endpoint::LOCAL);
303 }
304
305 let mut name_part = name.clone();
306 let mut instance = EndpointInstance::Any;
307 if name.contains('/') {
309 let parts: Vec<&str> = name.split('/').collect();
310 if parts.len() != 2 {
311 return Err(InvalidEndpointError::InvalidCharacters);
312 }
313 name_part = parts[0].to_string();
314 let instance_str = parts[1];
315 if instance_str == "*" {
316 instance = EndpointInstance::All;
317 } else {
318 let instance_num = instance_str
319 .parse::<u16>()
320 .map_err(|_| InvalidEndpointError::InvalidInstance)?;
321 instance = EndpointInstance::new(instance_num);
322 }
323 }
324
325 match name_part {
326 s if s.starts_with(&format!(
327 "{}{}",
328 Endpoint::PREFIX_ANONYMOUS,
329 Endpoint::ALIAS_ANY
330 )) =>
331 {
332 Ok(Endpoint {
333 type_: EndpointType::Anonymous,
334 identifier: [255u8; 18],
335 instance,
336 })
337 }
338 s if s.starts_with(&format!(
339 "{}{}",
340 Endpoint::PREFIX_ANONYMOUS,
341 Endpoint::ALIAS_LOCAL
342 )) =>
343 {
344 Ok(Endpoint {
345 type_: EndpointType::Anonymous,
346 identifier: [0u8; 18],
347 instance,
348 })
349 }
350 s if s.starts_with(Endpoint::PREFIX_ANONYMOUS) => {
351 let s = s.trim_start_matches(Endpoint::PREFIX_ANONYMOUS);
352 if s.len() < 18 * 2 {
353 return Err(InvalidEndpointError::MinLengthNotMet);
354 } else if s.len() > 18 * 2 {
355 return Err(InvalidEndpointError::MaxLengthExceeded);
356 }
357 let bytes = decode(s)
358 .map_err(|_| InvalidEndpointError::InvalidCharacters)?;
359 let byte_slice: &[u8] = &bytes;
360 Endpoint::anonymous(byte_slice.try_into().unwrap(), instance)
361 }
362 s if s.starts_with(Endpoint::PREFIX_INSTITUTION) => {
363 Endpoint::named(&s[2..], instance, EndpointType::Institution)
364 }
365 s if s.starts_with(Endpoint::PREFIX_PERSON) => {
366 Endpoint::named(&s[1..], instance, EndpointType::Person)
367 }
368 _ => Err(InvalidEndpointError::InvalidCharacters),
369 }
370 }
371
372 pub fn from_slice(
374 binary: [u8; 21],
375 ) -> Result<Endpoint, EndpointParsingError> {
376 let mut reader = Cursor::new(binary);
377 let endpoint =
378 Endpoint::read(&mut reader).map_err(|_| EndpointParsingError)?;
379
380 if !Self::is_endpoint_valid(&endpoint) {
382 return Err(EndpointParsingError);
383 }
384 Ok(endpoint)
385 }
386
387 fn named(
388 name: &str,
389 instance: EndpointInstance,
390 type_: EndpointType,
391 ) -> Result<Endpoint, InvalidEndpointError> {
392 if !Self::is_instance_valid(&instance) {
394 return Err(InvalidEndpointError::InvalidInstance);
395 }
396
397 let name_bytes = Endpoint::name_to_bytes(name)?;
399
400 Ok(Endpoint {
401 type_,
402 identifier: name_bytes,
403 instance,
404 })
405 }
406
407 fn name_to_bytes(name: &str) -> Result<[u8; 18], InvalidEndpointError> {
408 let mut identifier = String::into_bytes(
409 name.to_string().trim_end_matches('\0').to_string(),
410 );
411 if identifier.len() > 18 {
413 return Err(InvalidEndpointError::MaxLengthExceeded);
414 }
415 if identifier.len() < 3 {
417 return Err(InvalidEndpointError::MinLengthNotMet);
418 }
419
420 identifier.resize(18, 0);
421
422 if !Self::are_name_chars_valid(identifier.clone().try_into().unwrap()) {
424 return Err(InvalidEndpointError::InvalidCharacters);
425 };
426
427 Ok(identifier.try_into().unwrap())
428 }
429
430 fn random_anonymous_id() -> [u8; 18] {
431 let buffer = random::random_bytes_slice();
432 if buffer.iter().any(|&b| b != 0) {
433 return buffer;
434 }
435 core::panic!("Could not generate random anonymous id");
437 }
438
439 fn are_name_chars_valid(name: [u8; 18]) -> bool {
440 let mut is_null = false;
441 for c in name.iter() {
442 if is_null && *c != 0x00 {
444 return false;
445 }
446 if *c == 0x00 {
447 is_null = true;
448 continue;
449 }
450 if !(*c >= 0x30 && *c <= 0x39 || *c >= 0x61 && *c <= 0x7A) && *c != 0x2D && *c != 0x5F
454 {
455 return false;
457 }
458 }
459 true
460 }
461
462 fn is_endpoint_valid(endpoint: &Endpoint) -> bool {
463 if !Self::is_instance_valid(&endpoint.instance) {
465 return false;
466 }
467
468 match endpoint.type_ {
469 EndpointType::Person | EndpointType::Institution => {
470 Self::are_name_chars_valid(endpoint.identifier)
472 }
473 _ => true,
474 }
475 }
476
477 fn is_instance_valid(endpoint_instance: &EndpointInstance) -> bool {
478 match endpoint_instance {
479 EndpointInstance::All => true,
480 EndpointInstance::Any => true,
481 EndpointInstance::Instance(instance) => {
482 *instance > 0 && *instance < 65535
484 }
485 }
486 }
487
488 pub fn to_slice(&self) -> [u8; 21] {
489 let mut writer = Cursor::new(Vec::new());
490 self.write(&mut writer).unwrap();
491 writer.into_inner().try_into().unwrap()
492 }
493
494 pub fn type_(&self) -> EndpointType {
496 self.type_
497 }
498
499 pub fn instance(&self) -> EndpointInstance {
501 self.instance
502 }
503
504 pub fn is_broadcast(&self) -> bool {
506 self.instance == EndpointInstance::All
507 }
508
509 pub fn is_local(&self) -> bool {
511 self == &Endpoint::LOCAL
512 }
513
514 pub fn is_any(&self) -> bool {
516 self == &Endpoint::ANY
517 }
518
519 pub fn is_any_instance(&self) -> bool {
521 self.instance == EndpointInstance::Any
522 }
523
524 pub fn any_instance_endpoint(&self) -> Endpoint {
526 Endpoint {
527 type_: self.type_,
528 identifier: self.identifier,
529 instance: EndpointInstance::Any,
530 }
531 }
532
533 pub fn broadcast(&self) -> Endpoint {
535 Endpoint {
536 type_: self.type_,
537 identifier: self.identifier,
538 instance: EndpointInstance::All,
539 }
540 }
541}
542
543impl Display for Endpoint {
544 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
545 match self.type_ {
546 EndpointType::Anonymous => {
547 if self.identifier == [255; 18] {
549 core::write!(
550 f,
551 "{}{}",
552 Endpoint::PREFIX_ANONYMOUS,
553 Endpoint::ALIAS_ANY
554 )?;
555 }
556 else if self.identifier == [0; 18] {
558 core::write!(
559 f,
560 "{}{}",
561 Endpoint::PREFIX_ANONYMOUS,
562 Endpoint::ALIAS_LOCAL
563 )?;
564 }
565 else {
567 core::write!(
568 f,
569 "{}{}",
570 Endpoint::PREFIX_ANONYMOUS,
571 buffer_to_hex(self.identifier.to_vec())
572 )?
573 }
574 }
575 EndpointType::Person => core::write!(
576 f,
577 "{}{}",
578 Endpoint::PREFIX_PERSON,
579 str::from_utf8(&self.identifier)
580 .unwrap()
581 .trim_end_matches('\0')
582 )?,
583 EndpointType::Institution => core::write!(
584 f,
585 "{}{}",
586 Endpoint::PREFIX_INSTITUTION,
587 str::from_utf8(&self.identifier)
588 .unwrap()
589 .trim_end_matches('\0')
590 )?,
591 };
592
593 match self.instance {
594 EndpointInstance::Any => (),
595 EndpointInstance::All => f.write_str("/*")?,
596 EndpointInstance::Instance(instance) => {
597 core::write!(f, "/{instance}")?
598 }
599 };
600
601 Ok(())
602 }
603}
604
605impl FromStr for Endpoint {
606 type Err = InvalidEndpointError;
607
608 fn from_str(name: &str) -> Result<Self, Self::Err> {
609 Endpoint::from_string(name)
610 }
611}
612
613impl Serialize for Endpoint {
614 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
615 where
616 S: serde::Serializer,
617 {
618 serializer
619 .serialize_newtype_struct("datex::endpoint", &self.to_string())
620 }
621}
622
623impl<'a> Deserialize<'a> for Endpoint {
624 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
625 where
626 D: serde::Deserializer<'a>,
627 {
628 let s: String = Deserialize::deserialize(deserializer)?;
629 Endpoint::from_string(&s).map_err(serde::de::Error::custom)
630 }
631}
632
633#[cfg(test)]
634mod tests {
635 use super::*;
636
637 #[test]
638 fn utilities() {
639 let endpoint: Endpoint = Endpoint::from_string("@ben/42").unwrap();
640 assert!(!endpoint.is_any_instance());
641 assert!(!endpoint.is_broadcast());
642
643 let main_endpoint = endpoint.any_instance_endpoint();
644 assert!(main_endpoint.is_any_instance());
645 assert_eq!(main_endpoint.to_string(), "@ben");
646 assert_eq!(main_endpoint.instance, EndpointInstance::Any);
647
648 let broadcast_endpoint = endpoint.broadcast();
649 assert!(broadcast_endpoint.is_broadcast());
650 assert_eq!(broadcast_endpoint.to_string(), "@ben/*");
651 }
652
653 #[test]
654 fn parse_from_string() {
655 let endpoint = Endpoint::from_string("@jonas").unwrap();
657 assert_eq!(endpoint.type_, EndpointType::Person);
658 assert_eq!(endpoint.instance, EndpointInstance::Any);
659 assert_eq!(endpoint.to_string(), "@jonas");
660 assert_eq!(
661 endpoint.identifier,
662 [
663 106, 111, 110, 97, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
664 ]
665 );
666
667 let endpoint = Endpoint::from_string("@+unyt").unwrap();
669 assert_eq!(endpoint.type_, EndpointType::Institution);
670 assert_eq!(endpoint.instance, EndpointInstance::Any);
671 assert_eq!(endpoint.to_string(), "@+unyt");
672
673 let endpoint = Endpoint::from_string(
675 &format!("@@{}", "A".repeat(18 * 2)).to_string(),
676 )
677 .unwrap();
678 assert_eq!(endpoint.type_, EndpointType::Anonymous);
679 assert_eq!(endpoint.instance, EndpointInstance::Any);
680 assert_eq!(endpoint.to_string(), format!("@@{}", "A".repeat(18 * 2)));
681
682 let valid_endpoint_names = vec![
683 "@jonas",
684 "@@any/*",
685 "@@local",
686 "@+unyt",
687 "@test/42",
688 "@test/*",
689 "@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
690 "@@BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
691 "@+institution/42",
692 "@+deno/9999",
693 "@+deno/65534",
694 ];
695 for name in valid_endpoint_names {
696 let endpoint = Endpoint::from_string(name).unwrap();
697 assert_eq!(endpoint.to_string(), name);
698 }
699 }
700
701 #[test]
702 fn too_long() {
703 let endpoint =
704 Endpoint::person("too-long-endpoint-name", EndpointInstance::Any);
705 assert_eq!(endpoint, Err(InvalidEndpointError::MaxLengthExceeded));
706
707 let endpoint = Endpoint::from_string("@too-long-endpoint-name");
708 assert_eq!(endpoint, Err(InvalidEndpointError::MaxLengthExceeded));
709
710 let to_long_endpoint_names = vec![
711 "@too-long-endpoint-name",
712 "@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA",
713 "@+too-long-endpoint-name",
714 "@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA/0001",
715 ];
716 for name in to_long_endpoint_names {
717 let endpoint = Endpoint::from_string(name);
718 assert_eq!(endpoint, Err(InvalidEndpointError::MaxLengthExceeded));
719 }
720 }
721
722 #[test]
723 fn too_short() {
724 let endpoint = Endpoint::person("ab", EndpointInstance::Any);
725 assert_eq!(endpoint, Err(InvalidEndpointError::MinLengthNotMet));
726
727 let endpoint =
728 Endpoint::person("ab\0\0\0\0\0\0\0\0", EndpointInstance::Any);
729 assert_eq!(endpoint, Err(InvalidEndpointError::MinLengthNotMet));
730
731 let endpoint = Endpoint::from_string("@ab");
732 assert_eq!(endpoint, Err(InvalidEndpointError::MinLengthNotMet));
733
734 let to_short_endpoint_names = vec![
735 "@ab",
736 "@@ff",
737 "@+ff",
738 "@@fffffff",
739 "@ab\0\0\0\0\0\0\0\0",
740 "@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA/0001",
741 ];
742 for name in to_short_endpoint_names {
743 let endpoint = Endpoint::from_string(name);
744 assert_eq!(endpoint, Err(InvalidEndpointError::MinLengthNotMet));
745 }
746 }
747
748 #[test]
749 fn invalid_characters() {
750 let endpoint = Endpoint::person("äüö", EndpointInstance::Any);
751 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
752
753 let endpoint = Endpoint::person("__O", EndpointInstance::Any);
754 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
755
756 let endpoint = Endpoint::person("#@!", EndpointInstance::Any);
757 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
758
759 let endpoint = Endpoint::person("\0__", EndpointInstance::Any);
760 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
761
762 let endpoint = Endpoint::from_string("@äüö");
763 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
764
765 let endpoint = Endpoint::from_string("@Jonas");
766 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
767
768 let endpoint =
769 Endpoint::from_string(&format!("@@{}X", "F".repeat(18 * 2 - 1)));
770 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
771
772 let invalid_endpoint_names = vec![
773 "@äüö",
774 "@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFX",
775 "@+äüö",
776 "@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFX/0001",
777 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFX/0001",
778 "test",
779 "@dff@",
780 "1",
781 "",
782 ];
783 for name in invalid_endpoint_names {
784 let endpoint = Endpoint::from_string(name);
785 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidCharacters));
786 }
787 }
788
789 #[test]
790 fn invalid_instance() {
791 let endpoint = Endpoint::person("test", EndpointInstance::Instance(0));
792 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidInstance));
793
794 let endpoint =
795 Endpoint::person("test", EndpointInstance::Instance(65535));
796 assert_eq!(endpoint, Err(InvalidEndpointError::InvalidInstance));
797 }
798
799 #[test]
800 fn special_instances() {
801 let endpoint = Endpoint::from_string("@+unyt/0");
802 assert_eq!(
803 endpoint,
804 Ok(Endpoint {
805 type_: EndpointType::Institution,
806 identifier: Endpoint::name_to_bytes("unyt").unwrap(),
807 instance: EndpointInstance::Any,
808 })
809 );
810
811 let endpoint = Endpoint::from_string("@+unyt/65535");
812 assert_eq!(
813 endpoint,
814 Ok(Endpoint {
815 type_: EndpointType::Institution,
816 identifier: Endpoint::name_to_bytes("unyt").unwrap(),
817 instance: EndpointInstance::All,
818 })
819 );
820
821 let endpoint = Endpoint::from_string("@+unyt/*");
822 assert_eq!(
823 endpoint,
824 Ok(Endpoint {
825 type_: EndpointType::Institution,
826 identifier: Endpoint::name_to_bytes("unyt").unwrap(),
827 instance: EndpointInstance::All,
828 })
829 );
830 }
831
832 #[test]
833 fn any_instance() {
834 let binary = [
836 EndpointType::Anonymous as u8,
837 0xFF,
838 0xFF,
839 0xFF,
840 0xFF,
841 0xFF,
842 0xFF,
843 0xFF,
844 0xFF,
845 0xFF,
846 0xFF,
847 0xFF,
848 0xFF,
849 0xFF,
850 0xFF,
851 0xFF,
852 0xFF,
853 0xFF,
854 0xFF,
855 0x00,
856 0x00,
857 ];
858 let endpoint = Endpoint::from_slice(binary);
859 assert_eq!(endpoint, Ok(Endpoint::ANY));
860 }
861
862 #[test]
863 fn special_endpoints() {
864 let endpoint = Endpoint::from_string("@@any").unwrap();
865 assert_eq!(endpoint.to_string(), "@@any");
866 assert_eq!(endpoint, Endpoint::ANY);
867
868 let endpoint = Endpoint::from_string("@@any/42").unwrap();
869 assert_eq!(endpoint.to_string(), "@@any/42");
870
871 let endpoint = Endpoint::from_string("@@local").unwrap();
872 assert_eq!(endpoint.to_string(), "@@local");
873 assert_eq!(endpoint, Endpoint::LOCAL);
874
875 let endpoint =
876 Endpoint::from_string("@@FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF")
877 .unwrap();
878 assert_eq!(endpoint, Endpoint::ANY);
879
880 let endpoint =
881 Endpoint::from_string("@@000000000000000000000000000000000000")
882 .unwrap();
883 assert_eq!(endpoint, Endpoint::LOCAL);
884 }
885
886 #[test]
887 fn format_named_endpoint() {
888 let endpoint = Endpoint::person("test", EndpointInstance::Any).unwrap();
889 assert_eq!(endpoint.to_string(), "@test");
890
891 let endpoint =
892 Endpoint::institution("test", EndpointInstance::Any).unwrap();
893 assert_eq!(endpoint.to_string(), "@+test");
894
895 let endpoint =
896 Endpoint::person("test", EndpointInstance::Instance(42)).unwrap();
897 assert_eq!(endpoint.to_string(), "@test/42");
898
899 let endpoint = Endpoint::person("test", EndpointInstance::All).unwrap();
900 assert_eq!(endpoint.to_string(), "@test/*");
901 }
902
903 #[test]
904 fn format_anonymous_endpoint() {
905 let endpoint =
906 Endpoint::anonymous([0xaa; 18], EndpointInstance::Any).unwrap();
907 assert_eq!(
908 endpoint.to_string(),
909 "@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
910 );
911
912 let endpoint =
913 Endpoint::from_string("@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/42")
914 .unwrap();
915 assert_eq!(
916 endpoint.to_string(),
917 "@@AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/42"
918 );
919 }
920}