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