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